@ -4,17 +4,25 @@ import styled from '@emotion/styled';
|
|||||||
import { SoonPill } from '@/ui/pill/components/SoonPill';
|
import { SoonPill } from '@/ui/pill/components/SoonPill';
|
||||||
import { rgba } from '@/ui/themes/colors';
|
import { rgba } from '@/ui/themes/colors';
|
||||||
|
|
||||||
export type ButtonVariant =
|
export enum ButtonSize {
|
||||||
| 'primary'
|
Medium = 'medium',
|
||||||
| 'secondary'
|
Small = 'small',
|
||||||
| 'tertiary'
|
}
|
||||||
| 'tertiaryBold'
|
|
||||||
| 'tertiaryLight'
|
|
||||||
| 'danger';
|
|
||||||
|
|
||||||
export type ButtonSize = 'medium' | 'small';
|
export enum ButtonPosition {
|
||||||
|
Left = 'left',
|
||||||
|
Middle = 'middle',
|
||||||
|
Right = 'right',
|
||||||
|
}
|
||||||
|
|
||||||
export type ButtonPosition = 'left' | 'middle' | 'right' | undefined;
|
export enum ButtonVariant {
|
||||||
|
Primary = 'primary',
|
||||||
|
Secondary = 'secondary',
|
||||||
|
Tertiary = 'tertiary',
|
||||||
|
TertiaryBold = 'tertiaryBold',
|
||||||
|
TertiaryLight = 'tertiaryLight',
|
||||||
|
Danger = 'danger',
|
||||||
|
}
|
||||||
|
|
||||||
export type ButtonProps = {
|
export type ButtonProps = {
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
@ -24,6 +32,7 @@ export type ButtonProps = {
|
|||||||
size?: ButtonSize;
|
size?: ButtonSize;
|
||||||
position?: ButtonPosition;
|
position?: ButtonPosition;
|
||||||
soon?: boolean;
|
soon?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
} & React.ComponentProps<'button'>;
|
} & React.ComponentProps<'button'>;
|
||||||
|
|
||||||
const StyledButton = styled.button<
|
const StyledButton = styled.button<
|
||||||
@ -172,8 +181,8 @@ export function Button({
|
|||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
fullWidth = false,
|
fullWidth = false,
|
||||||
variant = 'primary',
|
variant = ButtonVariant.Primary,
|
||||||
size = 'medium',
|
size = ButtonSize.Medium,
|
||||||
position,
|
position,
|
||||||
soon = false,
|
soon = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
|||||||
@ -1,145 +1,30 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { expect, jest } from '@storybook/jest';
|
import { expect, jest } from '@storybook/jest';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { IconSearch } from '@/ui/icon';
|
import { IconSearch } from '@/ui/icon';
|
||||||
|
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|
||||||
import { Button } from '../Button';
|
import { Button, ButtonPosition, ButtonSize, ButtonVariant } from '../Button';
|
||||||
import { ButtonGroup } from '../ButtonGroup';
|
|
||||||
|
|
||||||
type ButtonProps = React.ComponentProps<typeof Button>;
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 20px;
|
|
||||||
width: 800px;
|
|
||||||
> * + * {
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTitle = styled.h1`
|
|
||||||
font-size: ${({ theme }) => theme.font.size.lg};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(3)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledDescription = styled.span`
|
|
||||||
color: ${({ theme }) => theme.font.color.light};
|
|
||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
|
||||||
text-align: center;
|
|
||||||
text-transform: uppercase;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledLine = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledButtonContainer = styled.div`
|
|
||||||
border: 1px solid ${({ theme }) => theme.color.gray20};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const variants: ButtonProps['variant'][] = [
|
|
||||||
'primary',
|
|
||||||
'secondary',
|
|
||||||
'tertiary',
|
|
||||||
'tertiaryBold',
|
|
||||||
'tertiaryLight',
|
|
||||||
'danger',
|
|
||||||
];
|
|
||||||
|
|
||||||
const states = {
|
|
||||||
'with-icon': {
|
|
||||||
description: 'With icon',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
'data-testid': `${variant}-button-with-icon`,
|
|
||||||
icon: <IconSearch size={14} />,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
default: {
|
|
||||||
description: 'Default',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
'data-testid': `${variant}-button-default`,
|
|
||||||
onClick: clickJestFn,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
description: 'Hover',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
id: `${variant}-button-hover`,
|
|
||||||
'data-testid': `${variant}-button-hover`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
pressed: {
|
|
||||||
description: 'Pressed',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
id: `${variant}-button-pressed`,
|
|
||||||
'data-testid': `${variant}-button-pressed`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
description: 'Disabled',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
'data-testid': `${variant}-button-disabled`,
|
|
||||||
disabled: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
soon: {
|
|
||||||
description: 'Soon',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
'data-testid': `${variant}-button-soon`,
|
|
||||||
soon: true,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
focus: {
|
|
||||||
description: 'Focus',
|
|
||||||
extraProps: (variant: string) => ({
|
|
||||||
id: `${variant}-button-focus`,
|
|
||||||
'data-testid': `${variant}-button-focus`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const meta: Meta<typeof Button> = {
|
const meta: Meta<typeof Button> = {
|
||||||
title: 'UI/Button/Button',
|
title: 'UI/Button/Button',
|
||||||
component: Button,
|
component: Button,
|
||||||
decorators: [
|
argTypes: {
|
||||||
(Story) => (
|
icon: {
|
||||||
<StyledContainer>
|
type: 'boolean',
|
||||||
<Story />
|
mapping: {
|
||||||
</StyledContainer>
|
true: <IconSearch size={14} />,
|
||||||
),
|
false: undefined,
|
||||||
],
|
},
|
||||||
parameters: {
|
},
|
||||||
pseudo: Object.keys(states).reduce(
|
position: {
|
||||||
(acc, state) => ({
|
control: 'radio',
|
||||||
...acc,
|
options: [undefined, ...Object.values(ButtonPosition)],
|
||||||
[state]: variants.map(
|
},
|
||||||
(variant) =>
|
|
||||||
variant &&
|
|
||||||
['#left', '#center', '#right'].map(
|
|
||||||
(pos) => `${pos}-${variant}-button-${state}`,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
argTypes: { icon: { control: false }, variant: { control: false } },
|
args: { title: 'Lorem ipsum' },
|
||||||
args: { title: 'A button title' },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
@ -147,34 +32,12 @@ type Story = StoryObj<typeof Button>;
|
|||||||
|
|
||||||
const clickJestFn = jest.fn();
|
const clickJestFn = jest.fn();
|
||||||
|
|
||||||
export const MediumSize: Story = {
|
export const Default: Story = {
|
||||||
args: { size: 'medium' },
|
args: { onClick: clickJestFn },
|
||||||
render: (args) => (
|
decorators: [ComponentDecorator],
|
||||||
<>
|
|
||||||
{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 }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
const button = canvas.getByTestId('primary-button-default');
|
const button = canvas.getByRole('button');
|
||||||
|
|
||||||
const numberOfClicks = clickJestFn.mock.calls.length;
|
const numberOfClicks = clickJestFn.mock.calls.length;
|
||||||
await userEvent.click(button);
|
await userEvent.click(button);
|
||||||
@ -182,54 +45,78 @@ export const MediumSize: Story = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SmallSize: Story = {
|
export const Sizes: Story = {
|
||||||
...MediumSize,
|
argTypes: {
|
||||||
args: { size: 'small' },
|
size: { control: false },
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
|
parameters: {
|
||||||
|
catalog: [
|
||||||
|
{
|
||||||
|
name: 'sizes',
|
||||||
|
values: Object.values(ButtonSize),
|
||||||
|
props: (size: ButtonSize) => ({ size }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
decorators: [CatalogDecorator],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SmallSizeGroup: Story = {
|
export const Variants: Story = {
|
||||||
...MediumSizeGroup,
|
argTypes: {
|
||||||
args: { size: 'small' },
|
disabled: { control: false },
|
||||||
|
variant: { control: false },
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
pseudo: { hover: ['.hover'], active: ['.active'], focus: ['.focus'] },
|
||||||
|
catalog: [
|
||||||
|
{
|
||||||
|
name: 'state',
|
||||||
|
values: ['default', 'disabled', 'hover', 'active', 'focus'],
|
||||||
|
props: (state: string) => {
|
||||||
|
if (state === 'disabled') return { disabled: true };
|
||||||
|
if (state === 'default') return {};
|
||||||
|
return { className: state };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'variants',
|
||||||
|
values: Object.values(ButtonVariant),
|
||||||
|
props: (variant: ButtonVariant) => ({ variant }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
decorators: [CatalogDecorator],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Positions: Story = {
|
||||||
|
argTypes: {
|
||||||
|
position: { control: false },
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
catalog: [
|
||||||
|
{
|
||||||
|
name: 'positions',
|
||||||
|
values: ['none', ...Object.values(ButtonPosition)],
|
||||||
|
props: (position: ButtonPosition | 'none') =>
|
||||||
|
position === 'none' ? {} : { position },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
decorators: [CatalogDecorator],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithAdornments: Story = {
|
||||||
|
parameters: {
|
||||||
|
catalog: [
|
||||||
|
{
|
||||||
|
name: 'adornments',
|
||||||
|
values: ['with icon', 'with soon pill'],
|
||||||
|
props: (value: string) =>
|
||||||
|
value === 'with icon'
|
||||||
|
? { icon: <IconSearch size={14} /> }
|
||||||
|
: { soon: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
decorators: [CatalogDecorator],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { expect, jest } from '@storybook/jest';
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|
||||||
|
import { Button, ButtonPosition } from '../Button';
|
||||||
|
import { ButtonGroup } from '../ButtonGroup';
|
||||||
|
|
||||||
|
const clickJestFn = jest.fn();
|
||||||
|
|
||||||
|
const meta: Meta<typeof ButtonGroup> = {
|
||||||
|
title: 'UI/Button/ButtonGroup',
|
||||||
|
component: ButtonGroup,
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
argTypes: { children: { control: false } },
|
||||||
|
args: {
|
||||||
|
children: Object.values(ButtonPosition).map((position) => (
|
||||||
|
<Button title={position} onClick={clickJestFn} />
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ButtonGroup>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const leftButton = canvas.getByRole('button', { name: 'left' });
|
||||||
|
|
||||||
|
const numberOfClicks = clickJestFn.mock.calls.length;
|
||||||
|
await userEvent.click(leftButton);
|
||||||
|
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
import { ExhaustiveComponentDecorator } from '~/testing/decorators/ExhaustiveComponentDecorator';
|
|
||||||
|
|
||||||
import { Chip, ChipAccent, ChipSize, ChipVariant } from '../Chip';
|
import { Chip, ChipAccent, ChipSize, ChipVariant } from '../Chip';
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export const Default: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Catalog: Story = {
|
export const Catalog: Story = {
|
||||||
args: { size: ChipSize.Large, clickable: true, label: 'Hello' },
|
args: { clickable: true, label: 'Hello' },
|
||||||
argTypes: {
|
argTypes: {
|
||||||
size: { control: false },
|
size: { control: false },
|
||||||
variant: { control: false },
|
variant: { control: false },
|
||||||
@ -38,14 +38,29 @@ export const Catalog: Story = {
|
|||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
pseudo: { hover: ['.hover'], active: ['.active'] },
|
pseudo: { hover: ['.hover'], active: ['.active'] },
|
||||||
variants: [
|
catalog: [
|
||||||
ChipVariant.Highlighted,
|
{
|
||||||
ChipVariant.Regular,
|
name: 'variants',
|
||||||
ChipVariant.Transparent,
|
values: Object.values(ChipVariant),
|
||||||
|
props: (variant: ChipVariant) => ({ variant }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sizes',
|
||||||
|
values: Object.values(ChipSize),
|
||||||
|
props: (size: ChipSize) => ({ size }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accents',
|
||||||
|
values: Object.values(ChipAccent),
|
||||||
|
props: (accent: ChipAccent) => ({ accent }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'states',
|
||||||
|
values: ['default', 'hover', 'active', 'disabled'],
|
||||||
|
props: (state: string) =>
|
||||||
|
state === 'default' ? {} : { className: state },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
sizes: [ChipSize.Small, ChipSize.Large],
|
|
||||||
accents: [ChipAccent.TextPrimary, ChipAccent.TextSecondary],
|
|
||||||
states: ['default', 'hover', 'active', 'disabled'],
|
|
||||||
},
|
},
|
||||||
decorators: [ExhaustiveComponentDecorator],
|
decorators: [CatalogDecorator],
|
||||||
};
|
};
|
||||||
|
|||||||
124
front/src/testing/decorators/CatalogDecorator.tsx
Normal file
124
front/src/testing/decorators/CatalogDecorator.tsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Decorator } from '@storybook/react';
|
||||||
|
|
||||||
|
const ColumnTitle = styled.h1`
|
||||||
|
font-size: ${({ theme }) => theme.font.size.lg};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
margin: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RowsTitle = styled.h2`
|
||||||
|
color: ${({ theme }) => theme.font.color.secondary};
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
margin: ${({ theme }) => theme.spacing(2)};
|
||||||
|
width: 100px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RowTitle = styled.h3`
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
margin: ${({ theme }) => theme.spacing(2)};
|
||||||
|
width: 100px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ElementTitle = styled.span`
|
||||||
|
color: ${({ theme }) => theme.font.color.light};
|
||||||
|
font-size: ${({ theme }) => theme.font.size.xs};
|
||||||
|
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ColumnContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RowsContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RowContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ElementContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const emptyVariable = {
|
||||||
|
name: '',
|
||||||
|
values: [undefined],
|
||||||
|
props: () => ({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CatalogDecorator: Decorator = (Story, context) => {
|
||||||
|
const { catalog } = context.parameters;
|
||||||
|
const [
|
||||||
|
variable1,
|
||||||
|
variable2 = emptyVariable,
|
||||||
|
variable3 = emptyVariable,
|
||||||
|
variable4 = emptyVariable,
|
||||||
|
] = catalog;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
{variable4.values.map((value4: string) => (
|
||||||
|
<ColumnContainer key={value4}>
|
||||||
|
{(variable4.labels?.(value4) || value4) && (
|
||||||
|
<ColumnTitle>{variable4.labels?.(value4) || value4}</ColumnTitle>
|
||||||
|
)}
|
||||||
|
{variable3.values.map((value3: string) => (
|
||||||
|
<RowsContainer key={value3}>
|
||||||
|
{(variable3.labels?.(value3) || value3) && (
|
||||||
|
<RowsTitle>{variable3.labels?.(value3) || value3}</RowsTitle>
|
||||||
|
)}
|
||||||
|
{variable2.values.map((value2: string) => (
|
||||||
|
<RowContainer key={value2}>
|
||||||
|
{(variable2.labels?.(value2) || value2) && (
|
||||||
|
<RowTitle>{variable2.labels?.(value2) || value2}</RowTitle>
|
||||||
|
)}
|
||||||
|
{variable1.values.map((value1: string) => (
|
||||||
|
<ElementContainer key={value1}>
|
||||||
|
{(variable1.labels?.(value1) || value1) && (
|
||||||
|
<ElementTitle>
|
||||||
|
{variable1.labels?.(value1) || value1}
|
||||||
|
</ElementTitle>
|
||||||
|
)}
|
||||||
|
<Story
|
||||||
|
args={{
|
||||||
|
...context.args,
|
||||||
|
...variable1.props(value1),
|
||||||
|
...variable2.props(value2),
|
||||||
|
...variable3.props(value3),
|
||||||
|
...variable4.props(value4),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ElementContainer>
|
||||||
|
))}
|
||||||
|
</RowContainer>
|
||||||
|
))}
|
||||||
|
</RowsContainer>
|
||||||
|
))}
|
||||||
|
</ColumnContainer>
|
||||||
|
))}
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,115 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { Decorator } from '@storybook/react';
|
|
||||||
|
|
||||||
function stateProps(state: string) {
|
|
||||||
switch (state) {
|
|
||||||
case 'default':
|
|
||||||
return {};
|
|
||||||
case 'hover':
|
|
||||||
return { className: 'hover' };
|
|
||||||
case 'active':
|
|
||||||
return { className: 'active' };
|
|
||||||
case 'disabled':
|
|
||||||
return { disabled: true };
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledSizeTitle = styled.h1`
|
|
||||||
font-size: ${({ theme }) => theme.font.size.lg};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledVariantTitle = styled.h2`
|
|
||||||
color: ${({ theme }) => theme.font.color.secondary};
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin: ${({ theme }) => theme.spacing(2)};
|
|
||||||
width: 100px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAccentTitle = styled.h3`
|
|
||||||
color: ${({ theme }) => theme.font.color.tertiary};
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin: ${({ theme }) => theme.spacing(2)};
|
|
||||||
width: 100px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledStateTitle = styled.span`
|
|
||||||
color: ${({ theme }) => theme.font.color.light};
|
|
||||||
font-size: ${({ theme }) => theme.font.size.xs};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
|
||||||
text-align: center;
|
|
||||||
text-transform: uppercase;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledSizeContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledVariantContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAccentContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledStateContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const ExhaustiveComponentDecorator: Decorator = (Story, context) => {
|
|
||||||
const parameters = context.parameters;
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
{parameters.sizes.map((size: string) => (
|
|
||||||
<StyledSizeContainer key={size}>
|
|
||||||
<StyledSizeTitle>{size}</StyledSizeTitle>
|
|
||||||
{parameters.variants.map((variant: string) => (
|
|
||||||
<StyledVariantContainer key={variant}>
|
|
||||||
<StyledVariantTitle>{variant}</StyledVariantTitle>
|
|
||||||
{parameters.accents.map((accent: string) => (
|
|
||||||
<StyledAccentContainer key={accent}>
|
|
||||||
<StyledAccentTitle>{accent}</StyledAccentTitle>
|
|
||||||
{parameters.states.map((state: string) => (
|
|
||||||
<StyledStateContainer key={state}>
|
|
||||||
<StyledStateTitle>{state}</StyledStateTitle>
|
|
||||||
<Story
|
|
||||||
args={{
|
|
||||||
...context.args,
|
|
||||||
accent: accent,
|
|
||||||
variant: variant,
|
|
||||||
...stateProps(state),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</StyledStateContainer>
|
|
||||||
))}
|
|
||||||
</StyledAccentContainer>
|
|
||||||
))}
|
|
||||||
</StyledVariantContainer>
|
|
||||||
))}
|
|
||||||
</StyledSizeContainer>
|
|
||||||
))}
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user