import { Title, Subtitle, Description, Stories } from '@storybook/addon-docs/blocks'; import { Meta, StoryObj } from '@storybook/react-webpack5'; import { MultiCurrency, Plus } from '@transferwise/icons'; import { lorem10, lorem5 } from '../../test-utils'; import List from '../../list'; import Link from '../../link'; import { ListItem, type ListItemProps } from '../ListItem'; import type { ListItemPromptProps } from '../Prompt'; import { disableControls, withoutKey } from './helpers'; import { SB_LIST_ITEM_TEXT as TEXT, SB_LIST_ITEM_ADDITIONAL_INFO as INFO, SB_LIST_ITEM_CONTROLS as CONTROLS, SB_LIST_ITEM_MEDIA as MEDIA, } from './subcomponents'; const hideControls = disableControls([ 'title', 'subtitle', 'valueTitle', 'valueSubtitle', 'media', 'additionalInfo', 'prompt', 'control', 'as', 'inverted', 'className', 'id', 'valueColumnWidth', ]); const renderPrompt = (sentiment: ListItemPromptProps['sentiment']) => ( This prompt contains an external link too. ); const disabledPromptMessage = ( <> This item is disabled because some reason has not been satisfied. ); /** * Please refer to the [design documentation](https://wise.design/components/list-item#accessibility:~:text=-,Disabled%20states,-) for more details */ export default { component: ListItem, title: 'Content/ListItem/Disabled State', tags: ['autodocs'], decorators: [withoutKey], argTypes: hideControls(), args: { disabled: true, subtitle: TEXT.subtitle, valueTitle: TEXT.valueTitle, valueSubtitle: TEXT.valueSubtitle, media: MEDIA.avatarFlag, }, parameters: { docs: { canvas: { sourceState: 'hidden', }, page: () => ( <> Disabled state ), }, }, } satisfies Meta; type Story = StoryObj; /** * When dealing with dynamic content, e.g. BFFs or general form submissions, it's a common practice * to stop the users from making changes to the inputs while the form is being processed. * * Because these form states are usually short-lived, and would take at most few seconds to * complete, we don't want to introduce any major content changes that could distract * the user or, even worse, cause layout shift. That's why, by default, toggling the `disabled` * prop will not show any additional visual indicators, apart from the luminosity and opacity * changes. */ export const ShortLived: Story = { name: 'Short-lived: fully interactive', args: { control: CONTROLS.button, }, argTypes: hideControls(['disabledPromptMessage']), render: function Render(args) { return ( ); }, }; /** * There's a difference between fully and partially interactive ListItems. While setting `disabled` * prop on the former applies the visual treatment to the whole item, on the latter ones it only * affects the control element (`ListItem.Button` or `ListItem.IconButton`). * * That way, the inline link or button in the additional info section remains interactive and * accessible. */ export const ShortLivedPartially: Story = { name: 'Short-lived: partially interactive', args: { additionalInfo: INFO.interactive, }, argTypes: hideControls(['spotlight', 'disabledPromptMessage']), render: function Render(args) { return ( ); }, }; /** * On the other hand, instances of ListItems that are disabled for longer periods of time and * remain in such state until some user interaction or event occur, can and should offer extended * affordances to the user, so they are aware of the current state of the item and the reason why * it's not available to them. * * To achieve that, we're introducing additional prop `disabledPromptMessage` – it's a * prompt content override only used when ListItem is in the disabled state, and it's expected to * offer reasoning for why given list item has been disabled. * * If set, it will render a variant of the `ListItem.Prompt` component with an icon and colour * scheme designed specifically for this scenario. * * Similarly to the short-lived scenario, fully interactive items will apply the visual treatment * to the whole item. Prompts will remain interactive. */ export const LongLived: Story = { name: 'Long-lived: fully interactive', args: { disabledPromptMessage, control: CONTROLS.button, }, argTypes: hideControls(['disabledPromptMessage']), render: function Render(args) { return ( ); }, }; /** * Similarly to the short-lived scenario, partially interactive items will only apply the visual * treatment to the control element, leaving the additional info section fully interactive and * accessible. * * While programmatically not required, you will be expected to use the `disabledPromptMessage` * prop to offer an explanation to the user on why the item was disabled. */ export const LongLivedPartially: Story = { name: 'Long-lived: partially interactive', args: { disabledPromptMessage, additionalInfo: INFO.interactive, }, argTypes: hideControls(['spotlight', 'disabledPromptMessage']), render: function Render(args) { return ( ); }, }; export const AllControls: Story = { parameters: { docs: { canvas: { sourceState: 'hidden', }, }, controls: { disable: true }, knobs: { disable: true }, }, render: (args) => ( } title="Navigation" subtitle={lorem5} control={ {}} />} /> } title="Button primary" subtitle={lorem5} control={Primary} /> } title="Button secondary neutral" subtitle={lorem5} control={Secondary} /> } title="Button secondary neutral" subtitle={lorem5} control={Secondary Neutral} /> } title="Button tertiary" subtitle={lorem5} control={Tertiary} /> } title="Icon Button" subtitle={lorem5} control={ } /> } title="Checkbox" subtitle={lorem5} control={} /> } title="Checkbox checked" subtitle={lorem5} control={} /> } title="Checkbox indeterminate" subtitle={lorem5} control={} /> } title="Switch on" subtitle={lorem5} control={ {}} />} /> } title="Switch off" subtitle={lorem5} control={ {}} />} /> } title="Switch off" subtitle={lorem5} control={} /> } title="Switch off" subtitle={lorem5} control={} /> } title="Non-interactive" subtitle={lorem10} additionalInfo={ Which has bold, strong, emphasis } /> ), };