Skip to content
Open
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
67 changes: 67 additions & 0 deletions frontend/documentation/components/Button.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
sizeClassNames,
} from 'components/base/forms/Button'
import type { ButtonType } from 'components/base/forms/Button'
import Icon from 'components/icons/Icon'

const themeOptions = Object.keys(themeClassNames) as Array<
keyof typeof themeClassNames
Expand Down Expand Up @@ -81,6 +82,72 @@ export const Variants: Story = {
),
}

export const IconAsChildren: Story = {
parameters: {
docs: {
description: {
story:
'Pattern used by `btn-with-icon` consumers (e.g. table-row delete affordances): the icon is passed as `children`, not via the `iconLeft`/`iconRight` props. Layout must match the iconLeft/iconRight rendering for visual consistency. Snapshotted to catch wrapper-introduced height/width drift.',
},
},
},
render: () => (
<div className='d-flex align-items-center flex-wrap gap-2'>
<Button className='btn btn-with-icon' type='button'>
<Icon name='trash-2' width={20} fill='#656D7B' />
</Button>
<Button className='btn btn-with-icon' type='button'>
<Icon name='edit' width={20} fill='#656D7B' />
</Button>
<Button className='btn btn-with-icon' type='button'>
<Icon name='copy' width={20} fill='#656D7B' />
</Button>
</div>
),
}

export const IconAndLabel: Story = {
parameters: {
docs: {
description: {
story:
'The most common icon usage in real code: `iconLeft` or `iconRight` paired with a text label. Dedicated snapshot so spacing regressions between the icon and the label are caught independently of the Themes story.',
},
},
},
render: () => (
<div className='d-flex align-items-center flex-wrap gap-2'>
<Button iconLeft='plus'>Add new</Button>
<Button iconRight='chevron-down'>Options</Button>
<Button iconLeft='copy' iconRight='chevron-down'>
Both icons
</Button>
</div>
),
}

export const AsAnchor: Story = {
parameters: {
docs: {
description: {
story:
"Button renders as an `<a>` element when `href` is set — a separate code path from the `<button>` branch with its own content wrapper. Snapshotted so changes to the anchor branch can't regress without showing up.",
},
},
},
render: () => (
<div className='d-flex align-items-center flex-wrap gap-2'>
<Button href='https://docs.flagsmith.com'>Docs</Button>
<Button href='https://docs.flagsmith.com' iconLeft='plus'>
Add new
</Button>
<Button href='https://docs.flagsmith.com' theme='secondary'>
Cancel
</Button>
</div>
),
}

export const Sizes: Story = {
parameters: {
docs: {
Expand Down
38 changes: 22 additions & 16 deletions frontend/web/components/base/forms/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,28 @@ export const Button = React.forwardRef<
)}
ref={ref as React.RefObject<HTMLButtonElement>}
>
{!!iconLeft && (
<Icon
fill={iconLeftColour ? iconColours[iconLeftColour] : undefined}
className='mr-2'
name={iconLeft}
width={iconSize}
/>
)}
{children}
{!!iconRight && (
<Icon
fill={iconRightColour ? iconColours[iconRightColour] : undefined}
className='ml-2'
name={iconRight}
width={iconSize}
/>
{iconLeft || iconRight ? (
<span className='d-flex align-items-center justify-content-center gap-2'>
{!!iconLeft && (
<Icon
fill={iconLeftColour ? iconColours[iconLeftColour] : undefined}
name={iconLeft}
width={iconSize}
/>
)}
{children}
{!!iconRight && (
<Icon
fill={
iconRightColour ? iconColours[iconRightColour] : undefined
}
name={iconRight}
width={iconSize}
/>
)}
</span>
) : (
children
)}
</button>
)
Expand Down
Loading