@indico-data/design-system 2.6.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.
Files changed (33) hide show
  1. package/lib/index.css +165 -0
  2. package/lib/index.d.ts +26 -2
  3. package/lib/index.esm.css +165 -0
  4. package/lib/index.esm.js +11 -1
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +12 -0
  7. package/lib/index.js.map +1 -1
  8. package/lib/src/components/forms/checkbox/Checkbox.d.ts +12 -0
  9. package/lib/src/components/forms/checkbox/Checkbox.stories.d.ts +7 -0
  10. package/lib/src/components/forms/checkbox/__tests__/Checkbox.test.d.ts +1 -0
  11. package/lib/src/components/forms/checkbox/index.d.ts +1 -0
  12. package/lib/src/components/forms/toggle/Toggle.d.ts +12 -0
  13. package/lib/src/components/forms/toggle/Toggle.stories.d.ts +6 -0
  14. package/lib/src/components/forms/toggle/__tests__/Toggle.test.d.ts +1 -0
  15. package/lib/src/components/forms/toggle/index.d.ts +1 -0
  16. package/lib/src/components/index.d.ts +2 -0
  17. package/lib/src/index.d.ts +2 -0
  18. package/package.json +1 -1
  19. package/src/components/forms/checkbox/Checkbox.mdx +72 -0
  20. package/src/components/forms/checkbox/Checkbox.stories.tsx +175 -0
  21. package/src/components/forms/checkbox/Checkbox.tsx +55 -0
  22. package/src/components/forms/checkbox/__tests__/Checkbox.test.tsx +35 -0
  23. package/src/components/forms/checkbox/index.ts +1 -0
  24. package/src/components/forms/checkbox/styles/Checkbox.scss +98 -0
  25. package/src/components/forms/toggle/Toggle.mdx +15 -0
  26. package/src/components/forms/toggle/Toggle.stories.tsx +126 -0
  27. package/src/components/forms/toggle/Toggle.tsx +51 -0
  28. package/src/components/forms/toggle/__tests__/Toggle.test.tsx +19 -0
  29. package/src/components/forms/toggle/index.ts +1 -0
  30. package/src/components/forms/toggle/styles/Toggle.scss +72 -0
  31. package/src/components/index.ts +2 -0
  32. package/src/index.ts +2 -0
  33. package/src/styles/index.scss +2 -0
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export interface CheckboxProps {
3
+ ref?: React.LegacyRef<HTMLInputElement>;
4
+ id: string;
5
+ label: string;
6
+ name: string;
7
+ value?: string;
8
+ isChecked?: boolean | undefined;
9
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
10
+ isDisabled?: boolean;
11
+ }
12
+ export declare const Checkbox: ({ ref, id, label, name, value, onChange, isDisabled, isChecked, ...rest }: CheckboxProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Checkbox } from './Checkbox';
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Checkbox>;
6
+ export declare const Default: Story;
7
+ export declare const GroupCheckbox: Story;
@@ -0,0 +1 @@
1
+ export { Checkbox } from './Checkbox';
@@ -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';
@@ -4,3 +4,5 @@ export { Icon } from './icons';
4
4
  export { Table } from './table';
5
5
  export { Input } from './forms/input';
6
6
  export { Radio } from './forms/radio';
7
+ export { Checkbox } from './forms/checkbox';
8
+ export { Toggle } from './forms/toggle';
@@ -9,3 +9,5 @@ export { Icon } from './components/icons';
9
9
  export { Table } from './components/table';
10
10
  export { Input } from './components/forms/input';
11
11
  export { Radio as RadioInput } from './components/forms/radio';
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.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -0,0 +1,72 @@
1
+ import { Canvas, Meta, Controls } from '@storybook/blocks';
2
+ import * as Checkbox from './Checkbox.stories';
3
+
4
+ <Meta title="Forms/Checkbox" name="Checkbox" />
5
+
6
+ # Checkbox
7
+ This component may be used on it's on or as part of a group. The source code is an example of how we would use it as part of a group.
8
+
9
+ <Canvas of={Checkbox.Default} source={{
10
+ code: `
11
+ const [isChecked, setIsChecked] = useState<boolean>(false);
12
+ const handleChange = () => {
13
+ setIsChecked(!isChecked);
14
+ };
15
+
16
+ <div>
17
+ <h2 className="mb-2">Checkbox Buttons</h2>
18
+ <Checkbox
19
+ onChange={handleChange}
20
+ id={id}
21
+ label={label}
22
+ name={name}
23
+ value={value}
24
+ isDisabled={isDisabled}
25
+ isChecked={isChecked}
26
+ />
27
+ </div>
28
+ `,
29
+ }} />
30
+ <Controls of={Checkbox.Default} />
31
+
32
+ ## Handling Required State
33
+ The checkbox component does not have a required state. If you need to enforce a required state, this will be managed in a parent component or wrapper element which handles the state. You will need to verify against a value that matches the value in a state array for the checkbox buttons and make sure it is not null.
34
+
35
+
36
+ <Canvas of={Checkbox.GroupCheckbox} source={{
37
+ code: `
38
+ const checkboxList = [
39
+ { id: 'one', label: 'Checkbox Label One', name: 'Checkbox', value: 'check1' },
40
+ { id: 'two', label: 'Checkbox Label Two', name: 'Checkbox', value: 'check2' },
41
+ { id: 'three', label: 'Checkbox Label Three', name: 'Checkbox', value: 'check3' },
42
+ ];
43
+
44
+ export const CheckboxGroup = {
45
+ const [selectedCheckboxes, setSelectedCheckboxes] = useState<string[]>([]);
46
+
47
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
48
+ const { value, checked } = e.target;
49
+ checked
50
+ ? setSelectedCheckboxes((prev) => [...prev, value])
51
+ : setSelectedCheckboxes((prev) => prev.filter((item) => item !== value));
52
+
53
+ return (
54
+ <div>
55
+ <h2 className="mb-2">Checkbox Buttons</h2>
56
+ {checkboxList.map((checkbox) => (
57
+ <Checkbox
58
+ key={checkbox.id}
59
+ onChange={handleChange}
60
+ id={checkbox.id}
61
+ label={checkbox.label}
62
+ name={checkbox.name}
63
+ value={checkbox.value}
64
+ isDisabled={args.isDisabled}
65
+ isChecked={selectedCheckboxes.includes(checkbox.value)}
66
+ />
67
+ ))}
68
+ </div>
69
+ );
70
+ },
71
+ `,
72
+ }} />
@@ -0,0 +1,175 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Checkbox, CheckboxProps } from './Checkbox';
3
+ import { SetStateAction, useState } from 'react';
4
+
5
+ const meta: Meta = {
6
+ title: 'Forms/Checkbox',
7
+ component: Checkbox,
8
+ argTypes: {
9
+ ref: {
10
+ table: {
11
+ disable: true,
12
+ },
13
+ },
14
+ onChange: {
15
+ control: false,
16
+ description: 'onChange event handler',
17
+ table: {
18
+ category: 'Callbacks',
19
+ type: {
20
+ summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
21
+ },
22
+ },
23
+ action: 'onChange',
24
+ },
25
+ label: {
26
+ control: 'text',
27
+ description: 'The label for the checkbox field',
28
+ table: {
29
+ category: 'Props',
30
+ type: {
31
+ summary: 'string',
32
+ },
33
+ },
34
+ defaultValue: { summary: '' },
35
+ },
36
+ name: {
37
+ control: 'text',
38
+ description: 'The name for the checkbox field',
39
+ table: {
40
+ category: 'Props',
41
+ type: {
42
+ summary: 'string',
43
+ },
44
+ },
45
+ defaultValue: { summary: '' },
46
+ },
47
+ value: {
48
+ control: 'text',
49
+ description: 'This holds the value that will be emitted when the checkbox is selected',
50
+ table: {
51
+ category: 'Props',
52
+ type: {
53
+ summary: 'string',
54
+ },
55
+ },
56
+ defaultValue: { summary: '' },
57
+ },
58
+ id: {
59
+ control: 'text',
60
+ description: 'This explains what button group this checkbox belongs to.',
61
+ table: {
62
+ category: 'Props',
63
+ type: {
64
+ summary: 'string',
65
+ },
66
+ },
67
+ defaultValue: { summary: '' },
68
+ },
69
+ isDisabled: {
70
+ control: 'boolean',
71
+ description: 'Toggles the disabled state of the checkbox field',
72
+ table: {
73
+ category: 'Props',
74
+ type: {
75
+ summary: 'boolean',
76
+ },
77
+ },
78
+ defaultValue: { summary: false },
79
+ },
80
+ isChecked: {
81
+ control: false,
82
+ description: 'Toggles the checked state of the checkbox field when true',
83
+ table: {
84
+ category: 'Props',
85
+ type: {
86
+ summary: 'boolean | undefined',
87
+ },
88
+ },
89
+ defaultValue: { summary: false },
90
+ },
91
+ },
92
+ };
93
+
94
+ export default meta;
95
+
96
+ type Story = StoryObj<typeof Checkbox>;
97
+
98
+ const checkboxList = [
99
+ { id: 'one', label: 'Checkbox Label One', name: 'Checkbox', value: 'check1' },
100
+ { id: 'two', label: 'Checkbox Label Two', name: 'Checkbox', value: 'check2' },
101
+ { id: 'three', label: 'Checkbox Label Three', name: 'Checkbox', value: 'check3' },
102
+ ];
103
+
104
+ export const Default: Story = {
105
+ args: {
106
+ id: 'one',
107
+ label: 'Checkbox Label',
108
+ name: 'checkbox',
109
+ value: 'checkbox',
110
+ isDisabled: false,
111
+ isChecked: false,
112
+ },
113
+ render: (args) => {
114
+ const [isChecked, setIsChecked] = useState<boolean>(false);
115
+
116
+ const handleChange = () => {
117
+ setIsChecked(!isChecked);
118
+ };
119
+
120
+ return (
121
+ <div>
122
+ <h2 className="mb-2">Checkbox Button</h2>
123
+ <Checkbox
124
+ key={args.id}
125
+ onChange={handleChange}
126
+ id={args.id}
127
+ label={args.label}
128
+ name={args.name}
129
+ value={args.value}
130
+ isDisabled={args.isDisabled}
131
+ isChecked={isChecked}
132
+ />
133
+ </div>
134
+ );
135
+ },
136
+ };
137
+
138
+ export const GroupCheckbox: Story = {
139
+ args: {
140
+ id: 'one',
141
+ label: 'Checkbox Label',
142
+ name: 'checkbox',
143
+ value: 'checkbox',
144
+ isDisabled: false,
145
+ isChecked: false,
146
+ },
147
+ render: (args) => {
148
+ const [selectedCheckboxes, setSelectedCheckboxes] = useState<string[]>([]);
149
+
150
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
151
+ const { value, checked } = e.target;
152
+ checked
153
+ ? setSelectedCheckboxes((prev) => [...prev, value])
154
+ : setSelectedCheckboxes((prev) => prev.filter((item) => item !== value));
155
+ };
156
+
157
+ return (
158
+ <div>
159
+ <h2 className="mb-2">Checkbox Buttons</h2>
160
+ {checkboxList.map((checkbox) => (
161
+ <Checkbox
162
+ key={checkbox.id}
163
+ onChange={handleChange}
164
+ id={checkbox.id}
165
+ label={checkbox.label}
166
+ name={checkbox.name}
167
+ value={checkbox.value}
168
+ isDisabled={args.isDisabled}
169
+ isChecked={selectedCheckboxes.includes(checkbox.value)}
170
+ />
171
+ ))}
172
+ </div>
173
+ );
174
+ },
175
+ };
@@ -0,0 +1,55 @@
1
+ import { isChecked } from '@indico-data/utils/dist/validators';
2
+ import React from 'react';
3
+
4
+ export interface CheckboxProps {
5
+ ref?: React.LegacyRef<HTMLInputElement>;
6
+ id: string;
7
+ label: string;
8
+ name: string;
9
+ value?: string;
10
+ isChecked?: boolean | undefined;
11
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
12
+ isDisabled?: boolean;
13
+ }
14
+
15
+ export const Checkbox = ({
16
+ ref,
17
+ id,
18
+ label,
19
+ name,
20
+ value,
21
+ onChange,
22
+ isDisabled,
23
+ isChecked = false,
24
+ ...rest
25
+ }: CheckboxProps) => {
26
+ return (
27
+ <div className="form-control">
28
+ <div className="checkbox-wrapper">
29
+ <input
30
+ data-testid={`form-checkbox-input-${name}`}
31
+ className="checkbox-input"
32
+ type="checkbox"
33
+ id={id}
34
+ checked={isChecked}
35
+ name={name}
36
+ value={value}
37
+ disabled={isDisabled}
38
+ ref={ref}
39
+ onChange={onChange}
40
+ tabIndex={0}
41
+ aria-describedby={id}
42
+ aria-label={label}
43
+ {...rest}
44
+ />
45
+ <label
46
+ htmlFor={id}
47
+ className="checkbox-input-label"
48
+ data-testid={`label-checkbox-input-${name}`}
49
+ >
50
+ {label}
51
+ </label>
52
+ </div>
53
+ </div>
54
+ );
55
+ };
@@ -0,0 +1,35 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { Checkbox } from '@/components/forms/checkbox/Checkbox';
3
+ import userEvent from '@testing-library/user-event';
4
+
5
+ const handleOnChange = jest.fn();
6
+
7
+ describe('checkbox', () => {
8
+ it('renders the checkbox input field', () => {
9
+ render(
10
+ <Checkbox
11
+ label="Option 1"
12
+ name="name"
13
+ ref={undefined}
14
+ onChange={handleOnChange}
15
+ id={'ButtonGroup'}
16
+ />,
17
+ );
18
+ expect(screen.getByLabelText('Option 1')).toBeInTheDocument();
19
+ });
20
+
21
+ it('calls the onChange function when the checkbox input is clicked', async () => {
22
+ render(
23
+ <Checkbox
24
+ label="Option 1"
25
+ name="name"
26
+ ref={undefined}
27
+ onChange={handleOnChange}
28
+ id={'ButtonGroup'}
29
+ />,
30
+ );
31
+ expect(handleOnChange).toHaveBeenCalledTimes(0);
32
+ await userEvent.click(screen.getByLabelText('Option 1'));
33
+ expect(handleOnChange).toHaveBeenCalledTimes(1);
34
+ });
35
+ });
@@ -0,0 +1 @@
1
+ export { Checkbox } from './Checkbox';
@@ -0,0 +1,98 @@
1
+ // Common Variables
2
+ :root,
3
+ :root [data-theme='light'],
4
+ :root [data-theme='dark'] {
5
+ --pf-checkbox-background-color: var(--pf-white-color);
6
+ --pf-checkbox-check-color: var(--pf-primary-color);
7
+ --pf-checkbox-border-color: var(--pf-gray-color);
8
+ --pf-checkbox-disabled-color: var(--pf-gray-color-400);
9
+ }
10
+
11
+ // Dark Theme Specific Variables
12
+ :root [data-theme='dark'] {
13
+ --pf-checkbox-background-color: transparent;
14
+ --pf-checkbox-check-color: var(--pf-white-color);
15
+ --pf-checkbox-border-color: var(--pf-white-color);
16
+ --pf-checkbox-disabled-color: var(--pf-gray-color-300);
17
+ }
18
+
19
+ .form-control {
20
+ .checkbox-wrapper {
21
+ display: flex;
22
+ margin-bottom: var(--pf-margin-2);
23
+ align-items: center;
24
+ }
25
+ .checkbox-input {
26
+ margin: 0;
27
+ margin-right: var(--pf-margin-2);
28
+ cursor: pointer;
29
+ }
30
+ .checkbox-input-label {
31
+ cursor: pointer;
32
+ }
33
+ .checkbox-input:checked,
34
+ .checkbox-input:not(:checked) {
35
+ position: absolute;
36
+ left: -9999px;
37
+ }
38
+ .checkbox-input:checked + label,
39
+ .checkbox-input:not(:checked) + label {
40
+ position: relative;
41
+ padding-left: var(--pf-padding-7);
42
+ cursor: pointer;
43
+ line-height: 20px;
44
+ display: inline-block;
45
+ }
46
+ .checkbox-input:checked + label:before,
47
+ .checkbox-input:not(:checked) + label:before {
48
+ content: '';
49
+ position: absolute;
50
+ left: 0;
51
+ top: 0;
52
+ width: 18px;
53
+ height: 18px;
54
+ border: 1px solid var(--pf-checkbox-border-color);
55
+ border-radius: var(--pf-rounded);
56
+ background: var(--pf-checkbox-background-color);
57
+ }
58
+ .checkbox-input:checked + label:after,
59
+ .checkbox-input:not(:checked) + label:after {
60
+ content: '';
61
+ width: 12px;
62
+ height: 12px;
63
+ background: var(--pf-checkbox-check-color);
64
+ position: absolute;
65
+ top: 4px;
66
+ left: 4px;
67
+ border-radius: var(--pf-rounded);
68
+ -webkit-transition: all 0.2s ease;
69
+ transition: all 0.2s ease;
70
+ }
71
+ .checkbox-input:not(:checked) + label:after {
72
+ opacity: 0;
73
+ -webkit-transform: scale(0);
74
+ transform: scale(0);
75
+ }
76
+ .checkbox-input:checked + label:after {
77
+ opacity: 1;
78
+ -webkit-transform: scale(1);
79
+ transform: scale(1);
80
+ }
81
+ .checkbox-input:disabled,
82
+ .checkbox-input:disabled + label {
83
+ cursor: not-allowed;
84
+ }
85
+
86
+ .checkbox-input:disabled + label {
87
+ color: var(--pf-checkbox-disabled-color);
88
+ opacity: 0.5;
89
+ }
90
+
91
+ .checkbox-input:disabled + label:before {
92
+ border-color: var(--pf-checkbox-disabled-color);
93
+ }
94
+
95
+ .checkbox-input:disabled + label:after {
96
+ background: var(--pf-checkbox-disabled-color);
97
+ }
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
+ };