Skip to content

Commit 262589f

Browse files
committed
✨ feat: added toggle shape options
1 parent 3294ce0 commit 262589f

File tree

5 files changed

+256
-13
lines changed

5 files changed

+256
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import styled from '@emotion/styled';
2+
import React from 'react';
3+
import { Toggle, ToggleProps } from './Toggle';
4+
5+
export type StyledToggleProps = Omit<ToggleProps, 'contrast' | 'themeId'> & {
6+
// colors
7+
backgroundColor: string;
8+
buttonBackgroundColor: string;
9+
checkedBackgroundColor: string;
10+
checkedButtonBackgroundColor: string;
11+
checkedHoveredFocusRingColor: string;
12+
hoveredFocusRingColor: string;
13+
checkedHoveredFocusRingOpacity?: number;
14+
15+
// sizes
16+
toggleWidth?: number;
17+
toggleHeight?: number;
18+
toggleBorderWidth?: number;
19+
toggleFocusRingSize?: number;
20+
toggleLabelTopMargin?: number;
21+
22+
// shape
23+
toggleBorderRadius?: number;
24+
toggleButtonBorderRadius?: number;
25+
};
26+
27+
const BaseStyledToggle = styled(Toggle, {
28+
shouldForwardProp: (prop: string) =>
29+
![
30+
'backgroundColor',
31+
'buttonBackgroundColor',
32+
'checkedBackgroundColor',
33+
'checkedButtonBackgroundColor',
34+
'checkedHoveredFocusRingColor',
35+
'hoveredFocusRingColor',
36+
'checkedHoveredFocusRingOpacity',
37+
'toggleWidth',
38+
'toggleHeight',
39+
'toggleBorderWidth',
40+
'toggleFocusRingSize',
41+
'toggleLabelTopMargin',
42+
'toggleBorderRadius',
43+
'toggleButtonBorderRadius',
44+
].includes(prop),
45+
})<StyledToggleProps>`
46+
${({ backgroundColor }) => backgroundColor && `--toggle-background-color: ${backgroundColor};`}
47+
${({ buttonBackgroundColor }) =>
48+
buttonBackgroundColor && `--toggle-button-background-color: ${buttonBackgroundColor};`}
49+
${({ hoveredFocusRingColor }) =>
50+
hoveredFocusRingColor && `--toggle-unchecked-hovered-focus-ring-color: ${hoveredFocusRingColor};`}
51+
${({ checkedBackgroundColor }) =>
52+
checkedBackgroundColor && `--toggle-checked-background-color: ${checkedBackgroundColor};`}
53+
${({ checkedButtonBackgroundColor }) =>
54+
checkedButtonBackgroundColor && `--toggle-checked-button-background-color: ${checkedButtonBackgroundColor};`}
55+
${({ checkedHoveredFocusRingColor }) =>
56+
checkedHoveredFocusRingColor && `--toggle-checked-hovered-focus-ring-color: ${checkedHoveredFocusRingColor};`}
57+
${({ checkedHoveredFocusRingOpacity }) =>
58+
checkedHoveredFocusRingOpacity && `--toggle-checked-hovered-focus-ring-opacity: ${checkedHoveredFocusRingOpacity};`}
59+
60+
${({ toggleWidth }) => toggleWidth && `--toggle-width: ${toggleWidth}px;`}
61+
${({ toggleHeight }) => toggleHeight && `--toggle-height: ${toggleHeight}px;`}
62+
${({ toggleBorderWidth }) => toggleBorderWidth && `--toggle-border-width: ${toggleBorderWidth}px;`}
63+
${({ toggleFocusRingSize }) => toggleFocusRingSize && `--toggle-focus-ring-size: ${toggleFocusRingSize}px;`}
64+
${({ toggleLabelTopMargin }) => toggleLabelTopMargin && `--toggle-label-top-margin: ${toggleLabelTopMargin}px;`}
65+
66+
${({ toggleBorderRadius }) => toggleBorderRadius && `--toggle-border-radius: ${toggleBorderRadius}px;`}
67+
${({ toggleButtonBorderRadius }) =>
68+
toggleButtonBorderRadius && `--toggle-button-border-radius: ${toggleButtonBorderRadius}px;`}
69+
`;
70+
71+
/**
72+
* A styled toggle is an extension of the `Toggle` component
73+
* that will have a custom background and button color.
74+
*/
75+
export const StyledToggle = (props: StyledToggleProps) => <BaseStyledToggle {...props} unthemed />;
76+
77+
StyledToggle.displayName = 'StyledToggle';

src/components/Form/Toggle/Toggle.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type LocalToggleProps = ThemeProps & {
1919
onChange: (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => void;
2020
persistEvents?: boolean;
2121
reverse?: boolean;
22+
shape?: 'pill' | 'brick';
2223
size?: 'small';
2324
style?: React.CSSProperties;
2425
value?: string;
@@ -40,6 +41,7 @@ export function ToggleBase(
4041
onChange,
4142
persistEvents,
4243
reverse = false,
44+
shape = 'pill',
4345
size,
4446
themeId: initThemeId,
4547
unthemed = false,
@@ -78,6 +80,7 @@ export function ToggleBase(
7880
size && styles[`toggle--${size}`],
7981
themeId && !unthemed && styles[`toggle--${themeId}`],
8082
color && !unthemed && styles[`toggle--${color}`],
83+
shape && styles[`toggle--${shape}`],
8184
accessible && styles['is-accessible'],
8285
checked && styles['is-checked'],
8386
disabled && styles['is-disabled'],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { ArgsTable, Description, Primary, Stories, Subtitle, PRIMARY_STORY } from '@storybook/addon-docs';
2+
import { ComponentStory, ComponentMeta } from '@storybook/react';
3+
import React from 'react';
4+
import { themes } from 'config/themes';
5+
import { PageTitle } from 'stories/helpers/PageTitle';
6+
import { StyledToggle } from '../StyledToggle';
7+
import ToggleStory from './Toggle.stories';
8+
9+
const argTypes = { ...ToggleStory.argTypes };
10+
delete argTypes.themeId;
11+
12+
export default {
13+
...ToggleStory,
14+
title: 'Form/Toggle/StyledToggle',
15+
component: StyledToggle,
16+
argTypes: {
17+
toggleWidth: {
18+
table: {
19+
category: 'Size',
20+
},
21+
},
22+
toggleHeight: {
23+
table: {
24+
category: 'Size',
25+
},
26+
},
27+
toggleBorderRadius: {
28+
table: {
29+
category: 'Size',
30+
},
31+
},
32+
toggleBorderWidth: {
33+
table: {
34+
category: 'Size',
35+
},
36+
},
37+
toggleButtonBorderRadius: {
38+
table: {
39+
category: 'Size',
40+
},
41+
},
42+
toggleFocusRingSize: {
43+
table: {
44+
category: 'Size',
45+
},
46+
},
47+
toggleLabelTopMargin: {
48+
table: {
49+
category: 'Size',
50+
},
51+
},
52+
53+
backgroundColor: {
54+
table: {
55+
category: 'Styled',
56+
},
57+
},
58+
buttonBackgroundColor: {
59+
table: {
60+
category: 'Styled',
61+
},
62+
},
63+
checkedBackgroundColor: {
64+
table: {
65+
category: 'Styled',
66+
},
67+
},
68+
checkedButtonBackgroundColor: {
69+
table: {
70+
category: 'Styled',
71+
},
72+
},
73+
checkedHoveredFocusRingColor: {
74+
table: {
75+
category: 'Styled',
76+
},
77+
},
78+
hoveredFocusRingColor: {
79+
table: {
80+
category: 'Styled',
81+
},
82+
},
83+
checkedHoveredFocusRingOpacity: {
84+
table: {
85+
category: 'Styled',
86+
},
87+
},
88+
...argTypes,
89+
},
90+
parameters: {
91+
...ToggleStory.parameters,
92+
docs: {
93+
...ToggleStory.parameters?.docs,
94+
page: () => (
95+
<React.Fragment>
96+
<PageTitle src="components/Toggle" title="StyledToggle" />
97+
<Subtitle />
98+
<Description />
99+
<Primary />
100+
<ArgsTable story={PRIMARY_STORY} />
101+
<Stories />
102+
</React.Fragment>
103+
),
104+
},
105+
},
106+
} as ComponentMeta<typeof StyledToggle>;
107+
108+
const Template: ComponentStory<typeof StyledToggle> = args => <StyledToggle {...args} />;
109+
110+
const defaultArgs = {
111+
'aria-label': 'Super fantastic toggle',
112+
checked: true,
113+
children: 'Super fantastic label',
114+
contrast: false,
115+
disabled: false,
116+
full: false,
117+
persistEvents: false,
118+
reverse: false,
119+
120+
backgroundColor: themes.light['color-FG0-O20'],
121+
buttonBackgroundColor: themes.light['color-BG05'],
122+
hoveredFocusRingColor: themes.light['color-FG0-O40'],
123+
checkedBackgroundColor: themes.light['color-P60'],
124+
checkedButtonBackgroundColor: themes.light['color-BG05'],
125+
checkedHoveredFocusRingColor: themes.light['color-P60'],
126+
checkedHoveredFocusRingOpacity: undefined,
127+
};
128+
129+
export const Default = Template.bind({});
130+
Default.args = {
131+
...defaultArgs,
132+
};
133+
134+
/*
135+
Default.parameters = {
136+
jest: ['StyledToggle.test.js', 'Toggle.test.js'],
137+
};
138+
*/
139+
140+
Default.argTypes = {
141+
style: { table: { disable: true } },
142+
unthemed: { table: { disable: true } },
143+
};

src/components/Form/Toggle/stories/Toggle.stories.tsx

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ComponentStory, ComponentMeta } from '@storybook/react';
22
import React from 'react';
33
import { FormComponentDemo } from '../../stories/helpers/FormComponentDemo';
4-
import { Toggle } from '../Toggle';
4+
import { Toggle, ToggleProps } from '../Toggle';
55
import ToggleDocumentation from './Toggle.docs.mdx';
66

77
export default {
@@ -24,8 +24,16 @@ export default {
2424
category: 'Appearance',
2525
},
2626
},
27+
shape: {
28+
table: {
29+
category: 'Appearance',
30+
},
31+
},
2732
size: {
28-
option: { type: 'radio' },
33+
options: ['small', undefined],
34+
contol: {
35+
option: { type: 'radio' },
36+
},
2937
table: {
3038
category: 'Appearance',
3139
},
@@ -130,6 +138,7 @@ const defaultArgs = {
130138
full: false,
131139
persistEvents: false,
132140
reverse: false,
141+
shape: 'pill' as ToggleProps['shape'],
133142
unthemed: false,
134143
};
135144

src/components/Form/Toggle/styles/Toggle.module.css

+22-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
$default-toggle-width: 40px;
33
$default-toggle-height: 24px;
44
$default-toggle-border-width: 3px;
5+
$default-toggle-focus-ring-size: 4px;
56
$button-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
67

78
align-items: flex-start;
@@ -34,7 +35,7 @@ input.toggleInput {
3435

3536
.toggleLabel {
3637
margin-left: calc(0.35 * var(--toggle-width, $default-toggle-width));
37-
margin-top: 5px;
38+
margin-top: var(--toggle-label-top-margin, 5px);
3839

3940
.toggle--reverse & {
4041
margin-left: 0;
@@ -50,7 +51,7 @@ input.toggleInput {
5051

5152
&:before {
5253
background-color: var(--toggle-background-color);
53-
border-radius: var(--toggle-height, $default-toggle-height);
54+
border-radius: var(--toggle-border-radius);
5455
content: '';
5556
height: var(--toggle-height, $default-toggle-height);
5657
left: 0;
@@ -63,7 +64,7 @@ input.toggleInput {
6364

6465
&:after {
6566
background-color: var(--toggle-button-background-color);
66-
border-radius: 50%;
67+
border-radius: var(--toggle-button-border-radius);
6768
bottom: var(--toggle-border-width, $default-toggle-border-width);
6869
box-shadow: $button-box-shadow;
6970
content: '';
@@ -105,7 +106,7 @@ input.toggleInput {
105106
}
106107

107108
.toggleInputContainerFocusRing {
108-
border-radius: var(--toggle-height, $default-toggle-height);
109+
border-radius: var(--toggle-border-radius);
109110
bottom: 0;
110111
left: 0;
111112
position: absolute;
@@ -118,25 +119,35 @@ input.toggleInput {
118119
.toggle:hover,
119120
.toggle.is-focused {
120121
.toggleInputContainerFocusRing {
121-
@mixin makeFocusRing var(--toggle-unchecked-hovered-focus-ring-color);
122+
@mixin makeFocusRing var(--toggle-unchecked-hovered-focus-ring-color),
123+
var(--toggle-focus-ring-size, $default-toggle-focus-ring-size);
122124
}
123125

124126
&.is-checked {
125127
.toggleInputContainerFocusRing {
126-
@mixin makeFocusRing var(--toggle-checked-hovered-focus-ring-color);
128+
@mixin makeFocusRing var(--toggle-checked-hovered-focus-ring-color),
129+
var(--toggle-focus-ring-size, $default-toggle-focus-ring-size);
127130
@mixin setFocusRingOpacity var(--toggle-checked-hovered-focus-ring-opacity, 0.2);
128131
}
129132
}
130133
}
131134

135+
.toggle--pill {
136+
--toggle-border-radius: var(--toggle-height, $default-toggle-height);
137+
--toggle-button-border-radius: 50%;
138+
}
139+
140+
.toggle--brick {
141+
--toggle-border-radius: $border-radius-medium;
142+
--toggle-button-border-radius: $border-radius-small;
143+
}
144+
132145
.toggle--small {
133146
--toggle-width: 32px;
134147
--toggle-height: 20px;
135-
--toggle-border-width: 2px;
136-
137-
.toggleLabel {
138-
margin-top: 3px;
139-
}
148+
--toggle-border-width: 3px;
149+
--toggle-focus-ring-size: 3px;
150+
--toggle-label-top-margin: 3px;
140151
}
141152

142153
.toggle--primary {

0 commit comments

Comments
 (0)