Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions frontend/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react-webpack5'
import Button, {
themeClassNames,
sizeClassNames,
} from 'components/base/forms/Button'

const themes = Object.keys(themeClassNames) as Array<
keyof typeof themeClassNames
>
const sizes = Object.keys(sizeClassNames) as Array<keyof typeof sizeClassNames>

const meta: Meta<typeof Button> = {
component: Button,
parameters: {
layout: 'padded',
},
title: 'Components/Button',
}
export default meta

type Story = StoryObj<typeof Button>

// ---------------------------------------------------------------------------
// All themes
// ---------------------------------------------------------------------------

export const AllThemes: Story = {
name: 'All themes',
render: () => (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
{themes.map((theme) => (
<Button key={theme} theme={theme}>
{theme}
</Button>
))}
</div>
),
}

// ---------------------------------------------------------------------------
// All sizes
// ---------------------------------------------------------------------------

export const AllSizes: Story = {
name: 'All sizes',
render: () => (
<div
style={{
alignItems: 'center',
display: 'flex',
flexWrap: 'wrap',
gap: 12,
}}
>
{sizes.map((size) => (
<Button key={size} size={size}>
{size}
</Button>
))}
</div>
),
}

// ---------------------------------------------------------------------------
// Theme × Size matrix
// ---------------------------------------------------------------------------

export const ThemeSizeMatrix: Story = {
name: 'Theme × Size matrix',
render: () => (
<table style={{ borderCollapse: 'collapse' }}>
<thead>
<tr>
<th
style={{
color: 'var(--colorTextSecondary, #656d7b)',
fontSize: 11,
fontWeight: 600,
letterSpacing: '0.05em',
padding: '8px 12px',
textAlign: 'left',
textTransform: 'uppercase',
}}
>
Theme / Size
</th>
{sizes.map((size) => (
<th
key={size}
style={{
color: 'var(--colorTextSecondary, #656d7b)',
fontSize: 11,
fontWeight: 600,
letterSpacing: '0.05em',
padding: '8px 12px',
textAlign: 'center',
textTransform: 'uppercase',
}}
>
{size}
</th>
))}
</tr>
</thead>
<tbody>
{themes.map((theme) => (
<tr key={theme}>
<td
style={{
fontFamily: 'monospace',
fontSize: 12,
padding: '8px 12px',
}}
>
{theme}
</td>
{sizes.map((size) => (
<td
key={size}
style={{ padding: '8px 12px', textAlign: 'center' }}
>
<Button theme={theme} size={size}>
Label
</Button>
</td>
))}
</tr>
))}
</tbody>
</table>
),
}

// ---------------------------------------------------------------------------
// With icons
// ---------------------------------------------------------------------------

export const WithIcons: Story = {
name: 'With icons',
render: () => (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
<Button iconLeft='plus'>Icon left</Button>
<Button iconRight='chevron-right'>Icon right</Button>
<Button iconLeft='setting' iconRight='chevron-down'>
Both icons
</Button>
<Button theme='secondary' iconLeft='copy'>
Copy
</Button>
<Button theme='danger' iconLeft='trash-2'>
Delete
</Button>
</div>
),
}

// ---------------------------------------------------------------------------
// States
// ---------------------------------------------------------------------------

export const States: Story = {
render: () => (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
<Button>Default</Button>
<Button disabled>Disabled</Button>
<Button theme='secondary'>Secondary</Button>
<Button theme='secondary' disabled>
Secondary disabled
</Button>
<Button theme='danger'>Danger</Button>
<Button theme='danger' disabled>
Danger disabled
</Button>
</div>
),
}

// ---------------------------------------------------------------------------
// As link
// ---------------------------------------------------------------------------

export const AsLink: Story = {
name: 'As link (href)',
render: () => (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
<Button href='#'>Primary link</Button>
<Button href='#' theme='secondary'>
Secondary link
</Button>
<Button href='#' theme='text'>
Text link
</Button>
</div>
),
}
28 changes: 13 additions & 15 deletions frontend/web/components/base/forms/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import React from 'react'
import cn from 'classnames'
import { ButtonHTMLAttributes, HTMLAttributeAnchorTarget } from 'react'
import Icon, { IconName } from 'components/Icon'
import Constants from 'common/constants'

const iconColours = {
primary: 'var(--color-brand-default, #6837fc)',
white: 'var(--color-text-on-fill, #ffffff)',
} as const

export type IconColour = keyof typeof iconColours

export const themeClassNames = {
danger: 'btn btn-danger',
Expand All @@ -26,8 +32,8 @@ export const sizeClassNames = {

export type ButtonType = ButtonHTMLAttributes<HTMLButtonElement> & {
iconRight?: IconName
iconRightColour?: keyof typeof Constants.colours
iconLeftColour?: keyof typeof Constants.colours
iconRightColour?: IconColour
iconLeftColour?: IconColour
iconLeft?: IconName
href?: string
target?: HTMLAttributeAnchorTarget
Expand Down Expand Up @@ -71,9 +77,7 @@ export const Button = React.forwardRef<
<div className='d-flex h-100 align-items-center justify-content-center gap-2'>
{!!iconLeft && (
<Icon
fill={
iconLeftColour ? Constants.colours[iconLeftColour] : undefined
}
fill={iconLeftColour ? iconColours[iconLeftColour] : undefined}
name={iconLeft}
width={iconSize}
/>
Expand All @@ -82,9 +86,7 @@ export const Button = React.forwardRef<
</div>
{!!iconRight && (
<Icon
fill={
iconRightColour ? Constants.colours[iconRightColour] : undefined
}
fill={iconRightColour ? iconColours[iconRightColour] : undefined}
className='ml-2'
name={iconRight}
width={iconSize}
Expand All @@ -106,9 +108,7 @@ export const Button = React.forwardRef<
>
{!!iconLeft && (
<Icon
fill={
iconLeftColour ? Constants.colours[iconLeftColour] : undefined
}
fill={iconLeftColour ? iconColours[iconLeftColour] : undefined}
className='mr-2'
name={iconLeft}
width={iconSize}
Expand All @@ -117,9 +117,7 @@ export const Button = React.forwardRef<
{children}
{!!iconRight && (
<Icon
fill={
iconRightColour ? Constants.colours[iconRightColour] : undefined
}
fill={iconRightColour ? iconColours[iconRightColour] : undefined}
className='ml-2'
name={iconRight}
width={iconSize}
Expand Down