-
Notifications
You must be signed in to change notification settings - Fork 645
Add ToggleSwitch component #1933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
81d5b63
f02595e
872bdb4
0316ed3
27dccc7
ca8950c
108b40f
40ff806
cebfa1b
176b5b8
d653469
30612f9
b475d8a
1d52067
37ed9ae
1971e6f
3b8d444
55817f2
22c19ae
f43c423
3ce5352
301e05a
8c32664
650a89f
1bd2e3f
5516bee
16a3c9c
f79f4a3
b9558bb
bbadab8
ce275cd
d3022e4
97cea00
09b735e
7afe8e3
2871849
5aee49d
fa9ff75
bb78093
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@primer/react': minor | ||
| --- | ||
|
|
||
| Adds a toggle switch component |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,225 @@ | ||||||
| --- | ||||||
| componentId: switch | ||||||
| title: Switch | ||||||
| description: Toggles a setting on or off, and immediately saves the change | ||||||
| status: Alpha | ||||||
| source: https://github.com/primer/react/blob/main/src/Switch.tsx | ||||||
| storybook: '/react/storybook?path=/story/switch-examples--default' | ||||||
| --- | ||||||
|
|
||||||
| ## Examples | ||||||
|
|
||||||
| ### Basic | ||||||
|
|
||||||
| ```jsx live | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={2} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch aria-labelledby="switchLabel" /> | ||||||
| </Box> | ||||||
| ``` | ||||||
|
|
||||||
| ### Uncontrolled (on) | ||||||
|
|
||||||
| ```jsx live | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={2} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch defaultOn aria-labelledby="switchLabel" /> | ||||||
| </Box> | ||||||
| ``` | ||||||
|
|
||||||
| ### Controlled | ||||||
|
|
||||||
| ```javascript noinline live | ||||||
| const Controlled = () => { | ||||||
| const [isOn, setIsOn] = React.useState(false) | ||||||
|
|
||||||
| const onClick = () => { | ||||||
| setIsOn(!isOn) | ||||||
| } | ||||||
|
|
||||||
| const handleSwitchChange = on => { | ||||||
| console.log(`new switch "on" state: ${on}`) | ||||||
| } | ||||||
|
|
||||||
| return ( | ||||||
| <> | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={2} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch onClick={onClick} onChange={handleSwitchChange} on={isOn} aria-labelledby="switchLabel" /> | ||||||
| </Box> | ||||||
| <p>The switch is {isOn ? 'on' : 'off'}</p> | ||||||
| </> | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| render(Controlled) | ||||||
| ``` | ||||||
|
|
||||||
| ### Small | ||||||
|
|
||||||
| ```jsx live | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={1} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch aria-labelledby="switchLabel" size="small" /> | ||||||
| </Box> | ||||||
| ``` | ||||||
|
|
||||||
| ### Delayed toggle with loading state | ||||||
|
|
||||||
| ```javascript noinline live | ||||||
| const LoadingToggle = () => { | ||||||
| const [isLoading, setIsLoading] = React.useState(false) | ||||||
| const [isOn, setIsOn] = React.useState(false) | ||||||
|
|
||||||
| async function switchSlowly(currentOn) { | ||||||
| await new Promise(resolve => setTimeout(resolve, 1500)) | ||||||
| return await !currentOn | ||||||
| } | ||||||
|
|
||||||
| async function onClick() { | ||||||
| setIsLoading(!isLoading) | ||||||
| const newSwitchState = await switchSlowly(isOn) | ||||||
| setIsOn(newSwitchState) | ||||||
| } | ||||||
|
|
||||||
| const handleSwitchChange = React.useCallback( | ||||||
| on => { | ||||||
| setIsLoading(false) | ||||||
| }, | ||||||
| [setIsLoading] | ||||||
| ) | ||||||
|
|
||||||
| return ( | ||||||
| <> | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={2} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch | ||||||
| aria-labelledby="switchLabel" | ||||||
| isLoading={isLoading} | ||||||
| on={isOn} | ||||||
| onClick={onClick} | ||||||
| onChange={handleSwitchChange} | ||||||
| /> | ||||||
| </Box> | ||||||
| <p>The switch is {isOn ? 'on' : 'off'}</p> | ||||||
| </> | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| render(LoadingToggle) | ||||||
| ``` | ||||||
|
|
||||||
| ### Disabled | ||||||
|
|
||||||
| ```jsx live | ||||||
| <Box display="flex" maxWidth="300px"> | ||||||
| <Box flexGrow={1} fontSize={2} fontWeight="bold" id="switchLabel"> | ||||||
| Notifications | ||||||
| </Box> | ||||||
| <Switch aria-labelledby="switchLabel" disabled /> | ||||||
| </Box> | ||||||
| ``` | ||||||
|
|
||||||
| ### With associated caption text | ||||||
|
|
||||||
| ```jsx live | ||||||
| <Box display="flex"> | ||||||
| <Box flexGrow={1}> | ||||||
| <Text fontSize={2} fontWeight="bold" id="switchLabel" display="block"> | ||||||
| Notifications | ||||||
| </Text> | ||||||
| <Text color="fg.subtle" fontSize={1} id="switchCaption" display="block"> | ||||||
| Notifications will be delivered via email and the GitHub notification center | ||||||
| </Text> | ||||||
| </Box> | ||||||
| <Switch aria-labelledby="switchLabel" aria-describedby="switchCaption" /> | ||||||
| </Box> | ||||||
| ``` | ||||||
|
|
||||||
| ### Left-aligned with label | ||||||
|
|
||||||
| ```jsx live | ||||||
| <> | ||||||
| <Text fontSize={2} fontWeight="bold" id="switchLabel" display="block" mb={1}> | ||||||
| Notifications | ||||||
| </Text> | ||||||
| <Switch statusLabelPosition="end" aria-labelledby="switchLabel" /> | ||||||
| </> | ||||||
| ``` | ||||||
mperrotti marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ## Props | ||||||
|
|
||||||
| /\*\* Whether the "on" and "off" labels should appear before or after the switch. | ||||||
|
||||||
|
|
||||||
| - **This should only be changed when the switch's alignment needs to be adjusted.** For example: It needs to be left-aligned because the label appears above it and the caption appears below it. | ||||||
| \*/ | ||||||
| statusLabelPosition?: 'start' | 'end' | ||||||
|
|
||||||
| <PropsTable> | ||||||
| <PropsTableRow name="aria-describedby" type="string" description="The id of the DOM node that describes the switch" /> | ||||||
| <PropsTableRow | ||||||
| name="aria-labelledby" | ||||||
| type="string" | ||||||
| required | ||||||
| description="The id of the DOM node that labels the switch" | ||||||
| /> | ||||||
colebemis marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| <PropsTableRow name="defaultOn" type="boolean" description="Uncontrolled - whether the switch is turned on" /> | ||||||
| <PropsTableRow name="disabled" type="boolean" description="Whether the switch is ready for user input" /> | ||||||
| <PropsTableRow name="isLoading" type="boolean" description="Whether the switch is ready for user input" /> | ||||||
|
||||||
| <PropsTableRow name="isLoading" type="boolean" description="Whether the switch is ready for user input" /> | |
| <PropsTableRow name="loading" type="boolean" description="Whether the switch is ready for user input" /> |
Also, is it intentional that the description is the same as the disabled prop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not intentional. Thanks for flagging that.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call this prop checked instead of on to align with the <input type="checkbox"> API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good with that.

Uh oh!
There was an error while loading. Please reload this page.