Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9d91f76
feat: implement Checkbox 2.0 with Field integration
pomfrida Jan 19, 2026
f449c45
refactor(eds-core-react): update Checkbox to use Field internally
pomfrida Jan 19, 2026
d9f4879
feat(eds-core-react): add helperMessage prop to Checkbox
pomfrida Jan 19, 2026
ed0b75d
chore(eds-core-react): update build artifacts
pomfrida Jan 19, 2026
4f2aaa5
Re-add comment
pomfrida Jan 19, 2026
cf83c51
refactor(eds-core-react): remove error prop from Checkbox
pomfrida Jan 19, 2026
f594b66
docs(eds-core-react): add ColorAppearance story to Checkbox
pomfrida Jan 19, 2026
9602c6e
feat(eds-core-react): add data-color-appearance support to Checkbox
pomfrida Jan 19, 2026
f6fb93d
chore: revert unrelated jest.config change
pomfrida Jan 19, 2026
be2b88f
refactor(eds-core-react): remove redundant danger override from Field…
pomfrida Jan 19, 2026
f8c3c9b
refactor(eds-core-react): use design tokens for Checkbox spacing
pomfrida Jan 19, 2026
0c6ba74
chore: ignore build folder and remove tracked build files
pomfrida Jan 19, 2026
b8611f8
fix(eds-core-react): move @layer before @import in CSS
pomfrida Jan 19, 2026
8dfedaf
fix(eds-core-react): use neutral hover color for Checkbox
pomfrida Jan 20, 2026
5308554
chore(eds-core-react): remove WithHelperMessage and ColorAppearance s…
pomfrida Jan 20, 2026
b23b23c
fix: improve checkbox centering, height and hover behavior
pomfrida Jan 20, 2026
f9c0023
fix(eds-core-react): use 24px icon with negative margin for checkbox
pomfrida Jan 20, 2026
62a6a30
fix(eds-core-react): update checkbox colors to match Figma spec
pomfrida Jan 20, 2026
70de5cc
fix: use density-aware tokens for checkbox sizing
pomfrida Jan 20, 2026
b6a3e00
fix: use explicit size prop on Icon for consistent checkbox sizing
pomfrida Jan 20, 2026
03f4a8b
fix: use fixed height instead of padding for correct density sizing
pomfrida Jan 20, 2026
e51e93f
chore: clean up Checkbox stories
pomfrida Jan 20, 2026
bc1d6e8
fix: use grey color for disabled checkbox icon per Figma spec
pomfrida Jan 20, 2026
56674b4
fix(eds-core-react): use Figma spacing tokens for checkbox sizing
pomfrida Jan 21, 2026
93d0fd7
fix(eds-core-react): add density support for standalone checkbox
pomfrida Jan 21, 2026
8946c7b
refactor(eds-core-react): improve Checkbox props and stories
pomfrida Jan 21, 2026
b02b399
refactor(eds-core-react): improve Checkbox code quality
pomfrida Jan 21, 2026
5bc0459
Remove props in story
pomfrida Jan 21, 2026
4a27990
fix(eds-core-react): move transition to base element for consistent fade
pomfrida Jan 21, 2026
9a0315c
fix(eds-core-react): remove data-color-appearance customization from …
pomfrida Jan 22, 2026
535f808
fix(eds-core-react): remove redundant aria-disabled from Checkbox
pomfrida Jan 22, 2026
29164f2
chore(eds-core-react): remove unnecessary eslint-disable from Checkbo…
pomfrida Jan 22, 2026
fc4fc86
chore(eds-core-react): remove unused types.ts from next folder
pomfrida Jan 22, 2026
f0e6598
docs(eds-core-react): add beta warning to Checkbox stories
pomfrida Jan 22, 2026
40442cb
docs(eds-core-react): add usage examples to Checkbox stories
pomfrida Jan 22, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dist
build
.cache
node_modules
pnpm-debug.log
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
import { useState, useRef, ChangeEvent } from 'react'
import { action } from 'storybook/actions'
import { useForm } from 'react-hook-form'
import { StoryFn, Meta } from '@storybook/react-vite'
import { data } from '../../../stories/data'
import { Stack } from './../../../../.storybook/components'
import { Typography, Button, Table } from '../../..'
import { Checkbox } from './Checkbox'
import type { CheckboxProps } from './Checkbox.types'

const meta: Meta<typeof Checkbox> = {
title: 'EDS 2.0 (beta)/Inputs/Selection Controls/Checkbox',
component: Checkbox,
parameters: {
docs: {
description: {
component:
'New Checkbox component using vanilla CSS, EDS foundation tokens, and Field for layout. The component handles accessibility automatically via the Field component.',
},
source: {
excludeDecorators: true,
},
},
},
decorators: [
(Story) => {
return (
<Stack>
<Story />
</Stack>
)
},
],
}

export default meta

const UnstyledList = ({ children, ...props }) => (
<ul
style={{
margin: 0,
padding: 0,
listStyleType: 'none',
display: 'flex',
flexDirection: 'column',
gap: '1rem',
}}
{...props}
>
{children}
</ul>
)

const CheckboxWrapper = ({ children, ...props }) => (
<div style={{ display: 'flex' }} {...props}>
{children}
</div>
)

export const Introduction: StoryFn<CheckboxProps> = (args) => {
return <Checkbox id="intro-checkbox" label="Play with me" {...args} />
}

export const SingleCheckbox: StoryFn<CheckboxProps> = () => {
// Use this to set the input to indeterminate = true as this must be done via JavaScript
// (cannot use an HTML attribute for this)
const indeterminateRef = useRef<HTMLInputElement | null>(null)
// State for controlled example
const [checked, updateChecked] = useState(false)
return (
<UnstyledList>
<li>
<Checkbox label="Check me" />
</li>
<li>
<Checkbox label="You can't check me!" disabled />
</li>
<li>
<Checkbox label="I'm preselected" defaultChecked />
</li>
<li>
<Checkbox label="You can't uncheck me!" disabled defaultChecked />
</li>
<li>
<Checkbox
label="I'm in indeterminate state"
indeterminate
ref={indeterminateRef}
/>
</li>
<li>
<Checkbox
label="I'm a controlled component"
onChange={(e: ChangeEvent<HTMLInputElement>) => {
updateChecked(e.target.checked)
}}
checked={checked}
/>
</li>
</UnstyledList>
)
}
SingleCheckbox.storyName = 'Single checkbox'

export const GroupedCheckbox: StoryFn<CheckboxProps> = () => {
return (
<fieldset>
<legend>
We are in this together!
<span role="img" aria-label="raising hands emoji">
🙌
</span>
</legend>
<UnstyledList>
<li>
<Checkbox label="Check me first" name="multiple" value="first" />
</li>
<li>
<Checkbox label="Check me second" name="multiple" value="second" />
</li>
<li>
<Checkbox label="Check me third" name="multiple" value="third" />
</li>
</UnstyledList>
</fieldset>
)
}
GroupedCheckbox.storyName = 'Grouped checkboxes'

type FormData = {
favourites: string[]
agree: string
}

export const WithFormsControl: StoryFn<CheckboxProps> = () => {
// Example with external forms library, react-hook-form
// eslint-disable-next-line @typescript-eslint/unbound-method
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>()
const [isSubmitted, updateIsSubmitted] = useState(false)
const [formData, updateFormData] = useState<FormData>(null)

const onSubmit = (data: FormData) => {
updateFormData(data)
updateIsSubmitted(true)
action('onSubmit')(data)
}

return (
<div>
<Typography variant="body_short" style={{ marginBottom: '1rem' }}>
Real life example with an external{' '}
<a
href="https://react-hook-form.com/"
rel="noreferrer noopener"
target="blank"
>
form library
</a>
</Typography>
<form onSubmit={handleSubmit(onSubmit)}>
{isSubmitted ? (
<>
<span>Submitted data:</span>
<p>{JSON.stringify(formData)}</p>
<Button
variant="outlined"
onClick={() => {
updateIsSubmitted(false)
updateFormData(null)
}}
>
Reset
</Button>
</>
) : (
<div>
<fieldset
style={{
display: 'flex',
flexDirection: 'column',
gap: '1rem',
}}
>
<legend>What&apos;s your favourites?</legend>
<CheckboxWrapper>
<Checkbox
name="favourites"
value="pineapple"
label="Pineapple"
{...register('favourites')}
/>
</CheckboxWrapper>
<CheckboxWrapper>
<Checkbox
name="favourites"
value="strawberry"
label="Strawberries"
{...register('favourites')}
/>
</CheckboxWrapper>
<CheckboxWrapper>
<Checkbox
name="favourites"
value="honeyMelon"
label="Honey melon"
{...register('favourites')}
/>
</CheckboxWrapper>
<CheckboxWrapper>
<Checkbox
name="favourites"
value="apples"
label="Apples"
{...register('favourites')}
/>
</CheckboxWrapper>
</fieldset>
<div style={{ marginTop: '1rem' }}>
<Checkbox
name="agree"
label="I understand that these preferences will not be saved*"
id="agree"
aria-invalid={errors.agree ? 'true' : 'false'}
aria-describedby="error-name-required"
aria-required
{...register('agree', { required: true })}
/>
</div>
<div style={{ padding: '1rem' }}>
<Button type="submit">I&apos;m done</Button>
</div>
</div>
)}
</form>
</div>
)
}
WithFormsControl.storyName = 'Example with React Hook Form'

export const AlternativeToLabel: StoryFn<CheckboxProps> = () => (
<Checkbox aria-label="This label is invisible, but read by screen-readers" />
)
AlternativeToLabel.storyName = 'Alternative to label'

export const TableCheckbox: StoryFn<CheckboxProps> = () => (
<Table>
<Table.Head>
<Table.Row>
<Table.Cell>Selected</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
{data.map((data) => (
<Table.Row key={data.number}>
<Table.Cell>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Checkbox aria-label={`Select ${data.description}`} />
</div>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
)
TableCheckbox.storyName = 'Table checkbox'
TableCheckbox.parameters = {
docs: {
description: {
story:
'Checkbox without label for use in tables. Wrap in a flex container with `justify-content: center` to center the checkbox in the cell.',
},
},
}

export const DarkMode: StoryFn<CheckboxProps> = () => {
return (
<div
data-color-scheme="dark"
style={{ padding: '2rem', background: '#0b0b0b' }}
>
<UnstyledList>
<li>
<Checkbox label="Check me in dark mode" />
</li>
<li>
<Checkbox label="I'm preselected" defaultChecked />
</li>
<li>
<Checkbox label="I'm disabled" disabled />
</li>
<li>
<Checkbox label="I'm disabled and checked" disabled defaultChecked />
</li>
<li>
<Checkbox label="I'm indeterminate" indeterminate />
</li>
</UnstyledList>
</div>
)
}
DarkMode.storyName = 'Dark mode'

export const Compact: StoryFn<CheckboxProps> = () => {
return (
<div data-density="comfortable">
<UnstyledList>
<li>
<Checkbox label="Comfortable density (24px)" />
</li>
<li>
<Checkbox label="Preselected" defaultChecked />
</li>
<li>
<Checkbox label="Disabled" disabled />
</li>
<li>
<Checkbox label="Indeterminate" indeterminate />
</li>
</UnstyledList>
</div>
)
}
Compact.storyName = 'Compact (Comfortable density)'
Compact.parameters = {
docs: {
description: {
story:
'Use `data-density="comfortable"` on a parent element to render checkboxes in a more compact size (24px height instead of 36px).',
},
},
}
Loading
Loading