-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #450 from omnifed/448-feat-make-checkbox-recipe
448 feat make checkbox recipe
- Loading branch information
Showing
15 changed files
with
636 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
--- | ||
|
||
import { | ||
WhenToUseAdmonition | ||
} from '@/app/components/Admonition' | ||
import OverviewList from '@/app/components/OverviewList' | ||
|
||
## Use Cases | ||
|
||
<OverviewList intro="Users should be able to:" rules={[ | ||
'Navigate to a checkbox with assistive technology', | ||
'Toggle the checkbox on and off', | ||
'Get appropriate feedback based on input type documented under Interaction & style', | ||
]} /> | ||
|
||
## Interaction & Style | ||
|
||
The parent checkbox has three states: selected, unselected, and mixed. | ||
|
||
Checkboxes can be selected or unselected regardless of the state of the other checkboxes in a group. | ||
|
||
If some, but not all, child checkboxes are checked, the parent checkbox becomes mixed. Selecting an mixed parent checkbox will check all of its child checkboxes. | ||
|
||
## Keyboard Navigation | ||
|
||
| Keys | Actions | | ||
| -------- | --------------------------------------------------------------- | | ||
| Tab | To move to the next element in a group | | ||
| Space | Toggles a focused checkbox between selected and unselected | | ||
|
||
## Labeling Elements | ||
|
||
If the UI text is correctly linked to the checkbox, assistive tech (such as a screen reader) will read the UI text followed by the component's role. | ||
|
||
The accessibility label for an individual checkbox is typically the same as its adjacent text label. |
101 changes: 101 additions & 0 deletions
101
docs/app/react/checkbox/components/checkbox-preview.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
'use client' | ||
|
||
import { Checkmark } from '@cerberus-design/icons' | ||
import { Field, Label, Show } from '@cerberus-design/react' | ||
import { hstack, vstack } from '@cerberus/styled-system/patterns' | ||
import { | ||
checkbox, | ||
type CheckboxVariantProps, | ||
} from '@cerberus/styled-system/recipes' | ||
import { | ||
useCallback, | ||
useState, | ||
type ChangeEvent, | ||
type InputHTMLAttributes, | ||
} from 'react' | ||
|
||
type CheckboxProps = CheckboxVariantProps & | ||
Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'id'> & { | ||
id: string | ||
} | ||
|
||
function Checkbox(props: CheckboxProps) { | ||
const { size, ...nativeProps } = props | ||
const styles = checkbox({ size }) | ||
return ( | ||
<div className={styles.root}> | ||
<input {...nativeProps} className={styles.input} type="checkbox" /> | ||
<Show when={props.checked}> | ||
<span className={styles.icon}> | ||
<Checkmark /> | ||
</span> | ||
</Show> | ||
</div> | ||
) | ||
} | ||
|
||
interface OverviewState { | ||
legal: boolean | ||
terms: boolean | ||
} | ||
|
||
export function OverviewPreview() { | ||
const [checked, setChecked] = useState<OverviewState>({ | ||
legal: false, | ||
terms: false, | ||
}) | ||
|
||
const handleChange = useCallback((event: ChangeEvent<HTMLInputElement>) => { | ||
const key = event.currentTarget.name as keyof OverviewState | ||
setChecked((prev) => ({ | ||
...prev, | ||
[key]: !prev[key], | ||
})) | ||
}, []) | ||
|
||
return ( | ||
<div | ||
className={vstack({ | ||
alignItems: 'start', | ||
gap: '4', | ||
})} | ||
> | ||
<Field required> | ||
<Label | ||
className={hstack({ | ||
justify: 'flex-start', | ||
})} | ||
htmlFor="terms" | ||
size="sm" | ||
> | ||
<Checkbox | ||
checked={checked.terms} | ||
id="terms" | ||
name="terms" | ||
onChange={handleChange} | ||
size="md" | ||
/> | ||
I agree to the terms and conditions | ||
</Label> | ||
</Field> | ||
<Field> | ||
<Label | ||
className={hstack({ | ||
justify: 'flex-start', | ||
})} | ||
htmlFor="legal" | ||
size="sm" | ||
> | ||
<Checkbox | ||
checked={checked.legal} | ||
id="legal" | ||
name="legal" | ||
onChange={handleChange} | ||
size="md" | ||
/> | ||
I would like to receive marketing emails | ||
</Label> | ||
</Field> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Field, Label, Radio } from '@cerberus-design/react' | ||
import { css } from '@cerberus/styled-system/css' | ||
import { hstack } from '@cerberus/styled-system/patterns' | ||
|
||
export function DefaultRadioPreview() { | ||
return ( | ||
<Field> | ||
<Radio id="valid" name="states" value="valid" defaultChecked> | ||
<Label htmlFor="valid">Default (valid)</Label> | ||
</Radio> | ||
</Field> | ||
) | ||
} | ||
|
||
export function InvalidRadioPreview() { | ||
return ( | ||
<Field invalid> | ||
<Radio id="invalid" name="states" value="invalid" defaultChecked> | ||
<Label htmlFor="invalid">Invalid</Label> | ||
</Radio> | ||
</Field> | ||
) | ||
} | ||
|
||
export function DisabledRadioPreview() { | ||
return ( | ||
<Field disabled> | ||
<Radio id="disabled" name="states" value="disabled" defaultChecked> | ||
<Label htmlFor="disabled">Disabled</Label> | ||
</Radio> | ||
</Field> | ||
) | ||
} | ||
|
||
export function CustomRadioPreview() { | ||
return ( | ||
<Field> | ||
<Radio | ||
className={css({ | ||
borderColor: 'yellow', | ||
_groupHover: { | ||
bgColor: 'black', | ||
}, | ||
_checked: { | ||
bg: 'yellow', | ||
}, | ||
})} | ||
id="custom" | ||
name="states" | ||
value="custom" | ||
defaultChecked | ||
> | ||
<Label htmlFor="custom">Wu-Tang</Label> | ||
</Radio> | ||
</Field> | ||
) | ||
} | ||
|
||
export function OverviewRadioGroup() { | ||
return ( | ||
<fieldset | ||
className={hstack({ | ||
gap: '4', | ||
p: '4', | ||
rounded: 'xl', | ||
})} | ||
name="pet" | ||
role="radiogroup" | ||
> | ||
<Field> | ||
<Radio id="dog" name="pet" value="dog" defaultChecked> | ||
<Label htmlFor="dog">🐶 Dog</Label> | ||
</Radio> | ||
</Field> | ||
|
||
<Field> | ||
<Radio id="cat" name="pet" value="cat"> | ||
<Label htmlFor="cat">😸 Cat</Label> | ||
</Radio> | ||
</Field> | ||
|
||
<Field> | ||
<Radio id="both" name="pet" value="both"> | ||
<Label htmlFor="both">🐶😸 Both</Label> | ||
</Radio> | ||
</Field> | ||
</fieldset> | ||
) | ||
} | ||
|
||
export function OverviewRadioSizes() { | ||
return ( | ||
<fieldset | ||
className={hstack({ | ||
gap: '4', | ||
p: '4', | ||
rounded: 'xl', | ||
})} | ||
name="sizes" | ||
role="radiogroup" | ||
> | ||
<Field> | ||
<Radio id="sm" name="sizes" value="sm" size="sm"> | ||
<Label htmlFor="sm" size="sm"> | ||
Small | ||
</Label> | ||
</Radio> | ||
</Field> | ||
<Field> | ||
<Radio id="md" name="sizes" value="md"> | ||
<Label htmlFor="md">Medium (default)</Label> | ||
</Radio> | ||
</Field> | ||
</fieldset> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
--- | ||
npm: '@cerberus-design/react' | ||
source: 'components/Checkbox.tsx' | ||
recipe: 'slots/checkbox.ts' | ||
--- | ||
|
||
import { NoteAdmonition } from '@/app/components/Admonition' | ||
import CodePreview from '@/app/components/CodePreview' | ||
import { | ||
OverviewPreview | ||
} from '@/app/react/checkbox/components/checkbox-preview' | ||
|
||
```ts | ||
import { Field, Label, Checkbox } from '@cerberus-design/react' | ||
``` | ||
|
||
## Usage | ||
|
||
<CodePreview preview={<OverviewPreview />} /> | ||
|
||
## Customizing | ||
|
||
<CodePreview preview={<OverviewPreview />}> | ||
```tsx title="custom-radio.tsx" | ||
import { Field, Label, Radio } from '@cerberus-design/react' | ||
|
||
function CustomRadioPreview() { | ||
return ( | ||
<Field> | ||
<Radio | ||
className={css({ | ||
borderColor: 'yellow', | ||
_groupHover: { | ||
bgColor: 'black', | ||
}, | ||
_checked: { | ||
bg: 'yellow', | ||
}, | ||
})} | ||
id="custom" | ||
name="states" | ||
value="custom" | ||
defaultChecked | ||
> | ||
<Label htmlFor="custom">Wu-Tang</Label> | ||
</Radio> | ||
</Field> | ||
) | ||
} | ||
``` | ||
</CodePreview> | ||
|
||
## API | ||
|
||
<NoteAdmonition description="The Radio component must be used within a Field provider." /> | ||
|
||
```ts showLineNumbers=false | ||
export interface FieldProps { | ||
disabled?: boolean | ||
invalid?: boolean | ||
required?: boolean | ||
readOnly?: boolean | ||
} | ||
|
||
define function Field(props: PropsWithChildren<FieldProps>): ReactNode | ||
``` | ||
|
||
```ts showLineNumbers=false | ||
export interface RadioProps extends InputHTMLAttributes<HTMLInputElement> { | ||
id: string | ||
size?: 'sm' | 'md' | 'lg' | ||
} | ||
define function Radio(props: RadioProps): ReactNode | ||
``` | ||
|
||
### Props | ||
|
||
The Radio component accepts the following props: | ||
|
||
| Name | Default | Description | | ||
| -------- | ------- | ------------------------------------------------------------- | | ||
| id | | An identifier that is shared with the Label `htmlFor` attribute. | | ||
| size | `md` | The size of the input field. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
--- | ||
|
||
import CodePreview from '@/app/components/CodePreview' | ||
import OverviewList from '@/app/components/OverviewList' | ||
import { | ||
WhenToUseAdmonition, | ||
WhenNotToUseAdmonition, | ||
} from '@/app/components/Admonition' | ||
import { | ||
OverviewPreview | ||
} from '@/app/react/checkbox/components/checkbox-preview' | ||
|
||
## Usage | ||
|
||
Checkboxes visually group similar items effectively and take up less space than toggles. | ||
|
||
<WhenToUseAdmonition description="Use a checkbox when a user needs to opt-into multiple options in a list." /> | ||
|
||
<WhenNotToUseAdmonition description="When you need the user to select a single option use a Toggle." /> | ||
|
||
## Standard Checkbox | ||
|
||
<CodePreview preview={<OverviewPreview />} /> | ||
|
||
|
||
## Alternate selection controls | ||
|
||
Checkboxes, radio buttons, and toggles are the three main types of selection controls. They all help people make choices, like selecting options or switching settings on or off. | ||
|
||
Use checkboxes to select multiple related options in a list. | ||
|
||
Use radio buttons to select a single option in a list. | ||
|
||
Use toggles to select standalone or more verbose options in a list, like settings. |
Oops, something went wrong.