@dhasdk/simple-ui 1.0.7 → 1.0.8
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/.babelrc +12 -0
- package/.storybook/main.ts +35 -0
- package/.storybook/preview.ts +4 -0
- package/BAKpostcss.config.jsBAK +15 -0
- package/BAKtailwind.config.mjsBAK +99 -0
- package/README.md +464 -16
- package/coverage/storybook/coverage-storybook.json +32411 -0
- package/coverage/storybook/lcov-report/Accordion.tsx.html +805 -0
- package/coverage/storybook/lcov-report/Badge.tsx.html +346 -0
- package/coverage/storybook/lcov-report/Breadcrumbs.tsx.html +742 -0
- package/coverage/storybook/lcov-report/Button.tsx.html +448 -0
- package/coverage/storybook/lcov-report/ButtonGroup.tsx.html +403 -0
- package/coverage/storybook/lcov-report/Card.tsx.html +292 -0
- package/coverage/storybook/lcov-report/CharacterCounter.tsx.html +253 -0
- package/coverage/storybook/lcov-report/CheckBox.tsx.html +1555 -0
- package/coverage/storybook/lcov-report/DatePicker.tsx.html +826 -0
- package/coverage/storybook/lcov-report/Input.tsx.html +1012 -0
- package/coverage/storybook/lcov-report/List.tsx.html +364 -0
- package/coverage/storybook/lcov-report/Modal.tsx.html +745 -0
- package/coverage/storybook/lcov-report/Pill.tsx.html +358 -0
- package/coverage/storybook/lcov-report/Search.tsx.html +997 -0
- package/coverage/storybook/lcov-report/SearchContent.tsx.html +235 -0
- package/coverage/storybook/lcov-report/SectionHeader.tsx.html +358 -0
- package/coverage/storybook/lcov-report/Select.tsx.html +1012 -0
- package/coverage/storybook/lcov-report/Shield.tsx.html +802 -0
- package/coverage/storybook/lcov-report/SideBarNav.tsx.html +490 -0
- package/coverage/storybook/lcov-report/Skeleton.tsx.html +394 -0
- package/coverage/storybook/lcov-report/Slider.tsx.html +385 -0
- package/coverage/storybook/lcov-report/Status.tsx.html +322 -0
- package/coverage/storybook/lcov-report/Tabs.tsx.html +610 -0
- package/coverage/storybook/lcov-report/Toggle.tsx.html +373 -0
- package/coverage/storybook/lcov-report/Tooltip.tsx.html +496 -0
- package/coverage/storybook/lcov-report/base.css +224 -0
- package/coverage/storybook/lcov-report/block-navigation.js +87 -0
- package/coverage/storybook/lcov-report/favicon.png +0 -0
- package/coverage/storybook/lcov-report/index.html +476 -0
- package/coverage/storybook/lcov-report/prettify.css +1 -0
- package/coverage/storybook/lcov-report/prettify.js +2 -0
- package/coverage/storybook/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/storybook/lcov-report/sorter.js +196 -0
- package/coverage/storybook/lcov.info +2312 -0
- package/dist/README.md +1815 -0
- package/eslint.config.mjs +13 -0
- package/package.json +6 -7
- package/project.json +11 -0
- package/src/assets/img/Frame.svg +5 -0
- package/src/assets/img/backArrowRight.svg +10 -0
- package/src/assets/img/bc-separator.png +0 -0
- package/src/assets/img/calendar.png +0 -0
- package/src/assets/img/calendar.svg +4 -0
- package/src/assets/img/check.svg +5 -0
- package/src/assets/img/check_box.svg +10 -0
- package/src/assets/img/check_box_empty.svg +10 -0
- package/src/assets/img/check_box_fill.svg +10 -0
- package/src/assets/img/check_box_fill_empty.svg +10 -0
- package/src/assets/img/chevron-down-white.svg +2 -0
- package/src/assets/img/chevron-down.svg +2 -0
- package/src/assets/img/chevron-left.svg +1 -0
- package/src/assets/img/chevron-right-light.svg +4 -0
- package/src/assets/img/chevron-right.svg +3 -0
- package/src/assets/img/chevron-up-white.svg +1 -0
- package/src/assets/img/chevron-up.svg +1 -0
- package/src/assets/img/clock.svg +6 -0
- package/src/assets/img/close.svg +1 -0
- package/src/assets/img/close2.svg +6 -0
- package/src/assets/img/closeModal.svg +10 -0
- package/src/assets/img/close_icon_dark.svg +10 -0
- package/src/assets/img/close_small.svg +3 -0
- package/src/assets/img/emergency_home.svg +10 -0
- package/src/assets/img/first-aid-kit.svg +7 -0
- package/src/assets/img/heartbeat.svg +4 -0
- package/src/assets/img/home-gray.svg +3 -0
- package/src/assets/img/home.svg +3 -0
- package/src/assets/img/hospital.jpg +0 -0
- package/src/assets/img/indeterminate_check_box.svg +10 -0
- package/src/assets/img/indeterminate_check_box_fill.svg +10 -0
- package/src/assets/img/info_24_ 1d4ed8.svg +3 -0
- package/src/assets/img/info_24_ 2c6441.svg +3 -0
- package/src/assets/img/marker_check_by_default.svg +10 -0
- package/src/assets/img/marker_check_by_default_fill.svg +10 -0
- package/src/assets/img/minus-accordion.svg +5 -0
- package/src/assets/img/minus.svg +3 -0
- package/src/assets/img/open.svg +1 -0
- package/src/assets/img/pill-white.svg +7 -0
- package/src/assets/img/pill.svg +5 -0
- package/src/assets/img/plus-accordion.svg +5 -0
- package/src/assets/img/plus.svg +4 -0
- package/src/assets/img/prescription.svg +6 -0
- package/src/assets/img/search.svg +10 -0
- package/src/assets/img/search_icon_light.svg +10 -0
- package/src/assets/img/separator.svg +3 -0
- package/src/assets/img/stethoscope-white.svg +8 -0
- package/src/assets/img/stethoscope.svg +8 -0
- package/src/assets/img/thumb_up.svg +10 -0
- package/src/assets/img/vector.svg +3 -0
- package/src/assets/img/warning-badge-disabled.svg +11 -0
- package/src/assets/img/warning-badge-green.svg +11 -0
- package/src/assets/img/warning-badge-red.svg +11 -0
- package/src/assets/img/warning-badge-yellow.svg +11 -0
- package/src/assets/img/warning.svg +10 -0
- package/src/global.d.ts +13 -0
- package/{index.d.ts → src/index.ts} +13 -5
- package/src/lib/Accordian--Accordian.stories.tsx +312 -0
- package/src/lib/Accordion.spec.tsx +384 -0
- package/src/lib/Accordion.tsx +240 -0
- package/src/lib/AppointmentPicker.spec.tsx +138 -0
- package/src/lib/AppointmentPicker.tsx +97 -0
- package/src/lib/Badge--Badge.stories.tsx +60 -0
- package/src/lib/Badge.spec.tsx +70 -0
- package/src/lib/Badge.tsx +87 -0
- package/src/lib/Breadcrumbs-Breadcrumbs.stories.tsx +114 -0
- package/src/lib/Breadcrumbs.spec.tsx +218 -0
- package/src/lib/Breadcrumbs.tsx +219 -0
- package/src/lib/Button--Button.stories.tsx +220 -0
- package/src/lib/Button.spec.tsx +241 -0
- package/src/lib/Button.tsx +121 -0
- package/src/lib/ButtonGroup--ButtonGroup.stories.tsx +129 -0
- package/src/lib/ButtonGroup.spec.tsx +89 -0
- package/src/lib/ButtonGroup.tsx +107 -0
- package/src/lib/Card--Card.stories.tsx +113 -0
- package/src/lib/Card.spec.tsx +112 -0
- package/src/lib/Card.tsx +69 -0
- package/src/lib/CharacterCounter--CharacterCounter.stories.tsx +169 -0
- package/src/lib/CharacterCounter.spec.tsx +123 -0
- package/src/lib/CharacterCounter.tsx +56 -0
- package/src/lib/CheckBox--CheckBox.stories.tsx +107 -0
- package/src/lib/CheckBox.spec.tsx +412 -0
- package/src/lib/CheckBox.tsx +491 -0
- package/src/lib/DatePicker--DatePicker.stories.tsx +228 -0
- package/src/lib/DatePicker.spec.tsx +424 -0
- package/src/lib/DatePicker.tsx +247 -0
- package/src/lib/Input--Input.stories.tsx +449 -0
- package/src/lib/Input.spec.tsx +281 -0
- package/src/lib/Input.tsx +309 -0
- package/src/lib/List--List.stories.tsx +157 -0
- package/src/lib/List.spec.tsx +211 -0
- package/src/lib/List.tsx +93 -0
- package/src/lib/Modal--Modal.stories.tsx +454 -0
- package/src/lib/Modal.spec.tsx +202 -0
- package/src/lib/Modal.tsx +220 -0
- package/src/lib/Pill--Pill.stories.tsx +98 -0
- package/src/lib/Pill.spec.tsx +103 -0
- package/src/lib/Pill.tsx +91 -0
- package/src/lib/ProgressBar.spec.tsx +106 -0
- package/src/lib/ProgressBar.tsx +112 -0
- package/src/lib/RadioGroup.spec.tsx +84 -0
- package/src/lib/RadioGroup.tsx +74 -0
- package/src/lib/RadioIcon.tsx +13 -0
- package/src/lib/Search--Search.stories.tsx +67 -0
- package/src/lib/Search.spec.tsx +182 -0
- package/src/lib/Search.tsx +304 -0
- package/src/lib/SearchContent.tsx +51 -0
- package/src/lib/SectionHeader--SectionHeader.stories.tsx +98 -0
- package/src/lib/SectionHeader.spec.tsx +60 -0
- package/src/lib/SectionHeader.tsx +91 -0
- package/src/lib/Select--Select.stories.tsx +387 -0
- package/src/lib/Select.spec.tsx +493 -0
- package/src/lib/Select.tsx +311 -0
- package/src/lib/Shield--Shield.stories.tsx +196 -0
- package/src/lib/Shield.spec.tsx +275 -0
- package/src/lib/Shield.tsx +239 -0
- package/src/lib/SideBarNav--SideBarNav.stories.tsx +136 -0
- package/src/lib/SideBarNav.spec.tsx +178 -0
- package/src/lib/SideBarNav.tsx +135 -0
- package/src/lib/Skeleton--Skeleton.stories.tsx +77 -0
- package/src/lib/Skeleton.module.css +16 -0
- package/src/lib/Skeleton.spec.tsx +83 -0
- package/src/lib/Skeleton.tsx +103 -0
- package/src/lib/SkipLink.spec.tsx +76 -0
- package/src/lib/SkipLink.tsx +48 -0
- package/src/lib/Slider--Slider.stories.tsx +108 -0
- package/src/lib/Slider.module.css +109 -0
- package/src/lib/Slider.spec.tsx +67 -0
- package/src/lib/Slider.tsx +101 -0
- package/src/lib/Status--Status.stories.tsx +93 -0
- package/src/lib/Status.spec.tsx +118 -0
- package/src/lib/Status.tsx +79 -0
- package/src/lib/Tabs--Tabs.stories.tsx +294 -0
- package/src/lib/Tabs.spec.tsx +249 -0
- package/src/lib/Tabs.tsx +188 -0
- package/src/lib/Tester.spec.tsx +17 -0
- package/src/lib/Toggle--Toggle.stories.tsx +162 -0
- package/src/lib/Toggle.spec.tsx +122 -0
- package/src/lib/Toggle.tsx +96 -0
- package/src/lib/Tooltip--Tooltip.stories.tsx +315 -0
- package/src/lib/Tooltip.spec.tsx +307 -0
- package/src/lib/Tooltip.tsx +137 -0
- package/src/lib/bak-simple-ui.stories.tsx-bak +24 -0
- package/src/styles.css +190 -0
- package/tsconfig.json +25 -0
- package/tsconfig.lib.json +42 -0
- package/tsconfig.spec.json +29 -0
- package/tsconfig.storybook.json +36 -0
- package/vite.config.mts +87 -0
- package/vitest.setup.ts +12 -0
- package/index.css +0 -1
- package/index.js +0 -35
- package/index.mjs +0 -4981
- package/lib/Accordion.d.ts +0 -36
- package/lib/AppointmentPicker.d.ts +0 -21
- package/lib/Badge.d.ts +0 -11
- package/lib/Breadcrumbs.d.ts +0 -13
- package/lib/Button.d.ts +0 -15
- package/lib/ButtonGroup.d.ts +0 -8
- package/lib/Card.d.ts +0 -11
- package/lib/CharacterCounter.d.ts +0 -11
- package/lib/CheckBox.d.ts +0 -30
- package/lib/DatePicker.d.ts +0 -7
- package/lib/Input.d.ts +0 -16
- package/lib/List.d.ts +0 -22
- package/lib/Modal.d.ts +0 -18
- package/lib/Pill.d.ts +0 -13
- package/lib/ProgressBar.d.ts +0 -19
- package/lib/RadioGroup.d.ts +0 -15
- package/lib/Search.d.ts +0 -26
- package/lib/SearchContent.d.ts +0 -6
- package/lib/SectionHeader.d.ts +0 -18
- package/lib/Select.d.ts +0 -19
- package/lib/Shield.d.ts +0 -12
- package/lib/SideBarNav.d.ts +0 -21
- package/lib/Skeleton.d.ts +0 -15
- package/lib/SkipLink.d.ts +0 -22
- package/lib/Slider.d.ts +0 -14
- package/lib/Status.d.ts +0 -10
- package/lib/Tabs.d.ts +0 -23
- package/lib/Toggle.d.ts +0 -11
- package/lib/Tooltip.d.ts +0 -14
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Meta } from '@storybook/react';
|
|
2
|
+
import { CheckBox, CheckBoxGroup, CheckBoxProps } from './CheckBox';
|
|
3
|
+
|
|
4
|
+
// import imagePath from 'src/assets/img/pill.svg';
|
|
5
|
+
const imagePath = 'src/assets/img/hospital.jpg';
|
|
6
|
+
// imagePath: 'src/assets/img/heartbeat.svg',
|
|
7
|
+
|
|
8
|
+
// Meta object - defines basic storybook options for this story
|
|
9
|
+
export default {
|
|
10
|
+
title: 'Components/CheckBox',
|
|
11
|
+
component: CheckBox,
|
|
12
|
+
argTypes: {
|
|
13
|
+
variant: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['','']
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
// label: 'Button', // set default argument values
|
|
20
|
+
},
|
|
21
|
+
parameters: {
|
|
22
|
+
layout: 'centered', // options are 'centered', 'fullscreen', and 'padded' (default value)
|
|
23
|
+
backgrounds: { default: 'light' }, // options are light, medium, or dark
|
|
24
|
+
},
|
|
25
|
+
} as Meta<CheckBoxProps>;
|
|
26
|
+
|
|
27
|
+
// Define "Default" story
|
|
28
|
+
export const Default = {
|
|
29
|
+
args: {
|
|
30
|
+
ariaLabel: 'test label',
|
|
31
|
+
alt: 'image alt',
|
|
32
|
+
children: 'CheckBox 1'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Group = () => {
|
|
37
|
+
// const [searchResults, setSearchResults] = useState<DataSearchResults>();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<div className='mb-4'>Note: 'level' prop does not take effect w/o use of the CheckBoxContainer component.</div>
|
|
42
|
+
<CheckBox level={0}>Group 1</CheckBox>
|
|
43
|
+
<CheckBox level={1}>g1, item 1</CheckBox>
|
|
44
|
+
<CheckBox level={1}>g1, item 2</CheckBox>
|
|
45
|
+
<CheckBox level={0}>Group 2</CheckBox>
|
|
46
|
+
<CheckBox level={1}>g2, item 1</CheckBox>
|
|
47
|
+
<CheckBox level={1}>g2, item 2</CheckBox>
|
|
48
|
+
<CheckBox level={2}>g2, item 2, item 1</CheckBox>
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export const GroupContainer = () => {
|
|
55
|
+
// const [searchResults, setSearchResults] = useState<DataSearchResults>();
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<CheckBoxGroup>
|
|
59
|
+
<CheckBox level={0}>Group 1</CheckBox>
|
|
60
|
+
<CheckBox level={1}>g1, item 1</CheckBox>
|
|
61
|
+
<CheckBox level={1}>g1, item 2</CheckBox>
|
|
62
|
+
<CheckBox level={0}>Group 2</CheckBox>
|
|
63
|
+
<CheckBox level={1}>g2, item 1</CheckBox>
|
|
64
|
+
<CheckBox level={1}>g2, item 2</CheckBox>
|
|
65
|
+
<CheckBox level={2}>g2, item 2, item 1</CheckBox>
|
|
66
|
+
</CheckBoxGroup>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
export const GroupContainer2 = () => {
|
|
72
|
+
// const [searchResults, setSearchResults] = useState<DataSearchResults>();
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<CheckBoxGroup>
|
|
76
|
+
<CheckBox level={0}>Group 1</CheckBox>
|
|
77
|
+
<CheckBox level={1}>g1, item 1</CheckBox>
|
|
78
|
+
<CheckBox level={1}>g1, item 2</CheckBox>
|
|
79
|
+
<CheckBox level={2}>g1, item 2, item 1</CheckBox>
|
|
80
|
+
<CheckBox level={2}>g1, item 2, item 2</CheckBox>
|
|
81
|
+
<CheckBox level={0}>Group 2</CheckBox>
|
|
82
|
+
<CheckBox level={1}>g2, item 1</CheckBox>
|
|
83
|
+
<CheckBox level={1}>g2, item 2</CheckBox>
|
|
84
|
+
<CheckBox level={2}>g2, item 2, item 1</CheckBox>
|
|
85
|
+
</CheckBoxGroup>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
export const GroupContainer3 = () => {
|
|
91
|
+
// const [searchResults, setSearchResults] = useState<DataSearchResults>();
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<CheckBoxGroup>
|
|
95
|
+
<CheckBox level={0}>Group 1</CheckBox>
|
|
96
|
+
<CheckBox level={1}>g1, item 1</CheckBox>
|
|
97
|
+
<CheckBox level={1}>g1, item 2</CheckBox>
|
|
98
|
+
<CheckBox level={2}>g1, item 2, item 1</CheckBox>
|
|
99
|
+
<CheckBox level={2}>g1, item 2, item 2</CheckBox>
|
|
100
|
+
<CheckBox level={1}>g1, item 3</CheckBox>
|
|
101
|
+
<CheckBox level={0}>Group 2</CheckBox>
|
|
102
|
+
<CheckBox level={1}>g2, item 1</CheckBox>
|
|
103
|
+
<CheckBox level={1}>g2, item 2</CheckBox>
|
|
104
|
+
<CheckBox level={2}>g2, item 2, item 1</CheckBox>
|
|
105
|
+
</CheckBoxGroup>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import React, { createRef } from "react";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { describe, it, expect, vi } from "vitest";
|
|
5
|
+
import { axe } from "vitest-axe";
|
|
6
|
+
|
|
7
|
+
import { CheckBox, CheckBoxGroup } from "./CheckBox";
|
|
8
|
+
|
|
9
|
+
// add jest-dom / vitest-dom matchers
|
|
10
|
+
// expect.extend(toHaveNoViolations);
|
|
11
|
+
|
|
12
|
+
describe("CheckBox component", () => {
|
|
13
|
+
it("should render unchecked by default and have no accessibility violations", async () => {
|
|
14
|
+
render(<main><CheckBox ariaLabel="accept terms">Accept terms</CheckBox></main>);
|
|
15
|
+
|
|
16
|
+
// find checkbox input
|
|
17
|
+
const input = screen.getByRole("checkbox", { name: "accept terms" });
|
|
18
|
+
expect(input).toBeInTheDocument();
|
|
19
|
+
expect(input).not.toBeChecked();
|
|
20
|
+
|
|
21
|
+
// icon img alt should reflect 'unchecked'
|
|
22
|
+
const img = screen.getByAltText("unchecked") as HTMLImageElement;
|
|
23
|
+
expect(img).toBeInTheDocument();
|
|
24
|
+
expect(img.src).toContain("check_box_empty.svg");
|
|
25
|
+
|
|
26
|
+
// axe accessibility scan
|
|
27
|
+
const results = await axe(document.body);
|
|
28
|
+
expect(results).toHaveNoViolations();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("toggles to checked on click and calls setStatusUpdate", async () => {
|
|
32
|
+
const mockSet = vi.fn();
|
|
33
|
+
render(
|
|
34
|
+
<CheckBox
|
|
35
|
+
ariaLabel="my box"
|
|
36
|
+
setStatusUpdate={mockSet}
|
|
37
|
+
index={0}
|
|
38
|
+
>
|
|
39
|
+
Label
|
|
40
|
+
</CheckBox>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const input = screen.getByRole("checkbox", { name: "my box" });
|
|
44
|
+
// click to check
|
|
45
|
+
await userEvent.click(input);
|
|
46
|
+
expect(input).toBeChecked();
|
|
47
|
+
|
|
48
|
+
// after toggle, the internal effect should call setStatusUpdate('checked', 0)
|
|
49
|
+
expect(mockSet).toHaveBeenCalledWith("checked", 0);
|
|
50
|
+
|
|
51
|
+
// the icon src should update to checked icon
|
|
52
|
+
const img = screen.getByAltText("checked") as HTMLImageElement;
|
|
53
|
+
expect(img.src).toContain("check_box.svg");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
it("uses marker icon when checked with marker prop", async () => {
|
|
58
|
+
render(
|
|
59
|
+
<CheckBox
|
|
60
|
+
ariaLabel="marker box"
|
|
61
|
+
marker={true}
|
|
62
|
+
setStatusUpdate={vi.fn()}
|
|
63
|
+
index={0}
|
|
64
|
+
>
|
|
65
|
+
Marker
|
|
66
|
+
</CheckBox>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const input = screen.getByRole("checkbox", { name: "marker box" }) as HTMLInputElement;
|
|
70
|
+
// check it
|
|
71
|
+
await userEvent.click(input);
|
|
72
|
+
expect(input).toBeChecked();
|
|
73
|
+
|
|
74
|
+
// marker (unchecked) initially shows regular empty – marker only kicks in on checked
|
|
75
|
+
// now that it's checked, we expect the marker icon
|
|
76
|
+
const img = screen.getByAltText("checked") as HTMLImageElement;
|
|
77
|
+
expect(img.src).toContain("marker_check_by_default.svg");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("uses filled marker icon when checked with marker and fill props", async () => {
|
|
81
|
+
render(
|
|
82
|
+
<CheckBox
|
|
83
|
+
ariaLabel="filled marker box"
|
|
84
|
+
marker={true}
|
|
85
|
+
fill={true}
|
|
86
|
+
setStatusUpdate={vi.fn()}
|
|
87
|
+
index={0}
|
|
88
|
+
>
|
|
89
|
+
Filled Marker
|
|
90
|
+
</CheckBox>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const input = screen.getByRole("checkbox", { name: "filled marker box" }) as HTMLInputElement;
|
|
94
|
+
// check it
|
|
95
|
+
await userEvent.click(input);
|
|
96
|
+
expect(input).toBeChecked();
|
|
97
|
+
|
|
98
|
+
// with fill and marker, we expect the filled marker icon
|
|
99
|
+
const img = screen.getByAltText("checked") as HTMLImageElement;
|
|
100
|
+
expect(img.src).toContain("marker_check_by_default_fill.svg");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
// CHECKBOX GROUP COMPONENT --------------------------
|
|
107
|
+
|
|
108
|
+
describe("CheckBoxGroup component", () => {
|
|
109
|
+
|
|
110
|
+
const Siblings = (props: { bridgeParent?: boolean }) => (
|
|
111
|
+
<CheckBoxGroup bridgeParent>
|
|
112
|
+
<CheckBox level={0} ariaLabel="item A">A</CheckBox>
|
|
113
|
+
<CheckBox level={0} ariaLabel="item B">B</CheckBox>
|
|
114
|
+
<CheckBox level={1} ariaLabel="item BA">BA</CheckBox>
|
|
115
|
+
<CheckBox level={1} ariaLabel="item BB">BB</CheckBox>
|
|
116
|
+
<CheckBox level={2} ariaLabel="item BBA">BBA</CheckBox>
|
|
117
|
+
<CheckBox level={2} ariaLabel="item BBB">BBB</CheckBox>
|
|
118
|
+
<CheckBox level={2} ariaLabel="item BBC">BBC</CheckBox>
|
|
119
|
+
<CheckBox level={1} ariaLabel="item BC">BC</CheckBox>
|
|
120
|
+
<CheckBox level={0} ariaLabel="item C">C</CheckBox>
|
|
121
|
+
<CheckBox level={1} ariaLabel="item CA">CA</CheckBox>
|
|
122
|
+
<CheckBox level={2} ariaLabel="item CAA">CAA</CheckBox>
|
|
123
|
+
<CheckBox level={2} ariaLabel="item CAB">CAB</CheckBox>
|
|
124
|
+
<CheckBox level={2} ariaLabel="item CAC">CAC</CheckBox>
|
|
125
|
+
</CheckBoxGroup>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
const SiblingsFill = (props: { bridgeParent?: boolean }) => (
|
|
130
|
+
<CheckBoxGroup bridgeParent fill>
|
|
131
|
+
<CheckBox level={0} ariaLabel="item A">A</CheckBox>
|
|
132
|
+
<CheckBox level={0} ariaLabel="item B">B</CheckBox>
|
|
133
|
+
<CheckBox level={1} ariaLabel="item BA">BA</CheckBox>
|
|
134
|
+
<CheckBox level={1} ariaLabel="item BB">BB</CheckBox>
|
|
135
|
+
<CheckBox level={2} ariaLabel="item BBA">BBA</CheckBox>
|
|
136
|
+
<CheckBox level={2} ariaLabel="item BBB">BBB</CheckBox>
|
|
137
|
+
<CheckBox level={2} ariaLabel="item BBC">BBC</CheckBox>
|
|
138
|
+
<CheckBox level={1} ariaLabel="item BC">BC</CheckBox>
|
|
139
|
+
<CheckBox level={0} ariaLabel="item C">C</CheckBox>
|
|
140
|
+
<CheckBox level={1} ariaLabel="item CA">CA</CheckBox>
|
|
141
|
+
<CheckBox level={2} ariaLabel="item CAA">CAA</CheckBox>
|
|
142
|
+
<CheckBox level={2} ariaLabel="item CAB">CAB</CheckBox>
|
|
143
|
+
<CheckBox level={2} ariaLabel="item CAC">CAC</CheckBox>
|
|
144
|
+
</CheckBoxGroup>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Build a tiny hierarchy:
|
|
151
|
+
* - parent (level 0)
|
|
152
|
+
* - child 1 (level 1)
|
|
153
|
+
* - child 2 (level 1)
|
|
154
|
+
*/
|
|
155
|
+
const Parent = () => (
|
|
156
|
+
<CheckBoxGroup>
|
|
157
|
+
<CheckBox level={0} ariaLabel="parent">Parent</CheckBox>
|
|
158
|
+
<CheckBox level={1} ariaLabel="child 1">Child 1</CheckBox>
|
|
159
|
+
<CheckBox level={1} ariaLabel="child 2">Child 2</CheckBox>
|
|
160
|
+
</CheckBoxGroup>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
it("checks all children when parent is clicked", async () => {
|
|
164
|
+
render(<Parent />);
|
|
165
|
+
|
|
166
|
+
const parentInput = screen.getByRole("checkbox", { name: "parent" });
|
|
167
|
+
const child1 = screen.getByRole("checkbox", { name: "child 1" });
|
|
168
|
+
const child2 = screen.getByRole("checkbox", { name: "child 2" });
|
|
169
|
+
|
|
170
|
+
// click parent → should check parent and both children
|
|
171
|
+
await userEvent.click(parentInput);
|
|
172
|
+
expect(parentInput).toBeChecked();
|
|
173
|
+
expect(child1).toBeChecked();
|
|
174
|
+
expect(child2).toBeChecked();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("sets parent to indeterminate if only one child is checked", async () => {
|
|
178
|
+
render(<Parent />);
|
|
179
|
+
|
|
180
|
+
const parentInput = screen.getByRole("checkbox", { name: "parent" }) as HTMLInputElement;
|
|
181
|
+
const child1 = screen.getByRole("checkbox", { name: "child 1" });
|
|
182
|
+
const child2 = screen.getByRole("checkbox", { name: "child 2" });
|
|
183
|
+
|
|
184
|
+
// click only the first child
|
|
185
|
+
await userEvent.click(child1);
|
|
186
|
+
expect(child1).toBeChecked();
|
|
187
|
+
expect(child2).not.toBeChecked();
|
|
188
|
+
|
|
189
|
+
// parent should now be indeterminate
|
|
190
|
+
expect(parentInput.indeterminate).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("unchecks all children when parent is clicked twice", async () => {
|
|
194
|
+
render(<Parent />);
|
|
195
|
+
|
|
196
|
+
const parentInput = screen.getByRole("checkbox", { name: "parent" });
|
|
197
|
+
const child1 = screen.getByRole("checkbox", { name: "child 1" });
|
|
198
|
+
const child2 = screen.getByRole("checkbox", { name: "child 2" });
|
|
199
|
+
|
|
200
|
+
// first click → all checked
|
|
201
|
+
await userEvent.click(parentInput);
|
|
202
|
+
expect(child1).toBeChecked();
|
|
203
|
+
expect(child2).toBeChecked();
|
|
204
|
+
|
|
205
|
+
// second click → all unchecked
|
|
206
|
+
await userEvent.click(parentInput);
|
|
207
|
+
expect(parentInput).not.toBeChecked();
|
|
208
|
+
expect(child1).not.toBeChecked();
|
|
209
|
+
expect(child2).not.toBeChecked();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
// Uses SIBLINGS
|
|
214
|
+
it("only toggles the clicked checkbox among siblings at the same level", async () => {
|
|
215
|
+
render(<Siblings />);
|
|
216
|
+
|
|
217
|
+
const [a, b, ba, bb, bba, bbb, bbc, bc, c, ca, caa, cab, cac] = ["item A", "item B", "item BA",
|
|
218
|
+
"item BB", "item BBA", "item BBB", "item BBC", "item BC", "item C",
|
|
219
|
+
"item CA", "item CAA", "item CAB", "item CAC"].map(name =>
|
|
220
|
+
screen.getByRole("checkbox", { name }) as HTMLInputElement
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// click A → only A becomes checked
|
|
224
|
+
await userEvent.click(a);
|
|
225
|
+
expect(a).toBeChecked(); // Checked
|
|
226
|
+
expect(b).not.toBeChecked();
|
|
227
|
+
expect(ba).not.toBeChecked();
|
|
228
|
+
expect(bb).not.toBeChecked();
|
|
229
|
+
expect(bba).not.toBeChecked();
|
|
230
|
+
expect(bbb).not.toBeChecked();
|
|
231
|
+
expect(bbc).not.toBeChecked();
|
|
232
|
+
expect(bc).not.toBeChecked();
|
|
233
|
+
expect(c).not.toBeChecked();
|
|
234
|
+
|
|
235
|
+
// click B → A remains checked, B toggles on, C stays off
|
|
236
|
+
await userEvent.click(b);
|
|
237
|
+
expect(a).toBeChecked(); // Checked
|
|
238
|
+
expect(b).toBeChecked(); // Checked
|
|
239
|
+
expect(ba).toBeChecked(); // Checked
|
|
240
|
+
expect(bb).toBeChecked(); // Checked
|
|
241
|
+
expect(bba).toBeChecked(); // Checked
|
|
242
|
+
expect(bbb).toBeChecked(); // Checked
|
|
243
|
+
expect(bbc).toBeChecked(); // Checked
|
|
244
|
+
expect(bc).toBeChecked(); // Checked
|
|
245
|
+
expect(c).not.toBeChecked();
|
|
246
|
+
|
|
247
|
+
// click A again → A toggles off, B stays on, C stays off
|
|
248
|
+
await userEvent.click(a);
|
|
249
|
+
expect(a).not.toBeChecked();
|
|
250
|
+
expect(b).toBeChecked(); // Checked
|
|
251
|
+
expect(ba).toBeChecked(); // Checked
|
|
252
|
+
expect(bb).toBeChecked(); // Checked
|
|
253
|
+
expect(bba).toBeChecked(); // Checked
|
|
254
|
+
expect(bbb).toBeChecked(); // Checked
|
|
255
|
+
expect(bbc).toBeChecked(); // Checked
|
|
256
|
+
expect(bc).toBeChecked(); // Checked
|
|
257
|
+
expect(c).not.toBeChecked();
|
|
258
|
+
|
|
259
|
+
// click BB → A remains unchecked, BB toggles off
|
|
260
|
+
// click BB → BB and its descendants are unchecked; B should become indeterminate
|
|
261
|
+
await userEvent.click(bb);
|
|
262
|
+
|
|
263
|
+
// first grab everything as HTMLInputElements
|
|
264
|
+
const bInput = b as HTMLInputElement;
|
|
265
|
+
|
|
266
|
+
expect(a).not.toBeChecked();
|
|
267
|
+
|
|
268
|
+
// instead of expecting B to be unchecked, we assert that:
|
|
269
|
+
// • it's not checked,
|
|
270
|
+
// • but it *is* indeterminate
|
|
271
|
+
expect(bInput.checked).toBe(false);
|
|
272
|
+
expect(bInput.indeterminate).toBe(true);
|
|
273
|
+
|
|
274
|
+
// BA stays checked
|
|
275
|
+
expect(ba).toBeChecked();
|
|
276
|
+
|
|
277
|
+
// BB itself is now unchecked
|
|
278
|
+
expect(bb).not.toBeChecked();
|
|
279
|
+
|
|
280
|
+
// its children (BBA, BBB, BBC) are all unchecked
|
|
281
|
+
expect(bba).not.toBeChecked();
|
|
282
|
+
expect(bbb).not.toBeChecked();
|
|
283
|
+
expect(bbc).not.toBeChecked();
|
|
284
|
+
|
|
285
|
+
// BC (sibling-child of B) remains checked
|
|
286
|
+
expect(bc).toBeChecked();
|
|
287
|
+
|
|
288
|
+
expect(c).not.toBeChecked();
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
await userEvent.click(caa);
|
|
292
|
+
expect(c).not.toBeChecked();
|
|
293
|
+
expect(ca).not.toBeChecked();
|
|
294
|
+
expect(caa).toBeChecked();
|
|
295
|
+
expect(cab).not.toBeChecked();
|
|
296
|
+
expect(cac).not.toBeChecked();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
it("only toggles the clicked checkbox among siblings at the same level - fill variant", async () => {
|
|
301
|
+
render(<SiblingsFill />);
|
|
302
|
+
|
|
303
|
+
const [a, b, ba, bb, bba, bbb, bbc, bc, c, ca, caa, cab, cac] = ["item A", "item B", "item BA",
|
|
304
|
+
"item BB", "item BBA", "item BBB", "item BBC", "item BC", "item C",
|
|
305
|
+
"item CA", "item CAA", "item CAB", "item CAC"].map(name =>
|
|
306
|
+
screen.getByRole("checkbox", { name }) as HTMLInputElement
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// click A → only A becomes checked
|
|
310
|
+
await userEvent.click(a);
|
|
311
|
+
expect(a).toBeChecked(); // Checked
|
|
312
|
+
expect(b).not.toBeChecked();
|
|
313
|
+
expect(ba).not.toBeChecked();
|
|
314
|
+
expect(bb).not.toBeChecked();
|
|
315
|
+
expect(bba).not.toBeChecked();
|
|
316
|
+
expect(bbb).not.toBeChecked();
|
|
317
|
+
expect(bbc).not.toBeChecked();
|
|
318
|
+
expect(bc).not.toBeChecked();
|
|
319
|
+
expect(c).not.toBeChecked();
|
|
320
|
+
|
|
321
|
+
// click B → A remains checked, B toggles on, C stays off
|
|
322
|
+
await userEvent.click(b);
|
|
323
|
+
expect(a).toBeChecked(); // Checked
|
|
324
|
+
expect(b).toBeChecked(); // Checked
|
|
325
|
+
expect(ba).toBeChecked(); // Checked
|
|
326
|
+
expect(bb).toBeChecked(); // Checked
|
|
327
|
+
expect(bba).toBeChecked(); // Checked
|
|
328
|
+
expect(bbb).toBeChecked(); // Checked
|
|
329
|
+
expect(bbc).toBeChecked(); // Checked
|
|
330
|
+
expect(bc).toBeChecked(); // Checked
|
|
331
|
+
expect(c).not.toBeChecked();
|
|
332
|
+
|
|
333
|
+
// click A again → A toggles off, B stays on, C stays off
|
|
334
|
+
await userEvent.click(a);
|
|
335
|
+
expect(a).not.toBeChecked();
|
|
336
|
+
expect(b).toBeChecked(); // Checked
|
|
337
|
+
expect(ba).toBeChecked(); // Checked
|
|
338
|
+
expect(bb).toBeChecked(); // Checked
|
|
339
|
+
expect(bba).toBeChecked(); // Checked
|
|
340
|
+
expect(bbb).toBeChecked(); // Checked
|
|
341
|
+
expect(bbc).toBeChecked(); // Checked
|
|
342
|
+
expect(bc).toBeChecked(); // Checked
|
|
343
|
+
expect(c).not.toBeChecked();
|
|
344
|
+
|
|
345
|
+
// click BB → A remains unchecked, BB toggles off
|
|
346
|
+
// click BB → BB and its descendants are unchecked; B should become indeterminate
|
|
347
|
+
await userEvent.click(bb);
|
|
348
|
+
|
|
349
|
+
// first grab everything as HTMLInputElements
|
|
350
|
+
const bInput = b as HTMLInputElement;
|
|
351
|
+
|
|
352
|
+
expect(a).not.toBeChecked();
|
|
353
|
+
|
|
354
|
+
// instead of expecting B to be unchecked, we assert that:
|
|
355
|
+
// • it's not checked,
|
|
356
|
+
// • but it *is* indeterminate
|
|
357
|
+
expect(bInput.checked).toBe(false);
|
|
358
|
+
expect(bInput.indeterminate).toBe(true);
|
|
359
|
+
|
|
360
|
+
// BA stays checked
|
|
361
|
+
expect(ba).toBeChecked();
|
|
362
|
+
|
|
363
|
+
// BB itself is now unchecked
|
|
364
|
+
expect(bb).not.toBeChecked();
|
|
365
|
+
|
|
366
|
+
// its children (BBA, BBB, BBC) are all unchecked
|
|
367
|
+
expect(bba).not.toBeChecked();
|
|
368
|
+
expect(bbb).not.toBeChecked();
|
|
369
|
+
expect(bbc).not.toBeChecked();
|
|
370
|
+
|
|
371
|
+
// BC (sibling-child of B) remains checked
|
|
372
|
+
expect(bc).toBeChecked();
|
|
373
|
+
|
|
374
|
+
expect(c).not.toBeChecked();
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
await userEvent.click(caa);
|
|
378
|
+
expect(c).not.toBeChecked();
|
|
379
|
+
expect(ca).not.toBeChecked();
|
|
380
|
+
expect(caa).toBeChecked();
|
|
381
|
+
expect(cab).not.toBeChecked();
|
|
382
|
+
expect(cac).not.toBeChecked();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Uses SIBLINGS
|
|
386
|
+
it("with bridgeParent=true still treats siblings independently", async () => {
|
|
387
|
+
render(<Siblings bridgeParent={true} />);
|
|
388
|
+
|
|
389
|
+
const [a, b, c] = ["item A", "item B", "item C"].map(name =>
|
|
390
|
+
screen.getByRole("checkbox", { name }) as HTMLInputElement
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// click C → only C becomes checked
|
|
394
|
+
await userEvent.click(c);
|
|
395
|
+
expect(a).not.toBeChecked();
|
|
396
|
+
expect(b).not.toBeChecked();
|
|
397
|
+
expect(c).toBeChecked();
|
|
398
|
+
|
|
399
|
+
// click B → C remains checked, B toggles on, A stays off
|
|
400
|
+
await userEvent.click(b);
|
|
401
|
+
expect(a).not.toBeChecked();
|
|
402
|
+
expect(b).toBeChecked();
|
|
403
|
+
expect(c).toBeChecked();
|
|
404
|
+
|
|
405
|
+
// click B again → B toggles off, C stays on
|
|
406
|
+
await userEvent.click(b);
|
|
407
|
+
expect(a).not.toBeChecked();
|
|
408
|
+
expect(b).not.toBeChecked();
|
|
409
|
+
expect(c).toBeChecked();
|
|
410
|
+
|
|
411
|
+
});
|
|
412
|
+
});
|