From 0e37f2820ccd3e61cc0204529f34dfb3d50dff38 Mon Sep 17 00:00:00 2001 From: Pietro Date: Tue, 3 Sep 2024 13:31:44 +0200 Subject: [PATCH] chore: update slider component --- .../ui/src/1_atoms/Slider/Slider.module.css | 15 +- .../ui/src/1_atoms/Slider/Slider.stories.tsx | 144 ++++++++++++++++++ .../ui/src/1_atoms/Slider/Slider.test.tsx | 59 +++++++ packages/ui/src/1_atoms/Slider/Slider.tsx | 46 +++++- packages/ui/src/1_atoms/Slider/index.ts | 1 + packages/ui/src/1_atoms/index.tsx | 1 + 6 files changed, 253 insertions(+), 13 deletions(-) create mode 100644 packages/ui/src/1_atoms/Slider/Slider.stories.tsx create mode 100644 packages/ui/src/1_atoms/Slider/Slider.test.tsx create mode 100644 packages/ui/src/1_atoms/Slider/index.ts diff --git a/packages/ui/src/1_atoms/Slider/Slider.module.css b/packages/ui/src/1_atoms/Slider/Slider.module.css index 938ee38d4..3058fe151 100644 --- a/packages/ui/src/1_atoms/Slider/Slider.module.css +++ b/packages/ui/src/1_atoms/Slider/Slider.module.css @@ -1,15 +1,18 @@ .track { @apply h-1 bg-gray-50 rounded; +} - & .track-0 { - @apply h-1 bg-gray-50 rounded; - } - - &.track-1 { - @apply h-1 rounded bg-primary-30; +.isDouble { + & .track { + &:nth-child(1), &:nth-child(3) { + @apply bg-gray-50 !important; + } } } .thumb { @apply h-2.5 w-2.5 bg-sov-white rounded-full -top-[3px]; + &:focus { + @apply outline-none; + } } diff --git a/packages/ui/src/1_atoms/Slider/Slider.stories.tsx b/packages/ui/src/1_atoms/Slider/Slider.stories.tsx new file mode 100644 index 000000000..bed3e0b75 --- /dev/null +++ b/packages/ui/src/1_atoms/Slider/Slider.stories.tsx @@ -0,0 +1,144 @@ +import { Story, Meta } from '@storybook/react'; + +import React, { ComponentProps, useState } from 'react'; + +import { Slider } from './Slider'; + +export default { + title: 'Atoms/Slider', + component: Slider, +} as Meta; + +const Template: Story> = args => ( + +); + +const DoubleSliderTemplate: Story> = ({ + value: initialValue, + ...args +}) => { + const [value, setValue] = useState( + initialValue || [30, 70], + ); + + const handleChange = (newValue: number | number[], thumbIndex: number) => { + if (Array.isArray(newValue)) { + setValue(newValue); + } + if (args.onChange) args.onChange(newValue, thumbIndex); + }; + + const handleAfterChange = ( + newValue: number | number[], + thumbIndex: number, + ) => { + if (Array.isArray(newValue)) { + setValue(newValue); + } + if (args.onAfterChange) args.onAfterChange(newValue, thumbIndex); + }; + + return ( + + ); +}; + +export const Basic = Template.bind({}); +Basic.args = { + value: 50, + dataAttribute: 'slider-basic', +}; + +Basic.argTypes = { + value: { + control: 'number', + description: 'The value of the slider', + }, + min: { + control: 'number', + description: 'The minimum value of the slider', + }, + max: { + control: 'number', + description: 'The maximum value of the slider', + }, + step: { + control: 'number', + description: + 'Value to be added or subtracted on each step the slider makes. Must be greater than zero. max - min should be evenly divisible by the step value', + }, + onChange: { + action: 'onChange', + description: + 'Callback called on every value change. The function will be called with two arguments, the first being the new value(s) the second being thumb index', + }, + onAfterChange: { + action: 'onAfterChange', + description: + 'Callback called only after moving a thumb has ended. The callback will only be called if the action resulted in a change. The function will be called with two arguments, the first being the result value(s) the second being thumb index', + }, + disabled: { + control: 'boolean', + description: "If true the thumbs can't be moved", + }, + className: { + control: 'text', + description: 'The class name to apply to the Slider', + }, + thumbClassName: { + control: 'text', + description: 'The css class set on each thumb node', + }, + trackClassName: { + control: 'text', + description: + 'The css class set on the tracks between the thumbs. In addition track fragment will receive a numbered css class of the form {trackClassName}-{i}, e.g. track-0, track-1, ...', + }, + thumbActiveClassName: { + control: 'text', + description: 'The css class set on the thumb that is currently being moved', + }, + dataAttribute: { + control: 'text', + description: 'The data attribute to apply to the Slider', + }, + isDouble: { + control: 'boolean', + description: 'If true the slider will be a double slider', + }, +}; + +export const DoubleSlider = DoubleSliderTemplate.bind({}); +DoubleSlider.args = { + value: [20, 80], + step: 5, + thumbClassName: 'bg-primary', + trackClassName: 'bg-primary', + dataAttribute: 'slider-advanced', + isDouble: true, +}; + +DoubleSlider.argTypes = { + ...Basic.argTypes, +}; + +export const StyledSlider = DoubleSliderTemplate.bind({}); +StyledSlider.args = { + value: [20, 80], + step: 10, + className: 'w-96 m-auto', + thumbClassName: 'bg-success ring-2 ring-success', + thumbActiveClassName: 'outline-2 outline-sov-white', + trackClassName: 'bg-success', + dataAttribute: 'slider-styled', + isDouble: true, +}; + +StyledSlider.argTypes = { + ...Basic.argTypes, +}; diff --git a/packages/ui/src/1_atoms/Slider/Slider.test.tsx b/packages/ui/src/1_atoms/Slider/Slider.test.tsx new file mode 100644 index 000000000..019aa2487 --- /dev/null +++ b/packages/ui/src/1_atoms/Slider/Slider.test.tsx @@ -0,0 +1,59 @@ +import '@testing-library/jest-dom/extend-expect'; +import { render, screen } from '@testing-library/react'; + +import React from 'react'; + +import { Slider } from './Slider'; + +beforeAll(() => { + global.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} + }; +}); + +describe('Slider', () => { + it('renders the slider with default props', () => { + render(); + const slider = screen.getByRole('slider'); + expect(slider).toBeInTheDocument(); + }); + + it('applies the isDouble class when isDouble is true', () => { + const { container } = render(); + const slider = container.firstChild; + expect(slider).toHaveClass('isDouble'); + }); + + it('applies custom class names to thumbs and tracks', () => { + const { container } = render( + , + ); + + const thumb = container.querySelector('.thumb'); + const track = container.querySelector('.track'); + + expect(thumb).toHaveClass('custom-thumb'); + expect(track).toHaveClass('custom-track'); + }); + + it('renders correctly with isDouble and custom class names', () => { + const { container } = render( + , + ); + + const slider = container.firstChild; + expect(slider).toHaveClass('isDouble'); + + const thumb = container.querySelector('.thumb'); + const track = container.querySelector('.track'); + + expect(thumb).toHaveClass('custom-thumb'); + expect(track).toHaveClass('custom-track'); + }); +}); diff --git a/packages/ui/src/1_atoms/Slider/Slider.tsx b/packages/ui/src/1_atoms/Slider/Slider.tsx index 7dc2fde39..26af5f404 100644 --- a/packages/ui/src/1_atoms/Slider/Slider.tsx +++ b/packages/ui/src/1_atoms/Slider/Slider.tsx @@ -1,22 +1,54 @@ import React, { FC } from 'react'; +import classNames from 'classnames'; import ReactSlider from 'react-slider'; +import { applyDataAttr } from '../../utils'; import styles from './Slider.module.css'; type SliderProps = { max?: number; min?: number; step?: number; - onAfterChange?: (value: number, thumbIndex: number) => void; - onChange?: (value: number, thumbIndex: number) => void; - value?: number; + value?: number | number[]; + isDouble?: boolean; + onChange?: (value: number | number[], thumbIndex: number) => void; + disabled?: boolean; + className?: string; + dataAttribute?: string; + onAfterChange?: (value: number | number[], thumbIndex: number) => void; + thumbClassName?: string; + trackClassName?: string; + thumbActiveClassName?: string; }; -export const Slider: FC = props => ( +export const Slider: FC = ({ + max = 100, + min = 0, + step = 1, + value, + onChange, + isDouble = false, + disabled = false, + className, + dataAttribute, + onAfterChange, + thumbClassName, + trackClassName, + thumbActiveClassName, +}) => ( ); diff --git a/packages/ui/src/1_atoms/Slider/index.ts b/packages/ui/src/1_atoms/Slider/index.ts new file mode 100644 index 000000000..f48a85415 --- /dev/null +++ b/packages/ui/src/1_atoms/Slider/index.ts @@ -0,0 +1 @@ +export * from './Slider'; diff --git a/packages/ui/src/1_atoms/index.tsx b/packages/ui/src/1_atoms/index.tsx index b57e4555c..06ab25585 100644 --- a/packages/ui/src/1_atoms/index.tsx +++ b/packages/ui/src/1_atoms/index.tsx @@ -14,5 +14,6 @@ export * from './DynamicValue'; export * from './Lottie'; export * from './ErrorBadge'; export * from './Toggle'; +export * from './Slider'; export * from './Bar/Bar'; export * from './Slider/Slider';