@nypl/design-system-react-components 0.25.9 → 0.25.10
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/CHANGELOG.md +45 -0
- package/dist/components/DatePicker/DatePicker.d.ts +1 -1
- package/dist/components/Fieldset/Fieldset.d.ts +1 -3
- package/dist/components/Form/Form.d.ts +13 -12
- package/dist/components/Form/FormTypes.d.ts +2 -2
- package/dist/components/HorizontalRule/HorizontalRule.d.ts +1 -1
- package/dist/components/RadioGroup/RadioGroup.d.ts +3 -3
- package/dist/components/SearchBar/SearchBar.d.ts +5 -5
- package/dist/components/Table/Table.d.ts +9 -3
- package/dist/components/Template/Template.d.ts +23 -4
- package/dist/design-system-react-components.cjs.development.js +197 -89
- package/dist/design-system-react-components.cjs.development.js.map +1 -1
- package/dist/design-system-react-components.cjs.production.min.js +1 -1
- package/dist/design-system-react-components.cjs.production.min.js.map +1 -1
- package/dist/design-system-react-components.esm.js +186 -91
- package/dist/design-system-react-components.esm.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/resources.scss +0 -2
- package/dist/theme/components/breadcrumb.d.ts +1 -1
- package/dist/theme/components/customTable.d.ts +12 -3
- package/package.json +40 -36
- package/src/components/Accordion/Accordion.stories.mdx +1 -1
- package/src/components/Accordion/Accordion.test.tsx +45 -1
- package/src/components/Accordion/Accordion.tsx +20 -8
- package/src/components/Accordion/__snapshots__/Accordion.test.tsx.snap +243 -0
- package/src/components/Breadcrumbs/Breadcrumbs.stories.mdx +13 -2
- package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +15 -0
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +9 -3
- package/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap +5 -5
- package/src/components/Card/Card.stories.mdx +1 -1
- package/src/components/Card/Card.tsx +4 -1
- package/src/components/Chakra/Flex.stories.mdx +113 -0
- package/src/components/DatePicker/DatePicker.stories.mdx +1 -1
- package/src/components/DatePicker/DatePicker.test.tsx +6 -6
- package/src/components/DatePicker/DatePicker.tsx +3 -4
- package/src/components/Fieldset/Fieldset.stories.mdx +1 -1
- package/src/components/Fieldset/Fieldset.tsx +2 -4
- package/src/components/Form/Form.stories.mdx +34 -16
- package/src/components/Form/Form.test.tsx +92 -3
- package/src/components/Form/Form.tsx +25 -21
- package/src/components/Form/FormTypes.tsx +2 -2
- package/src/components/Form/__snapshots__/Form.test.tsx.snap +0 -1
- package/src/components/Hero/Hero.stories.mdx +1 -1
- package/src/components/HorizontalRule/HorizontalRule.stories.mdx +3 -2
- package/src/components/HorizontalRule/HorizontalRule.tsx +2 -2
- package/src/components/HorizontalRule/__snapshots__/HorizontalRule.test.tsx.snap +4 -4
- package/src/components/List/List.stories.mdx +1 -1
- package/src/components/List/List.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.mdx +1 -1
- package/src/components/Pagination/Pagination.tsx +2 -2
- package/src/components/Radio/__snapshots__/Radio.test.tsx.snap +5 -5
- package/src/components/RadioGroup/RadioGroup.stories.mdx +1 -1
- package/src/components/RadioGroup/RadioGroup.test.tsx +13 -11
- package/src/components/RadioGroup/RadioGroup.tsx +88 -89
- package/src/components/RadioGroup/__snapshots__/RadioGroup.test.tsx.snap +18 -18
- package/src/components/SearchBar/SearchBar.Test.tsx +106 -28
- package/src/components/SearchBar/SearchBar.stories.mdx +7 -4
- package/src/components/SearchBar/SearchBar.tsx +19 -20
- package/src/components/Select/Select.test.tsx +89 -0
- package/src/components/Select/Select.tsx +7 -1
- package/src/components/Select/__snapshots__/Select.test.tsx.snap +545 -0
- package/src/components/Slider/__snapshots__/Slider.test.tsx.snap +7 -0
- package/src/components/Table/Table.stories.mdx +118 -19
- package/src/components/Table/Table.test.tsx +80 -3
- package/src/components/Table/Table.tsx +26 -16
- package/src/components/Table/__snapshots__/Table.test.tsx.snap +1179 -0
- package/src/components/Tabs/Tabs.stories.mdx +1 -1
- package/src/components/Tabs/Tabs.test.tsx +21 -5
- package/src/components/Tabs/Tabs.tsx +33 -18
- package/src/components/Tabs/__snapshots__/Tabs.test.tsx.snap +195 -0
- package/src/components/Template/Template.stories.mdx +79 -4
- package/src/components/Template/Template.test.tsx +65 -3
- package/src/components/Template/Template.tsx +58 -8
- package/src/components/Template/__snapshots__/Template.test.tsx.snap +93 -0
- package/src/index.ts +8 -2
- package/src/theme/components/breadcrumb.ts +1 -1
- package/src/theme/components/customTable.ts +16 -3
- package/src/utils/componentCategories.ts +1 -0
|
@@ -6,8 +6,6 @@ import {
|
|
|
6
6
|
useStyleConfig,
|
|
7
7
|
} from "@chakra-ui/react";
|
|
8
8
|
|
|
9
|
-
import generateUUID from "../../helpers/generateUUID";
|
|
10
|
-
import { ColorVariants } from "./BreadcrumbsTypes";
|
|
11
9
|
import Icon from "../Icons/Icon";
|
|
12
10
|
import {
|
|
13
11
|
IconNames,
|
|
@@ -15,6 +13,8 @@ import {
|
|
|
15
13
|
IconSizes,
|
|
16
14
|
IconTypes,
|
|
17
15
|
} from "../Icons/IconTypes";
|
|
16
|
+
import generateUUID from "../../helpers/generateUUID";
|
|
17
|
+
import { ColorVariants } from "./BreadcrumbsTypes";
|
|
18
18
|
import { getVariant } from "../../utils/utils";
|
|
19
19
|
|
|
20
20
|
export interface BreadcrumbsDataProps {
|
|
@@ -83,9 +83,15 @@ function Breadcrumbs(props: React.PropsWithChildren<BreadcrumbProps>) {
|
|
|
83
83
|
const styles = useStyleConfig("Breadcrumb", { variant });
|
|
84
84
|
const finalStyles = { ...styles, ...additionalStyles };
|
|
85
85
|
const breadcrumbItems = getElementsFromData(breadcrumbsData, id);
|
|
86
|
+
const ariaAttrs = { "aria-label": "Breadcrumb" };
|
|
86
87
|
|
|
87
88
|
return (
|
|
88
|
-
<ChakraBreadcrumb
|
|
89
|
+
<ChakraBreadcrumb
|
|
90
|
+
className={className}
|
|
91
|
+
id={id}
|
|
92
|
+
{...ariaAttrs}
|
|
93
|
+
__css={finalStyles}
|
|
94
|
+
>
|
|
89
95
|
{breadcrumbItems}
|
|
90
96
|
</ChakraBreadcrumb>
|
|
91
97
|
);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 1`] = `
|
|
4
4
|
<nav
|
|
5
|
-
aria-label="
|
|
5
|
+
aria-label="Breadcrumb"
|
|
6
6
|
className="chakra-breadcrumb css-0"
|
|
7
7
|
id="breadcrumbs-test"
|
|
8
8
|
>
|
|
@@ -101,7 +101,7 @@ exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 1`] = `
|
|
|
101
101
|
|
|
102
102
|
exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 2`] = `
|
|
103
103
|
<nav
|
|
104
|
-
aria-label="
|
|
104
|
+
aria-label="Breadcrumb"
|
|
105
105
|
className="chakra-breadcrumb css-0"
|
|
106
106
|
id="breadcrumbs-test"
|
|
107
107
|
>
|
|
@@ -200,7 +200,7 @@ exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 2`] = `
|
|
|
200
200
|
|
|
201
201
|
exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 3`] = `
|
|
202
202
|
<nav
|
|
203
|
-
aria-label="
|
|
203
|
+
aria-label="Breadcrumb"
|
|
204
204
|
className="chakra-breadcrumb css-0"
|
|
205
205
|
id="breadcrumbs-test"
|
|
206
206
|
>
|
|
@@ -299,7 +299,7 @@ exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 3`] = `
|
|
|
299
299
|
|
|
300
300
|
exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 4`] = `
|
|
301
301
|
<nav
|
|
302
|
-
aria-label="
|
|
302
|
+
aria-label="Breadcrumb"
|
|
303
303
|
className="chakra-breadcrumb css-0"
|
|
304
304
|
id="breadcrumbs-test"
|
|
305
305
|
>
|
|
@@ -398,7 +398,7 @@ exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 4`] = `
|
|
|
398
398
|
|
|
399
399
|
exports[`Breadcrumbs Snapshot Renders the UI snapshot correctly 5`] = `
|
|
400
400
|
<nav
|
|
401
|
-
aria-label="
|
|
401
|
+
aria-label="Breadcrumb"
|
|
402
402
|
className="chakra-breadcrumb css-1f2fw9u"
|
|
403
403
|
id="breadcrumbs-test"
|
|
404
404
|
>
|
|
@@ -222,7 +222,10 @@ export default function Card(props: React.PropsWithChildren<CardProps>) {
|
|
|
222
222
|
child.props.children
|
|
223
223
|
);
|
|
224
224
|
const elem = React.cloneElement(child, {
|
|
225
|
-
additionalStyles:
|
|
225
|
+
additionalStyles: {
|
|
226
|
+
...styles.heading,
|
|
227
|
+
...child.props.additionalStyles,
|
|
228
|
+
},
|
|
226
229
|
key,
|
|
227
230
|
center,
|
|
228
231
|
// Override the child text with the potential `CardLinkOverlay`.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Box, Flex, Spacer, VStack } from "@chakra-ui/react";
|
|
2
|
+
import { Canvas, Meta, Story } from "@storybook/addon-docs";
|
|
3
|
+
|
|
4
|
+
import Heading from "../Heading/Heading";
|
|
5
|
+
import { HeadingLevels } from "../Heading/HeadingTypes";
|
|
6
|
+
import Link from "../Link/Link";
|
|
7
|
+
import { LinkTypes } from "../Link/LinkTypes";
|
|
8
|
+
import { getCategory } from "../../utils/componentCategories";
|
|
9
|
+
import DSProvider from "../../theme/provider";
|
|
10
|
+
|
|
11
|
+
<Meta title={getCategory("Flex")} component={Flex} />
|
|
12
|
+
|
|
13
|
+
# Flex
|
|
14
|
+
|
|
15
|
+
| Component Version | DS Version |
|
|
16
|
+
| ----------------- | ---------- |
|
|
17
|
+
| Added | `0.25.10` |
|
|
18
|
+
| Latest | `0.25.10` |
|
|
19
|
+
|
|
20
|
+
Note: This needs the use of the `DSProvider` component. See the
|
|
21
|
+
[README](https://nypl.github.io/nypl-design-system/storybook-static/?path=/story/chakra-ui--page#dsprovider)
|
|
22
|
+
for more information.
|
|
23
|
+
|
|
24
|
+
This component is directly exported from Chakra UI. The `Flex` component is
|
|
25
|
+
useful for simple layouts and can be used along with Chakra's `Spacer` component.
|
|
26
|
+
The combination can be used to create a container where the children span the
|
|
27
|
+
entire width of the container.
|
|
28
|
+
|
|
29
|
+
Details about available props and related child components can be found on the
|
|
30
|
+
[Flex component](https://chakra-ui.com/docs/layout/flex) page on the Chakra UI site.
|
|
31
|
+
|
|
32
|
+
<Canvas>
|
|
33
|
+
<Story
|
|
34
|
+
name="Flex"
|
|
35
|
+
args={{
|
|
36
|
+
alignItems: "baseline",
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
{(args) => (
|
|
40
|
+
<Flex {...args}>
|
|
41
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
42
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
43
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
44
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
45
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
46
|
+
</Flex>
|
|
47
|
+
)}
|
|
48
|
+
</Story>
|
|
49
|
+
</Canvas>
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
Use the `justify` prop to move the children around.
|
|
54
|
+
|
|
55
|
+
<Canvas>
|
|
56
|
+
<DSProvider>
|
|
57
|
+
<VStack align="stretch">
|
|
58
|
+
<div>
|
|
59
|
+
<p>`justify` set to "center"</p>
|
|
60
|
+
<Flex alignItems="baseline" justify="center">
|
|
61
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
62
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
63
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
64
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
65
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
66
|
+
</Flex>
|
|
67
|
+
</div>
|
|
68
|
+
<div>
|
|
69
|
+
<p>`justify` set to "space-between"</p>
|
|
70
|
+
<Flex alignItems="baseline" justify="space-between">
|
|
71
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
72
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
73
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
74
|
+
<Box w="20" h="20" bg="brand.secondary" />
|
|
75
|
+
<Box w="20" h="20" bg="brand.primary" />
|
|
76
|
+
</Flex>
|
|
77
|
+
</div>
|
|
78
|
+
</VStack>
|
|
79
|
+
</DSProvider>
|
|
80
|
+
</Canvas>
|
|
81
|
+
|
|
82
|
+
## With Spacer
|
|
83
|
+
|
|
84
|
+
A common use-case is displaying a row with two children where the first aligns
|
|
85
|
+
left and the second aligns right. Add the `Spacer` component between the children.
|
|
86
|
+
This is similar to setting `justify="space-between"` on the `Flex` parent but
|
|
87
|
+
the `Spacer` component is more flexible for most situations.
|
|
88
|
+
|
|
89
|
+
<Canvas>
|
|
90
|
+
<DSProvider>
|
|
91
|
+
<Flex alignItems="baseline">
|
|
92
|
+
<Heading id="row-heading" level={HeadingLevels.Three}>
|
|
93
|
+
Heading
|
|
94
|
+
</Heading>
|
|
95
|
+
<Spacer />
|
|
96
|
+
<Link href="#viewmore" type={LinkTypes.Forwards}>
|
|
97
|
+
View more
|
|
98
|
+
</Link>
|
|
99
|
+
</Flex>
|
|
100
|
+
</DSProvider>
|
|
101
|
+
</Canvas>
|
|
102
|
+
|
|
103
|
+
```jsx
|
|
104
|
+
<Flex alignItems="baseline">
|
|
105
|
+
<Heading id="row-heading" level={HeadingLevels.Three}>
|
|
106
|
+
Heading
|
|
107
|
+
</Heading>
|
|
108
|
+
<Spacer />
|
|
109
|
+
<Link href="#viewmore" type={LinkTypes.Forwards}>
|
|
110
|
+
View more
|
|
111
|
+
</Link>
|
|
112
|
+
</Flex>
|
|
113
|
+
```
|
|
@@ -9,7 +9,7 @@ import { DatePickerTypes } from "./DatePickerTypes";
|
|
|
9
9
|
import { TextInputRefType } from "../TextInput/TextInput";
|
|
10
10
|
|
|
11
11
|
/** This adds a "0" padding for date values under "10". */
|
|
12
|
-
const
|
|
12
|
+
const strPad = (n) => String("0" + n).slice(-2);
|
|
13
13
|
const monthArray = [
|
|
14
14
|
"January",
|
|
15
15
|
"February",
|
|
@@ -49,8 +49,8 @@ describe("DatePicker", () => {
|
|
|
49
49
|
/** Returns today's year, month, and day values. */
|
|
50
50
|
const getTodaysValues = () => {
|
|
51
51
|
const year = todaysDate.getFullYear();
|
|
52
|
-
const month =
|
|
53
|
-
const day =
|
|
52
|
+
const month = strPad(todaysDate.getMonth() + 1);
|
|
53
|
+
const day = strPad(todaysDate.getDate());
|
|
54
54
|
return [year, month, day];
|
|
55
55
|
};
|
|
56
56
|
/** Returns today's date in string format based on the DatePicker type. */
|
|
@@ -328,9 +328,9 @@ describe("DatePicker", () => {
|
|
|
328
328
|
expect(screen.getByDisplayValue(newDayValue)).toBeInTheDocument();
|
|
329
329
|
|
|
330
330
|
const { startDate } = dateObject;
|
|
331
|
-
const valueFromOnChange = `${startDate.getFullYear()}-${
|
|
331
|
+
const valueFromOnChange = `${startDate.getFullYear()}-${strPad(
|
|
332
332
|
startDate.getMonth() + 1
|
|
333
|
-
)}-${
|
|
333
|
+
)}-${strPad(startDate.getDate())}`;
|
|
334
334
|
expect(newDayValue).toEqual(valueFromOnChange);
|
|
335
335
|
});
|
|
336
336
|
|
|
@@ -724,7 +724,7 @@ describe("DatePicker", () => {
|
|
|
724
724
|
// We are two months ahead but still selecting the midmonth day.
|
|
725
725
|
userEvent.click(screen.getByText(midMonthDay));
|
|
726
726
|
// So only the month should change accordingly.
|
|
727
|
-
const newMonthValue = `${newDayValue.substr(0, 5)}${
|
|
727
|
+
const newMonthValue = `${newDayValue.substr(0, 5)}${strPad(
|
|
728
728
|
"10"
|
|
729
729
|
)}${newDayValue.substr(7)}`;
|
|
730
730
|
expect(screen.getByDisplayValue(newMonthValue)).toBeInTheDocument();
|
|
@@ -4,7 +4,7 @@ import ReactDatePicker from "react-datepicker";
|
|
|
4
4
|
import { DatePickerTypes } from "./DatePickerTypes";
|
|
5
5
|
import Fieldset from "../Fieldset/Fieldset";
|
|
6
6
|
import { FormRow, FormField } from "../Form/Form";
|
|
7
|
-
import {
|
|
7
|
+
import { FormGaps } from "../Form/FormTypes";
|
|
8
8
|
import HelperErrorText, {
|
|
9
9
|
HelperErrorTextType,
|
|
10
10
|
} from "../HelperErrorText/HelperErrorText";
|
|
@@ -16,8 +16,7 @@ import generateUUID from "../../helpers/generateUUID";
|
|
|
16
16
|
import { useMultiStyleConfig } from "@chakra-ui/system";
|
|
17
17
|
|
|
18
18
|
// The object shape for the DatePicker's start and end date state values.
|
|
19
|
-
|
|
20
|
-
interface FullDateType {
|
|
19
|
+
export interface FullDateType {
|
|
21
20
|
/** Date object that gets returned for the onChange
|
|
22
21
|
* function only for date ranges. */
|
|
23
22
|
endDate?: Date;
|
|
@@ -217,7 +216,7 @@ const DateRangeRow: React.FC<DateRangeRowProps> = ({
|
|
|
217
216
|
children,
|
|
218
217
|
}) =>
|
|
219
218
|
isDateRange ? (
|
|
220
|
-
<FormRow id={`${id}-form-row`} gap={
|
|
219
|
+
<FormRow id={`${id}-form-row`} gap={FormGaps.ExtraSmall}>
|
|
221
220
|
{children}
|
|
222
221
|
</FormRow>
|
|
223
222
|
) : (
|
|
@@ -2,8 +2,6 @@ import React from "react";
|
|
|
2
2
|
import { Box, useMultiStyleConfig } from "@chakra-ui/react";
|
|
3
3
|
|
|
4
4
|
interface FieldsetProps {
|
|
5
|
-
/** Children to render. Typically form-related components are used. */
|
|
6
|
-
children: React.ReactNode;
|
|
7
5
|
/** Additional class name to add. */
|
|
8
6
|
className?: string;
|
|
9
7
|
/** ID that other components can cross reference for accessibility purposes */
|
|
@@ -24,7 +22,7 @@ interface FieldsetProps {
|
|
|
24
22
|
* A wrapper component that renders a `fieldset` element along with a `legend`
|
|
25
23
|
* element as its first child. Commonly used to wrap form components.
|
|
26
24
|
*/
|
|
27
|
-
const Fieldset
|
|
25
|
+
const Fieldset = ({
|
|
28
26
|
children,
|
|
29
27
|
className,
|
|
30
28
|
id,
|
|
@@ -32,7 +30,7 @@ const Fieldset: React.FC<FieldsetProps> = ({
|
|
|
32
30
|
isRequired = false,
|
|
33
31
|
legendText,
|
|
34
32
|
optReqFlag = true,
|
|
35
|
-
}) => {
|
|
33
|
+
}: React.PropsWithChildren<FieldsetProps>) => {
|
|
36
34
|
const styles = useMultiStyleConfig("Fieldset", { isLegendHidden });
|
|
37
35
|
return (
|
|
38
36
|
<Box as="fieldset" id={id} __css={styles} className={className}>
|
|
@@ -14,7 +14,7 @@ import CheckboxGroup from "../CheckboxGroup/CheckboxGroup";
|
|
|
14
14
|
import DatePicker from "../DatePicker/DatePicker";
|
|
15
15
|
import { DatePickerTypes } from "../DatePicker/DatePickerTypes";
|
|
16
16
|
import Form, { FormRow, FormField } from "./Form";
|
|
17
|
-
import {
|
|
17
|
+
import { FormGaps } from "./FormTypes";
|
|
18
18
|
import Heading from "../Heading/Heading";
|
|
19
19
|
import { HeadingLevels } from "../Heading/HeadingTypes";
|
|
20
20
|
import HorizontalRule from "../HorizontalRule/HorizontalRule";
|
|
@@ -27,7 +27,7 @@ import { getCategory } from "../../utils/componentCategories";
|
|
|
27
27
|
import SimpleGrid from "../Grid/SimpleGrid";
|
|
28
28
|
import { getStorybookEnumValues } from "../../utils/utils";
|
|
29
29
|
|
|
30
|
-
export const enumValues = getStorybookEnumValues(
|
|
30
|
+
export const enumValues = getStorybookEnumValues(FormGaps, "FormGaps");
|
|
31
31
|
|
|
32
32
|
<Meta
|
|
33
33
|
title={getCategory("Form")}
|
|
@@ -47,9 +47,9 @@ export const enumValues = getStorybookEnumValues(FormSpacing, "FormSpacing");
|
|
|
47
47
|
control: { type: "radio" },
|
|
48
48
|
options: ["get", "post"],
|
|
49
49
|
},
|
|
50
|
-
|
|
50
|
+
gap: {
|
|
51
51
|
control: { type: "select" },
|
|
52
|
-
table: { defaultValue: { summary: "
|
|
52
|
+
table: { defaultValue: { summary: "FormGaps.Large" } },
|
|
53
53
|
options: enumValues.options,
|
|
54
54
|
},
|
|
55
55
|
}}
|
|
@@ -60,15 +60,31 @@ export const enumValues = getStorybookEnumValues(FormSpacing, "FormSpacing");
|
|
|
60
60
|
| Component Version | DS Version |
|
|
61
61
|
| ----------------- | ---------- |
|
|
62
62
|
| Added | `0.23.2` |
|
|
63
|
-
| Latest | `0.25.
|
|
63
|
+
| Latest | `0.25.10` |
|
|
64
64
|
|
|
65
65
|
<Description of={Form} />
|
|
66
66
|
|
|
67
|
-
The `Form` component renders a standard `<form>` element and should be used to
|
|
67
|
+
The `Form` component renders a standard `<form>` element and should be used to
|
|
68
|
+
handle layout and spacing for child input fields. `FormRow` and `FormField`
|
|
69
|
+
components should be used to build the `<form>` structure and to arrange input
|
|
70
|
+
fields as needed.
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
```jsx
|
|
73
|
+
<Form>
|
|
74
|
+
<FormRow>
|
|
75
|
+
<FormField>{/* ... */}</FormField>
|
|
76
|
+
</FormRow>
|
|
77
|
+
<FormRow>
|
|
78
|
+
<FormField>{/* ... */}</FormField>
|
|
79
|
+
</FormRow>
|
|
80
|
+
</Form>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`FormField` should be used as a parent for all input components from the DS
|
|
84
|
+
(`Button`, `Select`, `TextInput`, etc.).
|
|
70
85
|
|
|
71
|
-
`FormRow` should be used as a parent of multiple `FormField` components when you
|
|
86
|
+
`FormRow` should be used as a parent of multiple `FormField` components when you
|
|
87
|
+
need to render multiple input components in a horizontal row.
|
|
72
88
|
|
|
73
89
|
<Canvas withToolbar>
|
|
74
90
|
<Story
|
|
@@ -78,11 +94,11 @@ The `Form` component renders a standard `<form>` element and should be used to h
|
|
|
78
94
|
className: undefined,
|
|
79
95
|
id: "form-id",
|
|
80
96
|
method: "get",
|
|
81
|
-
|
|
97
|
+
gap: "FormGaps.Large",
|
|
82
98
|
}}
|
|
83
99
|
>
|
|
84
100
|
{(args) => (
|
|
85
|
-
<Form {...args}
|
|
101
|
+
<Form {...args} gap={enumValues.getValue(args.gap)}>
|
|
86
102
|
<FormRow>
|
|
87
103
|
<FormField>
|
|
88
104
|
<TextInput
|
|
@@ -205,7 +221,7 @@ export const formRow = (nameString, size) => {
|
|
|
205
221
|
return (
|
|
206
222
|
<li key={size}>
|
|
207
223
|
<Heading level={HeadingLevels.Three}>{labelText}</Heading>
|
|
208
|
-
<Form
|
|
224
|
+
<Form gap={size}>
|
|
209
225
|
<FormRow>
|
|
210
226
|
<FormField>
|
|
211
227
|
<Select
|
|
@@ -250,16 +266,18 @@ export const formRow = (nameString, size) => {
|
|
|
250
266
|
);
|
|
251
267
|
};
|
|
252
268
|
export const sizes = [];
|
|
253
|
-
for (const
|
|
254
|
-
sizes.push(formRow(`
|
|
269
|
+
for (const FormGaps in FormGaps) {
|
|
270
|
+
sizes.push(formRow(`FormGaps.${FormGaps}`, FormGaps[FormGaps]));
|
|
255
271
|
}
|
|
256
272
|
export const getForms = (list) => <ul style={{ listStyle: "none" }}>{list}</ul>;
|
|
257
273
|
|
|
258
|
-
By default, the `Form` component will handle the NYPL spacing around form input
|
|
274
|
+
By default, the `Form` component will handle the NYPL spacing around form input
|
|
275
|
+
elements. The default spacing value is `large`, which corresponds to the CSS
|
|
276
|
+
variable `--nypl-space-l` (2rem / 32px).
|
|
259
277
|
|
|
260
278
|
**IMPORTANT:** The default spacing should not be overwritten without a very good reason.
|
|
261
279
|
|
|
262
|
-
Below are the spacing variants available with the `
|
|
280
|
+
Below are the spacing variants available with the `FormGaps` enum.
|
|
263
281
|
|
|
264
282
|
<Canvas>
|
|
265
283
|
<Story
|
|
@@ -277,7 +295,7 @@ Below are the spacing variants available with the `FormSpacing` enum.
|
|
|
277
295
|
<Story name="Example Code" />
|
|
278
296
|
|
|
279
297
|
```jsx
|
|
280
|
-
<Form action="/end/point" method="get"
|
|
298
|
+
<Form action="/end/point" method="get" gap={FormGaps.Large}>
|
|
281
299
|
<FormField>
|
|
282
300
|
<TextInput
|
|
283
301
|
labelText="Username"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
3
|
import { axe } from "jest-axe";
|
|
4
4
|
import renderer from "react-test-renderer";
|
|
5
5
|
|
|
6
6
|
import Form, { FormRow, FormField } from "./Form";
|
|
7
|
-
// import {
|
|
7
|
+
// import { FormGaps } from "./FormTypes";
|
|
8
8
|
import TextInput from "../TextInput/TextInput";
|
|
9
9
|
|
|
10
10
|
describe("Form Accessibility", () => {
|
|
@@ -12,6 +12,19 @@ describe("Form Accessibility", () => {
|
|
|
12
12
|
const { container } = render(<Form />);
|
|
13
13
|
expect(await axe(container)).toHaveNoViolations();
|
|
14
14
|
});
|
|
15
|
+
|
|
16
|
+
it("passes axe accessibility test for the full hierachy", async () => {
|
|
17
|
+
const { container } = render(
|
|
18
|
+
<Form>
|
|
19
|
+
<FormRow>
|
|
20
|
+
<FormField>Form Field 1</FormField>
|
|
21
|
+
<FormField>Form Field 2</FormField>
|
|
22
|
+
<FormField>Form Field 3</FormField>
|
|
23
|
+
</FormRow>
|
|
24
|
+
</Form>
|
|
25
|
+
);
|
|
26
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
27
|
+
});
|
|
15
28
|
});
|
|
16
29
|
|
|
17
30
|
describe("Form Snapshot", () => {
|
|
@@ -101,11 +114,87 @@ describe("Form", () => {
|
|
|
101
114
|
expect(form).toHaveAttribute("method", "get");
|
|
102
115
|
});
|
|
103
116
|
|
|
117
|
+
it("passes down the `Form`'s id down to its children", () => {
|
|
118
|
+
const { container } = render(
|
|
119
|
+
<Form id="formId">
|
|
120
|
+
<FormRow>
|
|
121
|
+
<FormField>
|
|
122
|
+
<TextInput labelText="Input Field" />
|
|
123
|
+
</FormField>
|
|
124
|
+
<FormField>
|
|
125
|
+
<TextInput labelText="Input Field" />
|
|
126
|
+
</FormField>
|
|
127
|
+
</FormRow>
|
|
128
|
+
<FormRow>
|
|
129
|
+
<FormField>
|
|
130
|
+
<TextInput labelText="Input Field" />
|
|
131
|
+
</FormField>
|
|
132
|
+
<FormField>
|
|
133
|
+
<TextInput labelText="Input Field" />
|
|
134
|
+
</FormField>
|
|
135
|
+
</FormRow>
|
|
136
|
+
</Form>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(container.querySelector("#formId")).toBeInTheDocument();
|
|
140
|
+
// The first `FormRow` adds "child0" to its id
|
|
141
|
+
expect(container.querySelector("#formId-child0")).toBeInTheDocument();
|
|
142
|
+
// The first `FormRow`'s first `FormField` adds "grandchild0" to its id
|
|
143
|
+
expect(
|
|
144
|
+
container.querySelector("#formId-child0-grandchild0")
|
|
145
|
+
).toBeInTheDocument();
|
|
146
|
+
// The first `FormRow`'s second `FormField` adds "grandchild1" to its id
|
|
147
|
+
expect(
|
|
148
|
+
container.querySelector("#formId-child0-grandchild1")
|
|
149
|
+
).toBeInTheDocument();
|
|
150
|
+
// The second `FormRow` adds "child1" to its id
|
|
151
|
+
expect(container.querySelector("#formId-child1")).toBeInTheDocument();
|
|
152
|
+
// The second `FormRow`'s first `FormField` adds "grandchild0" to its id
|
|
153
|
+
expect(
|
|
154
|
+
container.querySelector("#formId-child1-grandchild0")
|
|
155
|
+
).toBeInTheDocument();
|
|
156
|
+
// The second `FormRow`'s second `FormField` adds "grandchild1" to its id
|
|
157
|
+
expect(
|
|
158
|
+
container.querySelector("#formId-child1-grandchild1")
|
|
159
|
+
).toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("logs a warning if a child of `FormRow` is not a `FormField`", () => {
|
|
163
|
+
const warn = jest.spyOn(console, "warn");
|
|
164
|
+
render(
|
|
165
|
+
<Form>
|
|
166
|
+
<FormRow>
|
|
167
|
+
<div>Not a FormField</div>
|
|
168
|
+
</FormRow>
|
|
169
|
+
</Form>
|
|
170
|
+
);
|
|
171
|
+
expect(warn).toHaveBeenCalledWith(
|
|
172
|
+
"FormRow children must be `FormField` components."
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("calls the onSubmit function", () => {
|
|
177
|
+
const onSubmit = jest.fn();
|
|
178
|
+
render(
|
|
179
|
+
<Form onSubmit={onSubmit}>
|
|
180
|
+
<FormRow>
|
|
181
|
+
<FormField>
|
|
182
|
+
<TextInput labelText="Input Field" />
|
|
183
|
+
</FormField>
|
|
184
|
+
</FormRow>
|
|
185
|
+
</Form>
|
|
186
|
+
);
|
|
187
|
+
const form = screen.getByRole("form");
|
|
188
|
+
expect(onSubmit).toHaveBeenCalledTimes(0);
|
|
189
|
+
fireEvent.submit(form);
|
|
190
|
+
expect(onSubmit).toHaveBeenCalledTimes(1);
|
|
191
|
+
});
|
|
192
|
+
|
|
104
193
|
// TO DO: There's somethign weird about checking for the "grid-gap" style.
|
|
105
194
|
// Other styles can be validated, but "grid-gap" is being ellusive.
|
|
106
195
|
// it("Renders a <form> element with spacing variant applied", () => {
|
|
107
196
|
// render(
|
|
108
|
-
// <Form
|
|
197
|
+
// <Form gap={FormGaps.ExtraSmall}>
|
|
109
198
|
// <FormRow />
|
|
110
199
|
// </Form>
|
|
111
200
|
// );
|
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
import { Box } from "@chakra-ui/react";
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { FormGaps } from "./FormTypes";
|
|
5
4
|
import SimpleGrid from "../Grid/SimpleGrid";
|
|
6
5
|
import generateUUID from "../../helpers/generateUUID";
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
/** className to be applied to FormRow */
|
|
7
|
+
interface FormBaseProps {
|
|
8
|
+
/** className to be applied to FormRow, FormField, and Form */
|
|
10
9
|
className?: string;
|
|
11
|
-
/**
|
|
12
|
-
|
|
10
|
+
/** Optional spacing size; if omitted, the default `large` (2rem / 32px)
|
|
11
|
+
* spacing will be used; ```IMPORTANT: for general form layout, this prop
|
|
12
|
+
* should not be used``` */
|
|
13
|
+
gap?: FormGaps;
|
|
13
14
|
/** ID that other components can cross reference (internal use) */
|
|
14
15
|
id?: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export interface
|
|
18
|
+
export interface FormChildProps extends FormBaseProps {}
|
|
19
|
+
|
|
20
|
+
export interface FormProps extends FormBaseProps {
|
|
18
21
|
/** Optional form `action` attribute */
|
|
19
22
|
action?: string;
|
|
20
|
-
/** Optional className you can add in addition to `form` */
|
|
21
|
-
className?: string;
|
|
22
|
-
/** Optional ID that other components can cross reference */
|
|
23
|
-
id?: string;
|
|
24
23
|
/** Optional form `method` attribute */
|
|
25
24
|
method?: "get" | "post";
|
|
26
|
-
/**
|
|
27
|
-
|
|
25
|
+
/** Function to call for the `onSubmit` form event. */
|
|
26
|
+
onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
/** FormRow child-component */
|
|
@@ -35,7 +34,11 @@ export function FormRow(props: React.PropsWithChildren<FormChildProps>) {
|
|
|
35
34
|
children,
|
|
36
35
|
(child: React.ReactElement, i) => {
|
|
37
36
|
if (!child) return null;
|
|
38
|
-
|
|
37
|
+
if (child.type === FormField || child.props.mdxType === "FormField") {
|
|
38
|
+
return React.cloneElement(child, { id: `${id}-grandchild${i}` });
|
|
39
|
+
}
|
|
40
|
+
console.warn("FormRow children must be `FormField` components.");
|
|
41
|
+
return null;
|
|
39
42
|
}
|
|
40
43
|
);
|
|
41
44
|
return (
|
|
@@ -61,9 +64,10 @@ export default function Form(props: React.PropsWithChildren<FormProps>) {
|
|
|
61
64
|
action,
|
|
62
65
|
children,
|
|
63
66
|
className,
|
|
67
|
+
gap = FormGaps.Large,
|
|
64
68
|
id = generateUUID(),
|
|
65
69
|
method,
|
|
66
|
-
|
|
70
|
+
onSubmit,
|
|
67
71
|
} = props;
|
|
68
72
|
|
|
69
73
|
let attributes = {};
|
|
@@ -76,21 +80,21 @@ export default function Form(props: React.PropsWithChildren<FormProps>) {
|
|
|
76
80
|
const alteredChildren = React.Children.map(
|
|
77
81
|
children,
|
|
78
82
|
(child: React.ReactElement, i) => {
|
|
79
|
-
return React.cloneElement(child, { gap
|
|
83
|
+
return React.cloneElement(child, { gap, id: `${id}-child${i}` });
|
|
80
84
|
}
|
|
81
85
|
);
|
|
82
86
|
|
|
83
87
|
return (
|
|
84
|
-
<
|
|
85
|
-
as="form"
|
|
88
|
+
<form
|
|
86
89
|
aria-label="form"
|
|
90
|
+
className={className}
|
|
87
91
|
id={id}
|
|
92
|
+
onSubmit={onSubmit}
|
|
88
93
|
{...attributes}
|
|
89
|
-
className={className}
|
|
90
94
|
>
|
|
91
|
-
<SimpleGrid columns={1} gap={
|
|
95
|
+
<SimpleGrid columns={1} gap={gap} id={`${id}-parent`}>
|
|
92
96
|
{alteredChildren}
|
|
93
97
|
</SimpleGrid>
|
|
94
|
-
</
|
|
98
|
+
</form>
|
|
95
99
|
);
|
|
96
100
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { GridGaps as
|
|
1
|
+
import { GridGaps as FormGaps } from "../Grid/GridTypes";
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { FormGaps };
|