import { Meta, StoryObj } from '@storybook/react-webpack5'; import { fn } from 'storybook/test'; import { Freeze, ArrowRight } from '@transferwise/icons'; import List from '../../list'; import { ListItem } from '../ListItem'; import { SB_LIST_ITEM_ADDITIONAL_INFO as INFO, SB_LIST_ITEM_MEDIA as MEDIA, } from '../_stories/subcomponents'; import { disableControls } from '../_stories/helpers'; import type { ListItemButtonProps } from './ListItemButton'; const hideControls = disableControls([ 'onClick', 'children', 'className', 'addonStart', 'addonEnd', ]); /** * Convenience controls for previewing rich markup, * not otherwise possible via Storybook */ type PreviewStoryArgs = ListItemButtonProps & { previewAddonStart: boolean | ListItemButtonProps['addonStart']; previewAddonEnd: boolean | ListItemButtonProps['addonEnd']; }; const previewArgTypes = { previewAddonStart: { control: 'boolean', name: 'Preview with `addonStart`', mapping: { true: { type: 'icon', value: }, }, description: '**NB:** ListItem.Button only supports icon addons.', table: { category: 'Preview options', type: { summary: undefined, }, }, }, previewAddonEnd: { control: 'boolean', name: 'Preview with `addonEnd`', mapping: { true: { type: 'icon', value: }, }, description: '**NB:** ListItem.Button only supports icon addons.', table: { category: 'Preview options', type: { summary: undefined, }, }, }, } as const; const getPropsForPreview = (args: PreviewStoryArgs) => { const { previewAddonStart, previewAddonEnd, ...props } = args as { previewAddonStart: ListItemButtonProps['addonStart']; previewAddonEnd: ListItemButtonProps['addonEnd']; [key: string]: any; }; return [ props, { addonStart: previewAddonStart, addonEnd: previewAddonEnd, }, ] as const; }; const meta: Meta = { component: ListItem.Button, title: 'Content/ListItem/ListItem.Button', args: { partiallyInteractive: false, priority: 'secondary-neutral', sentiment: undefined, loading: undefined, href: undefined, target: undefined, as: undefined, addonStart: undefined, addonEnd: undefined, className: undefined, children: 'Click me', onClick: fn(), }, argTypes: { children: { table: { type: { summary: 'ReactNode' }, }, }, loading: { control: 'boolean', description: 'Toggles the loading state', }, priority: { control: 'radio', options: ['unset (undefined)', 'primary', 'secondary', 'secondary-neutral', 'tertiary'], mapping: { 'unset (undefined)': undefined, }, description: 'Sets a visual hierarchy amongst the buttons displayed on the screen', }, sentiment: { options: ['unset (undefined)', 'default', 'negative'], mapping: { 'unset (undefined)': undefined, }, description: 'Sets a visual hierarchy amongst the buttons displayed on the screen.
**NB:** `negative` variant only affects the `primary` and `secondary` priorities.', }, href: { control: 'text', description: 'If set, the component will render as an HTML anchor.', }, target: { control: 'select', options: [undefined, '_blank', '_self', '_parent', '_top'], description: 'The `target` attribute for HTML anchor. If set to `_blank`, `rel="noopener noreferrer"` is automatically added to the rendered node.', }, addonStart: { description: 'Icon accessory to be displayed before the label', }, addonEnd: { description: 'Icon accessory to be displayed after the label', }, onClick: { table: { type: { summary: '(event: MouseEvent) => void' }, }, }, }, } satisfies Meta; export default meta; type Story = StoryObj; export const Playground: StoryObj = { render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( } additionalInfo={INFO.nonInteractive} /> ); }, args: { previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...previewArgTypes, }, }; /** * By default, ListItem is fully interactive, which means its whole surface is clickable * and triggers the control. To remain WCAG-compliant there can be no nested interactive * elements inside the item.
* * To work around this limitation, this control also allows for a partially interactive mode, * which can be enabled via `partiallyInteractive` prop. Once set, only the control will * remain interactive, which allows for interactive element to be attached to the * `ListItem.AdditionalInfo`. This allows for more complex layouts while still providing a * clear, accessible action point for users.
* * If you still require a fully interactive ListItem, you can alternatively use `ListItem.Prompt` * which allows for a single instance of a link or an inline button. * * Refer to the [design documentation](https://wise.design/components/list-item#interaction) for details. */ export const Interactivity: StoryObj = { args: { children: 'Action', previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...hideControls(['partiallyInteractive']), ...previewArgTypes, }, render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( } /> } /> ); }, }; /** * If `href` prop is set, the component will be automatically rendered as an HTML anchor element. */ export const AsAnchor: StoryObj = { args: { children: 'Visit link', href: 'https://wise.com', target: '_blank', previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...hideControls(['partiallyInteractive']), ...previewArgTypes, }, render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( } /> ); }, }; /** * There are two different types of button – default and negative – designed to emphasise the nature of the action.
* **NB:** Sentiment only applies to `primary` and `secondary` priorities.
* [Design documentation](https://wise.design/components/button#types) */ export const Sentiment: StoryObj = { args: { children: 'Button text', previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...hideControls(['sentiment', 'priority']), ...previewArgTypes, }, render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( Default Sentiment } /> Negative Sentiment } /> Default Sentiment } /> Negative Sentiment } /> ); }, }; /** * Priorities set a visual hierarchy amongst the buttons displayed on the * screen to help more important buttons to take precedence over others.
* [Design documentation](https://wise.design/components/button#priorities) */ export const Priority: StoryObj = { args: { children: 'Button text', previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...hideControls(['priority']), ...previewArgTypes, }, render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( Primary Priority } /> Secondary Priority } /> Secondary Neutral Priority } /> Tertiary Priority } /> ); }, }; /** * Button in loading state. */ export const Loading: StoryObj = { args: { loading: true, previewAddonStart: false, previewAddonEnd: false, }, argTypes: { ...hideControls(['loading']), ...previewArgTypes, }, render: (args: PreviewStoryArgs) => { const [props, previewProps] = getPropsForPreview(args); return ( Loading Button } additionalInfo={INFO.nonInteractive} /> ); }, }; const addonProps = { addonStart: { type: 'icon', value: }, addonEnd: { type: 'icon', value: }, } as const; /** * Icons can be displayed before and after the button label.
* **NB:** ListItem.Button only supports icon addons. */ export const WithIcons: Story = { args: { children: 'Button text', }, argTypes: hideControls(), render: (args: ListItemButtonProps) => { return ( With start icon } /> With end icon } /> With both icons } /> ); }, };