Skip to content

Commit 763fbdd

Browse files
authored
Merge pull request #1328 from undp/issue-1299-create-new-switch-component
Added switch component (#1299)
2 parents 2e0a2aa + 5773b44 commit 763fbdd

11 files changed

+374
-2
lines changed

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React, { useEffect, useState } from 'react';
2+
import './switch.scss';
3+
import '../../../../Atom/Icons/icons.scss';
4+
5+
export function Switch({
6+
size = 'default',
7+
state = 'default',
8+
isToggled = true,
9+
showIcon = false,
10+
showLabel = false,
11+
showValue = false,
12+
onToggle,
13+
label = 'Label:',
14+
valueOn = 'On',
15+
valueOff = 'Off',
16+
...args
17+
}) {
18+
const [toggled, setToggled] = useState(isToggled);
19+
20+
useEffect(() => {
21+
setToggled(isToggled);
22+
}, [isToggled]);
23+
24+
const handleToggle = () => {
25+
if (state !== 'disabled') {
26+
setToggled(!toggled);
27+
if (onToggle) onToggle(!toggled);
28+
}
29+
};
30+
31+
const switchClass = `
32+
switch
33+
switch--${size}
34+
switch--${state}
35+
${toggled ? 'switch--toggled' : 'switch--untoggled'}
36+
`;
37+
38+
return (
39+
<div className={switchClass} onClick={handleToggle} {...args}>
40+
{showLabel && <div className="switch__label">{label}</div>}
41+
<div className='switch__wrapper'>
42+
<button
43+
className="switch__track"
44+
tabIndex={0}
45+
aria-label={label}
46+
aria-pressed={toggled}
47+
>
48+
{showIcon && <div className='switch__icon'/>}
49+
<div className="switch__thumb">
50+
</div>
51+
</button>
52+
{showValue && (
53+
<span className="switch__value">
54+
{toggled ? valueOn : valueOff}
55+
</span>
56+
)}
57+
</div>
58+
</div>
59+
);
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { Meta, Story, Canvas } from "@storybook/addon-docs";
2+
import { Switch } from "./Switch";
3+
4+
export const getCaptionForLocale = (locale) => {
5+
switch (locale) {
6+
case "english":
7+
return { on: "On", off: "Off", label: "Label:" };
8+
case "ukrainian":
9+
return { on: "Увімкн.", off: "Вимкн.", label: "Перемикач" };
10+
case "arabic":
11+
return { on: "على", off: "إيقاف", label: "تبديل" };
12+
case "burmese":
13+
return { on: "ဖွင့်", off: "ပိတ်", label: "ပြောင်း" };
14+
case "japanese":
15+
return { on: "オン", off: "オフ", label: "スイッチラベル" };
16+
default:
17+
return { on: "On", off: "Off", label: "Label:" };
18+
}
19+
};
20+
21+
<Meta
22+
title="Components/UI components/Switch"
23+
argTypes={{
24+
size: {
25+
name: "Size",
26+
options: ["default", "small"],
27+
control: { type: "inline-radio" },
28+
},
29+
state: {
30+
name: "State",
31+
options: ["default", "disabled"],
32+
control: { type: "inline-radio" },
33+
},
34+
isToggled: {
35+
name: "Is Toggled",
36+
control: { type: "boolean" },
37+
},
38+
showIcon: {
39+
name: "Show Icon",
40+
control: { type: "boolean" },
41+
},
42+
showLabel: {
43+
name: "Show Label",
44+
control: { type: "boolean" },
45+
},
46+
showValue: {
47+
name: "Show Value",
48+
control: { type: "boolean" },
49+
},
50+
}}
51+
args={{
52+
size: "default",
53+
state: "default",
54+
isToggled: true,
55+
showIcon: false,
56+
showLabel: false,
57+
showValue: false,
58+
}}
59+
/>
60+
61+
# Switch Component
62+
63+
A switch allows a user to toggle between two mutually exclusive options.
64+
65+
### Overview
66+
67+
The switch component is used to toggle between two mutually exclusive options, such as on and off. It can be customized in size, state, and display options.
68+
69+
#### When to use:
70+
71+
- Use a switch when you need to allow the user to toggle between two states.
72+
- Switches are commonly used in settings and forms.
73+
74+
### Formatting
75+
76+
#### Default
77+
78+
The switch component can have different sizes and states.
79+
80+
1. Sizes: `default`, `small`
81+
2. States: `default`, `disabled`
82+
83+
### Content
84+
85+
### Behaviors
86+
87+
#### States
88+
89+
Switch components can have the following states:
90+
91+
- **Default**: The switch is in its normal state.
92+
- **Focus**: The switch is focused, often indicated with a border or shadow.
93+
- **Disabled**: The switch is disabled and cannot be toggled.
94+
95+
<Canvas>
96+
<Story name="Switch">
97+
{(args, { globals: { locale } }) => {
98+
const caption = getCaptionForLocale(locale);
99+
return (
100+
<Switch
101+
{...args}
102+
label={caption.label}
103+
valueOn={caption.on}
104+
valueOff={caption.off}
105+
/>
106+
);
107+
}}
108+
</Story>
109+
</Canvas>
110+
111+
### Usage
112+
113+
- Copy the HTML from the HTML Tab of the canvas for the switch component from the controls.
114+
- Include the CSS files listed below concerning the switch component.
115+
116+
### CSS and JS References
117+
118+
#### CSS:
119+
120+
Add the base layout style from
121+
122+
- https://cdn.jsdelivr.net/npm/@undp/design-system/docs/css/base-minimal.min.css
123+
- https://cdn.jsdelivr.net/npm/@undp/design-system/docs/css/components/switch.min.css
124+
125+
Use the above CSS concerning the switch component.
126+
127+
#### JS:
128+
129+
NA
130+
131+
### Interactions
132+
133+
- The switch changes its appearance when toggled.
134+
- The switch changes its appearance when hovered or focused.
135+
136+
### Changelog
137+
138+
1.0 — Released component
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
@import '../../../../assets/scss/variables';
2+
@import '../../../../assets/scss/mixins';
3+
4+
.switch {
5+
display: flex;
6+
flex-direction: column;
7+
cursor: pointer;
8+
user-select: none;
9+
10+
&__track {
11+
background-color: $color-gray-400;
12+
border-radius: 20px;
13+
position: relative;
14+
width: 3.75rem;
15+
height: 1.875rem;
16+
gap: 5px;
17+
border: none;
18+
cursor: pointer;
19+
20+
.switch--small & {
21+
width: 2.5rem;
22+
height: 1.25rem;
23+
}
24+
25+
&:focus {
26+
box-shadow: 0 0 4px $color-blue-800;
27+
outline: none;
28+
}
29+
30+
}
31+
32+
&__thumb {
33+
width: 1.375rem;
34+
height: 1.375rem;
35+
background-color: $color-white;
36+
border-radius: 50%;
37+
position: absolute;
38+
top: 50%;
39+
transform: translate(-10%, -50%);
40+
transition: left 250ms ease;
41+
42+
.switch--small & {
43+
width: 0.875rem;
44+
height: 0.875rem;
45+
}
46+
47+
.switch--toggled & {
48+
left: 60%;
49+
}
50+
51+
.switch--untoggled & {
52+
left: 10%;
53+
}
54+
}
55+
56+
&__icon {
57+
position: absolute;
58+
transform: translate(-20%, -50%);
59+
top: 50%;
60+
display: flex;
61+
align-items: center;
62+
63+
.switch--small & {
64+
width: 0.5rem;
65+
}
66+
}
67+
68+
&__label {
69+
font-size: 1.25rem;
70+
display: block;
71+
margin-bottom: $spacing-03;
72+
73+
.switch--small & {
74+
font-size: 1rem;
75+
}
76+
}
77+
78+
&__value {
79+
font-size: 1rem;
80+
font-weight: 400;
81+
}
82+
83+
&--toggled {
84+
.switch__track {
85+
background-color: $color-blue-800;
86+
}
87+
88+
.switch__icon {
89+
background: transparent url(~icons/checkmark-regular.svg) no-repeat left center;
90+
color: $color-gray-400;
91+
left: 20%;
92+
width: 13px;
93+
height: 13px;
94+
}
95+
96+
&.switch--small .switch__icon {
97+
background: transparent url(~icons/checkmark-small.svg) no-repeat left center;
98+
width: 0.625rem;
99+
height: 0.625rem;
100+
}
101+
}
102+
103+
&--untoggled {
104+
.switch__icon {
105+
background: transparent url(~icons/disabled-x-regular.svg) no-repeat right center;
106+
width: 0.625rem;
107+
height: 0.625rem;
108+
right: 15%;
109+
color: $color-gray-600;
110+
}
111+
112+
&.switch--small .switch__icon {
113+
background: transparent url(~icons/disabled-x-small.svg) no-repeat right center;
114+
width: 0.5rem;
115+
}
116+
}
117+
118+
&--disabled {
119+
cursor: not-allowed;
120+
121+
.switch__track {
122+
background-color: $color-gray-400;
123+
124+
&:focus {
125+
box-shadow: none;
126+
outline: none;
127+
}
128+
}
129+
130+
.switch__thumb {
131+
background-color: $color-gray-500;
132+
}
133+
134+
&.switch--toggled {
135+
&.switch--small {
136+
.switch__icon {
137+
background: transparent url(~icons/disabled-checkmark-small.svg) no-repeat left center;
138+
width: 0.625rem;
139+
height: 0.625rem;
140+
}
141+
}
142+
143+
.switch__icon {
144+
background: transparent url(~icons/disabled-checkmark-regular.svg) no-repeat left center;
145+
width: 13px;
146+
height: 13px;
147+
}
148+
}
149+
}
150+
151+
&__wrapper {
152+
display: flex;
153+
align-items: center;
154+
gap: 5px;
155+
}
156+
}

stories/Documentation/Intro.stories.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const data = [
1616
button: "READ MORE",
1717
type: "color",
1818
scale: "medium",
19-
url: "/?path=/story/getting-started-how-to-use-our-design-system--page",
19+
url: "/?path=/docs/getting-started-how-to-use-our-design-system--docs",
2020
},
2121
{
2222
contenttile: "02",
Loading
+3
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)