import { Meta, StoryObj } from '@storybook/react-webpack5'; import { fn } from 'storybook/test'; import { Freeze, ArrowRight, ChevronRight } from '@transferwise/icons'; import { Flag } from '@wise/art'; import { lorem10, lorem20 } from '../../test-utils'; import SentimentSurface from '../../sentimentSurface'; import type { ButtonProps } from '../Button.types'; import Button from '../Button.resolver'; const withContainer = (Story: any) => (
); /** * Used for showing multiple components within a Canvas. * @decorator */ const withComponentGrid = (maxWidth = 'auto') => (Story: any) => (
); /** * Not all stories need access to all controls as it causes unnecessary UI noise. */ const hideControls = (args: string[]) => { const hidden = [ 'addonStart', 'addonEnd', 'onClick', 'onBlur', 'onFocus', 'onKeyDown', 'onMouseEnter', 'onMouseLeave', ...args, ]; return Object.fromEntries(hidden.map((item) => [item, { table: { disable: true } }])); }; /** * Convenience controls for previewing rich markup, * not otherwise possible via Storybook */ type PreviewStoryArgs = Parameters[0] & { previewAddonStart: boolean | ButtonProps['addonStart']; previewAddonEnd: boolean | ButtonProps['addonEnd']; }; const previewArgTypes = { previewAddonStart: { control: 'select', options: [ 'undefined', 'icon', 'avatar: flag', 'avatar: initials', 'avatar: icon', 'avatar: image', 'avatar: double', ], name: 'Preview with `addonStart`', mapping: { undefined, icon: { type: 'icon', value: , }, 'avatar: flag': { type: 'avatar', value: [{ asset: }], }, 'avatar: initials': { type: 'avatar', value: [{ profileName: 'Jay Jay' }], }, 'avatar: icon': { type: 'avatar', value: [{ asset: }], }, 'avatar: image': { type: 'avatar', value: [{ imgSrc: '../avatar-square-dude.webp' }], }, 'avatar: double': { type: 'avatar', value: [{ asset: }, { imgSrc: '../avatar-square-dude.webp' }], }, }, table: { category: 'Preview options', type: { summary: undefined, }, }, description: '**NB:** The `lg` button does not support any addons. `md` button accepts either icons or avatars, while `sm` is restricted to icons only.', }, previewAddonEnd: { control: 'boolean', name: 'Preview with `addonEnd`', mapping: { true: { type: 'icon', value: }, }, description: '**NB:** The `lg` button does not support any addons. `md` and `sm` allow for the use of icons only.', table: { category: 'Preview options', type: { summary: undefined, }, }, }, } as const; const getPropsForPreview = (args: PreviewStoryArgs) => { const { previewAddonStart, previewAddonEnd, ...props } = args as { previewAddonStart: ButtonProps['addonStart']; previewAddonEnd: ButtonProps['addonEnd']; props: typeof Button; }; return [ props, { addonStart: previewAddonStart, addonEnd: previewAddonEnd, }, ]; }; /** * The stories below document the new Button API, which requires `v2` prop to function.
For more details please refer to the [release notes](https://transferwise.atlassian.net/wiki/spaces/DS/pages/3542158737/Button+Updates+New+sizes+Loader+and+Cue) and the [design spec](https://wise.design/components/button).
* You can still find the old Button documentation under [Legacy Button](?path=/docs/actions-button-legacy--docs) * * */ const meta: Meta = { component: Button, title: 'Actions/Button', argTypes: { v2: { table: { readonly: true, }, }, size: { type: { name: 'enum', value: ['lg', 'md', 'sm'], }, table: { type: { summary: 'ButtonSize', }, }, }, priority: { type: { name: 'enum', value: ['primary', 'secondary', 'secondary-neutral', 'tertiary'], }, table: { type: { summary: 'ButtonPriority', }, defaultValue: { summary: '"primary"' }, }, }, sentiment: { type: { name: 'enum', value: ['default', 'negative'], }, table: { type: { summary: 'ButtonSentiment', }, }, }, disabled: { table: { defaultValue: { summary: 'false' }, }, }, loading: { table: { defaultValue: { summary: 'false' }, }, }, block: { table: { defaultValue: { summary: 'false' }, }, }, href: { type: { name: 'string', }, }, target: { type: { name: 'string', }, table: { type: { summary: 'string', }, }, }, addonStart: { control: 'object', }, addonEnd: { control: 'object', }, type: { control: 'text', table: { type: { summary: 'string', }, }, }, htmlType: { table: { disable: true, }, }, children: { table: { type: { summary: 'ReactNode', }, }, }, id: { table: { category: 'Common', }, }, className: { table: { category: 'Common', }, }, onClick: { table: { category: 'Common', }, }, }, args: { v2: true, size: undefined, priority: undefined, sentiment: undefined, disabled: false, loading: false, block: false, href: undefined, target: undefined, as: undefined, type: undefined, addonStart: undefined, addonEnd: undefined, className: undefined, children: 'Button text', onClick: fn(), }, decorators: [withContainer], }; export default meta; type Story = StoryObj; export const Playground: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ); }, argTypes: { ...hideControls([ 'sentiment', 'priority', 'block', 'href', 'target', 'children', 'type', 'className', ]), ...previewArgTypes, }, args: { previewAddonStart: false, previewAddonEnd: false, }, decorators: [withComponentGrid('30rem')], }; /** * 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 = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ( <> ); }, argTypes: { ...hideControls(['priority', 'block', 'href', 'target', 'children', 'type', 'className']), ...previewArgTypes, }, args: { previewAddonStart: false, previewAddonEnd: false, }, decorators: [withComponentGrid()], }; /** * There are three different button sizes – small (`sm`), medium (`md`) and large (`lg`) – each used for different purposes.
* [Design documentation](https://wise.design/components/button#sizes) */ export const Size: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ( <> ); }, argTypes: { ...hideControls(['size', 'block', 'href', 'target', 'children', 'type', 'className']), ...previewArgTypes, }, args: { previewAddonStart: false, previewAddonEnd: false, }, decorators: [withComponentGrid()], }; /** * If `href` prop is set, the component will be automatically rendered as an HTML anchor element. */ export const AsAnchor: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ( ); }, argTypes: { ...hideControls(['block']), ...previewArgTypes, }, args: { href: '#', previewAddonStart: false, previewAddonEnd: false, onClick: undefined, }, }; /** * Buttons can be in `disabled` or `loading` states to indicate they're not currently actionable. */ export const States: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ( <> ); }, argTypes: { ...hideControls(['disabled', 'loading', 'block']), ...previewArgTypes, }, args: { previewAddonStart: false, previewAddonEnd: false, }, decorators: [withComponentGrid()], }; /** * A Button that takes up the full width of its container (`display: block`). */ export const DisplayBlock: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return ( ); }, argTypes: { ...hideControls([ 'href', 'target', 'priority', 'sentiment', 'disabled', 'children', 'type', 'className', ]), ...previewArgTypes, }, args: { block: true, previewAddonStart: false, previewAddonEnd: false, }, }; /** * Icons are only supported for `sm` and `md` size Buttons.
* [Design documentation](https://wise.design/components/button#accessories) */ export const WithIcons: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props] = getPropsForPreview(args); return ( <> ); }, argTypes: { ...hideControls(['href', 'target', 'sentiment', 'disabled', 'children', 'type', 'className']), }, args: { size: 'md', }, parameters: { docs: { source: { code: ` <> `, }, }, }, decorators: [withComponentGrid()], }; /** * Avatars are only supported by the `md` size Buttons and are only allowed before the label.
* [Design documentation](https://wise.design/components/button#accessories) */ export const WithAvatars: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props] = getPropsForPreview(args); return ( <> ); }, argTypes: hideControls([ 'href', 'target', 'sentiment', 'disabled', 'children', 'type', 'className', ]), args: { size: 'md', }, parameters: { docs: { source: { code: ` <> `, }, }, }, decorators: [withComponentGrid()], }; /** * **NB:** The button doesn't know how long its label is, how many words it consists of and * how zoomed in it is. That is likely to lead to scenarios in which the text will overflow * its container. This is being investigated. */ export const DealingWithLongText: StoryObj = { render: function Render(args: PreviewStoryArgs) { const [props, previewProps] = getPropsForPreview(args); return (
{lorem20}
{lorem20}
); }, argTypes: { ...hideControls([ 'href', 'target', 'priority', 'sentiment', 'disabled', 'loading', 'children', 'as', 'type', 'className', ]), ...previewArgTypes, }, args: { block: true, previewAddonStart: false, previewAddonEnd: false, }, }; /** * `Button` is sentiment-aware and will automatically adjust its colours if wrapped inside * the [SentimentSurface](?path=/docs/foundations-sentimentsurface--docs) component */ export const SentimentAwareness: Story = { render: (args) => { return ( <> {(['success', 'warning', 'negative', 'neutral', 'proposition'] as const).map( (sentiment) => ( ), )} ); }, parameters: { docs: { source: { type: 'dynamic' }, canvas: { sourceState: 'hidden', }, }, }, decorators: [ (Story) => (
), ], };