Skip to content

Commit

Permalink
Checkbox (#1511)
Browse files Browse the repository at this point in the history
* fix: use tailwind for checkbox stories

* fix: remove state from checkbox stories

* fix: use clip-path instead of <svg> for checkmarks

Let's us style based on the underlying input's state, instead of
a prop. This paves the way towards allowing uncontrolled
checkboxes.

* feat: allow uncontrolled checkboxes

* fix: note a question about indeterminate checkboxes
  • Loading branch information
ahuth authored Mar 6, 2023
1 parent 8268d8e commit 6fc50f5
Show file tree
Hide file tree
Showing 13 changed files with 571 additions and 2,038 deletions.
7 changes: 0 additions & 7 deletions src/components/Checkbox/Checkbox.module.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
/*------------------------------------*\
# CHECKBOX
\*------------------------------------*/

/**
* A custom individual checkbox control
*/
.checkbox {
display: flex;
gap: var(--eds-size-1);
Expand Down
14 changes: 0 additions & 14 deletions src/components/Checkbox/Checkbox.stories.module.css

This file was deleted.

85 changes: 41 additions & 44 deletions src/components/Checkbox/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import { Checkbox } from './Checkbox';
import CheckboxInput from '../CheckboxInput';
import CheckboxLabel from '../CheckboxLabel';
import styles from './Checkbox.stories.module.css';

const defaultArgs = {
disabled: false,
Expand All @@ -24,9 +23,8 @@ export default {
decorators: [
(Story) => (
<div
style={{
margin: '0.25rem', // Provides spacing to see focus indicator around checkbox.
}}
// Provides spacing to see focus indicator around checkbox.
className="m-1"
>
<Story />
</div>
Expand All @@ -36,47 +34,55 @@ export default {

type Args = React.ComponentProps<typeof Checkbox>;

/**
* Controlled example to make checked stories interactive.
*/
function CheckboxExample(args: Args) {
const [checked, setChecked] = React.useState<
boolean | 'indeterminate' | undefined
>(args.checked);
const handleChange = () => {
setChecked(!checked);
};

return <Checkbox checked={checked} onChange={handleChange} {...args} />;
}

export const Default: StoryObj<Args> = {
render: (args) => <CheckboxExample {...args} />,
};
export const Default: StoryObj<Args> = {};

export const Checked: StoryObj<Args> = {
render: (args) => <CheckboxExample {...args} checked />,
...Default,
args: {
defaultChecked: true,
},
};

export const Medium: StoryObj<Args> = {
render: (args) => <CheckboxExample {...args} size="md" />,
...Default,
args: {
size: 'md',
},
};

export const MediumChecked: StoryObj<Args> = {
render: (args) => <CheckboxExample {...args} checked size="md" />,
...Medium,
args: {
...Checked.args,
...Medium.args,
},
};

export const Large: StoryObj<Args> = {
...Default,
args: {
size: 'lg',
},
};

export const LargeChecked: StoryObj<Args> = {
...Large,
args: {
...Checked.args,
...Large.args,
},
};

export const Indeterminate: StoryObj<Args> = {
args: {
checked: 'indeterminate',
readOnly: true,
readOnly: true, // Prevent console warning about the field being read only
},
render: (args) => <CheckboxExample {...args} />,
};

export const Disabled: StoryObj<Args> = {
render: () => (
<table style={{ borderSpacing: '2rem' }}>
<table className="border-spacing-8">
<tbody>
{[false, true, 'indeterminate' as const].map((checked, i) => (
// FIXME
Expand Down Expand Up @@ -106,14 +112,14 @@ export const WithoutVisibleLabel: StoryObj<Args> = {
label: undefined,
},
render: (args) => (
<>
<div className="flex flex-col gap-2">
<Checkbox {...args} readOnly />
<Checkbox {...args} checked readOnly />
<Checkbox {...args} checked="indeterminate" readOnly />
<Checkbox {...args} disabled />
<Checkbox {...args} checked disabled />
<Checkbox {...args} checked="indeterminate" disabled />
</>
</div>
),
};

Expand All @@ -122,7 +128,7 @@ export const LongLabels = {
const label = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';

return (
<div className={styles['longlabels--grid']}>
<div className="grid w-80 grid-cols-2 gap-4">
<Checkbox label={label} readOnly />
<Checkbox label={label} readOnly size="md" />
<Checkbox disabled label={label} />
Expand All @@ -137,22 +143,13 @@ export const LongLabels = {
},
};

function CheckboxInputExample() {
const [checked, setChecked] = React.useState<
boolean | 'indeterminate' | undefined
>(false);
const handleChange = () => {
setChecked(!checked);
};

return <CheckboxInput checked={checked} id="test" onChange={handleChange} />;
}

export const WithCustomPositioning = {
render: () => (
<div style={{ display: 'flex', alignItems: 'center' }}>
<CheckboxLabel htmlFor="test">Label on Left</CheckboxLabel>
<CheckboxInputExample />
<div className="flex items-center">
<CheckboxLabel className="mr-2" htmlFor="test">
Label on Left
</CheckboxLabel>
<CheckboxInput id="test" />
</div>
),
};
4 changes: 1 addition & 3 deletions src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
const generatedId = useId();
const checkboxId = id || generatedId;

const componentClassName = clsx(styles['checkbox'], className);

return (
<div className={componentClassName}>
<div className={clsx(className, styles.checkbox)}>
<CheckboxInput
disabled={disabled}
id={checkboxId}
Expand Down
Loading

0 comments on commit 6fc50f5

Please sign in to comment.