@nypl/design-system-react-components 0.25.0 → 0.25.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.
Files changed (236) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/Accordion/Accordion.d.ts +14 -14
  3. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +11 -14
  4. package/dist/components/Breadcrumbs/BreadcrumbsTypes.d.ts +6 -0
  5. package/dist/components/Button/ButtonTypes.d.ts +2 -1
  6. package/dist/components/Card/Card.d.ts +1 -1
  7. package/dist/components/Card/CardTypes.d.ts +2 -2
  8. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts +43 -0
  9. package/dist/components/CheckboxGroup/CheckboxGroupLayoutTypes.d.ts +4 -0
  10. package/dist/components/DatePicker/DatePicker.d.ts +3 -3
  11. package/dist/components/Form/Form.d.ts +13 -8
  12. package/dist/components/Form/FormTypes.d.ts +2 -8
  13. package/dist/components/Grid/GridTypes.d.ts +9 -0
  14. package/dist/components/Grid/SimpleGrid.d.ts +14 -0
  15. package/dist/components/Heading/Heading.d.ts +4 -4
  16. package/dist/components/Hero/Hero.d.ts +19 -14
  17. package/dist/components/Hero/HeroTypes.d.ts +10 -5
  18. package/dist/components/Icons/Icon.d.ts +13 -16
  19. package/dist/components/Icons/IconTypes.d.ts +78 -64
  20. package/dist/components/Label/Label.d.ts +5 -17
  21. package/dist/components/Link/Link.d.ts +8 -12
  22. package/dist/components/SearchBar/SearchBar.d.ts +45 -27
  23. package/dist/components/Select/Select.d.ts +32 -35
  24. package/dist/components/Select/SelectTypes.d.ts +4 -0
  25. package/dist/components/SkeletonLoader/SkeletonLoader.d.ts +1 -1
  26. package/dist/components/SkeletonLoader/SkeletonLoaderTypes.d.ts +2 -2
  27. package/dist/components/StatusBadge/StatusBadge.d.ts +8 -6
  28. package/dist/components/StatusBadge/StatusBadgeTypes.d.ts +5 -0
  29. package/dist/components/Template/Template.d.ts +91 -0
  30. package/dist/components/Text/Text.d.ts +16 -0
  31. package/dist/components/Text/TextTypes.d.ts +6 -0
  32. package/dist/components/TextInput/TextInput.d.ts +32 -31
  33. package/dist/components/TextInput/TextInputTypes.d.ts +5 -0
  34. package/dist/design-system-react-components.cjs.development.js +2597 -1170
  35. package/dist/design-system-react-components.cjs.development.js.map +1 -1
  36. package/dist/design-system-react-components.cjs.production.min.js +1 -1
  37. package/dist/design-system-react-components.cjs.production.min.js.map +1 -1
  38. package/dist/design-system-react-components.esm.js +2580 -1173
  39. package/dist/design-system-react-components.esm.js.map +1 -1
  40. package/dist/index.d.ts +8 -1
  41. package/dist/styles.css +1 -1
  42. package/dist/theme/components/accordion.d.ts +25 -0
  43. package/dist/theme/components/breadcrumb.d.ts +90 -0
  44. package/dist/theme/components/button.d.ts +17 -7
  45. package/dist/theme/components/checkbox.d.ts +7 -7
  46. package/dist/theme/components/customCheckboxGroup.d.ts +18 -0
  47. package/dist/theme/components/customRadioGroup.d.ts +4 -3
  48. package/dist/theme/components/global.d.ts +55 -0
  49. package/dist/theme/components/globalMixins.d.ts +15 -0
  50. package/dist/theme/components/heading.d.ts +50 -20
  51. package/dist/theme/components/hero.d.ts +492 -0
  52. package/dist/theme/components/icon.d.ts +13 -0
  53. package/dist/theme/components/label.d.ts +16 -0
  54. package/dist/theme/components/link.d.ts +45 -0
  55. package/dist/theme/components/radio.d.ts +8 -7
  56. package/dist/theme/components/searchBar.d.ts +20 -0
  57. package/dist/theme/components/select.d.ts +58 -0
  58. package/dist/theme/components/statusBadge.d.ts +25 -0
  59. package/dist/theme/components/tabs.d.ts +9 -9
  60. package/dist/theme/components/template.d.ts +105 -0
  61. package/dist/theme/components/text.d.ts +20 -0
  62. package/dist/theme/components/textInput.d.ts +105 -0
  63. package/dist/theme/foundations/global.d.ts +3 -0
  64. package/dist/theme/foundations/shadows.d.ts +4 -0
  65. package/dist/utils/utils.d.ts +6 -0
  66. package/package.json +3 -2
  67. package/src/components/Accordion/Accordion.stories.mdx +227 -33
  68. package/src/components/Accordion/Accordion.test.tsx +135 -19
  69. package/src/components/Accordion/Accordion.tsx +81 -56
  70. package/src/components/Autosuggest/Autosuggest.stories.mdx +1 -1
  71. package/src/components/Autosuggest/Autosuggest.stories.tsx +1 -1
  72. package/src/components/Autosuggest/_Autosuggest.scss +2 -2
  73. package/src/components/Breadcrumbs/Breadcrumbs.stories.mdx +46 -52
  74. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +31 -25
  75. package/src/components/Breadcrumbs/Breadcrumbs.tsx +71 -73
  76. package/src/components/Breadcrumbs/BreadcrumbsTypes.tsx +6 -0
  77. package/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap +100 -0
  78. package/src/components/Button/Button.stories.mdx +31 -27
  79. package/src/components/Button/Button.test.tsx +17 -5
  80. package/src/components/Button/ButtonTypes.tsx +1 -0
  81. package/src/components/Button/_Button.scss +3 -27
  82. package/src/components/Button/__snapshots__/Button.test.tsx.snap +11 -0
  83. package/src/components/Card/Card.stories.mdx +24 -20
  84. package/src/components/Card/Card.test.tsx +13 -19
  85. package/src/components/Card/Card.tsx +1 -1
  86. package/src/components/Card/CardTypes.tsx +2 -2
  87. package/src/components/Card/_Card.scss +1 -1
  88. package/src/components/CardEdition/CardEdition.stories.tsx +11 -6
  89. package/src/components/CardEdition/CardEdition.test.tsx +23 -31
  90. package/src/components/CardEdition/_CardEdition.scss +2 -2
  91. package/src/components/Chakra/Center.stories.mdx +31 -14
  92. package/src/components/Chakra/Grid.stories.mdx +79 -0
  93. package/src/components/Chakra/Stack.stories.mdx +4 -4
  94. package/src/components/Checkbox/Checkbox.tsx +9 -12
  95. package/src/components/Checkbox/__snapshots__/Checkbox.test.tsx.snap +2 -5
  96. package/src/components/CheckboxGroup/CheckboxGroup.stories.mdx +249 -0
  97. package/src/components/CheckboxGroup/CheckboxGroup.test.tsx +345 -0
  98. package/src/components/CheckboxGroup/CheckboxGroup.tsx +148 -0
  99. package/src/components/CheckboxGroup/CheckboxGroupLayoutTypes.tsx +4 -0
  100. package/src/components/CheckboxGroup/__snapshots__/CheckboxGroup.test.tsx.snap +1360 -0
  101. package/src/components/DatePicker/DatePicker.test.tsx +4 -4
  102. package/src/components/DatePicker/DatePicker.tsx +13 -13
  103. package/src/components/DatePicker/_DatePicker.scss +1 -0
  104. package/src/components/Form/Form.stories.mdx +46 -21
  105. package/src/components/Form/Form.test.tsx +58 -45
  106. package/src/components/Form/Form.tsx +49 -21
  107. package/src/components/Form/FormTypes.tsx +3 -8
  108. package/src/components/Form/__snapshots__/Form.test.tsx.snap +24 -14
  109. package/src/components/Grid/GridTypes.tsx +9 -0
  110. package/src/components/Grid/SimpleGrid.stories.mdx +275 -0
  111. package/src/components/Grid/SimpleGrid.test.tsx +66 -0
  112. package/src/components/Grid/SimpleGrid.tsx +37 -0
  113. package/src/components/Grid/__snapshots__/SimpleGrid.test.tsx.snap +8 -0
  114. package/src/components/Heading/Heading.stories.mdx +1 -0
  115. package/src/components/Heading/Heading.tsx +12 -6
  116. package/src/components/HelperErrorText/_HelperErrorText.scss +1 -1
  117. package/src/components/Hero/Hero.stories.mdx +188 -121
  118. package/src/components/Hero/Hero.test.tsx +537 -107
  119. package/src/components/Hero/Hero.tsx +79 -92
  120. package/src/components/Hero/HeroTypes.tsx +17 -5
  121. package/src/components/Hero/__snapshots__/Hero.test.tsx.snap +307 -0
  122. package/src/components/HorizontalRule/_HorizontalRule.scss +1 -1
  123. package/src/components/Icons/Icon.stories.mdx +83 -74
  124. package/src/components/Icons/Icon.test.tsx +30 -22
  125. package/src/components/Icons/Icon.tsx +63 -61
  126. package/src/components/Icons/IconTypes.tsx +80 -64
  127. package/src/components/Input/_Input.scss +2 -2
  128. package/src/components/Label/Label.stories.mdx +28 -7
  129. package/src/components/Label/Label.test.tsx +43 -12
  130. package/src/components/Label/Label.tsx +21 -34
  131. package/src/components/Label/__snapshots__/Label.test.tsx.snap +41 -0
  132. package/src/components/Link/Link.stories.mdx +41 -41
  133. package/src/components/Link/Link.test.tsx +33 -44
  134. package/src/components/Link/Link.tsx +114 -100
  135. package/src/components/List/List.stories.mdx +0 -2
  136. package/src/components/List/List.stories.tsx +5 -5
  137. package/src/components/List/_List.scss +3 -3
  138. package/src/components/Modal/_Modal.scss +1 -1
  139. package/src/components/Notification/Notification.stories.mdx +12 -1
  140. package/src/components/Notification/Notification.test.tsx +3 -16
  141. package/src/components/Notification/Notification.tsx +9 -10
  142. package/src/components/Notification/_Notification.scss +4 -4
  143. package/src/components/Pagination/Pagination.test.tsx +16 -10
  144. package/src/components/RadioGroup/RadioGroup.stories.mdx +1 -1
  145. package/src/components/RadioGroup/RadioGroup.tsx +2 -10
  146. package/src/components/SearchBar/SearchBar.Test.tsx +151 -16
  147. package/src/components/SearchBar/SearchBar.stories.mdx +189 -219
  148. package/src/components/SearchBar/SearchBar.tsx +151 -46
  149. package/src/components/Select/Select.stories.mdx +188 -170
  150. package/src/components/Select/Select.test.tsx +125 -380
  151. package/src/components/Select/Select.tsx +118 -165
  152. package/src/components/Select/SelectTypes.tsx +4 -0
  153. package/src/components/SkeletonLoader/SkeletonLoader.stories.mdx +13 -25
  154. package/src/components/SkeletonLoader/SkeletonLoader.test.tsx +7 -7
  155. package/src/components/SkeletonLoader/SkeletonLoader.tsx +4 -2
  156. package/src/components/SkeletonLoader/SkeletonLoaderTypes.tsx +2 -2
  157. package/src/components/SkeletonLoader/_SkeletonLoader.scss +3 -3
  158. package/src/components/StatusBadge/StatusBadge.stories.mdx +91 -0
  159. package/src/components/StatusBadge/StatusBadge.test.tsx +35 -7
  160. package/src/components/StatusBadge/StatusBadge.tsx +24 -25
  161. package/src/components/StatusBadge/StatusBadgeTypes.tsx +5 -0
  162. package/src/components/StatusBadge/__snapshots__/StatusBadge.test.tsx.snap +28 -0
  163. package/src/components/StyleGuide/Bidirectionality.stories.mdx +16 -16
  164. package/src/components/StyleGuide/Buttons.stories.mdx +15 -15
  165. package/src/components/StyleGuide/Colors.stories.mdx +336 -0
  166. package/src/components/StyleGuide/Iconography.stories.mdx +88 -90
  167. package/src/components/StyleGuide/UIDocCard.tsx +1 -1
  168. package/src/components/Tabs/Tabs.tsx +7 -9
  169. package/src/components/Template/Template.stories.mdx +574 -0
  170. package/src/components/Template/Template.test.tsx +124 -0
  171. package/src/components/Template/Template.tsx +226 -0
  172. package/src/components/Text/Text.stories.mdx +70 -0
  173. package/src/components/Text/Text.test.tsx +63 -0
  174. package/src/components/Text/Text.tsx +55 -0
  175. package/src/components/Text/TextTypes.tsx +6 -0
  176. package/src/components/Text/__snapshots__/Text.test.tsx.snap +33 -0
  177. package/src/components/TextInput/TextInput.stories.mdx +89 -90
  178. package/src/components/TextInput/TextInput.test.tsx +65 -86
  179. package/src/components/TextInput/TextInput.tsx +101 -95
  180. package/src/components/TextInput/TextInputTypes.tsx +6 -0
  181. package/src/components/VideoPlayer/_VideoPlayer.scss +1 -1
  182. package/src/docs/Chakra.stories.mdx +4 -4
  183. package/src/docs/Intro.stories.mdx +15 -13
  184. package/src/index.ts +20 -0
  185. package/src/styles/01-colors/_colors-brand.scss +6 -0
  186. package/src/styles/01-colors/_colors-utility.scss +14 -12
  187. package/src/styles/base/_04-base.scss +2 -1
  188. package/src/styles/base/_place-holder.scss +1 -1
  189. package/src/styles.scss +10 -12
  190. package/src/theme/components/accordion.ts +30 -0
  191. package/src/theme/components/breadcrumb.ts +77 -0
  192. package/src/theme/components/button.ts +77 -63
  193. package/src/theme/components/checkbox.ts +15 -27
  194. package/src/theme/components/customCheckboxGroup.ts +12 -0
  195. package/src/theme/components/customRadioGroup.ts +4 -10
  196. package/src/theme/components/global.ts +71 -0
  197. package/src/theme/components/globalMixins.ts +16 -0
  198. package/src/theme/components/heading.ts +15 -8
  199. package/src/theme/components/hero.ts +239 -0
  200. package/src/theme/components/icon.ts +79 -0
  201. package/src/theme/components/label.ts +17 -0
  202. package/src/theme/components/link.ts +47 -0
  203. package/src/theme/components/radio.ts +20 -31
  204. package/src/theme/components/searchBar.ts +21 -0
  205. package/src/theme/components/select.ts +50 -0
  206. package/src/theme/components/statusBadge.ts +27 -0
  207. package/src/theme/components/tabs.ts +72 -69
  208. package/src/theme/components/template.ts +114 -0
  209. package/src/theme/components/text.ts +31 -0
  210. package/src/theme/components/textInput.ts +61 -0
  211. package/src/theme/foundations/colors.ts +29 -13
  212. package/src/theme/foundations/global.ts +3 -0
  213. package/src/theme/foundations/shadows.ts +5 -0
  214. package/src/theme/index.ts +37 -7
  215. package/src/utils/componentCategories.ts +8 -2
  216. package/src/utils/utils.ts +13 -0
  217. package/dist/components/Accordion/Accordion.stories.d.ts +0 -6
  218. package/dist/components/StatusBadge/StatusBadge.stories.d.ts +0 -8
  219. package/dist/components/StyleGuide/Colors.stories.d.ts +0 -25
  220. package/dist/components/Template/Template.stories.d.ts +0 -30
  221. package/src/components/Accordion/Accordion.stories.tsx +0 -66
  222. package/src/components/Accordion/_Accordion.scss +0 -81
  223. package/src/components/Breadcrumbs/_Breadcrumbs.scss +0 -97
  224. package/src/components/Form/_Form.scss +0 -67
  225. package/src/components/Hero/_Hero.scss +0 -256
  226. package/src/components/Icons/_Icons.scss +0 -116
  227. package/src/components/Label/_Label.scss +0 -22
  228. package/src/components/Link/_Link.scss +0 -73
  229. package/src/components/SearchBar/_SearchBar.scss +0 -16
  230. package/src/components/Select/_Select.scss +0 -82
  231. package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -34
  232. package/src/components/StatusBadge/_StatusBadge.scss +0 -23
  233. package/src/components/StyleGuide/Colors.stories.tsx +0 -289
  234. package/src/components/Template/Template.stories.tsx +0 -86
  235. package/src/components/Template/_Template.scss +0 -63
  236. package/src/components/TextInput/_TextInput.scss +0 -59
@@ -0,0 +1,345 @@
1
+ import * as React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { axe } from "jest-axe";
4
+ import renderer from "react-test-renderer";
5
+
6
+ import * as generateUUID from "../../helpers/generateUUID";
7
+ import CheckboxGroup from "./CheckboxGroup";
8
+ import Checkbox from "../Checkbox/Checkbox";
9
+ import { CheckboxGroupLayoutTypes } from "./CheckboxGroupLayoutTypes";
10
+ import userEvent from "@testing-library/user-event";
11
+
12
+ describe("Checkbox Accessibility", () => {
13
+ it("passes axe accessibility", async () => {
14
+ const { container } = render(
15
+ <CheckboxGroup labelText="CheckboxGroup example" name="a11y-test">
16
+ <Checkbox value="2" labelText="Checkbox 2" />
17
+ <Checkbox value="3" labelText="Checkbox 3" />
18
+ <Checkbox value="4" labelText="Checkbox 4" />
19
+ <Checkbox value="5" labelText="Checkbox 5" />
20
+ </CheckboxGroup>
21
+ );
22
+ expect(await axe(container)).toHaveNoViolations();
23
+ });
24
+ });
25
+
26
+ describe("Checkbox", () => {
27
+ it("renders with Checkbox inputs and a label", () => {
28
+ render(
29
+ <CheckboxGroup labelText="Test Label" name="test1">
30
+ <Checkbox value="2" labelText="Checkbox 2" />
31
+ <Checkbox value="3" labelText="Checkbox 3" />
32
+ <Checkbox value="4" labelText="Checkbox 4" />
33
+ </CheckboxGroup>
34
+ );
35
+ expect(screen.getByText(/Test Label/i)).toBeInTheDocument();
36
+ expect(screen.getAllByRole("checkbox")).toHaveLength(3);
37
+ expect(screen.getByLabelText("Checkbox 2")).toBeInTheDocument();
38
+ expect(screen.getByLabelText("Checkbox 3")).toBeInTheDocument();
39
+ expect(screen.getByLabelText("Checkbox 4")).toBeInTheDocument();
40
+ });
41
+
42
+ it("renders with appropriate 'aria-label' attribute and value when 'showLabel' prop is set to false", () => {
43
+ const { rerender } = render(
44
+ <CheckboxGroup labelText="Test Label" name="test2">
45
+ <Checkbox value="2" labelText="Checkbox 2" />
46
+ <Checkbox value="3" labelText="Checkbox 3" />
47
+ <Checkbox value="4" labelText="Checkbox 4" />
48
+ </CheckboxGroup>
49
+ );
50
+ expect(screen.getByTestId("checkbox-group")).not.toHaveAttribute(
51
+ "aria-label",
52
+ "Test Label"
53
+ );
54
+
55
+ rerender(
56
+ <CheckboxGroup labelText="Test Label" name="test2" showLabel={false}>
57
+ <Checkbox value="2" labelText="Checkbox 2" />
58
+ <Checkbox value="3" labelText="Checkbox 3" />
59
+ <Checkbox value="4" labelText="Checkbox 4" />
60
+ </CheckboxGroup>
61
+ );
62
+ expect(screen.getByTestId("checkbox-group")).toHaveAttribute(
63
+ "aria-label",
64
+ "Test Label"
65
+ );
66
+ });
67
+
68
+ it("renders visible helper or error text", () => {
69
+ const { rerender } = render(
70
+ <CheckboxGroup
71
+ labelText="Test Label"
72
+ name="test3"
73
+ helperText="This is the helper text for the full group."
74
+ invalidText="This is the error text :("
75
+ >
76
+ <Checkbox value="2" labelText="Checkbox 2" />
77
+ <Checkbox value="3" labelText="Checkbox 3" />
78
+ <Checkbox value="4" labelText="Checkbox 4" />
79
+ </CheckboxGroup>
80
+ );
81
+ expect(
82
+ screen.getByText("This is the helper text for the full group.")
83
+ ).toBeVisible();
84
+ expect(
85
+ screen.queryByText("This is the error text :(")
86
+ ).not.toBeInTheDocument();
87
+
88
+ rerender(
89
+ <CheckboxGroup
90
+ labelText="Test Label"
91
+ name="test3"
92
+ helperText="This is the helper text for the full group."
93
+ invalidText="This is the error text :("
94
+ isInvalid
95
+ >
96
+ <Checkbox value="2" labelText="Checkbox 2" />
97
+ <Checkbox value="3" labelText="Checkbox 3" />
98
+ <Checkbox value="4" labelText="Checkbox 4" />
99
+ </CheckboxGroup>
100
+ );
101
+ expect(screen.getByText("This is the error text :(")).toBeVisible();
102
+ expect(
103
+ screen.queryByText("This is the helper text for the full group.")
104
+ ).not.toBeInTheDocument();
105
+ });
106
+
107
+ it("sets the CheckboxGroup's ID", () => {
108
+ render(
109
+ <CheckboxGroup labelText="Test Label" name="test5" id="some-id">
110
+ <Checkbox value="2" labelText="Checkbox 2" />
111
+ </CheckboxGroup>
112
+ );
113
+
114
+ // The "group" role here is for the `fieldset` element.
115
+ expect(screen.getByRole("group")).toHaveAttribute(
116
+ "id",
117
+ "checkbox-group-some-id"
118
+ );
119
+ });
120
+
121
+ it("sets the next value through the onChange function", () => {
122
+ let newValue = [];
123
+ const onChange = (value) => {
124
+ newValue = value;
125
+ };
126
+ render(
127
+ <CheckboxGroup
128
+ labelText="Test Label"
129
+ name="getValue"
130
+ defaultValue={["4"]}
131
+ onChange={onChange}
132
+ >
133
+ <Checkbox value="2" labelText="Checkbox 2" />
134
+ <Checkbox value="3" labelText="Checkbox 3" />
135
+ <Checkbox value="4" labelText="Checkbox 4" />
136
+ </CheckboxGroup>
137
+ );
138
+
139
+ expect(newValue).toEqual([]);
140
+
141
+ userEvent.click(screen.getByText("Checkbox 3"));
142
+ // "4" was the initial selected value
143
+ expect(newValue).toEqual(["4", "3"]);
144
+ userEvent.click(screen.getByText("Checkbox 2"));
145
+ expect(newValue).toEqual(["4", "3", "2"]);
146
+ userEvent.click(screen.getByText("Checkbox 3"));
147
+ expect(newValue).toEqual(["4", "2"]);
148
+ });
149
+
150
+ it("calls the UUID generation function if no id prop value is passed", () => {
151
+ const generateUUIDSpy = jest.spyOn(generateUUID, "default");
152
+ expect(generateUUIDSpy).toHaveBeenCalledTimes(0);
153
+ render(
154
+ <CheckboxGroup labelText="Test Label" name="test6">
155
+ <Checkbox value="2" labelText="Checkbox 2" />
156
+ </CheckboxGroup>
157
+ );
158
+ expect(generateUUIDSpy).toHaveBeenCalledTimes(1);
159
+ });
160
+
161
+ it("sets the 'disabled' attribute for all its Checkbox children", () => {
162
+ render(
163
+ <CheckboxGroup labelText="Test Label" name="test7" isDisabled>
164
+ <Checkbox value="2" labelText="Checkbox 2" />
165
+ <Checkbox value="3" labelText="Checkbox 3" />
166
+ <Checkbox value="4" labelText="Checkbox 4" />
167
+ </CheckboxGroup>
168
+ );
169
+ const Checkboxes = screen.getAllByRole("checkbox");
170
+
171
+ expect(Checkboxes).toHaveLength(3);
172
+ expect(Checkboxes[0]).toHaveAttribute("disabled");
173
+ expect(Checkboxes[1]).toHaveAttribute("disabled");
174
+ expect(Checkboxes[2]).toHaveAttribute("disabled");
175
+ });
176
+
177
+ it("sets the 'required' attribute for all its Checkbox children", () => {
178
+ render(
179
+ <CheckboxGroup labelText="Test Label" name="test8" isRequired>
180
+ <Checkbox value="2" labelText="Checkbox 2" />
181
+ <Checkbox value="3" labelText="Checkbox 3" />
182
+ <Checkbox value="4" labelText="Checkbox 4" />
183
+ </CheckboxGroup>
184
+ );
185
+ const Checkboxes = screen.getAllByRole("checkbox");
186
+
187
+ expect(Checkboxes).toHaveLength(3);
188
+ expect(Checkboxes[0]).toHaveAttribute("required");
189
+ expect(Checkboxes[1]).toHaveAttribute("required");
190
+ expect(Checkboxes[2]).toHaveAttribute("required");
191
+ });
192
+
193
+ it("sets the error state for all its Checkbox children", () => {
194
+ render(
195
+ <CheckboxGroup labelText="Test Label" name="test9" isInvalid>
196
+ <Checkbox value="2" labelText="Checkbox 2" />
197
+ <Checkbox value="3" labelText="Checkbox 3" />
198
+ <Checkbox value="4" labelText="Checkbox 4" />
199
+ </CheckboxGroup>
200
+ );
201
+ const Checkboxes = screen.getAllByRole("checkbox");
202
+
203
+ expect(Checkboxes).toHaveLength(3);
204
+ expect(Checkboxes[0]).toHaveAttribute("aria-invalid");
205
+ expect(Checkboxes[1]).toHaveAttribute("aria-invalid");
206
+ expect(Checkboxes[2]).toHaveAttribute("aria-invalid");
207
+ });
208
+
209
+ it("renders the UI snapshot correctly", () => {
210
+ const column = renderer
211
+ .create(
212
+ <CheckboxGroup labelText="column" name="column" id="column">
213
+ <Checkbox value="2" labelText="Checkbox 2" />
214
+ <Checkbox value="3" labelText="Checkbox 3" />
215
+ </CheckboxGroup>
216
+ )
217
+ .toJSON();
218
+ const row = renderer
219
+ .create(
220
+ <CheckboxGroup
221
+ labelText="row"
222
+ name="row"
223
+ id="row"
224
+ layout={CheckboxGroupLayoutTypes.Row}
225
+ >
226
+ <Checkbox value="2" labelText="Checkbox 2" />
227
+ <Checkbox value="3" labelText="Checkbox 3" />
228
+ </CheckboxGroup>
229
+ )
230
+ .toJSON();
231
+ const noLabel = renderer
232
+ .create(
233
+ <CheckboxGroup
234
+ labelText="no label"
235
+ name="noLabel"
236
+ id="noLabel"
237
+ showLabel={false}
238
+ >
239
+ <Checkbox value="2" labelText="Checkbox 2" />
240
+ <Checkbox value="3" labelText="Checkbox 3" />
241
+ </CheckboxGroup>
242
+ )
243
+ .toJSON();
244
+ const helperText = renderer
245
+ .create(
246
+ <CheckboxGroup
247
+ labelText="helperText"
248
+ name="helperText"
249
+ id="helperText"
250
+ helperText="helper text"
251
+ >
252
+ <Checkbox value="2" labelText="Checkbox 2" />
253
+ <Checkbox value="3" labelText="Checkbox 3" />
254
+ </CheckboxGroup>
255
+ )
256
+ .toJSON();
257
+ const invalidText = renderer
258
+ .create(
259
+ <CheckboxGroup
260
+ labelText="invalidText"
261
+ name="invalidText"
262
+ id="invalidText"
263
+ invalidText="error text"
264
+ >
265
+ <Checkbox value="2" labelText="Checkbox 2" />
266
+ <Checkbox value="3" labelText="Checkbox 3" />
267
+ </CheckboxGroup>
268
+ )
269
+ .toJSON();
270
+ const noOptReqLabel = renderer
271
+ .create(
272
+ <CheckboxGroup
273
+ labelText="no optional or required label"
274
+ name="optReq"
275
+ id="optReq"
276
+ optReqFlag={false}
277
+ >
278
+ <Checkbox value="2" labelText="Checkbox 2" />
279
+ <Checkbox value="3" labelText="Checkbox 3" />
280
+ </CheckboxGroup>
281
+ )
282
+ .toJSON();
283
+ const isRequired = renderer
284
+ .create(
285
+ <CheckboxGroup
286
+ labelText="required"
287
+ name="required"
288
+ id="required"
289
+ isRequired
290
+ >
291
+ <Checkbox value="2" labelText="Checkbox 2" />
292
+ <Checkbox value="3" labelText="Checkbox 3" />
293
+ </CheckboxGroup>
294
+ )
295
+ .toJSON();
296
+ const isInvalid = renderer
297
+ .create(
298
+ <CheckboxGroup
299
+ labelText="invalid"
300
+ name="invalid"
301
+ id="invalid"
302
+ isInvalid
303
+ >
304
+ <Checkbox value="2" labelText="Checkbox 2" />
305
+ <Checkbox value="3" labelText="Checkbox 3" />
306
+ </CheckboxGroup>
307
+ )
308
+ .toJSON();
309
+ const isDisabled = renderer
310
+ .create(
311
+ <CheckboxGroup
312
+ labelText="disabled"
313
+ name="disabled"
314
+ id="disabled"
315
+ isDisabled
316
+ >
317
+ <Checkbox value="2" labelText="Checkbox 2" />
318
+ <Checkbox value="3" labelText="Checkbox 3" />
319
+ </CheckboxGroup>
320
+ )
321
+ .toJSON();
322
+
323
+ expect(column).toMatchSnapshot();
324
+ expect(row).toMatchSnapshot();
325
+ expect(noLabel).toMatchSnapshot();
326
+ expect(helperText).toMatchSnapshot();
327
+ expect(invalidText).toMatchSnapshot();
328
+ expect(noOptReqLabel).toMatchSnapshot();
329
+ expect(isRequired).toMatchSnapshot();
330
+ expect(isInvalid).toMatchSnapshot();
331
+ expect(isDisabled).toMatchSnapshot();
332
+ });
333
+
334
+ it("should throw warning when a non-Checkbox component is used as a child", () => {
335
+ const warn = jest.spyOn(console, "warn");
336
+ render(
337
+ <CheckboxGroup labelText="wrong child!" name="wrong" id="wrong-child">
338
+ <p>This is wrong!</p>
339
+ </CheckboxGroup>
340
+ );
341
+ expect(warn).toHaveBeenCalledWith(
342
+ "Only `Checkbox` components are allowed inside the `CheckboxGroup` component."
343
+ );
344
+ });
345
+ });
@@ -0,0 +1,148 @@
1
+ import * as React from "react";
2
+ import {
3
+ Box,
4
+ Stack,
5
+ CheckboxGroup as ChakraCheckboxGroup,
6
+ useMultiStyleConfig,
7
+ } from "@chakra-ui/react";
8
+
9
+ import HelperErrorText from "../HelperErrorText/HelperErrorText";
10
+ import generateUUID from "../../helpers/generateUUID";
11
+ import { spacing } from "../../theme/foundations/spacing";
12
+ import { CheckboxGroupLayoutTypes } from "./CheckboxGroupLayoutTypes";
13
+ import Checkbox from "../Checkbox/Checkbox";
14
+
15
+ export interface CheckboxGroupProps {
16
+ /** Any child node passed to the component. */
17
+ children: React.ReactNode;
18
+ /** Populates the initial value of the input */
19
+ defaultValue?: string[];
20
+ /** Optional string to populate the HelperErrorText for error state */
21
+ invalidText?: string;
22
+ /** Optional string to populate the HelperErrorText for standard state */
23
+ helperText?: string;
24
+ /** ID that other components can cross reference for accessibility purposes */
25
+ id?: string;
26
+ /** Adds the 'disabled' prop to the input when true. */
27
+ isDisabled?: boolean;
28
+ /** A`dds the 'aria-invalid' attribute to the input and
29
+ * sets the error state when true. */
30
+ isInvalid?: boolean;
31
+ /** Adds the 'required' attribute to the input when true. */
32
+ isRequired?: boolean;
33
+ /** The checkbox group label displayed in a `legend` element if `showlabel` is
34
+ * true, or an "aria-label" if `showLabel` is false. */
35
+ labelText: string;
36
+ /** Renders the checkbox buttons in a row or column (default). */
37
+ layout?: CheckboxGroupLayoutTypes;
38
+ /** The `name` prop indicates the form group for all the `Checkbox` children. */
39
+ name: string;
40
+ /** The action to perform on the `<input>`'s onChange function */
41
+ onChange?: (value: string[]) => void;
42
+ /** Whether or not to display "Required"/"Optional" in the label text. */
43
+ optReqFlag?: boolean;
44
+ /** Offers the ability to show the group's legend onscreen or hide it. Refer
45
+ * to the `labelText` property for more information. */
46
+ showLabel?: boolean;
47
+ }
48
+
49
+ const noop = () => {};
50
+
51
+ /**
52
+ * Wrapper component to wrap `Checkbox` components. Can be displayed in a
53
+ * column or in a row. The `CheckboxGroup` component renders all the necessary
54
+ * wrapping and associated text elements, but the checkbox input elements
55
+ * _need_ to be child `Checkbox` components from the NYPL Design System.
56
+ */
57
+ const CheckboxGroup = React.forwardRef<HTMLInputElement, CheckboxGroupProps>(
58
+ (props, ref?) => {
59
+ const {
60
+ children,
61
+ defaultValue = [],
62
+ invalidText,
63
+ helperText,
64
+ id = generateUUID(),
65
+ isDisabled = false,
66
+ isInvalid = false,
67
+ isRequired = false,
68
+ labelText,
69
+ layout = CheckboxGroupLayoutTypes.Column,
70
+ name,
71
+ onChange,
72
+ optReqFlag = true,
73
+ showLabel = true,
74
+ } = props;
75
+ const footnote = isInvalid ? invalidText : helperText;
76
+ const spacingProp =
77
+ layout === CheckboxGroupLayoutTypes.Column ? spacing.s : spacing.l;
78
+ const newChildren = [];
79
+ const checkboxProps =
80
+ defaultValue && onChange
81
+ ? {
82
+ defaultValue,
83
+ onChange,
84
+ }
85
+ : {};
86
+
87
+ // Go through the Checkbox children and update them as needed.
88
+ React.Children.map(children, (child: React.ReactElement, i) => {
89
+ if (child.type !== Checkbox) {
90
+ // Special case for Storybook MDX documentation.
91
+ if (child.props.mdxType && child.props.mdxType === "Checkbox") {
92
+ noop();
93
+ } else {
94
+ console.warn(
95
+ "Only `Checkbox` components are allowed inside the `CheckboxGroup` component."
96
+ );
97
+ }
98
+ }
99
+
100
+ if (child !== undefined && child !== null) {
101
+ const newProps = {
102
+ key: i,
103
+ id: `${id}-${i}`,
104
+ name,
105
+ isDisabled,
106
+ isInvalid,
107
+ isRequired,
108
+ };
109
+ newChildren.push(React.cloneElement(child, newProps));
110
+ }
111
+ });
112
+
113
+ // Get the Chakra-based styles for all the custom elements in this component.
114
+ const styles = useMultiStyleConfig("CustomCheckboxGroup", {});
115
+
116
+ return (
117
+ <Box as="fieldset" id={`checkbox-group-${id}`} __css={styles}>
118
+ <legend className={showLabel ? "" : "sr-only"}>
119
+ <span>{labelText}</span>
120
+ {optReqFlag && (
121
+ <Box as="span" __css={styles.required}>
122
+ {isRequired ? "Required" : "Optional"}
123
+ </Box>
124
+ )}
125
+ </legend>
126
+ <ChakraCheckboxGroup {...checkboxProps}>
127
+ <Stack
128
+ id={id}
129
+ data-testid="checkbox-group"
130
+ direction={[layout]}
131
+ spacing={spacingProp}
132
+ ref={ref}
133
+ aria-label={!showLabel ? labelText : null}
134
+ >
135
+ {newChildren}
136
+ </Stack>
137
+ </ChakraCheckboxGroup>
138
+ {footnote && (
139
+ <Box __css={styles.helper}>
140
+ <HelperErrorText isError={isInvalid}>{footnote}</HelperErrorText>
141
+ </Box>
142
+ )}
143
+ </Box>
144
+ );
145
+ }
146
+ );
147
+
148
+ export default CheckboxGroup;
@@ -0,0 +1,4 @@
1
+ export enum CheckboxGroupLayoutTypes {
2
+ Column = "column",
3
+ Row = "row",
4
+ }