-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat(web-react): Introduce UNSTABLE_Toggle component #DS-1346
- Loading branch information
Showing
22 changed files
with
624 additions
and
0 deletions.
There are no files selected for viewing
107 changes: 107 additions & 0 deletions
107
packages/web-react/src/components/UNSTABLE_Toggle/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# UNSTABLE Toggle | ||
|
||
> ⚠️ This component is UNSTABLE. It may significantly change at any point in the future. | ||
> Please use it with caution. | ||
Toggle is a form control that allows users to switch between two states. | ||
|
||
## Basic Usage | ||
|
||
The Toggle component implements the HTML [checkbox input][mdn-checkbox] element. It uses | ||
the native input element and styles it to look like a toggle switch. | ||
|
||
```jsx | ||
import { UNSTABLE_Toggle } from '@lmc-eu/spirit-web-react/components'; | ||
|
||
<UNSTABLE_Toggle id="toggle-default" label="Toggle Label" />; | ||
``` | ||
|
||
## Indicators | ||
|
||
If you need to indicate the state of the toggle, you can add the `hasIndicators` prop. This will add a visual indicators to the toggle switch. | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-indicators" label="Toggle Label" hasIndicators /> | ||
``` | ||
|
||
## Required | ||
|
||
Add the `isRequired` prop to mark it as required. | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-required" label="Toggle Label" isRequired /> | ||
``` | ||
|
||
## Hidden Label | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-hidden-label" label="Toggle Label" isLabelHidden /> | ||
``` | ||
|
||
## Fluid | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-fluid" label="Toggle Label" isFluid /> | ||
``` | ||
|
||
## Helper Text | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-helper-text" label="Toggle Label" helperText="Helper text" /> | ||
``` | ||
|
||
## Validation States | ||
|
||
Validation states can be presented either by prop `validationState`. See Validation state [dictionary][dictionary-validation]. | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-success" label="Toggle Label" validationState="success" /> | ||
<UNSTABLE_Toggle | ||
id="toggle-warning" | ||
label="Toggle Label" | ||
validationText="Validation text" | ||
validationState="warning" | ||
isChecked | ||
/> | ||
<UNSTABLE_Toggle | ||
id="toggle-danger" | ||
label="Toggle Label" | ||
validationText={['First validation text', 'Second validation text']} | ||
validationState="danger" | ||
/> | ||
``` | ||
|
||
## Disabled State | ||
|
||
On top of adding the `isDisabled` props to disable Toggle. | ||
|
||
```jsx | ||
<UNSTABLE_Toggle id="toggle-disabled" label="Toggle Label" isDisabled /> | ||
``` | ||
|
||
## API | ||
|
||
| Name | Type | Default | Required | Description | | ||
| ----------------- | ---------------------------------------------- | ------- | -------- | ------------------------------ | | ||
| `id` | string | - | ✓ | Input and label identification | | ||
| `isDisabled` | boolean | `false` | ✕ | Whether is toggle disabled | | ||
| `isChecked` | boolean | `false` | ✕ | Whether is toggle checked | | ||
| `isLabelHidden` | boolean | `false` | ✕ | Whether is label hidden | | ||
| `isFluid` | boolean | `false` | ✕ | Whether is toggle fluid | | ||
| `hasIndicators` | boolean | `false` | ✕ | Whether has visual indicators | | ||
| `label` | string | - | ✕ | Label text | | ||
| `helperText` | string | - | ✕ | Helper text | | ||
| `ref` | `ForwardedRef<HTMLInputElement>` | - | ✕ | Input element reference | | ||
| `onChange` | (event: ChangeEvent<HTMLInputElement>) => void | - | ✕ | Change event handler | | ||
| `validationState` | [Validation dictionary][dictionary-validation] | - | ✕ | Type of validation state | | ||
| `validationText` | `string` \| `string[]` | - | ✕ | Validation text | | ||
|
||
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] | ||
and [escape hatches][readme-escape-hatches]. | ||
|
||
[dictionary-validation]: https://github.com/lmc-eu/spirit-design-system/blob/main/docs/DICTIONARIES.md#validation | ||
[mdn-checkbox]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox | ||
[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 |
81 changes: 81 additions & 0 deletions
81
packages/web-react/src/components/UNSTABLE_Toggle/UNSTABLE_Toggle.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import classNames from 'classnames'; | ||
import React, { ForwardedRef, forwardRef } from 'react'; | ||
import { useStyleProps } from '../../hooks'; | ||
import { SpiritToggleProps } from '../../types'; | ||
import { HelperText, useAriaIds, ValidationText } from '../Field'; | ||
import { useToggleStyleProps } from './useToggleStyleProps'; | ||
|
||
/* We need an exception for components exported with forwardRef */ | ||
/* eslint no-underscore-dangle: ['error', { allow: ['_UNSTABLE_Toggle'] }] */ | ||
/* eslint-disable-next-line camelcase */ | ||
const _UNSTABLE_Toggle = (props: SpiritToggleProps, ref: ForwardedRef<HTMLInputElement>) => { | ||
const { classProps, props: modifiedProps } = useToggleStyleProps(props); | ||
const { | ||
'aria-describedby': ariaDescribedBy = '', | ||
id, | ||
isDisabled, | ||
isChecked, | ||
isRequired, | ||
label, | ||
helperText, | ||
onChange = () => {}, | ||
validationState, | ||
validationText, | ||
...restProps | ||
} = modifiedProps; | ||
|
||
const { styleProps, props: otherProps } = useStyleProps(restProps); | ||
|
||
const [ids, register] = useAriaIds(ariaDescribedBy); | ||
|
||
return ( | ||
<label {...styleProps} htmlFor={id} className={classNames(classProps.root, styleProps.className)}> | ||
<span className={classProps.text}> | ||
<span className={classProps.label}>{label}</span> | ||
<HelperText | ||
className={classProps.helperText} | ||
elementType="span" | ||
id={`${id}__helperText`} | ||
registerAria={register} | ||
helperText={helperText} | ||
/> | ||
{validationState && Array.isArray(validationText) && ( | ||
<ul className={classProps.validationText} id={`${id}-validation-text`}> | ||
{validationText.map((text, index) => ( | ||
<ValidationText | ||
key={`${index + 1}-${id}__validationText`} | ||
validationText={text} | ||
registerAria={register} | ||
elementType="li" | ||
/> | ||
))} | ||
</ul> | ||
)} | ||
{validationState && !Array.isArray(validationText) && ( | ||
<ValidationText | ||
className={classProps.validationText} | ||
id={`${id}__validationText`} | ||
validationText={validationText} | ||
registerAria={register} | ||
/> | ||
)} | ||
</span> | ||
<input | ||
{...otherProps} | ||
aria-describedby={ids.join(' ')} | ||
type="checkbox" | ||
id={id} | ||
className={classProps.input} | ||
disabled={isDisabled} | ||
checked={isChecked} | ||
required={isRequired} | ||
onChange={onChange} | ||
ref={ref} | ||
/> | ||
</label> | ||
); | ||
}; | ||
|
||
export const UNSTABLE_Toggle = forwardRef<HTMLInputElement, SpiritToggleProps>(_UNSTABLE_Toggle); | ||
|
||
export default UNSTABLE_Toggle; |
92 changes: 92 additions & 0 deletions
92
packages/web-react/src/components/UNSTABLE_Toggle/__tests__/UNSTABLE_Toggle.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import '@testing-library/jest-dom'; | ||
import { render, screen } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest'; | ||
import { validationStatePropsTest } from '../../../../tests/providerTests/dictionaryPropsTest'; | ||
import { requiredPropsTest } from '../../../../tests/providerTests/requiredPropsTest'; | ||
import { restPropsTest } from '../../../../tests/providerTests/restPropsTest'; | ||
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest'; | ||
import UNSTABLE_Toggle from '../UNSTABLE_Toggle'; | ||
|
||
describe('UNSTABLE_Toggle', () => { | ||
classNamePrefixProviderTest(UNSTABLE_Toggle, 'UNSTABLE_Toggle'); | ||
|
||
stylePropsTest(UNSTABLE_Toggle); | ||
|
||
restPropsTest(UNSTABLE_Toggle, 'input'); | ||
|
||
validationStatePropsTest(UNSTABLE_Toggle, 'UNSTABLE_Toggle--'); | ||
|
||
requiredPropsTest(UNSTABLE_Toggle, 'checkbox', 'id', 'example-id'); | ||
|
||
it('should have correct className', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" />); | ||
|
||
expect(screen.getByRole('checkbox').parentElement).toHaveClass('UNSTABLE_Toggle'); | ||
}); | ||
|
||
it('should have label classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" />); | ||
|
||
expect(screen.getByText('Toggle Label')).toHaveClass('UNSTABLE_Toggle__label'); | ||
expect(screen.getByText('Toggle Label')).toContainHTML('label'); | ||
}); | ||
|
||
it('should have label with required classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" isRequired />); | ||
|
||
expect(screen.getByText('Toggle Label')).toHaveClass('UNSTABLE_Toggle__label'); | ||
expect(screen.getByText('Toggle Label')).toHaveClass('UNSTABLE_Toggle__label--required'); | ||
}); | ||
|
||
it('should have hidden classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" isLabelHidden />); | ||
|
||
const label = screen.getByText('Toggle Label'); | ||
|
||
expect(label).toHaveClass('UNSTABLE_Toggle__label'); | ||
expect(label).toHaveClass('UNSTABLE_Toggle__label--hidden'); | ||
}); | ||
|
||
it('should have input classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" />); | ||
|
||
expect(screen.getByRole('checkbox')).toHaveClass('UNSTABLE_Toggle__input'); | ||
}); | ||
|
||
it('should have helper text with correct classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" helperText="Helper Text" />); | ||
|
||
const helperText = screen.getByText('Helper Text'); | ||
|
||
expect(helperText).toBeInTheDocument(); | ||
expect(helperText).toHaveClass('UNSTABLE_Toggle__helperText'); | ||
}); | ||
|
||
it('should have correct attribute when checked', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" isChecked />); | ||
|
||
expect(screen.getByRole('checkbox')).toBeChecked(); | ||
}); | ||
|
||
it('should have correct attribute when disabled', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" isDisabled />); | ||
|
||
expect(screen.getByRole('checkbox')).toBeDisabled(); | ||
}); | ||
|
||
it('should have correct classname if fluid', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" isFluid />); | ||
|
||
expect(screen.getByRole('checkbox').parentElement).toHaveClass('UNSTABLE_Toggle UNSTABLE_Toggle--fluid'); | ||
}); | ||
|
||
it('should have indicators classname', () => { | ||
render(<UNSTABLE_Toggle id="test-toggle" label="Toggle Label" hasIndicators />); | ||
|
||
const checkbox = screen.getByRole('checkbox'); | ||
|
||
expect(checkbox).toHaveClass('UNSTABLE_Toggle__input'); | ||
expect(checkbox).toHaveClass('UNSTABLE_Toggle__input--indicators'); | ||
}); | ||
}); |
62 changes: 62 additions & 0 deletions
62
packages/web-react/src/components/UNSTABLE_Toggle/__tests__/useToggleStyleProps.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import { ValidationStates } from '../../../constants'; | ||
import { SpiritToggleProps } from '../../../types'; | ||
import { useToggleStyleProps } from '../useToggleStyleProps'; | ||
|
||
describe('useToggleStyleProps', () => { | ||
it('should return defaults', () => { | ||
const props = { id: 'toggle', label: 'text' }; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps).toEqual({ | ||
root: 'UNSTABLE_Toggle', | ||
text: 'UNSTABLE_Toggle__text', | ||
input: 'UNSTABLE_Toggle__input', | ||
label: 'UNSTABLE_Toggle__label', | ||
helperText: 'UNSTABLE_Toggle__helperText', | ||
validationText: 'UNSTABLE_Toggle__validationText', | ||
}); | ||
}); | ||
|
||
it('should return hidden label', () => { | ||
const props = { id: 'toggle', label: 'text', isLabelHidden: true } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.label).toBe('UNSTABLE_Toggle__label UNSTABLE_Toggle__label--hidden'); | ||
}); | ||
|
||
it('should return disabled', () => { | ||
const props = { id: 'toggle', label: 'text', isDisabled: true } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.root).toBe('UNSTABLE_Toggle UNSTABLE_Toggle--disabled'); | ||
}); | ||
|
||
it.each([Object.values(ValidationStates)])('should return field with %s', (state) => { | ||
const props = { validationState: state } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.root).toBe(`UNSTABLE_Toggle UNSTABLE_Toggle--${state}`); | ||
}); | ||
|
||
it('should return fluid', () => { | ||
const props = { id: 'toggle', label: 'text', isFluid: true } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.root).toBe('UNSTABLE_Toggle UNSTABLE_Toggle--fluid'); | ||
}); | ||
|
||
it('should return required', () => { | ||
const props = { id: 'toggle', label: 'text', isRequired: true } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.label).toBe('UNSTABLE_Toggle__label UNSTABLE_Toggle__label--required'); | ||
}); | ||
|
||
it('should return input with indicators', () => { | ||
const props = { id: 'toggle', label: 'text', hasIndicators: true } as SpiritToggleProps; | ||
const { result } = renderHook(() => useToggleStyleProps(props)); | ||
|
||
expect(result.current.classProps.input).toBe('UNSTABLE_Toggle__input UNSTABLE_Toggle__input--indicators'); | ||
}); | ||
}); |
11 changes: 11 additions & 0 deletions
11
packages/web-react/src/components/UNSTABLE_Toggle/demo/ToggleDefault.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import { UNSTABLE_Toggle } from '../index'; | ||
|
||
const ToggleDefault = () => ( | ||
<> | ||
<UNSTABLE_Toggle id="toggle-default" label="Toggle Label" /> | ||
<UNSTABLE_Toggle id="toggle-default-checked" label="Toggle Label" isChecked /> | ||
</> | ||
); | ||
|
||
export default ToggleDefault; |
12 changes: 12 additions & 0 deletions
12
packages/web-react/src/components/UNSTABLE_Toggle/demo/ToggleDisabled.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import React from 'react'; | ||
import { UNSTABLE_Toggle } from '../index'; | ||
|
||
const ToggleDisabled = () => ( | ||
<> | ||
<UNSTABLE_Toggle id="toggle-disabled" label="Toggle Label" isDisabled /> | ||
<UNSTABLE_Toggle id="toggle-disabled-checked-disabled" label="Toggle Label" isDisabled isChecked /> | ||
<UNSTABLE_Toggle id="toggle-helper-text-disabled" label="Toggle Label" helperText="Helper text" isDisabled /> | ||
</> | ||
); | ||
|
||
export default ToggleDisabled; |
6 changes: 6 additions & 0 deletions
6
packages/web-react/src/components/UNSTABLE_Toggle/demo/ToggleFluid.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import React from 'react'; | ||
import { UNSTABLE_Toggle } from '../index'; | ||
|
||
const ToggleFluid = () => <UNSTABLE_Toggle id="toggle-fluid" label="Toggle Label" isFluid />; | ||
|
||
export default ToggleFluid; |
11 changes: 11 additions & 0 deletions
11
packages/web-react/src/components/UNSTABLE_Toggle/demo/ToggleHelperText.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import { UNSTABLE_Toggle } from '../index'; | ||
|
||
const ToggleHelperText = () => ( | ||
<> | ||
<UNSTABLE_Toggle id="toggle-helper-text" label="Toggle Label" helperText="Helper text" /> | ||
<UNSTABLE_Toggle id="toggle-helper-text-checked" label="Toggle Label" helperText="Helper text" isChecked /> | ||
</> | ||
); | ||
|
||
export default ToggleHelperText; |
6 changes: 6 additions & 0 deletions
6
packages/web-react/src/components/UNSTABLE_Toggle/demo/ToggleHiddenLabel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import React from 'react'; | ||
import { UNSTABLE_Toggle } from '../index'; | ||
|
||
const ToggleHiddenLabel = () => <UNSTABLE_Toggle id="toggle-hidden-label" label="Toggle Label" isLabelHidden />; | ||
|
||
export default ToggleHiddenLabel; |
Oops, something went wrong.