docs: use ComponentDecorator (#800)

Related to #702
This commit is contained in:
Thaïs
2023-07-21 21:02:21 +02:00
committed by GitHub
parent 79fccb0404
commit 56cff63c4b
36 changed files with 777 additions and 910 deletions

View File

@ -1,12 +1,9 @@
import React from 'react';
import styled from '@emotion/styled';
import { text, withKnobs } from '@storybook/addon-knobs';
import { expect, jest } from '@storybook/jest';
import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { IconSearch } from '@/ui/icon';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { Button } from '../Button';
import { ButtonGroup } from '../ButtonGroup';
@ -15,8 +12,8 @@ type ButtonProps = React.ComponentProps<typeof Button>;
const StyledContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
padding: 20px;
width: 800px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(4)};
@ -55,15 +52,6 @@ const StyledButtonContainer = styled.div`
padding: ${({ theme }) => theme.spacing(2)};
`;
const meta: Meta<typeof Button> = {
title: 'UI/Button/Button',
component: Button,
decorators: [withKnobs],
};
export default meta;
type Story = StoryObj<typeof Button>;
const variants: ButtonProps['variant'][] = [
'primary',
'secondary',
@ -73,8 +61,6 @@ const variants: ButtonProps['variant'][] = [
'danger',
];
const clickJestFn = jest.fn();
const states = {
'with-icon': {
description: 'With icon',
@ -127,81 +113,16 @@ const states = {
},
};
const ButtonLine: React.FC<ButtonProps> = ({ variant, ...props }) => (
<>
{Object.entries(states).map(([state, { description, extraProps }]) => (
<StyledButtonContainer key={`${variant}-container-${state}`}>
<StyledDescription>{description}</StyledDescription>
<Button {...props} {...extraProps(variant ?? '')} variant={variant} />
</StyledButtonContainer>
))}
</>
);
const ButtonGroupLine: React.FC<ButtonProps> = ({ variant, ...props }) => (
<>
{Object.entries(states).map(([state, { description, extraProps }]) => (
<StyledButtonContainer key={`${variant}-group-container-${state}`}>
<StyledDescription>{description}</StyledDescription>
<ButtonGroup>
<Button
{...props}
{...extraProps(`${variant}-left`)}
variant={variant}
title="Left"
/>
<Button
{...props}
{...extraProps(`${variant}-center`)}
variant={variant}
title="Center"
/>
<Button
{...props}
{...extraProps(`${variant}-right`)}
variant={variant}
title="Right"
/>
</ButtonGroup>
</StyledButtonContainer>
))}
</>
);
const generateStory = (
size: ButtonProps['size'],
type: 'button' | 'group',
LineComponent: React.ComponentType<ButtonProps>,
): Story => ({
render: getRenderWrapperForComponent(
<StyledContainer>
{variants.map((variant) => (
<div key={variant}>
<StyledTitle>{variant}</StyledTitle>
<StyledLine>
<LineComponent
size={size}
variant={variant}
title={text('Text', 'A button title')}
/>
</StyledLine>
</div>
))}
</StyledContainer>,
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
let button;
if (type === 'group') {
button = canvas.getByTestId(`primary-left-button-default`);
} else {
button = canvas.getByTestId(`primary-button-default`);
}
const numberOfClicks = clickJestFn.mock.calls.length;
await userEvent.click(button);
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
},
const meta: Meta<typeof Button> = {
title: 'UI/Button/Button',
component: Button,
decorators: [
(Story) => (
<StyledContainer>
<Story />
</StyledContainer>
),
],
parameters: {
pseudo: Object.keys(states).reduce(
(acc, state) => ({
@ -210,20 +131,105 @@ const generateStory = (
(variant) =>
variant &&
['#left', '#center', '#right'].map(
(pos) => `${pos}-${variant}-${type}-${state}`,
(pos) => `${pos}-${variant}-button-${state}`,
),
),
}),
{},
),
},
});
argTypes: { icon: { control: false }, variant: { control: false } },
args: { title: 'A button title' },
};
export const MediumSize = generateStory('medium', 'button', ButtonLine);
export const SmallSize = generateStory('small', 'button', ButtonLine);
export const MediumSizeGroup = generateStory(
'medium',
'group',
ButtonGroupLine,
);
export const SmallSizeGroup = generateStory('small', 'group', ButtonGroupLine);
export default meta;
type Story = StoryObj<typeof Button>;
const clickJestFn = jest.fn();
export const MediumSize: Story = {
args: { size: 'medium' },
render: (args) => (
<>
{variants.map((variant) => (
<div key={variant}>
<StyledTitle>{variant}</StyledTitle>
<StyledLine>
{Object.entries(states).map(
([state, { description, extraProps }]) => (
<StyledButtonContainer key={`${variant}-container-${state}`}>
<StyledDescription>{description}</StyledDescription>
<Button
{...args}
{...extraProps(variant ?? '')}
variant={variant}
/>
</StyledButtonContainer>
),
)}
</StyledLine>
</div>
))}
</>
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByTestId('primary-button-default');
const numberOfClicks = clickJestFn.mock.calls.length;
await userEvent.click(button);
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
},
};
export const SmallSize: Story = {
...MediumSize,
args: { size: 'small' },
};
export const MediumSizeGroup: Story = {
args: { size: 'medium' },
render: (args) => (
<>
{variants.map((variant) => (
<div key={variant}>
<StyledTitle>{variant}</StyledTitle>
<StyledLine>
{Object.entries(states).map(
([state, { description, extraProps }]) => (
<StyledButtonContainer
key={`${variant}-group-container-${state}`}
>
<StyledDescription>{description}</StyledDescription>
<ButtonGroup>
{['Left', 'Center', 'Right'].map((position) => (
<Button
{...args}
{...extraProps(`${variant}-${position.toLowerCase()}`)}
variant={variant}
title={position}
/>
))}
</ButtonGroup>
</StyledButtonContainer>
),
)}
</StyledLine>
</div>
))}
</>
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByTestId('primary-left-button-default');
const numberOfClicks = clickJestFn.mock.calls.length;
await userEvent.click(button);
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
},
};
export const SmallSizeGroup: Story = {
...MediumSizeGroup,
args: { size: 'small' },
};

View File

@ -1,12 +1,9 @@
import React from 'react';
import styled from '@emotion/styled';
import { withKnobs } from '@storybook/addon-knobs';
import { expect, jest } from '@storybook/jest';
import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { IconUser } from '@/ui/icon';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { IconButton } from '../IconButton';
@ -16,6 +13,7 @@ const StyledContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
padding: 20px;
width: 800px;
> * + * {
margin-top: ${({ theme }) => theme.spacing(4)};
@ -55,7 +53,14 @@ const StyledIconButtonContainer = styled.div`
const meta: Meta<typeof IconButton> = {
title: 'UI/Button/IconButton',
component: IconButton,
decorators: [withKnobs],
decorators: [
(Story) => (
<StyledContainer>
<Story />
</StyledContainer>
),
],
argTypes: { icon: { control: false }, variant: { control: false } },
};
export default meta;
@ -101,53 +106,51 @@ const states = {
},
};
function IconButtonRow({ variant, size, ...props }: IconButtonProps) {
const iconSize = size === 'small' ? 14 : 16;
return (
export const LargeSize: Story = {
args: { size: 'large' },
render: (args) => (
<>
{Object.entries(states).map(([state, { description, extraProps }]) => (
<StyledIconButtonContainer key={`${variant}-container-${state}`}>
<StyledDescription>{description}</StyledDescription>
<IconButton
{...props}
{...extraProps(variant ?? '')}
variant={variant}
size={size}
icon={<IconUser size={iconSize} />}
/>
</StyledIconButtonContainer>
))}
</>
);
}
const generateStory = (
size: IconButtonProps['size'],
LineComponent: React.ComponentType<IconButtonProps>,
): Story => ({
render: getRenderWrapperForComponent(
<StyledContainer>
{variants.map((variant) => (
<div key={variant}>
<StyledTitle>{variant}</StyledTitle>
<StyledLine>
<LineComponent size={size} variant={variant} />
{Object.entries(states).map(
([state, { description, extraProps }]) => (
<StyledIconButtonContainer
key={`${variant}-container-${state}`}
>
<StyledDescription>{description}</StyledDescription>
<IconButton
{...args}
{...extraProps(variant ?? '')}
variant={variant}
icon={<IconUser size={args.size === 'small' ? 14 : 16} />}
/>
</StyledIconButtonContainer>
),
)}
</StyledLine>
</div>
))}
</StyledContainer>,
</>
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByTestId(`transparent-button-default`);
const button = canvas.getByTestId('transparent-button-default');
const numberOfClicks = clickJestFn.mock.calls.length;
await userEvent.click(button);
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
},
});
};
export const LargeSize = generateStory('large', IconButtonRow);
export const MediumSize = generateStory('medium', IconButtonRow);
export const SmallSize = generateStory('small', IconButtonRow);
export const MediumSize: Story = {
...LargeSize,
args: { size: 'medium' },
};
export const SmallSize: Story = {
...LargeSize,
args: { size: 'small' },
};

View File

@ -3,24 +3,32 @@ import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { IconBrandGoogle } from '@/ui/icon';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { ComponentDecorator } from '~/testing/decorators';
import { MainButton } from '../MainButton';
const clickJestFn = jest.fn();
const meta: Meta<typeof MainButton> = {
title: 'UI/Button/MainButton',
component: MainButton,
decorators: [ComponentDecorator],
argTypes: {
icon: {
type: 'boolean',
mapping: {
true: <IconBrandGoogle size={16} stroke={4} />,
false: undefined,
},
},
},
args: { title: 'A primary Button', onClick: clickJestFn },
};
export default meta;
type Story = StoryObj<typeof MainButton>;
const clickJestFn = jest.fn();
export const DefaultPrimary: Story = {
render: getRenderWrapperForComponent(
<MainButton title="A primary Button" onClick={clickJestFn} />,
),
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
@ -32,64 +40,30 @@ export const DefaultPrimary: Story = {
},
};
export const WithIconPrimary: Story = {
render: getRenderWrapperForComponent(
<MainButton
icon={<IconBrandGoogle size={16} stroke={4} />}
title="A primary Button"
/>,
),
export const WithIcon: Story = {
args: { icon: true },
};
export const WithIconPrimaryDisabled: Story = {
render: getRenderWrapperForComponent(
<MainButton
icon={<IconBrandGoogle size={16} stroke={4} />}
title="A primary Button"
disabled
/>,
),
export const DisabledWithIcon: Story = {
args: { ...WithIcon.args, disabled: true },
};
export const FullWidthPrimary: Story = {
render: getRenderWrapperForComponent(
<MainButton title="A primary Button" fullWidth />,
),
export const FullWidth: Story = {
args: { fullWidth: true },
};
export const DefaultSecondary: Story = {
render: getRenderWrapperForComponent(
<MainButton
title="A secondary Button"
onClick={clickJestFn}
variant="secondary"
/>,
),
export const Secondary: Story = {
args: { title: 'A secondary Button', variant: 'secondary' },
};
export const WithIconSecondary: Story = {
render: getRenderWrapperForComponent(
<MainButton
icon={<IconBrandGoogle size={16} stroke={4} />}
title="A secondary Button"
variant="secondary"
/>,
),
export const SecondaryWithIcon: Story = {
args: { ...Secondary.args, ...WithIcon.args },
};
export const WithIconSecondaryDisabled: Story = {
render: getRenderWrapperForComponent(
<MainButton
icon={<IconBrandGoogle size={16} stroke={4} />}
title="A secondary Button"
variant="secondary"
disabled
/>,
),
export const SecondaryDisabledWithIcon: Story = {
args: { ...SecondaryWithIcon.args, disabled: true },
};
export const FullWidthSecondary: Story = {
render: getRenderWrapperForComponent(
<MainButton title="A secondary Button" variant="secondary" fullWidth />,
),
export const SecondaryFullWidth: Story = {
args: { ...Secondary.args, ...FullWidth.args },
};

View File

@ -3,27 +3,23 @@ import type { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { IconArrowRight } from '@/ui/icon';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { ComponentDecorator } from '~/testing/decorators';
import { RoundedIconButton } from '../RoundedIconButton';
const clickJestFn = jest.fn();
const meta: Meta<typeof RoundedIconButton> = {
title: 'UI/Button/RoundedIconButton',
component: RoundedIconButton,
decorators: [ComponentDecorator],
args: { onClick: clickJestFn, icon: <IconArrowRight size={15} /> },
};
export default meta;
type Story = StoryObj<typeof RoundedIconButton>;
const clickJestFn = jest.fn();
export const Default: Story = {
render: getRenderWrapperForComponent(
<RoundedIconButton
onClick={clickJestFn}
icon={<IconArrowRight size={15} />}
/>,
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);