@indico-data/design-system 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.css +91 -15
- package/lib/index.d.ts +14 -2
- package/lib/index.esm.css +91 -15
- package/lib/index.esm.js +7 -2
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +7 -1
- package/lib/index.js.map +1 -1
- package/lib/src/components/forms/toggle/Toggle.d.ts +12 -0
- package/lib/src/components/forms/toggle/Toggle.stories.d.ts +6 -0
- package/lib/src/components/forms/toggle/__tests__/Toggle.test.d.ts +1 -0
- package/lib/src/components/forms/toggle/index.d.ts +1 -0
- package/lib/src/components/index.d.ts +1 -0
- package/lib/src/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/forms/checkbox/Checkbox.mdx +6 -1
- package/src/components/forms/checkbox/Checkbox.stories.tsx +10 -4
- package/src/components/forms/checkbox/Checkbox.tsx +1 -1
- package/src/components/forms/checkbox/styles/Checkbox.scss +15 -15
- package/src/components/forms/toggle/Toggle.mdx +15 -0
- package/src/components/forms/toggle/Toggle.stories.tsx +126 -0
- package/src/components/forms/toggle/Toggle.tsx +51 -0
- package/src/components/forms/toggle/__tests__/Toggle.test.tsx +19 -0
- package/src/components/forms/toggle/index.ts +1 -0
- package/src/components/forms/toggle/styles/Toggle.scss +72 -0
- package/src/components/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/styles/index.scss +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ToggleProps {
|
|
3
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
4
|
+
id: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
name: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
isChecked?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const Toggle: ({ ref, id, label, name, value, onChange, isDisabled, isChecked, ...rest }: ToggleProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Toggle } from './Toggle';
|
package/lib/src/index.d.ts
CHANGED
|
@@ -10,3 +10,4 @@ export { Table } from './components/table';
|
|
|
10
10
|
export { Input } from './components/forms/input';
|
|
11
11
|
export { Radio as RadioInput } from './components/forms/radio';
|
|
12
12
|
export { Checkbox } from './components/forms/checkbox';
|
|
13
|
+
export { Toggle as ToggleInput } from './components/forms/toggle';
|
package/package.json
CHANGED
|
@@ -8,10 +8,15 @@ This component may be used on it's on or as part of a group. The source code is
|
|
|
8
8
|
|
|
9
9
|
<Canvas of={Checkbox.Default} source={{
|
|
10
10
|
code: `
|
|
11
|
+
const [isChecked, setIsChecked] = useState<boolean>(false);
|
|
12
|
+
const handleChange = () => {
|
|
13
|
+
setIsChecked(!isChecked);
|
|
14
|
+
};
|
|
15
|
+
|
|
11
16
|
<div>
|
|
12
17
|
<h2 className="mb-2">Checkbox Buttons</h2>
|
|
13
18
|
<Checkbox
|
|
14
|
-
onChange={
|
|
19
|
+
onChange={handleChange}
|
|
15
20
|
id={id}
|
|
16
21
|
label={label}
|
|
17
22
|
name={name}
|
|
@@ -78,7 +78,7 @@ const meta: Meta = {
|
|
|
78
78
|
defaultValue: { summary: false },
|
|
79
79
|
},
|
|
80
80
|
isChecked: {
|
|
81
|
-
control:
|
|
81
|
+
control: false,
|
|
82
82
|
description: 'Toggles the checked state of the checkbox field when true',
|
|
83
83
|
table: {
|
|
84
84
|
category: 'Props',
|
|
@@ -111,18 +111,24 @@ export const Default: Story = {
|
|
|
111
111
|
isChecked: false,
|
|
112
112
|
},
|
|
113
113
|
render: (args) => {
|
|
114
|
+
const [isChecked, setIsChecked] = useState<boolean>(false);
|
|
115
|
+
|
|
116
|
+
const handleChange = () => {
|
|
117
|
+
setIsChecked(!isChecked);
|
|
118
|
+
};
|
|
119
|
+
|
|
114
120
|
return (
|
|
115
121
|
<div>
|
|
116
|
-
<h2 className="mb-2">Checkbox
|
|
122
|
+
<h2 className="mb-2">Checkbox Button</h2>
|
|
117
123
|
<Checkbox
|
|
118
124
|
key={args.id}
|
|
119
|
-
onChange={
|
|
125
|
+
onChange={handleChange}
|
|
120
126
|
id={args.id}
|
|
121
127
|
label={args.label}
|
|
122
128
|
name={args.name}
|
|
123
129
|
value={args.value}
|
|
124
130
|
isDisabled={args.isDisabled}
|
|
125
|
-
isChecked={
|
|
131
|
+
isChecked={isChecked}
|
|
126
132
|
/>
|
|
127
133
|
</div>
|
|
128
134
|
);
|
|
@@ -28,7 +28,6 @@ export const Checkbox = ({
|
|
|
28
28
|
<div className="checkbox-wrapper">
|
|
29
29
|
<input
|
|
30
30
|
data-testid={`form-checkbox-input-${name}`}
|
|
31
|
-
{...rest}
|
|
32
31
|
className="checkbox-input"
|
|
33
32
|
type="checkbox"
|
|
34
33
|
id={id}
|
|
@@ -41,6 +40,7 @@ export const Checkbox = ({
|
|
|
41
40
|
tabIndex={0}
|
|
42
41
|
aria-describedby={id}
|
|
43
42
|
aria-label={label}
|
|
43
|
+
{...rest}
|
|
44
44
|
/>
|
|
45
45
|
<label
|
|
46
46
|
htmlFor={id}
|
|
@@ -30,21 +30,21 @@
|
|
|
30
30
|
.checkbox-input-label {
|
|
31
31
|
cursor: pointer;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
.checkbox-input:checked,
|
|
34
|
+
.checkbox-input:not(:checked) {
|
|
35
35
|
position: absolute;
|
|
36
36
|
left: -9999px;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
.checkbox-input:checked + label,
|
|
39
|
+
.checkbox-input:not(:checked) + label {
|
|
40
40
|
position: relative;
|
|
41
41
|
padding-left: var(--pf-padding-7);
|
|
42
42
|
cursor: pointer;
|
|
43
43
|
line-height: 20px;
|
|
44
44
|
display: inline-block;
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
.checkbox-input:checked + label:before,
|
|
47
|
+
.checkbox-input:not(:checked) + label:before {
|
|
48
48
|
content: '';
|
|
49
49
|
position: absolute;
|
|
50
50
|
left: 0;
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
border-radius: var(--pf-rounded);
|
|
56
56
|
background: var(--pf-checkbox-background-color);
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
.checkbox-input:checked + label:after,
|
|
59
|
+
.checkbox-input:not(:checked) + label:after {
|
|
60
60
|
content: '';
|
|
61
61
|
width: 12px;
|
|
62
62
|
height: 12px;
|
|
@@ -68,31 +68,31 @@
|
|
|
68
68
|
-webkit-transition: all 0.2s ease;
|
|
69
69
|
transition: all 0.2s ease;
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
.checkbox-input:not(:checked) + label:after {
|
|
72
72
|
opacity: 0;
|
|
73
73
|
-webkit-transform: scale(0);
|
|
74
74
|
transform: scale(0);
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
.checkbox-input:checked + label:after {
|
|
77
77
|
opacity: 1;
|
|
78
78
|
-webkit-transform: scale(1);
|
|
79
79
|
transform: scale(1);
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
.checkbox-input:disabled,
|
|
82
|
+
.checkbox-input:disabled + label {
|
|
83
83
|
cursor: not-allowed;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
.checkbox-input:disabled + label {
|
|
87
87
|
color: var(--pf-checkbox-disabled-color);
|
|
88
88
|
opacity: 0.5;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
.checkbox-input:disabled + label:before {
|
|
92
92
|
border-color: var(--pf-checkbox-disabled-color);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
.checkbox-input:disabled + label:after {
|
|
96
96
|
background: var(--pf-checkbox-disabled-color);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as Toggle from './Toggle.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Forms/Toggle" name="Toggle" />
|
|
5
|
+
|
|
6
|
+
# Toggle
|
|
7
|
+
|
|
8
|
+
Under the hood, Toggle component works the same as a regular Checkbox. Through CSS, we style the checkbox to resemble an on/off switch.
|
|
9
|
+
|
|
10
|
+
<Canvas of={Toggle.Default} source={{
|
|
11
|
+
}} />
|
|
12
|
+
<Controls of={Toggle.Default} />
|
|
13
|
+
|
|
14
|
+
## Note
|
|
15
|
+
Because of storybook limitations, the "isChecked" prop is not working in the storybook. In addition, neither is the on click. Please refer to the [default](http://localhost:6006/?path=/story/forms-toggle--default) example on the forms section.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Toggle } from './Toggle';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const meta: Meta = {
|
|
6
|
+
title: 'Forms/Toggle',
|
|
7
|
+
component: Toggle,
|
|
8
|
+
argTypes: {
|
|
9
|
+
isChecked: {
|
|
10
|
+
control: false,
|
|
11
|
+
description: 'This is the state of the toggle',
|
|
12
|
+
table: {
|
|
13
|
+
category: 'Props',
|
|
14
|
+
type: {
|
|
15
|
+
summary: 'boolean',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultValue: { summary: false },
|
|
19
|
+
},
|
|
20
|
+
ref: {
|
|
21
|
+
table: {
|
|
22
|
+
disable: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
onChange: {
|
|
26
|
+
control: false,
|
|
27
|
+
description: 'onChange event handler',
|
|
28
|
+
table: {
|
|
29
|
+
category: 'Callbacks',
|
|
30
|
+
type: {
|
|
31
|
+
summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
action: 'onChange',
|
|
35
|
+
},
|
|
36
|
+
label: {
|
|
37
|
+
control: 'text',
|
|
38
|
+
description: 'The label for the toggle field',
|
|
39
|
+
table: {
|
|
40
|
+
category: 'Props',
|
|
41
|
+
type: {
|
|
42
|
+
summary: 'string',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultValue: { summary: '' },
|
|
46
|
+
},
|
|
47
|
+
name: {
|
|
48
|
+
control: 'text',
|
|
49
|
+
description: 'The name for the toggle field',
|
|
50
|
+
table: {
|
|
51
|
+
category: 'Props',
|
|
52
|
+
type: {
|
|
53
|
+
summary: 'string',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
defaultValue: { summary: '' },
|
|
57
|
+
},
|
|
58
|
+
value: {
|
|
59
|
+
control: 'text',
|
|
60
|
+
description: 'This holds the value that will be emitted when the toggle is selected',
|
|
61
|
+
table: {
|
|
62
|
+
category: 'Props',
|
|
63
|
+
type: {
|
|
64
|
+
summary: 'string',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
defaultValue: { summary: '' },
|
|
68
|
+
},
|
|
69
|
+
id: {
|
|
70
|
+
control: 'text',
|
|
71
|
+
description: 'This explains what button group this toggle belongs to.',
|
|
72
|
+
table: {
|
|
73
|
+
category: 'Props',
|
|
74
|
+
type: {
|
|
75
|
+
summary: 'string',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
defaultValue: { summary: '' },
|
|
79
|
+
},
|
|
80
|
+
isDisabled: {
|
|
81
|
+
control: 'boolean',
|
|
82
|
+
description: 'This disables the toggle',
|
|
83
|
+
table: {
|
|
84
|
+
category: 'Props',
|
|
85
|
+
type: {
|
|
86
|
+
summary: 'boolean',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
defaultValue: { summary: false },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default meta;
|
|
95
|
+
|
|
96
|
+
type Story = StoryObj<typeof Toggle>;
|
|
97
|
+
|
|
98
|
+
export const Default: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
id: 'toggle',
|
|
101
|
+
label: 'switch label',
|
|
102
|
+
isDisabled: false,
|
|
103
|
+
name: 'toggle',
|
|
104
|
+
value: 'toggle',
|
|
105
|
+
isChecked: false,
|
|
106
|
+
},
|
|
107
|
+
render: (args) => {
|
|
108
|
+
const [checkedValue, setCheckedValue] = useState<boolean>(false);
|
|
109
|
+
|
|
110
|
+
const handleChange = () => {
|
|
111
|
+
setCheckedValue(!checkedValue);
|
|
112
|
+
};
|
|
113
|
+
return (
|
|
114
|
+
<Toggle
|
|
115
|
+
key={args.id}
|
|
116
|
+
onChange={handleChange}
|
|
117
|
+
id={args.id}
|
|
118
|
+
label={args.label}
|
|
119
|
+
name={args.name}
|
|
120
|
+
value={args.value}
|
|
121
|
+
isDisabled={args.isDisabled}
|
|
122
|
+
isChecked={checkedValue}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface ToggleProps {
|
|
4
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
5
|
+
id: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
name: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
10
|
+
isDisabled?: boolean;
|
|
11
|
+
isChecked?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Toggle = ({
|
|
15
|
+
ref,
|
|
16
|
+
id,
|
|
17
|
+
label,
|
|
18
|
+
name,
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
isDisabled,
|
|
22
|
+
isChecked,
|
|
23
|
+
...rest
|
|
24
|
+
}: ToggleProps) => {
|
|
25
|
+
return (
|
|
26
|
+
<div className="form-control">
|
|
27
|
+
<div className="toggle-wrapper">
|
|
28
|
+
<input
|
|
29
|
+
data-testid={`form-toggle-input-${name}`}
|
|
30
|
+
className="toggle-input"
|
|
31
|
+
type="checkbox"
|
|
32
|
+
id={id}
|
|
33
|
+
checked={isChecked}
|
|
34
|
+
name={name}
|
|
35
|
+
value={value}
|
|
36
|
+
disabled={isDisabled}
|
|
37
|
+
ref={ref}
|
|
38
|
+
onChange={onChange}
|
|
39
|
+
tabIndex={0}
|
|
40
|
+
aria-describedby={id}
|
|
41
|
+
aria-label={label}
|
|
42
|
+
{...rest}
|
|
43
|
+
/>
|
|
44
|
+
<label htmlFor={id} className="switch"></label>
|
|
45
|
+
<label className={'toggle-label'} htmlFor={id}>
|
|
46
|
+
{label}
|
|
47
|
+
</label>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { render, screen, act } from '@testing-library/react';
|
|
2
|
+
import { Toggle } from '@/components/forms/toggle/Toggle';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
|
|
5
|
+
const handleOnChange = jest.fn();
|
|
6
|
+
|
|
7
|
+
describe('Toggle', () => {
|
|
8
|
+
it('renders the Toggle input field', () => {
|
|
9
|
+
render(<Toggle label="Option 1" name="name" onChange={handleOnChange} id={'ButtonGroup'} />);
|
|
10
|
+
expect(screen.getByLabelText('Option 1')).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('calls the onChange function when the Toggle input is clicked', async () => {
|
|
14
|
+
render(<Toggle label="Option 1" name="name" onChange={handleOnChange} id={'ButtonGroup'} />);
|
|
15
|
+
expect(handleOnChange).toHaveBeenCalledTimes(0);
|
|
16
|
+
await userEvent.click(screen.getByLabelText('Option 1'));
|
|
17
|
+
expect(handleOnChange).toHaveBeenCalledTimes(1);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Toggle } from './Toggle';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Common Variables
|
|
2
|
+
:root,
|
|
3
|
+
:root [data-theme='light'],
|
|
4
|
+
:root [data-theme='dark'] {
|
|
5
|
+
--pf-toggle-background-color: var(--pf-gray-color-200);
|
|
6
|
+
--pf-toggle-circle-color: var(--pf-primary-color);
|
|
7
|
+
--pf-toggle-border-color: var(--pf-gray-color);
|
|
8
|
+
--pf-toggle-disabled-background-color: var(--pf-gray-color-400);
|
|
9
|
+
--pf-toggle-disabled-circle-color: var(--pf-gray-color-300);
|
|
10
|
+
--pf-toggle-checked-background-color: var(--pf-primary-color-200);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Dark Theme Specific Variables
|
|
14
|
+
:root [data-theme='dark'] {
|
|
15
|
+
--pf-toggle-background-color: var(--pf-primary-color-100);
|
|
16
|
+
--pf-toggle-circle-color: var(--pf-white-color);
|
|
17
|
+
--pf-toggle-border-color: var(--pf-white-color);
|
|
18
|
+
--pf-toggle-disabled-background-color: var(--pf-gray-color-600);
|
|
19
|
+
--pf-toggle-disabled-circle-color: var(--pf-gray-color-900);
|
|
20
|
+
--pf-toggle-checked-background-color: var(--pf-secondary-color-400);
|
|
21
|
+
}
|
|
22
|
+
.switch {
|
|
23
|
+
position: relative;
|
|
24
|
+
display: inline-block;
|
|
25
|
+
width: 40px;
|
|
26
|
+
height: 20px;
|
|
27
|
+
background-color: var(--pf-toggle-background-color);
|
|
28
|
+
border-radius: 20px;
|
|
29
|
+
transition: all 0.3s;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
}
|
|
32
|
+
.switch::after {
|
|
33
|
+
content: '';
|
|
34
|
+
position: absolute;
|
|
35
|
+
width: 18px;
|
|
36
|
+
height: 18px;
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
background-color: var(--pf-toggle-circle-color);
|
|
39
|
+
top: 1px;
|
|
40
|
+
left: 1px;
|
|
41
|
+
transition: all 0.3s;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.toggle-input:checked + label.switch::after {
|
|
45
|
+
left: 20px;
|
|
46
|
+
}
|
|
47
|
+
.toggle-input:checked + label.switch {
|
|
48
|
+
background-color: var(--pf-toggle-checked-background-color);
|
|
49
|
+
}
|
|
50
|
+
.toggle-input:checked,
|
|
51
|
+
.toggle-input:not(:checked) {
|
|
52
|
+
position: absolute;
|
|
53
|
+
left: -9999px;
|
|
54
|
+
}
|
|
55
|
+
.toggle-input:disabled + label.switch {
|
|
56
|
+
background-color: var(--pf-toggle-disabled-background-color);
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.toggle-input:disabled + label.switch::after {
|
|
61
|
+
background-color: var(--pf-toggle-disabled-circle-color);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.toggle-wrapper {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.toggle-label {
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
margin-left: var(--pf-margin-2);
|
|
72
|
+
}
|
package/src/components/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -64,3 +64,4 @@ export { Table } from './components/table';
|
|
|
64
64
|
export { Input } from './components/forms/input';
|
|
65
65
|
export { Radio as RadioInput } from './components/forms/radio';
|
|
66
66
|
export { Checkbox } from './components/forms/checkbox';
|
|
67
|
+
export { Toggle as ToggleInput } from './components/forms/toggle';
|
package/src/styles/index.scss
CHANGED