Skip to content

Commit

Permalink
♻️ Slider story to Typescript (#806)
Browse files Browse the repository at this point in the history
* ♻️ JSX to TSX

* ♻️ Slider story Typescript

* 🎨 Added a decorator for margins

* 🚧 Trying to fix lint errors

* 🚧 Some lint warnings fixed, a few left

* 🚚 Rename functions and props

* 🚧 Still trying to solve ts errors

Issues with getFormattedText function

* 🔥 Removed useless fix

toString() on type any not allowed

* 🔥 Removed comment

* 🚨 Finally fixed ts error

Children was explicitly typed in the subcomponents with number | string

* 🔥 removed unused

* ♻️ Fixed import in test

* 🐛 Fixed a bug where onChange mismatch on HTMLAttributes

Also changed children type back to ReactNode

* 🔥 Removed unused type

Had to replace this type with explicit type because the type was not read elsewhere

* 🔥 Removed another unused type

* ♻️ Changed all event types to FormEvent<HTMLDivElement>

But kept Omit onChange because of the weird error posted in the PR

* 🐛 onChange expected a function

Fixes TS event type error

* 🔥 Removed Omit from type import

Useless after fix

* 🔥 Went back to Omit solution for the onChange type mismatch
  • Loading branch information
pomfrida authored and vnys committed Nov 13, 2020
1 parent ec0be28 commit b590c03
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useState } from 'react'
import { Slider } from '@equinor/eds-core-react'
import React, { useState, FormEvent } from 'react'
import { Slider, SliderProps } from '@equinor/eds-core-react'
import { Story, Meta } from '@storybook/react'

import styled from 'styled-components'

const Body = styled.div`
display: grid;
grid-template-rows: min-content;
padding: 32px;
grid-gap: 4rem;
position: relative;
`
Expand All @@ -27,14 +27,28 @@ const getUnixTime = (iso) => {
export default {
title: 'Components/Slider',
component: Slider,
decorators: [
(Story) => (
<div style={{ margin: '3rem' }}>
<Story />
</div>
),
],
} as Meta

export const Default: Story<SliderProps> = (args) => {
return <Slider {...args} />
}

export const Examples = () => {
export const Examples: Story<SliderProps> = () => {
const [value, updateValue] = useState([30, 70])
const [valueTwo, updateValueTwo] = useState([0, 500])
const [valueTwoCommited, updateValueTwoCommited] = useState([0, 500])
const onChange = (event, value) => {
updateValue(value)
const changeHandler = (
event: FormEvent<HTMLDivElement>,
value: number[] | number,
) => {
updateValue(value as number[])
}

return (
Expand All @@ -43,7 +57,7 @@ export const Examples = () => {
<span id="range-slider-label">Range slider</span>
<Slider
value={value}
onChange={onChange}
onChange={changeHandler}
ariaLabelledby="range-slider-label"
/>
<p style={{ marginTop: '1.5rem' }}>
Expand All @@ -56,14 +70,17 @@ export const Examples = () => {
</span>
<Slider
value={valueTwo}
onChange={(event, value) => {
updateValueTwo(value)
onChange={(
event: FormEvent<HTMLDivElement>,
value: number[] | number,
) => {
updateValueTwo(value as number[])
}}
min={0}
max={500}
ariaLabelledby="range-slider-label-with-mouseevent"
onChangeCommitted={(event, value) => {
updateValueTwoCommited(value)
updateValueTwoCommited(value as number[])
}}
/>
<p style={{ marginTop: '1.5rem' }}>
Expand Down
4 changes: 2 additions & 2 deletions libraries/core-react/src/Slider/MinMax.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ const StyledMinMax = styled.span`
}
`

type Props = {
type MinMaxProps = {
/** Children is required */
children: ReactNode
} & JSX.IntrinsicElements['span']

export const MinMax = forwardRef<HTMLSpanElement, Props>(function EdsMinMax(
export const MinMax = forwardRef<HTMLSpanElement, MinMaxProps>(function MinMax(
{ children },
ref,
) {
Expand Down
27 changes: 14 additions & 13 deletions libraries/core-react/src/Slider/Output.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { forwardRef } from 'react'
import React, { forwardRef, ReactNode } from 'react'
import styled from 'styled-components'
import { typographyTemplate } from '../_common/templates'
import { slider as tokens } from './Slider.tokens'

const { enabled } = tokens

type StyledProps = Pick<Props, 'value'>
type StyledProps = Pick<OutputProps, 'value'>

const StyledOutput = styled.output<StyledProps>`
--val: ${({ value }) => value};
Expand All @@ -27,20 +27,21 @@ const StyledOutput = styled.output<StyledProps>`
grid-column: 1 / -1;
`

type Props = {
type OutputProps = {
/** Value */
value: number
/** HtmlFor */
htmlFor: string
/** Children are required */
children: ReactNode
} & JSX.IntrinsicElements['output']

export const Output = forwardRef<HTMLOutputElement, Props>(function EdsMinMax(
{ children, value, htmlFor },
ref,
) {
return (
<StyledOutput ref={ref} value={value} htmlFor={htmlFor}>
{children}
</StyledOutput>
)
})
export const Output = forwardRef<HTMLOutputElement, OutputProps>(
function Output({ children, value, htmlFor }, ref) {
return (
<StyledOutput ref={ref} value={value} htmlFor={htmlFor}>
{children}
</StyledOutput>
)
},
)
7 changes: 5 additions & 2 deletions libraries/core-react/src/Slider/Slider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import React from 'react'
import { render, cleanup, fireEvent, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import { Slider } from './Slider'
import type { Props } from './Slider'
import type { SliderProps } from './Slider'

afterEach(cleanup)

const getUnixTime = (iso) => {
return new Date(iso).getTime()
}

const DateSlider = ({ value, ariaLabelledby = 'date-range-slider' }: Props) => {
const DateSlider = ({
value,
ariaLabelledby = 'date-range-slider',
}: SliderProps) => {
function outputFunction(val) {
const date = new Date(parseInt(val, 10))
// The test node server doesn't have full i18n capabilities, using english is the easiest
Expand Down
70 changes: 41 additions & 29 deletions libraries/core-react/src/Slider/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import React, { forwardRef, useState, useRef } from 'react'
import React, {
forwardRef,
useState,
useRef,
HTMLAttributes,
MouseEvent,
KeyboardEvent,
FormEvent,
} from 'react'
import styled, { css } from 'styled-components'
import { slider as tokens } from './Slider.tokens'
import { MinMax } from './MinMax'
Expand Down Expand Up @@ -41,7 +49,7 @@ const wrapperGrid = css`
type RangeWrapperProps = {
valA: number
valB: number
} & Pick<Props, 'min' | 'max' | 'disabled'>
} & Pick<SliderProps, 'min' | 'max' | 'disabled'>

const RangeWrapper = styled.div<RangeWrapperProps>`
--a: ${({ valA }) => valA};
Expand Down Expand Up @@ -85,7 +93,7 @@ const RangeWrapper = styled.div<RangeWrapperProps>`
}
`

type WrapperProps = Pick<Props, 'min' | 'max' | 'disabled' | 'value'>
type WrapperProps = Pick<SliderProps, 'min' | 'max' | 'disabled' | 'value'>

const Wrapper = styled.div<WrapperProps>`
--min: ${({ min }) => min};
Expand Down Expand Up @@ -152,25 +160,24 @@ const SrOnlyLabel = styled.label`
white-space: nowrap;
border-width: 0;
`
type SliderValueType = number[] | number

export type Props = {
export type SliderProps = {
/** Id for the elements that labels this slider */
ariaLabelledby: string
/** Components value, range of numbers */
value: SliderValueType
value: number[] | number
/** Function to be called when value change */
onChange?: (
event: MouseEvent | KeyboardEvent,
newValue: SliderValueType,
event: FormEvent<HTMLDivElement>,
newValue: number[] | number,
) => void
/** Function to be called when value is committed by mouseup event */
onChangeCommitted?: (
event: MouseEvent | KeyboardEvent,
newValue: SliderValueType,
newValue: number[] | number,
) => void
/** Function for formatting the output, e.g. with dates */
outputFunction?: (text: string) => void
outputFunction?: (text: number) => string | number
/** Max value */
max?: number
/** Min value */
Expand All @@ -183,9 +190,9 @@ export type Props = {
minMaxValues?: boolean
/** Disabled */
disabled?: boolean
} & JSX.IntrinsicElements['div']
} & Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>

export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
export const Slider = forwardRef<HTMLDivElement, SliderProps>(function Slider(
{
min = 0,
max = 100,
Expand All @@ -208,8 +215,12 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
: useState([value])
const minRange = useRef<HTMLInputElement>(null)
const maxRange = useRef<HTMLInputElement>(null)
const onValueChange = (event, valueArrIdx?: number) => {
const changedValue = parseInt(event.target.value, 10)
const onValueChange = (
event: FormEvent<HTMLDivElement>,
valueArrIdx?: number,
) => {
const target = event.target as HTMLInputElement
const changedValue = parseInt(target.value, 10)
if (isRangeSlider) {
const newValue = (sliderValue as number[]).slice()
newValue[valueArrIdx] = changedValue
Expand All @@ -227,27 +238,28 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
onChange(event, [changedValue])
}
}
const handleKeyUp = (event) => {
const handleKeyUp = (event: KeyboardEvent) => {
if (event.keyCode === 37 || event.keyCode === 39) {
handleCommitedValue(event)
}
}

const handleCommitedValue = (event) => {
const handleCommitedValue = (event: MouseEvent | KeyboardEvent) => {
if (onChangeCommitted) {
onChangeCommitted(event, sliderValue as number[])
}
}

const getFormattedText = (text) => {
const getFormattedText = (text: number) => {
return outputFunction ? outputFunction(text) : text
}

const findClosestRange = (event) => {
if (event.target.type === 'output') {
const findClosestRange = (event: MouseEvent) => {
const target = event.target as HTMLOutputElement | HTMLInputElement
if (target.type === 'output') {
return
}
const bounds = event.target.getBoundingClientRect()
const bounds = target.getBoundingClientRect()
const x = event.clientX - bounds.left
const inputWidth = minRange.current.offsetWidth
const minValue = minRange.current.value
Expand Down Expand Up @@ -279,8 +291,8 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
ref={ref}
role="group"
aria-labelledby={ariaLabelledby}
valA={sliderValue[0]}
valB={sliderValue[1]}
valA={sliderValue[0] as number}
valB={sliderValue[1] as number}
max={max}
min={min}
disabled={disabled}
Expand All @@ -291,7 +303,7 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
<SliderInput
type="range"
ref={minRange}
value={sliderValue[0]}
value={sliderValue[0] as number}
max={max}
min={min}
id={inputIdA}
Expand All @@ -303,14 +315,14 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
onKeyUp={(event) => handleKeyUp(event)}
disabled={disabled}
/>
<Output htmlFor={inputIdA} value={sliderValue[0]}>
<Output htmlFor={inputIdA} value={sliderValue[0] as number}>
{getFormattedText(sliderValue[0])}
</Output>
{minMaxValues && <MinMax>{getFormattedText(min)}</MinMax>}
<SrOnlyLabel htmlFor={inputIdB}>Value B</SrOnlyLabel>
<SliderInput
type="range"
value={sliderValue[1]}
value={sliderValue[1] as number}
min={min}
max={max}
id={inputIdB}
Expand All @@ -323,7 +335,7 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
onKeyUp={(event) => handleKeyUp(event)}
disabled={disabled}
/>
<Output htmlFor={inputIdB} value={sliderValue[1]}>
<Output htmlFor={inputIdB} value={sliderValue[1] as number}>
{getFormattedText(sliderValue[1])}
</Output>
{minMaxValues && <MinMax>{getFormattedText(max)}</MinMax>}
Expand All @@ -334,12 +346,12 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
ref={ref}
max={max}
min={min}
value={sliderValue[0]}
value={sliderValue[0] as number}
disabled={disabled}
>
<SliderInput
type="range"
value={sliderValue[0]}
value={sliderValue[0] as number}
min={min}
max={max}
step={step}
Expand All @@ -352,7 +364,7 @@ export const Slider = forwardRef<HTMLDivElement, Props>(function EdsSlider(
onMouseUp={(event) => handleCommitedValue(event)}
onKeyUp={(event) => handleKeyUp(event)}
/>
<Output htmlFor={inputId} value={sliderValue[0]}>
<Output htmlFor={inputId} value={sliderValue[0] as number}>
{getFormattedText(sliderValue[0])}
</Output>
{/* Need an element for pseudo elems :/ */}
Expand Down
Loading

0 comments on commit b590c03

Please sign in to comment.