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
}
/>
);
},
};