@arbor-education/design-system.components 0.1.5 → 0.2.1
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/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/components/formField/fieldset/Fieldset.d.ts +6 -0
- package/dist/components/formField/fieldset/Fieldset.d.ts.map +1 -0
- package/dist/components/formField/fieldset/Fieldset.js +7 -0
- package/dist/components/formField/fieldset/Fieldset.js.map +1 -0
- package/dist/components/formField/fieldset/Fieldset.stories.d.ts +28 -0
- package/dist/components/formField/fieldset/Fieldset.stories.d.ts.map +1 -0
- package/dist/components/formField/fieldset/Fieldset.stories.js +51 -0
- package/dist/components/formField/fieldset/Fieldset.stories.js.map +1 -0
- package/dist/components/formField/fieldset/Fieldset.test.d.ts +2 -0
- package/dist/components/formField/fieldset/Fieldset.test.d.ts.map +1 -0
- package/dist/components/formField/fieldset/Fieldset.test.js +62 -0
- package/dist/components/formField/fieldset/Fieldset.test.js.map +1 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts +8 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.js +8 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.js.map +1 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.test.d.ts +2 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.test.d.ts.map +1 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.test.js +86 -0
- package/dist/components/formField/inputs/checkbox/CheckboxGroup.test.js.map +1 -0
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +3 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +7 -0
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts +11 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.d.ts.map +1 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.js +8 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.js.map +1 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.test.d.ts +2 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.test.d.ts.map +1 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.test.js +86 -0
- package/dist/components/formField/inputs/radio/RadioButtonGroup.test.js.map +1 -0
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +8 -2
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
- package/dist/components/modal/Modal.d.ts +25 -0
- package/dist/components/modal/Modal.d.ts.map +1 -0
- package/dist/components/modal/Modal.js +22 -0
- package/dist/components/modal/Modal.js.map +1 -0
- package/dist/components/modal/Modal.stories.d.ts +13 -0
- package/dist/components/modal/Modal.stories.d.ts.map +1 -0
- package/dist/components/modal/Modal.stories.js +23 -0
- package/dist/components/modal/Modal.stories.js.map +1 -0
- package/dist/components/modal/Modal.test.d.ts +2 -0
- package/dist/components/modal/Modal.test.d.ts.map +1 -0
- package/dist/components/modal/Modal.test.js +131 -0
- package/dist/components/modal/Modal.test.js.map +1 -0
- package/dist/components/modal/ModalBody.d.ts +7 -0
- package/dist/components/modal/ModalBody.d.ts.map +1 -0
- package/dist/components/modal/ModalBody.js +7 -0
- package/dist/components/modal/ModalBody.js.map +1 -0
- package/dist/components/modal/ModalCloseButton.d.ts +3 -0
- package/dist/components/modal/ModalCloseButton.d.ts.map +1 -0
- package/dist/components/modal/ModalCloseButton.js +25 -0
- package/dist/components/modal/ModalCloseButton.js.map +1 -0
- package/dist/components/modal/ModalFooter.d.ts +7 -0
- package/dist/components/modal/ModalFooter.d.ts.map +1 -0
- package/dist/components/modal/ModalFooter.js +7 -0
- package/dist/components/modal/ModalFooter.js.map +1 -0
- package/dist/components/modal/ModalHeader.d.ts +7 -0
- package/dist/components/modal/ModalHeader.d.ts.map +1 -0
- package/dist/components/modal/ModalHeader.js +8 -0
- package/dist/components/modal/ModalHeader.js.map +1 -0
- package/dist/components/modal/ModalTitle.d.ts +3 -0
- package/dist/components/modal/ModalTitle.d.ts.map +1 -0
- package/dist/components/modal/ModalTitle.js +8 -0
- package/dist/components/modal/ModalTitle.js.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.d.ts +7 -0
- package/dist/components/modal/modalManager/ModalManager.d.ts.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.js +22 -0
- package/dist/components/modal/modalManager/ModalManager.js.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.stories.d.ts +13 -0
- package/dist/components/modal/modalManager/ModalManager.stories.d.ts.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.stories.js +110 -0
- package/dist/components/modal/modalManager/ModalManager.stories.js.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.test.d.ts +2 -0
- package/dist/components/modal/modalManager/ModalManager.test.d.ts.map +1 -0
- package/dist/components/modal/modalManager/ModalManager.test.js +191 -0
- package/dist/components/modal/modalManager/ModalManager.test.js.map +1 -0
- package/dist/components/separator/Separator.d.ts +7 -0
- package/dist/components/separator/Separator.d.ts.map +1 -0
- package/dist/components/separator/Separator.js +8 -0
- package/dist/components/separator/Separator.js.map +1 -0
- package/dist/components/separator/Separator.stories.d.ts +10 -0
- package/dist/components/separator/Separator.stories.d.ts.map +1 -0
- package/dist/components/separator/Separator.stories.js +12 -0
- package/dist/components/separator/Separator.stories.js.map +1 -0
- package/dist/components/separator/Separator.test.d.ts +2 -0
- package/dist/components/separator/Separator.test.d.ts.map +1 -0
- package/dist/components/separator/Separator.test.js +10 -0
- package/dist/components/separator/Separator.test.js.map +1 -0
- package/dist/components/table/Table.d.ts +20 -0
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +45 -7
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +14 -1
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +315 -2
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/pagination/TableSettingsDropdown.d.ts +2 -0
- package/dist/components/table/pagination/TableSettingsDropdown.d.ts.map +1 -0
- package/dist/components/table/pagination/TableSettingsDropdown.js +43 -0
- package/dist/components/table/pagination/TableSettingsDropdown.js.map +1 -0
- package/dist/components/table/useTableSettings.d.ts +22 -0
- package/dist/components/table/useTableSettings.d.ts.map +1 -0
- package/dist/components/table/useTableSettings.js +36 -0
- package/dist/components/table/useTableSettings.js.map +1 -0
- package/dist/index.css +102 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/Constants.d.ts +5 -0
- package/dist/utils/Constants.d.ts.map +1 -1
- package/dist/utils/Constants.js +5 -0
- package/dist/utils/Constants.js.map +1 -1
- package/dist/utils/ModalUtils.d.ts +7 -0
- package/dist/utils/ModalUtils.d.ts.map +1 -0
- package/dist/utils/ModalUtils.js +14 -0
- package/dist/utils/ModalUtils.js.map +1 -0
- package/dist/utils/hooks/useComponentDidUpdate.d.ts +7 -0
- package/dist/utils/hooks/useComponentDidUpdate.d.ts.map +1 -0
- package/dist/utils/hooks/useComponentDidUpdate.js +18 -0
- package/dist/utils/hooks/useComponentDidUpdate.js.map +1 -0
- package/dist/utils/hooks/useComponentDidUpdate.test.d.ts +2 -0
- package/dist/utils/hooks/useComponentDidUpdate.test.d.ts.map +1 -0
- package/dist/utils/hooks/useComponentDidUpdate.test.js +69 -0
- package/dist/utils/hooks/useComponentDidUpdate.test.js.map +1 -0
- package/package.json +1 -1
- package/src/components/formField/fieldset/Fieldset.stories.tsx +89 -0
- package/src/components/formField/fieldset/Fieldset.test.tsx +85 -0
- package/src/components/formField/fieldset/Fieldset.tsx +17 -0
- package/src/components/formField/fieldset/fieldset.scss +19 -0
- package/src/components/formField/inputs/checkbox/CheckboxGroup.test.tsx +127 -0
- package/src/components/formField/inputs/checkbox/CheckboxGroup.tsx +17 -0
- package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +12 -1
- package/src/components/formField/inputs/radio/RadioButtonGroup.test.tsx +190 -0
- package/src/components/formField/inputs/radio/RadioButtonGroup.tsx +22 -0
- package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +16 -7
- package/src/components/formField/label/label.scss +1 -1
- package/src/components/modal/Modal.stories.tsx +37 -0
- package/src/components/modal/Modal.test.tsx +244 -0
- package/src/components/modal/Modal.tsx +65 -0
- package/src/components/modal/ModalBody.tsx +16 -0
- package/src/components/modal/ModalCloseButton.tsx +39 -0
- package/src/components/modal/ModalFooter.tsx +14 -0
- package/src/components/modal/ModalHeader.tsx +20 -0
- package/src/components/modal/ModalTitle.tsx +12 -0
- package/src/components/modal/modal.scss +74 -0
- package/src/components/modal/modalManager/ModalManager.stories.tsx +497 -0
- package/src/components/modal/modalManager/ModalManager.test.tsx +255 -0
- package/src/components/modal/modalManager/ModalManager.tsx +40 -0
- package/src/components/modal/modalManager/modalManager.scss +4 -0
- package/src/components/separator/Separator.stories.tsx +15 -0
- package/src/components/separator/Separator.test.tsx +10 -0
- package/src/components/separator/Separator.tsx +15 -0
- package/src/components/separator/separator.scss +6 -0
- package/src/components/table/Table.stories.tsx +14 -1
- package/src/components/table/Table.test.tsx +553 -1
- package/src/components/table/Table.tsx +80 -24
- package/src/components/table/pagination/TableSettingsDropdown.tsx +90 -0
- package/src/components/table/table.scss +8 -0
- package/src/components/table/useTableSettings.ts +59 -0
- package/src/index.scss +4 -0
- package/src/index.ts +4 -0
- package/src/tokens.scss +1 -0
- package/src/utils/Constants.ts +6 -0
- package/src/utils/ModalUtils.ts +17 -0
- package/src/utils/hooks/useComponentDidUpdate.test.ts +107 -0
- package/src/utils/hooks/useComponentDidUpdate.ts +19 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { expect, test, describe, vi } from 'vitest';
|
|
2
|
+
import { CheckboxGroup } from './CheckboxGroup';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
|
|
6
|
+
describe('CheckboxGroup component', () => {
|
|
7
|
+
const mockOptions = [
|
|
8
|
+
{ id: 'option1', label: 'Option 1', checked: false, onChange: vi.fn() },
|
|
9
|
+
{ id: 'option2', label: 'Option 2', checked: true, onChange: vi.fn() },
|
|
10
|
+
{ id: 'option3', label: 'Option 3', checked: false, onChange: vi.fn() },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
test('renders fieldset with legend', () => {
|
|
14
|
+
render(
|
|
15
|
+
<CheckboxGroup legend="Choose options" options={mockOptions} />,
|
|
16
|
+
);
|
|
17
|
+
expect(screen.getByText('Choose options')).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('renders all checkbox options', () => {
|
|
21
|
+
render(
|
|
22
|
+
<CheckboxGroup legend="Choose options" options={mockOptions} />,
|
|
23
|
+
);
|
|
24
|
+
expect(screen.getByRole('checkbox', { name: 'Option 1' })).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByRole('checkbox', { name: 'Option 2' })).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByRole('checkbox', { name: 'Option 3' })).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('applies checked state to checkboxes', () => {
|
|
30
|
+
render(
|
|
31
|
+
<CheckboxGroup legend="Choose options" options={mockOptions} />,
|
|
32
|
+
);
|
|
33
|
+
const option1 = screen.getByRole('checkbox', { name: 'Option 1' }) as HTMLInputElement;
|
|
34
|
+
const option2 = screen.getByRole('checkbox', { name: 'Option 2' }) as HTMLInputElement;
|
|
35
|
+
const option3 = screen.getByRole('checkbox', { name: 'Option 3' }) as HTMLInputElement;
|
|
36
|
+
|
|
37
|
+
expect(option1.checked).toBe(false);
|
|
38
|
+
expect(option2.checked).toBe(true);
|
|
39
|
+
expect(option3.checked).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('calls onChange when a checkbox is clicked', () => {
|
|
43
|
+
const handleChange1 = vi.fn();
|
|
44
|
+
const options = [
|
|
45
|
+
{ id: 'option1', label: 'Option 1', checked: false, onChange: handleChange1, value: 'value1' },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
render(
|
|
49
|
+
<CheckboxGroup legend="Choose options" options={options} />,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const checkbox = screen.getByRole('checkbox', { name: 'Option 1' });
|
|
53
|
+
fireEvent.click(checkbox);
|
|
54
|
+
expect(handleChange1).toHaveBeenCalledExactlyOnceWith(
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
target: checkbox,
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('renders empty group when options array is empty', () => {
|
|
62
|
+
render(
|
|
63
|
+
<CheckboxGroup legend="Choose options" options={[]} />,
|
|
64
|
+
);
|
|
65
|
+
expect(screen.getByText('Choose options')).toBeInTheDocument();
|
|
66
|
+
const checkboxes = screen.queryAllByRole('checkbox');
|
|
67
|
+
expect(checkboxes).toHaveLength(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('passes through fieldset props', () => {
|
|
71
|
+
render(
|
|
72
|
+
<CheckboxGroup
|
|
73
|
+
legend="Choose options"
|
|
74
|
+
options={mockOptions}
|
|
75
|
+
className="custom-class"
|
|
76
|
+
data-testid="test-fieldset"
|
|
77
|
+
/>,
|
|
78
|
+
);
|
|
79
|
+
const fieldset = screen.getByTestId('test-fieldset');
|
|
80
|
+
expect(fieldset).toBeInTheDocument();
|
|
81
|
+
expect(fieldset).toHaveClass('custom-class');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('renders options without labels', () => {
|
|
85
|
+
const optionsWithoutLabels = [
|
|
86
|
+
{ id: 'option1', checked: false, onChange: vi.fn() },
|
|
87
|
+
{ id: 'option2', checked: false, onChange: vi.fn() },
|
|
88
|
+
];
|
|
89
|
+
render(
|
|
90
|
+
<CheckboxGroup legend="Choose options" options={optionsWithoutLabels} />,
|
|
91
|
+
);
|
|
92
|
+
const checkboxes = screen.getAllByRole('checkbox');
|
|
93
|
+
expect(checkboxes).toHaveLength(2);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('supports disabled fieldset', () => {
|
|
97
|
+
render(
|
|
98
|
+
<CheckboxGroup
|
|
99
|
+
legend="Choose options"
|
|
100
|
+
options={mockOptions}
|
|
101
|
+
disabled
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
const fieldset = screen.getByText('Choose options').closest('fieldset');
|
|
105
|
+
expect(fieldset).toBeDisabled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('passes additional HTML attributes to checkboxes', () => {
|
|
109
|
+
const optionsWithAttributes = [
|
|
110
|
+
{
|
|
111
|
+
'id': 'option1',
|
|
112
|
+
'label': 'Option 1',
|
|
113
|
+
'checked': false,
|
|
114
|
+
'onChange': vi.fn(),
|
|
115
|
+
'data-custom': 'value1',
|
|
116
|
+
'aria-describedby': 'description1',
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
render(
|
|
120
|
+
<CheckboxGroup legend="Choose options" options={optionsWithAttributes} />,
|
|
121
|
+
);
|
|
122
|
+
const checkbox = screen.getByRole('checkbox', { name: 'Option 1' });
|
|
123
|
+
|
|
124
|
+
expect(checkbox).toHaveAttribute('data-custom', 'value1');
|
|
125
|
+
expect(checkbox).toHaveAttribute('aria-describedby', 'description1');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Fieldset, type FieldsetProps } from 'Components/formField/fieldset/Fieldset';
|
|
2
|
+
import { CheckboxInput, type CheckboxInputProps } from './CheckboxInput';
|
|
3
|
+
|
|
4
|
+
type CheckboxGroupProps = {
|
|
5
|
+
options: CheckboxInputProps[];
|
|
6
|
+
} & FieldsetProps;
|
|
7
|
+
|
|
8
|
+
export const CheckboxGroup = (props: CheckboxGroupProps) => {
|
|
9
|
+
const { options, ...rest } = props;
|
|
10
|
+
return (
|
|
11
|
+
<Fieldset {...rest}>
|
|
12
|
+
{options.map((option: CheckboxInputProps) => (
|
|
13
|
+
<CheckboxInput key={option.id} {...option} />
|
|
14
|
+
))}
|
|
15
|
+
</Fieldset>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react-vite';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
import { CheckboxInput } from './CheckboxInput';
|
|
3
|
+
import { CheckboxGroup } from './CheckboxGroup';
|
|
3
4
|
|
|
4
5
|
const meta: Meta<typeof CheckboxInput> = {
|
|
5
6
|
title: 'Components/FormField/Inputs/Checkbox',
|
|
6
7
|
component: CheckboxInput,
|
|
7
8
|
};
|
|
8
9
|
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
9
12
|
export const Default = {
|
|
10
13
|
parameters: {
|
|
11
14
|
layout: 'centered',
|
|
@@ -19,4 +22,12 @@ export const Default = {
|
|
|
19
22
|
},
|
|
20
23
|
};
|
|
21
24
|
|
|
25
|
+
export const CheckboxGroupStory: Story = {
|
|
26
|
+
render: () => {
|
|
27
|
+
return (
|
|
28
|
+
<CheckboxGroup legend="Checkbox group" options={[{ id: 'option1', label: 'Option 1' }, { id: 'option2', label: 'Option 2' }, { id: 'option3', label: 'Option 3' }]} />
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
22
33
|
export default meta;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { expect, test, describe, vi } from 'vitest';
|
|
2
|
+
import { RadioButtonGroup } from './RadioButtonGroup';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
|
|
6
|
+
describe('RadioButtonGroup component', () => {
|
|
7
|
+
const mockOptions = [
|
|
8
|
+
{ id: 'option1', value: 'value1', label: 'Option 1' },
|
|
9
|
+
{ id: 'option2', value: 'value2', label: 'Option 2' },
|
|
10
|
+
{ id: 'option3', value: 'value3', label: 'Option 3' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
test('renders fieldset with legend', () => {
|
|
14
|
+
render(
|
|
15
|
+
<RadioButtonGroup
|
|
16
|
+
legend="Choose an option"
|
|
17
|
+
name="test-group"
|
|
18
|
+
options={mockOptions}
|
|
19
|
+
checkedValue="value1"
|
|
20
|
+
onChange={vi.fn()}
|
|
21
|
+
/>,
|
|
22
|
+
);
|
|
23
|
+
expect(screen.getByText('Choose an option')).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('renders all radio button options', () => {
|
|
27
|
+
render(
|
|
28
|
+
<RadioButtonGroup
|
|
29
|
+
legend="Choose an option"
|
|
30
|
+
name="test-group"
|
|
31
|
+
options={mockOptions}
|
|
32
|
+
checkedValue="value1"
|
|
33
|
+
onChange={vi.fn()}
|
|
34
|
+
/>,
|
|
35
|
+
);
|
|
36
|
+
expect(screen.getByRole('radio', { name: 'Option 1' })).toBeInTheDocument();
|
|
37
|
+
expect(screen.getByRole('radio', { name: 'Option 2' })).toBeInTheDocument();
|
|
38
|
+
expect(screen.getByRole('radio', { name: 'Option 3' })).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('applies the same name attribute to all radio buttons', () => {
|
|
42
|
+
render(
|
|
43
|
+
<RadioButtonGroup
|
|
44
|
+
legend="Choose an option"
|
|
45
|
+
name="test-group"
|
|
46
|
+
options={mockOptions}
|
|
47
|
+
checkedValue="value1"
|
|
48
|
+
onChange={vi.fn()}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
const radios = screen.getAllByRole('radio');
|
|
52
|
+
radios.forEach((radio) => {
|
|
53
|
+
expect(radio).toHaveAttribute('name', 'test-group');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('initially checks the correct radio button based on checkedValue', () => {
|
|
58
|
+
render(
|
|
59
|
+
<RadioButtonGroup
|
|
60
|
+
legend="Choose an option"
|
|
61
|
+
name="test-group"
|
|
62
|
+
options={mockOptions}
|
|
63
|
+
checkedValue="value2"
|
|
64
|
+
onChange={vi.fn()}
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
const option1 = screen.getByRole('radio', { name: 'Option 1' }) as HTMLInputElement;
|
|
68
|
+
const option2 = screen.getByRole('radio', { name: 'Option 2' }) as HTMLInputElement;
|
|
69
|
+
const option3 = screen.getByRole('radio', { name: 'Option 3' }) as HTMLInputElement;
|
|
70
|
+
|
|
71
|
+
expect(option1.checked).toBe(false);
|
|
72
|
+
expect(option2.checked).toBe(true);
|
|
73
|
+
expect(option3.checked).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('calls onChange when a radio button is clicked', () => {
|
|
77
|
+
const handleChange = vi.fn();
|
|
78
|
+
render(
|
|
79
|
+
<RadioButtonGroup
|
|
80
|
+
legend="Choose an option"
|
|
81
|
+
name="test-group"
|
|
82
|
+
options={mockOptions}
|
|
83
|
+
checkedValue="value1"
|
|
84
|
+
onChange={handleChange}
|
|
85
|
+
/>,
|
|
86
|
+
);
|
|
87
|
+
const option2 = screen.getByRole('radio', { name: 'Option 2' });
|
|
88
|
+
fireEvent.click(option2);
|
|
89
|
+
expect(handleChange).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
90
|
+
target: expect.objectContaining({
|
|
91
|
+
value: 'value2',
|
|
92
|
+
}),
|
|
93
|
+
}));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('renders with single option', () => {
|
|
97
|
+
const singleOption = [{ id: 'option1', value: 'value1', label: 'Only Option' }];
|
|
98
|
+
render(
|
|
99
|
+
<RadioButtonGroup
|
|
100
|
+
legend="Choose an option"
|
|
101
|
+
name="test-group"
|
|
102
|
+
options={singleOption}
|
|
103
|
+
checkedValue="value1"
|
|
104
|
+
onChange={vi.fn()}
|
|
105
|
+
/>,
|
|
106
|
+
);
|
|
107
|
+
const radios = screen.getAllByRole('radio');
|
|
108
|
+
expect(radios).toHaveLength(1);
|
|
109
|
+
expect(screen.getByRole('radio', { name: 'Only Option' })).toBeInTheDocument();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('renders empty group when options array is empty', () => {
|
|
113
|
+
render(
|
|
114
|
+
<RadioButtonGroup
|
|
115
|
+
legend="Choose an option"
|
|
116
|
+
name="test-group"
|
|
117
|
+
options={[]}
|
|
118
|
+
checkedValue=""
|
|
119
|
+
onChange={vi.fn()}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
expect(screen.getByText('Choose an option')).toBeInTheDocument();
|
|
123
|
+
const radios = screen.queryAllByRole('radio');
|
|
124
|
+
expect(radios).toHaveLength(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('passes through fieldset props', () => {
|
|
128
|
+
render(
|
|
129
|
+
<RadioButtonGroup
|
|
130
|
+
legend="Choose an option"
|
|
131
|
+
name="test-group"
|
|
132
|
+
options={mockOptions}
|
|
133
|
+
checkedValue="value1"
|
|
134
|
+
onChange={vi.fn()}
|
|
135
|
+
className="custom-class"
|
|
136
|
+
data-testid="test-fieldset"
|
|
137
|
+
/>,
|
|
138
|
+
);
|
|
139
|
+
const fieldset = screen.getByTestId('test-fieldset');
|
|
140
|
+
expect(fieldset).toBeInTheDocument();
|
|
141
|
+
expect(fieldset).toHaveClass('custom-class');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('updates checked state when checkedValue changes', () => {
|
|
145
|
+
const { rerender } = render(
|
|
146
|
+
<RadioButtonGroup
|
|
147
|
+
legend="Choose an option"
|
|
148
|
+
name="test-group"
|
|
149
|
+
options={mockOptions}
|
|
150
|
+
checkedValue="value1"
|
|
151
|
+
onChange={vi.fn()}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
let option1 = screen.getByRole('radio', { name: 'Option 1' }) as HTMLInputElement;
|
|
156
|
+
let option2 = screen.getByRole('radio', { name: 'Option 2' }) as HTMLInputElement;
|
|
157
|
+
expect(option1.checked).toBe(true);
|
|
158
|
+
expect(option2.checked).toBe(false);
|
|
159
|
+
|
|
160
|
+
rerender(
|
|
161
|
+
<RadioButtonGroup
|
|
162
|
+
legend="Choose an option"
|
|
163
|
+
name="test-group"
|
|
164
|
+
options={mockOptions}
|
|
165
|
+
checkedValue="value2"
|
|
166
|
+
onChange={vi.fn()}
|
|
167
|
+
/>,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
option1 = screen.getByRole('radio', { name: 'Option 1' }) as HTMLInputElement;
|
|
171
|
+
option2 = screen.getByRole('radio', { name: 'Option 2' }) as HTMLInputElement;
|
|
172
|
+
expect(option1.checked).toBe(false);
|
|
173
|
+
expect(option2.checked).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('supports disabled fieldset', () => {
|
|
177
|
+
render(
|
|
178
|
+
<RadioButtonGroup
|
|
179
|
+
legend="Choose an option"
|
|
180
|
+
name="test-group"
|
|
181
|
+
options={mockOptions}
|
|
182
|
+
checkedValue="value1"
|
|
183
|
+
onChange={vi.fn()}
|
|
184
|
+
disabled
|
|
185
|
+
/>,
|
|
186
|
+
);
|
|
187
|
+
const fieldset = screen.getByText('Choose an option').closest('fieldset');
|
|
188
|
+
expect(fieldset).toBeDisabled();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ChangeEvent } from 'react';
|
|
2
|
+
import { RadioButtonInput, type RadioButtonInputProps } from './RadioButtonInput';
|
|
3
|
+
import { Fieldset, type FieldsetProps } from '../../fieldset/Fieldset';
|
|
4
|
+
|
|
5
|
+
export type RadioButtonGroupProps = {
|
|
6
|
+
name: string;
|
|
7
|
+
options: RadioButtonInputProps[];
|
|
8
|
+
checkedValue: string;
|
|
9
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
10
|
+
} & Omit<FieldsetProps, 'onChange'>;
|
|
11
|
+
|
|
12
|
+
export const RadioButtonGroup = (props: RadioButtonGroupProps) => {
|
|
13
|
+
const { legend, name, options, checkedValue, onChange, ...rest } = props;
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Fieldset legend={legend} {...rest}>
|
|
17
|
+
{options.map((option: RadioButtonInputProps) => (
|
|
18
|
+
<RadioButtonInput key={option.id} name={name} value={option.value} label={option.label} checked={checkedValue === option.value} onChange={onChange} />
|
|
19
|
+
))}
|
|
20
|
+
</Fieldset>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
|
2
2
|
import { fn } from 'storybook/test';
|
|
3
3
|
|
|
4
4
|
import { RadioButtonInput } from './RadioButtonInput';
|
|
5
|
+
import { RadioButtonGroup } from './RadioButtonGroup';
|
|
6
|
+
import { useState } from 'react';
|
|
5
7
|
|
|
6
8
|
const meta = {
|
|
7
9
|
title: 'Components/FormField/Inputs/RadioButton',
|
|
@@ -87,11 +89,18 @@ export const DisabledChecked: Story = {
|
|
|
87
89
|
|
|
88
90
|
// Radio button group example
|
|
89
91
|
export const RadioGroup: Story = {
|
|
90
|
-
render: () =>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
render: () => {
|
|
93
|
+
const options = [{ label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, { label: 'Option 3', value: 'option3' }];
|
|
94
|
+
const [checkedValue, setCheckedValue] = useState<string>(options[0]?.value ?? '');
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<RadioButtonGroup
|
|
98
|
+
legend="Radio group"
|
|
99
|
+
name="group"
|
|
100
|
+
options={options}
|
|
101
|
+
checkedValue={checkedValue}
|
|
102
|
+
onChange={e => setCheckedValue(e.target.value)}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
},
|
|
97
106
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.ds-label {
|
|
2
2
|
font-family: var(--font-family-standard);
|
|
3
3
|
font-size: var(--font-size-2-13);
|
|
4
|
-
font-weight: var(--
|
|
4
|
+
font-weight: var(--type-body-bold-weight);
|
|
5
5
|
color: var(--form-field-label-color-text);
|
|
6
6
|
line-height: 150%;
|
|
7
7
|
margin-bottom: var(--form-field-spacing-vertical);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Meta } from '@storybook/react-vite';
|
|
2
|
+
import { fn } from 'storybook/test';
|
|
3
|
+
import { Modal, type ModalProps } from './Modal';
|
|
4
|
+
import { Button } from 'Components/button/Button';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Modal> = {
|
|
8
|
+
title: 'Components/Modals/Modal',
|
|
9
|
+
component: Modal,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const Default = {
|
|
14
|
+
args: {
|
|
15
|
+
open: false,
|
|
16
|
+
closeHandler: fn(),
|
|
17
|
+
title: 'Modal Title',
|
|
18
|
+
},
|
|
19
|
+
render: (args: ModalProps) => {
|
|
20
|
+
const [showModal, setShowModal] = useState(args.open);
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<Button type="primary" onClick={() => setShowModal(true)}>Open Modal</Button>
|
|
24
|
+
<Modal {...args} open={showModal} closeHandler={() => setShowModal(false)}>
|
|
25
|
+
<Modal.Body>Cheddar and Pickle were Norwich terriers who lived next door. Cheddar, the older, was cautious and methodical; Pickle, the younger, was bold and impulsive. One morning, Pickle spotted a squirrel in the garden and gave chase, while Cheddar watched from the porch. When Pickle got stuck under the neighbor’s fence, Cheddar squeezed through a gap and nudged him free. From then on, they worked as a team: Pickle scouted, and Cheddar planned. They became inseparable, exploring the neighborhood together and always looking out for each other.</Modal.Body>
|
|
26
|
+
<Modal.Footer>
|
|
27
|
+
<Button variant="tertiary" onClick={() => setShowModal(false)}>Close</Button>
|
|
28
|
+
<Button variant="primary" onClick={() => setShowModal(false)}>Primary Action</Button>
|
|
29
|
+
</Modal.Footer>
|
|
30
|
+
</Modal>
|
|
31
|
+
</>
|
|
32
|
+
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default meta;
|