@hyphen/hyphen-components 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/package.json +138 -0
- package/src/components/Alert/Alert.constants.ts +19 -0
- package/src/components/Alert/Alert.mdx +29 -0
- package/src/components/Alert/Alert.module.scss +74 -0
- package/src/components/Alert/Alert.stories.tsx +102 -0
- package/src/components/Alert/Alert.test.tsx +187 -0
- package/src/components/Alert/Alert.tsx +157 -0
- package/src/components/Alert/Alert.types.ts +14 -0
- package/src/components/Badge/Badge.mdx +28 -0
- package/src/components/Badge/Badge.module.scss +155 -0
- package/src/components/Badge/Badge.stories.tsx +52 -0
- package/src/components/Badge/Badge.test.tsx +74 -0
- package/src/components/Badge/Badge.tsx +70 -0
- package/src/components/Box/Box.mdx +259 -0
- package/src/components/Box/Box.module.scss +16 -0
- package/src/components/Box/Box.stories.tsx +1679 -0
- package/src/components/Box/Box.test.tsx +478 -0
- package/src/components/Box/Box.tsx +636 -0
- package/src/components/Button/Button.constants.ts +10 -0
- package/src/components/Button/Button.mdx +71 -0
- package/src/components/Button/Button.module.scss +312 -0
- package/src/components/Button/Button.stories.tsx +117 -0
- package/src/components/Button/Button.test.tsx +460 -0
- package/src/components/Button/Button.tsx +241 -0
- package/src/components/Card/Card.mdx +46 -0
- package/src/components/Card/Card.module.scss +6 -0
- package/src/components/Card/Card.stories.tsx +101 -0
- package/src/components/Card/Card.test.tsx +11 -0
- package/src/components/Card/Card.tsx +61 -0
- package/src/components/Card/components/CardFooter/CardFooter.test.tsx +11 -0
- package/src/components/Card/components/CardFooter/CardFooter.tsx +35 -0
- package/src/components/Card/components/CardHeader/CardHeader.test.tsx +23 -0
- package/src/components/Card/components/CardHeader/CardHeader.tsx +54 -0
- package/src/components/Card/components/CardSection/CardSection.test.tsx +28 -0
- package/src/components/Card/components/CardSection/CardSection.tsx +102 -0
- package/src/components/Card/components/index.ts +3 -0
- package/src/components/CheckboxInput/CheckboxInput.mdx +98 -0
- package/src/components/CheckboxInput/CheckboxInput.stories.tsx +254 -0
- package/src/components/CheckboxInput/CheckboxInput.test.tsx +436 -0
- package/src/components/CheckboxInput/CheckboxInput.tsx +171 -0
- package/src/components/CheckboxInput/components/Checkbox.module.scss +174 -0
- package/src/components/CheckboxInput/components/Checkbox.test.tsx +201 -0
- package/src/components/CheckboxInput/components/Checkbox.tsx +176 -0
- package/src/components/CheckboxInput/components/CheckboxIcon.tsx +71 -0
- package/src/components/DateInput/DateInput.mdx +61 -0
- package/src/components/DateInput/DateInput.stories.tsx +168 -0
- package/src/components/DateInput/DateInput.test.tsx +258 -0
- package/src/components/DateInput/DateInput.tsx +189 -0
- package/src/components/DatePicker/DatePicker.mdx +52 -0
- package/src/components/DatePicker/DatePicker.module.scss +603 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +199 -0
- package/src/components/DatePicker/DatePicker.test.tsx +26 -0
- package/src/components/DatePicker/DatePicker.tsx +138 -0
- package/src/components/Details/Details.mdx +30 -0
- package/src/components/Details/Details.module.scss +32 -0
- package/src/components/Details/Details.stories.tsx +38 -0
- package/src/components/Details/Details.test.tsx +189 -0
- package/src/components/Details/Details.tsx +51 -0
- package/src/components/Details/DetailsSummary.tsx +65 -0
- package/src/components/Drawer/Drawer.mdx +117 -0
- package/src/components/Drawer/Drawer.module.scss +96 -0
- package/src/components/Drawer/Drawer.stories.tsx +380 -0
- package/src/components/Drawer/Drawer.test.tsx +90 -0
- package/src/components/Drawer/Drawer.tsx +249 -0
- package/src/components/FormControl/FormControl.tsx +78 -0
- package/src/components/FormLabel/FormLabel.mdx +24 -0
- package/src/components/FormLabel/FormLabel.module.scss +19 -0
- package/src/components/FormLabel/FormLabel.stories.tsx +20 -0
- package/src/components/FormLabel/FormLabel.test.tsx +35 -0
- package/src/components/FormLabel/FormLabel.tsx +96 -0
- package/src/components/Formik/Formik.mdx +10 -0
- package/src/components/Formik/Formik.stories.tsx +307 -0
- package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.test.tsx +172 -0
- package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.tsx +41 -0
- package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.test.tsx +205 -0
- package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.tsx +37 -0
- package/src/components/Formik/FormikSelectInput/FormikSelectInput.test.tsx +210 -0
- package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +41 -0
- package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.test.tsx +153 -0
- package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.tsx +44 -0
- package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.test.tsx +161 -0
- package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.tsx +46 -0
- package/src/components/Formik/FormikTextInput/FormikTextInput.test.tsx +176 -0
- package/src/components/Formik/FormikTextInput/FormikTextInput.tsx +38 -0
- package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.test.tsx +170 -0
- package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.tsx +42 -0
- package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.test.tsx +186 -0
- package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.tsx +42 -0
- package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.test.tsx +179 -0
- package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.tsx +42 -0
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.test.tsx +224 -0
- package/src/components/Formik/FormikTimePicker/FormikTimePicker.tsx +37 -0
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.test.tsx +175 -0
- package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.tsx +38 -0
- package/src/components/Formik/FormikToggle/FormikToggle.test.tsx +172 -0
- package/src/components/Formik/FormikToggle/FormikToggle.tsx +38 -0
- package/src/components/Heading/Heading.constants.ts +19 -0
- package/src/components/Heading/Heading.mdx +35 -0
- package/src/components/Heading/Heading.module.scss +5 -0
- package/src/components/Heading/Heading.stories.tsx +90 -0
- package/src/components/Heading/Heading.test.tsx +67 -0
- package/src/components/Heading/Heading.tsx +67 -0
- package/src/components/HelpText/HelpText.module.scss +14 -0
- package/src/components/HelpText/HelpText.tsx +33 -0
- package/src/components/Icon/Icon.mdx +40 -0
- package/src/components/Icon/Icon.stories.tsx +72 -0
- package/src/components/Icon/Icon.test.tsx +30 -0
- package/src/components/Icon/Icon.tsx +61 -0
- package/src/components/InputValidationMessage/InputValidationMessage.module.scss +3 -0
- package/src/components/InputValidationMessage/InputValidationMessage.tsx +27 -0
- package/src/components/Modal/Modal.mdx +60 -0
- package/src/components/Modal/Modal.module.scss +135 -0
- package/src/components/Modal/Modal.stories.tsx +194 -0
- package/src/components/Modal/Modal.test.tsx +81 -0
- package/src/components/Modal/Modal.tsx +174 -0
- package/src/components/Modal/components/ModalBody/ModalBody.test.tsx +20 -0
- package/src/components/Modal/components/ModalBody/ModalBody.tsx +24 -0
- package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +32 -0
- package/src/components/Modal/components/ModalFooter/ModalFooter.tsx +37 -0
- package/src/components/Modal/components/ModalHeader/ModalHeader.test.tsx +29 -0
- package/src/components/Modal/components/ModalHeader/ModalHeader.tsx +58 -0
- package/src/components/Modal/components/index.ts +5 -0
- package/src/components/Pagination/Pagination.mdx +26 -0
- package/src/components/Pagination/Pagination.stories.tsx +55 -0
- package/src/components/Pagination/Pagination.test.tsx +225 -0
- package/src/components/Pagination/Pagination.tsx +162 -0
- package/src/components/Pagination/Pagination.utilities.test.ts +133 -0
- package/src/components/Pagination/Pagination.utilities.ts +101 -0
- package/src/components/Popover/Popover.mdx +104 -0
- package/src/components/Popover/Popover.module.scss +74 -0
- package/src/components/Popover/Popover.stories.tsx +471 -0
- package/src/components/Popover/Popover.test.tsx +128 -0
- package/src/components/Popover/Popover.tsx +277 -0
- package/src/components/RadioGroup/RadioGroup.mdx +81 -0
- package/src/components/RadioGroup/RadioGroup.module.scss +23 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +375 -0
- package/src/components/RadioGroup/RadioGroup.test.tsx +282 -0
- package/src/components/RadioGroup/RadioGroup.tsx +145 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.module.scss +114 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.test.tsx +156 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.tsx +148 -0
- package/src/components/RadioGroup/RadioInput/RadioInputIcon.tsx +59 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.mdx +36 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.stories.tsx +54 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.test.tsx +70 -0
- package/src/components/ResponsiveProvider/ResponsiveProvider.tsx +73 -0
- package/src/components/SelectInput/SelectInput.mdx +115 -0
- package/src/components/SelectInput/SelectInput.module.scss +357 -0
- package/src/components/SelectInput/SelectInput.stories.tsx +373 -0
- package/src/components/SelectInput/SelectInput.test.tsx +403 -0
- package/src/components/SelectInput/SelectInput.tsx +245 -0
- package/src/components/SelectInputInset/SelectInputInset.mdx +56 -0
- package/src/components/SelectInputInset/SelectInputInset.module.scss +397 -0
- package/src/components/SelectInputInset/SelectInputInset.stories.tsx +189 -0
- package/src/components/SelectInputInset/SelectInputInset.test.tsx +305 -0
- package/src/components/SelectInputInset/SelectInputInset.tsx +235 -0
- package/src/components/SelectInputNative/SelectInputNative.mdx +87 -0
- package/src/components/SelectInputNative/SelectInputNative.module.scss +356 -0
- package/src/components/SelectInputNative/SelectInputNative.stories.tsx +282 -0
- package/src/components/SelectInputNative/SelectInputNative.test.tsx +341 -0
- package/src/components/SelectInputNative/SelectInputNative.tsx +121 -0
- package/src/components/Spinner/Spinner.mdx +29 -0
- package/src/components/Spinner/Spinner.module.scss +16 -0
- package/src/components/Spinner/Spinner.stories.tsx +48 -0
- package/src/components/Spinner/Spinner.test.tsx +47 -0
- package/src/components/Spinner/Spinner.tsx +116 -0
- package/src/components/Table/Table.mdx +216 -0
- package/src/components/Table/Table.module.scss +61 -0
- package/src/components/Table/Table.stories.tsx +884 -0
- package/src/components/Table/Table.test.tsx +437 -0
- package/src/components/Table/Table.tsx +171 -0
- package/src/components/Table/TableBody/TableBody.module.scss +19 -0
- package/src/components/Table/TableBody/TableBody.test.tsx +38 -0
- package/src/components/Table/TableBody/TableBody.tsx +96 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.module.scss +47 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.test.tsx +81 -0
- package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.tsx +94 -0
- package/src/components/Table/TableHead/TableHead.test.tsx +20 -0
- package/src/components/Table/TableHead/TableHead.tsx +78 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.module.scss +72 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.test.tsx +187 -0
- package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.tsx +192 -0
- package/src/components/Table/common/TableRow/TableRow.module.scss +5 -0
- package/src/components/Table/common/TableRow/TableRow.test.tsx +52 -0
- package/src/components/Table/common/TableRow/TableRow.tsx +155 -0
- package/src/components/TextInput/TextInput.mdx +96 -0
- package/src/components/TextInput/TextInput.module.scss +405 -0
- package/src/components/TextInput/TextInput.stories.tsx +268 -0
- package/src/components/TextInput/TextInput.test.tsx +231 -0
- package/src/components/TextInput/TextInput.tsx +263 -0
- package/src/components/TextInputInset/TextInputInset.mdx +62 -0
- package/src/components/TextInputInset/TextInputInset.module.scss +418 -0
- package/src/components/TextInputInset/TextInputInset.stories.tsx +213 -0
- package/src/components/TextInputInset/TextInputInset.test.tsx +222 -0
- package/src/components/TextInputInset/TextInputInset.tsx +261 -0
- package/src/components/TextareaInput/TextareaInput.mdx +117 -0
- package/src/components/TextareaInput/TextareaInput.module.scss +275 -0
- package/src/components/TextareaInput/TextareaInput.stories.tsx +293 -0
- package/src/components/TextareaInput/TextareaInput.test.tsx +195 -0
- package/src/components/TextareaInput/TextareaInput.tsx +182 -0
- package/src/components/TextareaInputInset/TextareaInputInset.mdx +55 -0
- package/src/components/TextareaInputInset/TextareaInputInset.module.scss +337 -0
- package/src/components/TextareaInputInset/TextareaInputInset.stories.tsx +160 -0
- package/src/components/TextareaInputInset/TextareaInputInset.test.tsx +199 -0
- package/src/components/TextareaInputInset/TextareaInputInset.tsx +213 -0
- package/src/components/ThemeProvider/ThemeProvider.mdx +11 -0
- package/src/components/ThemeProvider/ThemeProvider.stories.tsx +56 -0
- package/src/components/ThemeProvider/ThemeProvider.tsx +75 -0
- package/src/components/TimePicker/TimePicker.mdx +75 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +149 -0
- package/src/components/TimePicker/TimePicker.test.tsx +97 -0
- package/src/components/TimePicker/TimePicker.tsx +83 -0
- package/src/components/TimePickerNative/TimePickerNative.mdx +67 -0
- package/src/components/TimePickerNative/TimePickerNative.stories.tsx +151 -0
- package/src/components/TimePickerNative/TimePickerNative.test.tsx +117 -0
- package/src/components/TimePickerNative/TimePickerNative.tsx +93 -0
- package/src/components/Toast/Toast.mdx +134 -0
- package/src/components/Toast/Toast.store.ts +280 -0
- package/src/components/Toast/Toast.stories.tsx +283 -0
- package/src/components/Toast/Toast.test.tsx +784 -0
- package/src/components/Toast/Toast.types.ts +98 -0
- package/src/components/Toast/ToastContainer.tsx +170 -0
- package/src/components/Toast/ToastNotification.module.scss +63 -0
- package/src/components/Toast/ToastNotification.tsx +176 -0
- package/src/components/Toast/index.ts +4 -0
- package/src/components/Toast/toast.ts +102 -0
- package/src/components/Toast/useToasts.ts +102 -0
- package/src/components/Toggle/Toggle.mdx +51 -0
- package/src/components/Toggle/Toggle.module.scss +294 -0
- package/src/components/Toggle/Toggle.stories.tsx +128 -0
- package/src/components/Toggle/Toggle.test.tsx +308 -0
- package/src/components/Toggle/Toggle.tsx +184 -0
- package/src/constants/keyCodes.ts +2 -0
- package/src/docs/Brands.mdx +153 -0
- package/src/docs/Colors.mdx +158 -0
- package/src/docs/DesignTokens.mdx +415 -0
- package/src/docs/GetStarted.mdx +47 -0
- package/src/docs/intro.mdx +12 -0
- package/src/fonts/AvenirBold.otf +0 -0
- package/src/fonts/AvenirBold.woff +0 -0
- package/src/fonts/AvenirBold.woff2 +0 -0
- package/src/fonts/AvenirLight.otf +0 -0
- package/src/fonts/AvenirLight.woff +0 -0
- package/src/fonts/AvenirLight.woff2 +0 -0
- package/src/fonts/AvenirRegular.otf +0 -0
- package/src/fonts/AvenirRegular.woff +0 -0
- package/src/fonts/AvenirRegular.woff2 +0 -0
- package/src/fonts/Geist-Bold.otf +0 -0
- package/src/fonts/Geist-Bold.woff +0 -0
- package/src/fonts/Geist-Bold.woff2 +0 -0
- package/src/fonts/Geist-Medium.otf +0 -0
- package/src/fonts/Geist-Medium.woff +0 -0
- package/src/fonts/Geist-Medium.woff2 +0 -0
- package/src/fonts/Geist-Regular.otf +0 -0
- package/src/fonts/Geist-Regular.woff +0 -0
- package/src/fonts/Geist-Regular.woff2 +0 -0
- package/src/fonts/Geist-SemiBold.otf +0 -0
- package/src/fonts/Geist-SemiBold.woff +0 -0
- package/src/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/fonts/GeistMono-Regular.otf +0 -0
- package/src/fonts/GeistMono-Regular.woff +0 -0
- package/src/fonts/GeistMono-Regular.woff2 +0 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useBreakpoint/useBreakpoint.mdx +26 -0
- package/src/hooks/useBreakpoint/useBreakpoint.stories.tsx +30 -0
- package/src/hooks/useBreakpoint/useBreakpoint.test.tsx +19 -0
- package/src/hooks/useBreakpoint/useBreakpoint.ts +50 -0
- package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayouEffect.ts +5 -0
- package/src/hooks/useOpenClose/useOpenClose.mdx +15 -0
- package/src/hooks/useOpenClose/useOpenClose.stories.tsx +41 -0
- package/src/hooks/useOpenClose/useOpenClose.test.tsx +119 -0
- package/src/hooks/useOpenClose/useOpenClose.tsx +95 -0
- package/src/hooks/useWindowSize/useWindowSize.mdx +25 -0
- package/src/hooks/useWindowSize/useWindowSize.stories.tsx +35 -0
- package/src/hooks/useWindowSize/useWindowSize.ts +24 -0
- package/src/index.ts +48 -0
- package/src/lib/cssShorthandToClasses.test.ts +149 -0
- package/src/lib/cssShorthandToClasses.ts +133 -0
- package/src/lib/doesStringIncludeCssUnit.ts +6 -0
- package/src/lib/generateResponsiveClasses.test.ts +24 -0
- package/src/lib/generateResponsiveClasses.ts +30 -0
- package/src/lib/getAutoCompleteValue.test.ts +27 -0
- package/src/lib/getAutoCompleteValue.ts +12 -0
- package/src/lib/getColumnKeys.ts +27 -0
- package/src/lib/getDimensionCss.test.ts +148 -0
- package/src/lib/getDimensionCss.ts +73 -0
- package/src/lib/getElementType.test.tsx +42 -0
- package/src/lib/getElementType.ts +42 -0
- package/src/lib/getFlexCss.test.ts +122 -0
- package/src/lib/getFlexCss.ts +67 -0
- package/src/lib/index.ts +15 -0
- package/src/lib/isFunction.ts +6 -0
- package/src/lib/mergeRefs.ts +15 -0
- package/src/lib/prefersReducedMotion.ts +12 -0
- package/src/lib/react-children-utilities/filter.ts +12 -0
- package/src/lib/react-children-utilities/index.ts +1 -0
- package/src/lib/reactRouterClickHandler.ts +37 -0
- package/src/lib/resolveValue.ts +7 -0
- package/src/lib/tokens.ts +139 -0
- package/src/modes.ts +8 -0
- package/src/styles/animation.scss +152 -0
- package/src/styles/cursor.scss +43 -0
- package/src/styles/display.scss +119 -0
- package/src/styles/flex.scss +453 -0
- package/src/styles/fonts.scss +44 -0
- package/src/styles/globals/utilities.scss +4 -0
- package/src/styles/mixins.scss +14 -0
- package/src/styles/overflow.scss +41 -0
- package/src/styles/position.scss +45 -0
- package/src/styles/reset.scss +108 -0
- package/src/styles/text-align.scss +21 -0
- package/src/styles/utilities.scss +9 -0
- package/src/styles/variables/forms.scss +71 -0
- package/src/styles/variables/index.scss +3 -0
- package/src/styles/white-space.scss +21 -0
- package/src/types/index.ts +201 -0
- package/src/types/lib.types.ts +3 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { Drawer, DrawerProps } from './Drawer';
|
|
4
|
+
|
|
5
|
+
const renderDrawer = ({
|
|
6
|
+
isOpen,
|
|
7
|
+
title,
|
|
8
|
+
ariaLabel,
|
|
9
|
+
onDismiss,
|
|
10
|
+
closeButton,
|
|
11
|
+
}: DrawerProps) => (
|
|
12
|
+
<Drawer
|
|
13
|
+
isOpen={isOpen}
|
|
14
|
+
title={title}
|
|
15
|
+
ariaLabel={ariaLabel}
|
|
16
|
+
onDismiss={onDismiss}
|
|
17
|
+
closeButton={closeButton}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
describe('Drawer', () => {
|
|
22
|
+
test('renders its children', () => {
|
|
23
|
+
const { getByText } = render(
|
|
24
|
+
renderDrawer({
|
|
25
|
+
ariaLabel: 'Right Drawer',
|
|
26
|
+
isOpen: true,
|
|
27
|
+
title: 'Right Drawer',
|
|
28
|
+
onDismiss: () => null,
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
expect(getByText('Right Drawer')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('it applies the aria label', () => {
|
|
35
|
+
const { getByLabelText } = render(
|
|
36
|
+
renderDrawer({
|
|
37
|
+
ariaLabel: 'Right Drawer',
|
|
38
|
+
isOpen: true,
|
|
39
|
+
title: 'Right Drawer',
|
|
40
|
+
onDismiss: () => null,
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
expect(getByLabelText('Right Drawer')).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('it renders a close button and title', () => {
|
|
47
|
+
const { getByText, getByLabelText } = render(
|
|
48
|
+
renderDrawer({
|
|
49
|
+
ariaLabel: 'Right Drawer',
|
|
50
|
+
isOpen: true,
|
|
51
|
+
title: 'Right Drawer',
|
|
52
|
+
onDismiss: () => null,
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
expect(getByLabelText('close')).toBeInTheDocument();
|
|
56
|
+
expect(getByText('Right Drawer')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('it renders a close button without title', () => {
|
|
60
|
+
const { getByLabelText } = render(
|
|
61
|
+
renderDrawer({
|
|
62
|
+
ariaLabel: 'Right Drawer',
|
|
63
|
+
isOpen: true,
|
|
64
|
+
onDismiss: () => null,
|
|
65
|
+
closeButton: true,
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
expect(getByLabelText('close')).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('it open and closes based on isOpen prop', () => {
|
|
72
|
+
const { queryByLabelText, getByLabelText, rerender } = render(
|
|
73
|
+
renderDrawer({
|
|
74
|
+
ariaLabel: 'Right Drawer',
|
|
75
|
+
isOpen: false,
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(queryByLabelText('Right Drawer')).toBe(null);
|
|
80
|
+
|
|
81
|
+
rerender(
|
|
82
|
+
renderDrawer({
|
|
83
|
+
ariaLabel: 'Right Drawer',
|
|
84
|
+
isOpen: true,
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(getByLabelText('Right Drawer')).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
CSSProperties,
|
|
3
|
+
RefObject,
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import ReactModal from 'react-modal';
|
|
8
|
+
import FocusLock from 'react-focus-lock';
|
|
9
|
+
import { RemoveScroll } from 'react-remove-scroll';
|
|
10
|
+
import classNames from 'classnames';
|
|
11
|
+
import { DimensionSize, CssDimensionValue } from '../../types';
|
|
12
|
+
import { Box } from '../Box/Box';
|
|
13
|
+
import styles from './Drawer.module.scss';
|
|
14
|
+
import { Button } from '../Button/Button';
|
|
15
|
+
|
|
16
|
+
export type DrawerPlacementType = 'left' | 'right' | 'top' | 'bottom';
|
|
17
|
+
export interface DrawerProps {
|
|
18
|
+
/**
|
|
19
|
+
* If the drawer is open
|
|
20
|
+
*/
|
|
21
|
+
isOpen: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Handle zoom/pinch gestures on iOS devices when scroll locking is enabled.
|
|
24
|
+
*/
|
|
25
|
+
allowPinchZoom?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* A drawer needs to be properly labeled to provide context for users with
|
|
28
|
+
* assistive technology such as screen readers. If a drawer is announced to
|
|
29
|
+
* the user without a label, it can be confusing and difficult to navigate.
|
|
30
|
+
*/
|
|
31
|
+
ariaLabel?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The id of the element that should be used as the drawer's label by assistive technologies like screen readers.
|
|
34
|
+
*/
|
|
35
|
+
ariaLabelledBy?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Contents of the dialog.
|
|
38
|
+
*/
|
|
39
|
+
children?: React.ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* Additional class names to add to the drawer content.
|
|
42
|
+
*/
|
|
43
|
+
className?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the drawer has a visible close button.
|
|
46
|
+
* If a title is defined, then a close button will be rendered
|
|
47
|
+
*/
|
|
48
|
+
closeButton?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* If true, the drawer will close when the overlay is clicked
|
|
51
|
+
*/
|
|
52
|
+
closeOnOverlayClick?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The ref of the container where the drawer will be inserted into the DOM.
|
|
55
|
+
* By default, drawers are inserted in the document.body, but if need be they can
|
|
56
|
+
* be scoped as necessary.
|
|
57
|
+
*/
|
|
58
|
+
containerRef?: React.RefObject<Node>;
|
|
59
|
+
/**
|
|
60
|
+
* By default, focus is trapped within the drawer.
|
|
61
|
+
* If true, focus will not be trapped within the contents of the drawer.
|
|
62
|
+
*/
|
|
63
|
+
dangerouslyBypassFocusLock?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* By default, the drawer locks scrolling on the body, but in some cases you may want to allow for scrolling.
|
|
66
|
+
* If true, this will allow the body to scroll while the drawer is open.
|
|
67
|
+
*/
|
|
68
|
+
dangerouslyBypassScrollLock?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* If true, the overlay will not be rendered, scrolling for the entire page will remain enabled,
|
|
71
|
+
* and focus will not be locked to the contents of the drawer
|
|
72
|
+
*/
|
|
73
|
+
hideOverlay?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* By default the first focusable element will receive focus when the dialog
|
|
76
|
+
* opens but you can provide a ref to focus instead.
|
|
77
|
+
*
|
|
78
|
+
* @see Docs https://reach.tech/dialog#dialog-initialfocusref
|
|
79
|
+
*/
|
|
80
|
+
initialFocusRef?: RefObject<HTMLDivElement>;
|
|
81
|
+
/**
|
|
82
|
+
* Which edge of the viewport should the drawer appear from
|
|
83
|
+
*/
|
|
84
|
+
placement?: DrawerPlacementType;
|
|
85
|
+
/**
|
|
86
|
+
* Function that is called whenever the user either hits
|
|
87
|
+
* the "Escape" key, clicks the close button icon, or clicks the overlay.
|
|
88
|
+
*/
|
|
89
|
+
onDismiss?: (event?: React.SyntheticEvent) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Title to be displayed at the top of the Drawer.
|
|
92
|
+
* A close button will be rendered automatically if this prop is defined.
|
|
93
|
+
*/
|
|
94
|
+
title?: string;
|
|
95
|
+
/**
|
|
96
|
+
* The width of the Drawer when opened. Can be given a standard css value (px, rem, em, %),
|
|
97
|
+
* or a [width token](/?path=/story/design-tokens-design-tokens--page#width)
|
|
98
|
+
*/
|
|
99
|
+
width?: DimensionSize | CssDimensionValue;
|
|
100
|
+
}
|
|
101
|
+
export const Drawer: React.FC<DrawerProps> = forwardRef<
|
|
102
|
+
HTMLDivElement,
|
|
103
|
+
DrawerProps
|
|
104
|
+
>(
|
|
105
|
+
(
|
|
106
|
+
{
|
|
107
|
+
ariaLabel = undefined,
|
|
108
|
+
ariaLabelledBy = undefined,
|
|
109
|
+
allowPinchZoom = false,
|
|
110
|
+
children = undefined,
|
|
111
|
+
className = undefined,
|
|
112
|
+
closeButton = false,
|
|
113
|
+
closeOnOverlayClick = true,
|
|
114
|
+
containerRef = undefined,
|
|
115
|
+
dangerouslyBypassFocusLock = false,
|
|
116
|
+
dangerouslyBypassScrollLock = false,
|
|
117
|
+
hideOverlay = false,
|
|
118
|
+
initialFocusRef = undefined,
|
|
119
|
+
isOpen,
|
|
120
|
+
onDismiss = undefined,
|
|
121
|
+
placement = 'right',
|
|
122
|
+
title = undefined,
|
|
123
|
+
width = undefined,
|
|
124
|
+
},
|
|
125
|
+
ref
|
|
126
|
+
) => {
|
|
127
|
+
const activateFocusLock = useCallback(() => {
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
if (initialFocusRef && initialFocusRef.current) {
|
|
130
|
+
initialFocusRef.current.focus();
|
|
131
|
+
}
|
|
132
|
+
}, 100);
|
|
133
|
+
}, [initialFocusRef]);
|
|
134
|
+
|
|
135
|
+
const dynamicWidth = width;
|
|
136
|
+
|
|
137
|
+
const dynamicStyle: CSSProperties = {
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
+
['--w' as any]: dynamicWidth,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const overlayClassnames = classNames(styles.overlay, styles.drawer, {
|
|
143
|
+
[styles['hide-overlay']]: hideOverlay,
|
|
144
|
+
[styles[`hide-overlay-${placement}`]]: hideOverlay,
|
|
145
|
+
'position-fixed': containerRef === undefined,
|
|
146
|
+
'position-absolute': containerRef !== undefined,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const contentClassnames = classNames(
|
|
150
|
+
styles['drawer-content'],
|
|
151
|
+
styles[placement],
|
|
152
|
+
{
|
|
153
|
+
[styles['hide-overlay']]: hideOverlay,
|
|
154
|
+
'overflow-auto': !closeButton && !title,
|
|
155
|
+
className,
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const renderHeader = () => {
|
|
160
|
+
if (closeButton && onDismiss && !title) {
|
|
161
|
+
return (
|
|
162
|
+
<Box alignItems="flex-end" justifyContent="center" padding="md lg">
|
|
163
|
+
<Button
|
|
164
|
+
variant="tertiary"
|
|
165
|
+
onClick={onDismiss}
|
|
166
|
+
aria-label="close"
|
|
167
|
+
type="button"
|
|
168
|
+
iconPrefix="remove"
|
|
169
|
+
/>
|
|
170
|
+
</Box>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
if (title) {
|
|
174
|
+
return (
|
|
175
|
+
<Box
|
|
176
|
+
direction="row"
|
|
177
|
+
justifyContent="space-between"
|
|
178
|
+
alignItems="center"
|
|
179
|
+
padding="2xl"
|
|
180
|
+
>
|
|
181
|
+
<Box className={styles.title} fontWeight="bold">
|
|
182
|
+
{title}
|
|
183
|
+
</Box>
|
|
184
|
+
{onDismiss && (
|
|
185
|
+
<Button
|
|
186
|
+
variant="tertiary"
|
|
187
|
+
onClick={onDismiss}
|
|
188
|
+
aria-label="close"
|
|
189
|
+
type="button"
|
|
190
|
+
iconPrefix="remove"
|
|
191
|
+
/>
|
|
192
|
+
)}
|
|
193
|
+
</Box>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const content =
|
|
200
|
+
title || closeButton ? (
|
|
201
|
+
<Box flex="auto" overflow="auto">
|
|
202
|
+
{children}
|
|
203
|
+
</Box>
|
|
204
|
+
) : (
|
|
205
|
+
children
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const parentElement = containerRef?.current
|
|
209
|
+
? (containerRef.current as HTMLElement)
|
|
210
|
+
: document.body;
|
|
211
|
+
|
|
212
|
+
const isDisabledFocusLock = hideOverlay || dangerouslyBypassFocusLock;
|
|
213
|
+
const isDisabledRemoveScroll = hideOverlay || dangerouslyBypassScrollLock;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<FocusLock
|
|
217
|
+
autoFocus
|
|
218
|
+
returnFocus
|
|
219
|
+
disabled={isDisabledFocusLock || !isOpen}
|
|
220
|
+
onActivation={activateFocusLock}
|
|
221
|
+
>
|
|
222
|
+
<RemoveScroll
|
|
223
|
+
allowPinchZoom={allowPinchZoom}
|
|
224
|
+
enabled={!isDisabledRemoveScroll && isOpen}
|
|
225
|
+
>
|
|
226
|
+
<Box ref={ref}>
|
|
227
|
+
<ReactModal
|
|
228
|
+
isOpen={isOpen}
|
|
229
|
+
overlayClassName={overlayClassnames}
|
|
230
|
+
className={contentClassnames}
|
|
231
|
+
onRequestClose={closeOnOverlayClick ? onDismiss : undefined}
|
|
232
|
+
ariaHideApp={false}
|
|
233
|
+
style={{
|
|
234
|
+
content: dynamicStyle,
|
|
235
|
+
overlay: dynamicStyle,
|
|
236
|
+
}}
|
|
237
|
+
parentSelector={() => parentElement}
|
|
238
|
+
>
|
|
239
|
+
<Box aria-label={ariaLabel} aria-labelledby={ariaLabelledBy}>
|
|
240
|
+
{renderHeader()}
|
|
241
|
+
{content}
|
|
242
|
+
</Box>
|
|
243
|
+
</ReactModal>
|
|
244
|
+
</Box>
|
|
245
|
+
</RemoveScroll>
|
|
246
|
+
</FocusLock>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { Box, BoxProps } from '../Box/Box';
|
|
3
|
+
import { FormLabel } from '../FormLabel/FormLabel';
|
|
4
|
+
import { InputValidationMessage } from '../InputValidationMessage/InputValidationMessage';
|
|
5
|
+
|
|
6
|
+
export interface FormControlProps extends BoxProps {
|
|
7
|
+
/**
|
|
8
|
+
* The input's id attribute. Used to programmatically tie the input with its label.
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
/**
|
|
12
|
+
* Custom content to be displayed above the input. If the label is hidden, will be used to set aria-label attribute.
|
|
13
|
+
*/
|
|
14
|
+
label: string;
|
|
15
|
+
/**
|
|
16
|
+
* Mark the input field as invalid and display a validation message.
|
|
17
|
+
* Pass a string or node to render a validation message below the input.
|
|
18
|
+
*/
|
|
19
|
+
error?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Visually hide the label.
|
|
22
|
+
*/
|
|
23
|
+
hideLabel?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Additional clarifying text to help describe the input
|
|
26
|
+
*/
|
|
27
|
+
helpText?: ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* The input's disabled attribute
|
|
30
|
+
*/
|
|
31
|
+
isDisabled?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The required and aria-required attributes on the input
|
|
34
|
+
*/
|
|
35
|
+
isRequired?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Visual indicator that the field is required, that gets appended to the label
|
|
38
|
+
*/
|
|
39
|
+
requiredIndicator?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
|
|
43
|
+
(
|
|
44
|
+
{
|
|
45
|
+
label,
|
|
46
|
+
hideLabel,
|
|
47
|
+
children,
|
|
48
|
+
error,
|
|
49
|
+
id,
|
|
50
|
+
isRequired,
|
|
51
|
+
helpText,
|
|
52
|
+
isDisabled,
|
|
53
|
+
requiredIndicator,
|
|
54
|
+
width = '100',
|
|
55
|
+
...restProps
|
|
56
|
+
},
|
|
57
|
+
ref
|
|
58
|
+
) => {
|
|
59
|
+
const labelProps = {
|
|
60
|
+
inputId: id,
|
|
61
|
+
helpText,
|
|
62
|
+
margin: '0 0 xs 0',
|
|
63
|
+
isDisabled,
|
|
64
|
+
isFieldRequired: isRequired,
|
|
65
|
+
requiredIndicator,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Box width={width} ref={ref} {...restProps}>
|
|
70
|
+
{label && !hideLabel && <FormLabel {...labelProps}>{label}</FormLabel>}
|
|
71
|
+
{children}
|
|
72
|
+
{error && error !== true && (
|
|
73
|
+
<InputValidationMessage>{error}</InputValidationMessage>
|
|
74
|
+
)}
|
|
75
|
+
</Box>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import { FormLabel } from './FormLabel';
|
|
3
|
+
import * as Stories from './FormLabel.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={Stories} />
|
|
6
|
+
|
|
7
|
+
# FormLabel
|
|
8
|
+
|
|
9
|
+
FormLabel is a sub component of many of our inputs, and is meant to be paired with a form input. In most cases, the label will have already been built into another component as a prop. However, you can use it on its own or with another component.
|
|
10
|
+
|
|
11
|
+
<Canvas of={Stories.BasicUsage} />
|
|
12
|
+
|
|
13
|
+
## Accessibility
|
|
14
|
+
|
|
15
|
+
- Each label requires the `inputId` prop that equals the id of the associated input so that the `htmlFor` property can be set.
|
|
16
|
+
- Don't use placeholder text as a replacement for labels. It can be used to provide an example to users, but will disappear from the field when a user enters text. It's also not broadly supported by assistive technologies and won't display in older browsers.
|
|
17
|
+
- Use one of 3 ways to label a form field in order of most preferred:
|
|
18
|
+
1. Visible label with Label
|
|
19
|
+
2. Visible label that's associated to the input with aria-labelledby
|
|
20
|
+
3. Label directly using aria-label
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
<ArgTypes of={FormLabel} />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.label {
|
|
2
|
+
font-family: var(--INTERNAL_form-control-font-family);
|
|
3
|
+
color: var(--color-font-base);
|
|
4
|
+
font-size: var(--INTERNAL_form-control-size-md-label-size);
|
|
5
|
+
font-weight: var(--INTERNAL_form-control-label-font-weight);
|
|
6
|
+
|
|
7
|
+
&.radio-input-label {
|
|
8
|
+
font-weight: 400;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&.disabled {
|
|
12
|
+
color: var(--color-base-grey-300);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.help-text {
|
|
16
|
+
margin-top: var(--INTERNAL_form-control-help-margin);
|
|
17
|
+
font-weight: var(--INTERNAL_form-control-help-font-weight);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FormLabel } from './FormLabel';
|
|
2
|
+
|
|
3
|
+
import type { Meta } from '@storybook/react';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof FormLabel> = {
|
|
7
|
+
title: 'Components/FormLabel',
|
|
8
|
+
component: FormLabel,
|
|
9
|
+
parameters: {
|
|
10
|
+
controls: { hideNoControlsWarning: true },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
export const BasicUsage = () => (
|
|
17
|
+
<FormLabel inputId="inputId" helpText="More helpful text about the input">
|
|
18
|
+
Default Label
|
|
19
|
+
</FormLabel>
|
|
20
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { FormLabel } from './FormLabel';
|
|
4
|
+
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
console.error = jest.fn(); // eslint-disable-line no-console
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
(console.error as jest.Mock).mockRestore(); // eslint-disable-line no-console
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('FormLabel', () => {
|
|
14
|
+
test('Label correctly renders with base props', () => {
|
|
15
|
+
render(<FormLabel inputId="myId">my label</FormLabel>);
|
|
16
|
+
const labelElement = screen.getByText('my label');
|
|
17
|
+
expect(labelElement).toHaveAttribute('for', 'myId');
|
|
18
|
+
expect(labelElement).toHaveTextContent('my label');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('correctly assigns an id when given an inputId', () => {
|
|
22
|
+
render(<FormLabel inputId="myId">my label</FormLabel>);
|
|
23
|
+
const labelElement = screen.getByText('my label');
|
|
24
|
+
expect(labelElement).toHaveAttribute('id', 'myIdLabel');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('renders help text if provided', () => {
|
|
28
|
+
const { getByText } = render(
|
|
29
|
+
<FormLabel inputId="myId" helpText="i am help text">
|
|
30
|
+
my label
|
|
31
|
+
</FormLabel>
|
|
32
|
+
);
|
|
33
|
+
expect(getByText('i am help text')).toBeDefined();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React, { FC, ReactNode } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Box, BoxProps } from '../Box/Box';
|
|
4
|
+
import styles from './FormLabel.module.scss';
|
|
5
|
+
|
|
6
|
+
export interface FormLabelProps extends BoxProps {
|
|
7
|
+
/**
|
|
8
|
+
* Content to be rendered inside the label.
|
|
9
|
+
*/
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* The id of the form control that the label is labeling
|
|
13
|
+
*/
|
|
14
|
+
inputId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Custom class to pass to label element.
|
|
17
|
+
*/
|
|
18
|
+
className?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Additional clarifying text to that helps describe the field
|
|
21
|
+
*/
|
|
22
|
+
helpText?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Mark the label has disabled
|
|
25
|
+
*/
|
|
26
|
+
isDisabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* prop deprecated: no longer in use and will be remove in next major release.
|
|
29
|
+
*/
|
|
30
|
+
isFieldRequired?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Apply custom styling to labels for a radio input
|
|
33
|
+
*/
|
|
34
|
+
isRadioInputLabel?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Visual indicator that the field is required, that gets appended to the label
|
|
37
|
+
*/
|
|
38
|
+
requiredIndicator?: ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Additional props to be spread to rendered element
|
|
41
|
+
*/
|
|
42
|
+
[x: string]: any; // eslint-disable-line
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const FormLabel: FC<FormLabelProps> = ({
|
|
46
|
+
children,
|
|
47
|
+
inputId,
|
|
48
|
+
className = '',
|
|
49
|
+
display = 'block',
|
|
50
|
+
helpText,
|
|
51
|
+
isDisabled = false,
|
|
52
|
+
isFieldRequired = false,
|
|
53
|
+
isRadioInputLabel = false,
|
|
54
|
+
requiredIndicator = ' *',
|
|
55
|
+
margin = '0',
|
|
56
|
+
padding = '0',
|
|
57
|
+
...restProps
|
|
58
|
+
}) => {
|
|
59
|
+
const labelClasses = classNames(
|
|
60
|
+
'hyphen-components__variables__form-control',
|
|
61
|
+
styles.label,
|
|
62
|
+
className,
|
|
63
|
+
{
|
|
64
|
+
[styles.disabled]: isDisabled,
|
|
65
|
+
[styles.disabled]: isDisabled,
|
|
66
|
+
[styles['radio-input-label']]: isRadioInputLabel,
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Box
|
|
72
|
+
as="label"
|
|
73
|
+
id={`${inputId}Label`}
|
|
74
|
+
className={labelClasses}
|
|
75
|
+
display={display}
|
|
76
|
+
margin={margin}
|
|
77
|
+
padding={padding}
|
|
78
|
+
htmlFor={inputId}
|
|
79
|
+
{...restProps}
|
|
80
|
+
>
|
|
81
|
+
{children}
|
|
82
|
+
{isFieldRequired && requiredIndicator && <span>{requiredIndicator}</span>}
|
|
83
|
+
{helpText && (
|
|
84
|
+
<Box
|
|
85
|
+
as="p"
|
|
86
|
+
display="block"
|
|
87
|
+
fontSize="sm"
|
|
88
|
+
color="secondary"
|
|
89
|
+
className={styles['help-text']}
|
|
90
|
+
>
|
|
91
|
+
{helpText}
|
|
92
|
+
</Box>
|
|
93
|
+
)}
|
|
94
|
+
</Box>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Meta, Canvas } from '@storybook/addon-docs';
|
|
2
|
+
import * as Stories from './Formik.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# Formik Form
|
|
7
|
+
|
|
8
|
+
Formik Form demonstrates a pattern for integrating the input elements found in Hyphen Components with [Formik](https://formik.org/). The form is fully functional and even mock submits. Manipulate the inputs in the form and watch the state object below it update with the new values in real-time. Attempt to submit the form without filling out required inputs, and observe the error states for these inputs.
|
|
9
|
+
|
|
10
|
+
<Canvas of={Stories.FormikForm} />
|