@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.
@@ -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,6 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Toggle } from './Toggle';
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Toggle>;
6
+ export declare const Default: Story;
@@ -0,0 +1 @@
1
+ export { Toggle } from './Toggle';
@@ -5,3 +5,4 @@ export { Table } from './table';
5
5
  export { Input } from './forms/input';
6
6
  export { Radio } from './forms/radio';
7
7
  export { Checkbox } from './forms/checkbox';
8
+ export { Toggle } from './forms/toggle';
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indico-data/design-system",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -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={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: 'boolean',
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 Buttons</h2>
122
+ <h2 className="mb-2">Checkbox Button</h2>
117
123
  <Checkbox
118
124
  key={args.id}
119
- onChange={args.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={args.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
- [type='checkbox']:checked,
34
- [type='checkbox']:not(:checked) {
33
+ .checkbox-input:checked,
34
+ .checkbox-input:not(:checked) {
35
35
  position: absolute;
36
36
  left: -9999px;
37
37
  }
38
- [type='checkbox']:checked + label,
39
- [type='checkbox']:not(:checked) + label {
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
- [type='checkbox']:checked + label:before,
47
- [type='checkbox']:not(:checked) + label:before {
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
- [type='checkbox']:checked + label:after,
59
- [type='checkbox']:not(:checked) + label:after {
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
- [type='checkbox']:not(:checked) + label:after {
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
- [type='checkbox']:checked + label:after {
76
+ .checkbox-input:checked + label:after {
77
77
  opacity: 1;
78
78
  -webkit-transform: scale(1);
79
79
  transform: scale(1);
80
80
  }
81
- [type='checkbox']:disabled,
82
- [type='checkbox']:disabled + label {
81
+ .checkbox-input:disabled,
82
+ .checkbox-input:disabled + label {
83
83
  cursor: not-allowed;
84
84
  }
85
85
 
86
- [type='checkbox']:disabled + label {
86
+ .checkbox-input:disabled + label {
87
87
  color: var(--pf-checkbox-disabled-color);
88
88
  opacity: 0.5;
89
89
  }
90
90
 
91
- [type='checkbox']:disabled + label:before {
91
+ .checkbox-input:disabled + label:before {
92
92
  border-color: var(--pf-checkbox-disabled-color);
93
93
  }
94
94
 
95
- [type='checkbox']:disabled + label:after {
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
+ }
@@ -5,3 +5,4 @@ export { Table } from './table';
5
5
  export { Input } from './forms/input';
6
6
  export { Radio } from './forms/radio';
7
7
  export { Checkbox } from './forms/checkbox';
8
+ export { Toggle } from './forms/toggle';
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';
@@ -10,6 +10,7 @@
10
10
  @import '../components/forms/radio/styles/Radio.scss';
11
11
  @import '../components/forms/checkbox/styles/Checkbox.scss';
12
12
 
13
+ @import '../components/forms/toggle/styles/Toggle.scss';
13
14
  @import 'typography';
14
15
  @import 'colors';
15
16
  @import 'borders';