Skip to content

Commit

Permalink
Feat(web-react): Introduce disabled state to Slider #DS-1325
Browse files Browse the repository at this point in the history
Also, drop the `isSelected` and `isRequired` props.
  • Loading branch information
adamkudrna committed Jul 2, 2024
1 parent 86ba5ff commit 2cf320f
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 75 deletions.
116 changes: 99 additions & 17 deletions packages/web-react/src/components/UNSTABLE_Slider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
> ⚠️ This component is UNSTABLE. It may significantly change at any point in the future.
> Please use it with caution.
Slider is a form control that allows users to select a value from a range of values.

## Basic usage

The Slider component implements the HTML [range input][mdn-range] element.

```jsx
import { useState } from 'react';
import { UNSTABLE_Slider } from '@lmc-eu/spirit-web-react/components';
Expand All @@ -18,23 +22,99 @@ const handleChange = (event) => {
<UNSTABLE_Slider id="slider" label="Slider" value={value} onChange={handleChange} />;
```

### Slider Steps and Value

You can specify the Slider steps and value range by setting the `min`, `max`, and `step` props.

```jsx
import { useState } from 'react';
import { UNSTABLE_Slider } from '@lmc-eu/spirit-web-react/components';

const [value, setValue] = useState();

const handleChange = (event) => {
setValue(Number(event.target.value));
};

<UNSTABLE_Slider id="slider" label="Slider" min={3} max={12} value={value} onChange={handleChange} />;
```

## Required

ℹ️ As per the [HTML specification][html-spec-range], the Slider component does not support the `required` state.

## Hidden Label

```jsx
<UNSTABLE_Slider id="slider" label="Slider" value={value} onChange={handleChange} isLabelHidden />
```

## Fluid Width

```jsx
<UNSTABLE_Slider id="slider" label="Slider" value={value} onChange={handleChange} isFluid />
```

## Helper Text

```jsx
<UNSTABLE_Slider id="slider" label="Slider" value={value} onChange={handleChange} helperText="Helper text" />
```

## Validation States

Validation states implement the Validation state [dictionary][dictionary-validation].

```jsx
<UNSTABLE_Slider
id="slider-success"
label="Slider"
value={value}
onChange={handleChange}
validationState="success"
validationText="Validation text"
/>
<UNSTABLE_Slider
id="slider-warning"
label="Slider"
value={value}
onChange={handleChange}
validationState="warning"
validationText="Validation text"
/>
<UNSTABLE_Slider
id="slider-danger"
label="Slider"
value={value}
onChange={handleChange}
validationState="danger"
validationText={['First validation text', 'Second validation text']}
/>
```

## Disabled State

```jsx
<UNSTABLE_Slider id="slider-disabled" label="Slider" value={value} onChange={handleChange} isDisabled />
```

### API

| Attribute | Type | Default | Required | Description |
| ---------------- | ------------------------ | ------- | -------- | --------------------------------------------------------- |
| `helperText` | `string` | - || Custom helper text |
| `id` | `string` | - || Input and label identification |
| `isFluid` | `bool` | `false` || Whether the element spans to the full width of its parent |
| `isLabelHidden` | `bool` | `false` | | Whether the label is hidden |
| `isRequired` | `bool` | `false` || Whether is the input required |
| `isSelected` | `bool` | `false` | | Whether is the input selected |
| `label` | `string` | - | | Label text |
| `max` | `number` | 100 || Max value of slider input |
| `min` | `number` | 0 | | Min value of slider input |
| `onChange` | `() => void` | - | | On input change callback |
| `step` | `number` | 1 || Sets the stepping interval |
| `validationText` | [`string` \| `string[]`] | - || Validation text |
| `value` | `number` | - || Input value |
| Attribute | Type | Default | Required | Description |
| ----------------- | ---------------------------------------------- | ------- | -------- | --------------------------------------------------------- |
| `helperText` | `string` | - || Custom helper text |
| `id` | `string` | - || Input and label identification |
| `isDisabled` | `bool` | `false` || Whether is the input disabled |
| `isFluid` | `bool` | `false` | | Whether the element spans to the full width of its parent |
| `isLabelHidden` | `bool` | `false` || Whether the label is hidden |
| `label` | `string` | - | | Label text |
| `max` | `number` | 100 | | Max value of slider input |
| `min` | `number` | 0 || Min value of slider input |
| `onChange` | `() => void` | - | | On input change callback |
| `step` | `number` | 1 || Sets the stepping interval |
| `validationState` | [Validation dictionary][dictionary-validation] | - || Type of validation state |
| `validationText` | [`string` \| `string[]`] | - || Validation text |
| `value` | `number` | - || Input value |

On top of the API options, the components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
Expand All @@ -56,10 +136,9 @@ import UNSTABLE_UncontrolledSlider from '../UNSTABLE_UncontrolledSlider';
| ---------------- | ------------ | ------- | -------- | --------------------------------------------------------- |
| `helperText` | `string` | - || Custom helper text |
| `id` | `string` | - || Input and label identification |
| `isDisabled` | `bool` | `false` || Whether is the input disabled |
| `isFluid` | `bool` | `false` || Whether the element spans to the full width of its parent |
| `isLabelHidden` | `bool` | `false` || Whether the label is hidden |
| `isRequired` | `bool` | `false` || Whether is the input required |
| `isSelected` | `bool` | `false` || Whether is the input selected |
| `label` | `string` | - || Label text |
| `max` | `number` | 100 || Max value of slider input |
| `min` | `number` | 0 || Min value of slider input |
Expand All @@ -72,6 +151,9 @@ On top of the API options, the components accept [additional attributes][readme-
If you need more control over the styling of a component, you can use [style props][readme-style-props]
and [escape hatches][readme-escape-hatches].

[mdn-range]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
[html-spec-range]: https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)
[readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes
[readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches
[readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props
[dictionary-validation]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#validation
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const _UnstableSlider = (props: SpiritSliderProps, ref: ForwardedRef<HTMLInputEl
'aria-describedby': ariaDescribedBy,
helperText,
id,
isDisabled,
label,
max,
min,
Expand All @@ -28,13 +29,13 @@ const _UnstableSlider = (props: SpiritSliderProps, ref: ForwardedRef<HTMLInputEl
...restProps
} = propsWithDefaults;

const { classProps, props: modifiedProps } = useSliderStyleProps({ ...restProps, validationState });
const { classProps, props: modifiedProps } = useSliderStyleProps({ ...restProps, isDisabled, validationState });
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);
const [ids, register] = useAriaIds(ariaDescribedBy);

const CSSVariable = '--slider-position';

const getSliderPosition = (num: number) => `${Math.round((100 * num) / 100)}%`;
const getSliderPosition = (num: number) => `${Math.round((100 * num) / max)}%`;

const handleInput = (event: FormEvent<HTMLInputElement>) => {
const { target } = event as ChangeEvent<HTMLInputElement>;
Expand All @@ -60,6 +61,7 @@ const _UnstableSlider = (props: SpiritSliderProps, ref: ForwardedRef<HTMLInputEl
max={max}
step={step}
value={value}
disabled={isDisabled}
/>
<HelperText
className={classProps.helperText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ describe('useSliderStyleProps', () => {
expect(result.current.classProps.input).toBe('UNSTABLE_Slider__input');
});

it('should return selected class', () => {
const { result } = renderHook(() => useSliderStyleProps({ ...defaultProps, isSelected: true }));
it('should return disabled class', () => {
const { result } = renderHook(() => useSliderStyleProps({ ...defaultProps, isDisabled: true }));

expect(result.current.classProps.root).toContain('UNSTABLE_Slider--selected');
expect(result.current.classProps.root).toContain('UNSTABLE_Slider--disabled');
});

it('should return fluid class', () => {
Expand All @@ -40,12 +40,6 @@ describe('useSliderStyleProps', () => {
expect(result.current.classProps.label).toContain('UNSTABLE_Slider__label--hidden');
});

it('should return required label class', () => {
const { result } = renderHook(() => useSliderStyleProps({ ...defaultProps, isRequired: true }));

expect(result.current.classProps.label).toContain('UNSTABLE_Slider__label--required');
});

it('should return helper text class', () => {
const { result } = renderHook(() => useSliderStyleProps({ ...defaultProps, helperText: 'Helper text' }));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const DEMO_SLIDER_DEFAULT_VALUE = 30;
export const DEMO_SLIDER_STEPS_VALUE = 3;

export const SLIDER_DEFAULT_PROPS = {
max: 100, // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#specifying_the_minimum_and_maximum
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import React, { ChangeEvent, useState } from 'react';
import { DEMO_SLIDER_DEFAULT_VALUE } from '../constants';
import { DEMO_SLIDER_DEFAULT_VALUE, DEMO_SLIDER_STEPS_VALUE } from '../constants';
import UNSTABLE_Slider from '../UNSTABLE_Slider';

const SliderDefault = () => {
const [value, setValue] = useState(DEMO_SLIDER_DEFAULT_VALUE);
const [defaultValue, setDefaultValue] = useState(DEMO_SLIDER_DEFAULT_VALUE);
const [stepsValue, setStepsValue] = useState(DEMO_SLIDER_STEPS_VALUE);

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue(Number(event.target.value));
const handleDefaultChange = (event: ChangeEvent<HTMLInputElement>) => {
setDefaultValue(Number(event.target.value));
};

return <UNSTABLE_Slider id="slider-default" label="Slider" value={value} onChange={handleChange} />;
const handleStepsChange = (event: ChangeEvent<HTMLInputElement>) => {
setStepsValue(Number(event.target.value));
};

return (
<>
<UNSTABLE_Slider id="slider-default" label="Slider" value={defaultValue} onChange={handleDefaultChange} />
<UNSTABLE_Slider
id="slider-steps"
label="Custom steps"
max={10}
value={stepsValue}
onChange={handleStepsChange}
/>
</>
);
};

export default SliderDefault;
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import React, { ChangeEvent, useState } from 'react';
import { DEMO_SLIDER_DEFAULT_VALUE } from '../constants';
import UNSTABLE_Slider from '../UNSTABLE_Slider';

const SliderRequired = () => {
const SliderDisabled = () => {
const [value, setValue] = useState(DEMO_SLIDER_DEFAULT_VALUE);

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue(Number(event.target.value));
};

return <UNSTABLE_Slider id="slider-required" label="Slider" value={value} onChange={handleChange} isRequired />;
return <UNSTABLE_Slider id="slider-required" label="Slider" value={value} onChange={handleChange} isDisabled />;
};

export default SliderRequired;
export default SliderDisabled;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const SliderValidation = () => {
const [valueSuccess, setValueSuccess] = useState(DEMO_SLIDER_DEFAULT_VALUE);
const [valueWarning, setValueWarning] = useState(DEMO_SLIDER_DEFAULT_VALUE);
const [valueDanger, setValueDanger] = useState(DEMO_SLIDER_DEFAULT_VALUE);
const [valueDangerWithHelperText, setValueDangerWithHelperText] = useState(DEMO_SLIDER_DEFAULT_VALUE);

const handleChangeSuccess = (event: ChangeEvent<HTMLInputElement>) => {
setValueSuccess(Number(event.target.value));
Expand All @@ -17,6 +18,10 @@ const SliderValidation = () => {
setValueDanger(Number(event.target.value));
};

const handleChangeDangerWithHelperText = (event: ChangeEvent<HTMLInputElement>) => {
setValueDangerWithHelperText(Number(event.target.value));
};

return (
<>
<UNSTABLE_Slider
Expand All @@ -42,6 +47,15 @@ const SliderValidation = () => {
validationState="danger"
validationText={['First validation text', 'Second validation text']}
/>
<UNSTABLE_Slider
id="slider-danger-with-helper-text"
label="Slider"
value={valueDangerWithHelperText}
onChange={handleChangeDangerWithHelperText}
helperText="Helper text"
validationState="danger"
validationText="Validation text"
/>
</>
);
};
Expand Down
12 changes: 4 additions & 8 deletions packages/web-react/src/components/UNSTABLE_Slider/demo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import DocsSection from '../../../../docs/DocsSections';
import SliderDefault from './SliderDefault';
import SliderDisabled from './SliderDisabled';
import SliderFluid from './SliderFluid';
import SliderHelperText from './SliderHelperText';
import SliderHiddenLabel from './SliderHiddenLabel';
import SliderRequired from './SliderRequired';
import SliderSelected from './SliderSelected';
import SliderValidation from './SliderValidation';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<DocsSection title="Default" stackAlignment="stretch">
<SliderDefault />
</DocsSection>
<DocsSection title="Selected" stackAlignment="stretch">
<SliderSelected />
</DocsSection>
<DocsSection title="Required" stackAlignment="stretch">
<SliderRequired />
</DocsSection>
<DocsSection title="Hidden Label" stackAlignment="stretch">
<SliderHiddenLabel />
</DocsSection>
<DocsSection title="Helper Text" stackAlignment="stretch">
<SliderHelperText />
</DocsSection>
<DocsSection title="Disabled" stackAlignment="stretch">
<SliderDisabled />
</DocsSection>
<DocsSection title="Validation State with Validation Text" stackAlignment="stretch">
<SliderValidation />
</DocsSection>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,9 @@ const meta: Meta<typeof UNSTABLE_Slider> = {
defaultValue: { summary: 'false' },
},
},
isRequired: {
isDisabled: {
control: 'boolean',
description: 'Whether the slider is required',
table: {
defaultValue: { summary: 'false' },
},
},
isSelected: {
control: 'boolean',
description: 'Whether the slider is selected',
description: 'Whether the slider is disabled',
table: {
defaultValue: { summary: 'false' },
},
Expand Down Expand Up @@ -115,8 +108,7 @@ const meta: Meta<typeof UNSTABLE_Slider> = {
id: 'slider',
isFluid: false,
isLabelHidden: false,
isRequired: false,
isSelected: false,
isDisabled: false,
label: 'Label',
max: SLIDER_DEFAULT_PROPS.max,
min: SLIDER_DEFAULT_PROPS.min,
Expand Down
Loading

0 comments on commit 2cf320f

Please sign in to comment.