Skip to content

Commit

Permalink
Merge pull request #2125 from habx/feature/APP-24834
Browse files Browse the repository at this point in the history
APP-24834: update `Slider` component
  • Loading branch information
habx-auto-merge[bot] authored Nov 24, 2021
2 parents b88fd25 + 91fe005 commit 0bc1b9b
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 89 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
"pre-commit": "lint-staged && npm run test"
}
},
"lint-staged": {
Expand Down
4 changes: 4 additions & 0 deletions src/Slider/Slider.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ export interface SliderInnerProps
step?: number
range?: boolean
dots?: boolean
/** @default 'tag' */
dotType?: 'regular' | 'tag'
reversed?: boolean
indicators?: (Omit<Indicator, 'color' | 'position'> & { color?: string })[]

tooltipFormatter?: SliderTooltipFormatter
/** @deprecated Use `tooltipFormatter` instead. */
tooltipSuffix?: string

value: Value
Expand Down
21 changes: 11 additions & 10 deletions src/Slider/Slider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,30 @@ describe('Slider component', () => {
expect(bar).toHaveStyle('right: 50%')
})

it('should put the current value as tooltip if no tooltipSuffix and tooltipFormatter given', () => {
it('should put the current value as tooltip if no tooltipFormatter given', () => {
const { getByTestId } = render(<Slider value={50} />)

expect(getByTestId('slider-tooltip').textContent).toEqual('50')
expect(getByTestId('slider-tag').textContent).toEqual('50')
})

it('should add the tooltipSuffix to the value', () => {
const { getByTestId } = render(<Slider value={50} tooltipSuffix="m²" />)
it('should take into account the tooltipFormatter given', () => {
const { getByTestId } = render(
<Slider value={50} tooltipFormatter={(value) => `${value} m²`} />
)

expect(getByTestId('slider-tooltip').textContent).toEqual('50m²')
expect(getByTestId('slider-tag').textContent).toEqual('50 m²')
})

it('should position the tooltip under the dot', () => {
const { getByTestId } = render(<Slider value={50} />)
it('should position the tooltip under the dot when dotType is regular', () => {
const { getByTestId } = render(<Slider dotType="regular" value={50} />)

expect(getByTestId('slider-tooltip')).toHaveStyle('padding-left: 50%')
})

it('should update the tooltip position when the dot is programmatically moved', () => {
it('should update the tooltip position when the dot is programmatically moved when dotType is regular', () => {
const { queryByTestId, rerender } = render(<Slider value={50} />)

rerender(<Slider value={0} />)

rerender(<Slider dotType="regular" value={0} />)
expect(queryByTestId('slider-tooltip')).toHaveStyle('padding-left: 0%')
})
})
Expand Down
20 changes: 17 additions & 3 deletions src/Slider/Slider.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { withGrid } from '../_storybook/withGrid'

import { Slider, SliderProps } from './index'

type Props = Omit<SliderProps, 'onChange'>
type Props = Omit<SliderProps, 'onChange' | 'dotType'>

const SliderWithState: React.FunctionComponent<Props> = ({
value: initialValue,
Expand Down Expand Up @@ -35,16 +35,27 @@ const GRID_LINES = [
title: 'Regular',
props: {
value: 40,
dotType: 'regular',
},
},
{
title: 'With tag',
props: {
value: 40,
dotType: 'tag',
},
},
{
title: 'No value',
props: {},
props: {
dotType: 'regular',
},
},
{
title: 'No value range',
props: {
range: true,
dotType: 'regular',
},
},
{
Expand All @@ -67,6 +78,7 @@ const GRID_LINES = [
value: [0, 30] as [number, number],
indicators: [{ range: [70, 100] as [number, number] }],
range: true,
dotType: 'regular',
},
},
{
Expand All @@ -82,6 +94,7 @@ const GRID_LINES = [
props: {
value: 2,
customValues: ['T1', 'T2', 'T3', 'T4', 'T5', 'T6'],
range: true,
},
},
{
Expand All @@ -105,9 +118,10 @@ const GRID_ITEMS = [
},
},
{
label: 'Fixed tooltip',
label: 'Fixed tooltip (regular dot only)',
props: {
shouldTooltipFollowDot: false,
dotType: 'regular',
},
},
]
Expand Down
24 changes: 16 additions & 8 deletions src/Slider/Slider.style.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import styled from 'styled-components'

import { TagContainer } from '../Tag/Tag.style'
import { theme } from '../theme'

import { SliderBarContainer } from './SliderBar/SliderBar.style'

export const SliderContainer = styled.div`
position: relative;
margin: 0 4px;
padding-top: 32px;
padding: 16px 0;
&[data-dotType='regular'] {
padding-bottom: 8px;
padding-top: 32px;
}
`

export const SliderTooltips = styled.div`
Expand All @@ -28,7 +33,6 @@ export const SliderTooltips = styled.div`

export const SliderContent = styled.div`
position: relative;
padding: 8px 0;
cursor: pointer;
& ${SliderBarContainer}[data-main='true'] {
Expand All @@ -39,25 +43,29 @@ export const SliderContent = styled.div`
opacity: 0.7;
pointer-events: none;
filter: grayscale();
${TagContainer} {
color: ${theme.neutralColor(400)};
background-color: ${theme.color('secondary', { variation: 'calmer' })};
box-shadow: unset;
}
}
`

export const SliderMainBar = styled.div`
background-color: ${theme.neutralColor(300)};
position: absolute;
width: 100%;
height: 4px;
height: 3px;
border-radius: 2px;
`

export const SliderBackgroundDot = styled.div`
position: absolute;
margin-left: -4px;
margin-top: -2px;
z-index: 3;
z-index: 5;
cursor: grab;
width: 8px;
height: 8px;
width: 4px;
height: 4px;
background-color: ${theme.neutralColor(500)};
box-shadow: ${theme.shadow('lower')};
touch-action: pan-x;
Expand All @@ -71,7 +79,7 @@ export const SliderIndicator = styled.div`
dynamic: true,
valuePropName: 'color',
})};
height: 4px;
height: 3px;
border-radius: 8px;
z-index: 4;
`
138 changes: 78 additions & 60 deletions src/Slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
range = false,
onChange = () => {},
tooltipFormatter,
tooltipSuffix = '',
customValues,
indicators: rawIndicators = [],
min = 0,
max: rawMax = 100,
step: rawStep = 5,
dots: rawDots,
value: rawValue,
reversed = false,
dotType = 'tag',
...rest
} = props

Expand Down Expand Up @@ -71,6 +72,48 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
[max, min]
)

const tooltips = React.useMemo<Tooltip[]>(() => {
const buildTooltip = (tooltipValue: number): Tooltip => {
const label = customValues ? customValues[tooltipValue] : tooltipValue
const raw = `${isNil(label) ? '' : label}`

const content = isFunction(tooltipFormatter)
? tooltipFormatter(tooltipValue, raw)
: raw

return {
content,
position: getPositionFromValue(tooltipValue),
}
}

if (range && Array.isArray(localValue)) {
const rangeLocalValue =
isNil(localValue[0]) || isNil(localValue[1])
? [min, max]
: (localValue as [number, number])

return [
buildTooltip(rangeLocalValue[0]),
buildTooltip(rangeLocalValue[1]),
]
}

return [
buildTooltip(
!localValue || isNil(localValue) ? min : (localValue as number)
),
]
}, [
customValues,
getPositionFromValue,
localValue,
range,
tooltipFormatter,
min,
max,
])

const indicators = React.useMemo<Indicator[]>(
() =>
rawIndicators.map((indicator) => {
Expand Down Expand Up @@ -172,6 +215,8 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
onRest={() => handleChange(localValueRef.current)}
innerColor={matchingIndicator ? matchingIndicator.color : undefined}
large={rangeIndex > 0}
dotType={dotType}
tooltip={tooltips[rangeIndex]}
/>
)
}
Expand Down Expand Up @@ -249,10 +294,15 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
})
}

return getComponent({
from: min,
to: (hasValue ? localValue : min) as number,
})
return reversed
? getComponent({
from: (hasValue ? localValue : min) as number,
to: max,
})
: getComponent({
from: min,
to: (hasValue ? localValue : min) as number,
})
}, [
customValues,
getPositionFromValue,
Expand Down Expand Up @@ -288,47 +338,13 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
[indicators]
)

const tooltips = React.useMemo<Tooltip[]>(() => {
const buildTooltip = (tooltipValue: number): Tooltip => {
const label = customValues ? customValues[tooltipValue] : tooltipValue
const raw = `${isNil(label) ? '' : label}${tooltipSuffix}`

const content = isFunction(tooltipFormatter)
? tooltipFormatter(tooltipValue, raw)
: raw

return {
content,
position: getPositionFromValue(tooltipValue),
}
}

if (range) {
const rangeLocalValue = localValue as [number, number]

return [
buildTooltip(rangeLocalValue[0]),
buildTooltip(rangeLocalValue[1]),
]
}

return [buildTooltip(localValue as number)]
}, [
customValues,
getPositionFromValue,
localValue,
range,
tooltipFormatter,
tooltipSuffix,
])

const possibleValues = Array.from(
{ length: (max - min) / step + 1 },
(_, i) => (min + i) * step
)

return (
<SliderContainer>
<SliderContainer data-dotType={dotType}>
<SliderContent
{...rest}
data-disabled={disabled}
Expand All @@ -348,26 +364,28 @@ const InnerSlider = React.forwardRef<HTMLDivElement, SliderInnerProps>(
/>
))}
</SliderContent>
<SliderTooltips data-fixed={!shouldTooltipFollowDot}>
{tooltips.map((tooltip, index) => (
<Text
key={index}
data-testid="slider-tooltip"
style={
shouldTooltipFollowDot
? {
paddingLeft: `${tooltip.position}%`,
top: 0,
position: 'absolute',
}
: undefined
}
variation="title"
>
{tooltip.content}
</Text>
))}
</SliderTooltips>
{dotType === 'regular' && (
<SliderTooltips data-fixed={!shouldTooltipFollowDot}>
{tooltips.map((tooltip, index) => (
<Text
key={index}
data-testid="slider-tooltip"
style={
shouldTooltipFollowDot
? {
paddingLeft: `${tooltip.position}%`,
top: 0,
position: 'absolute',
}
: undefined
}
variation="title"
>
{tooltip.content}
</Text>
))}
</SliderTooltips>
)}
</SliderContainer>
)
}
Expand Down
Loading

0 comments on commit 0bc1b9b

Please sign in to comment.