diff --git a/.storybook/recipes/Sliders.stories.tsx b/.storybook/recipes/Sliders.stories.tsx new file mode 100644 index 000000000..b7f04ba47 --- /dev/null +++ b/.storybook/recipes/Sliders.stories.tsx @@ -0,0 +1,230 @@ +import type { StoryObj, Meta } from '@storybook/react'; +import React, { useState } from 'react'; +import { Slider, InputField, Text, Button, Label } from '../../src'; + +/** + * React is confused about what `render` is, preventing hooks: + * - `render` gets used in a hidden Story component, which follows the rules + */ +/* eslint-disable react-hooks/rules-of-hooks */ + +export default { + title: 'Recipes/Sliders', + component: Slider, + decorators: [ + (Story) => ( +
+ +
+ ), + ], + argTypes: { + fieldNote: { + type: 'string', + }, + }, +} as Meta; + +type Args = React.ComponentProps; + +const moodData = [ + { emoji: '😡', value: 0, description: 'Very Upset' }, + { emoji: '🙁', value: 25, description: 'Upset' }, + { emoji: '😐', value: 50, description: 'Neutral' }, + { emoji: '🙂', value: 75, description: 'Happy' }, + { emoji: '😍', value: 100, description: 'Very Happy' }, +]; + +export const UsingInputDisplay: StoryObj = { + parameters: { + axe: { + disabledRules: ['color-contrast'], // adding for disabled field example + }, + }, + render: ({ min = 0, max = 100, step = 1, value = 50, ...rest }) => { + const [sliderValue, setSliderValue] = useState(value); + + return ( +
+ {min} + setSliderValue(Number(target.value))} + value={sliderValue} + /> + {max} + +
+ ); + }, +}; + +export const UsingControlButtons: StoryObj = { + render: ({ min = 0, max = 100, step = 1, value = 50, ...rest }) => { + const [sliderValue, setSliderValue] = useState(value); + + return ( +
+ + {min} + setSliderValue(Number(target.value))} + value={sliderValue} + /> + {max} + +
+ ); + }, +}; + +export const WithHighlightedContent: StoryObj = { + render: ({ min = 0, max = 100, step = 25, value = 50, ...rest }) => { + const [sliderValue, setSliderValue] = useState(value); + + return ( +
+
+ mood.description)} + max={max} + min={min} + step={step} + {...rest} + onChange={({ target }) => setSliderValue(Number(target.value))} + value={sliderValue} + /> +
+ {moodData.map((mood) => { + return sliderValue === mood.value && <>{mood.emoji}; + })} +
+
+ + Current mood:{' '} + + {moodData.map((mood) => { + return sliderValue === mood.value && <>{mood.description}; + })} + + +
+ ); + }, +}; + +export const WithVisualLabel: StoryObj = { + render: ({ min = 0, max = 100, step = 25, value = 50, ...rest }) => { + const [sliderValue, setSliderValue] = useState(value); + + return ( +
+
+
+
+ ); + }, +}; + +export const WithMultipleVisualLabels: StoryObj = { + render: ({ min = 0, max = 100, step = 25, value = 50, ...rest }) => { + const [sliderValue, setSliderValue] = useState(value); + + return ( +
+
+
+
+ ); + }, +}; diff --git a/src/components/Slider/Slider.module.css b/src/components/Slider/Slider.module.css index 7e0919c32..57da5124a 100644 --- a/src/components/Slider/Slider.module.css +++ b/src/components/Slider/Slider.module.css @@ -143,7 +143,6 @@ */ .slider__markers { display: flex; - align-items: center; justify-content: space-between; /* Calculates offset of the markers to align with actual values */ @@ -166,3 +165,17 @@ .slider__marker--disabled { color: var(--eds-theme-color-text-disabled); } + +/** + * align the text of the last marker flush with the right edge + */ +.slider__marker:last-child { + text-align: right; +} + +/** + * Align all middle markers as centered on the mark point (if multi-line) + */ +.slider__marker:not(:first-child):not(:last-child) { + text-align: center; +} diff --git a/src/components/Slider/Slider.stories.tsx b/src/components/Slider/Slider.stories.tsx index 2dc41bc8f..610080cd4 100644 --- a/src/components/Slider/Slider.stories.tsx +++ b/src/components/Slider/Slider.stories.tsx @@ -19,6 +19,11 @@ export default { ), ], + argTypes: { + fieldNote: { + type: 'string', + }, + }, render: (args) => , } as Meta;