@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,403 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, screen, Matcher } from '@testing-library/react';
|
|
3
|
+
import selectEvent from 'react-select-event';
|
|
4
|
+
import { SelectInput, TextInputSize } from './SelectInput';
|
|
5
|
+
|
|
6
|
+
const selectOptions = [
|
|
7
|
+
{ value: 'chocolate', label: 'Chocolate' },
|
|
8
|
+
{ value: 'strawberry', label: 'Strawberry' },
|
|
9
|
+
{ value: 'vanilla', label: 'Vanilla' },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
function getByTextWithMarkup(
|
|
13
|
+
text: string
|
|
14
|
+
): (_content: any, element: Element) => boolean {
|
|
15
|
+
return (_content: any, element: Element) => {
|
|
16
|
+
const hasText = (node: Element) => node.textContent === text;
|
|
17
|
+
const elementHasText = hasText(element);
|
|
18
|
+
const childrenDontHaveText = Array.from(element.children).every(
|
|
19
|
+
(child) => !hasText(child)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
return elementHasText && childrenDontHaveText;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('SelectInput', () => {
|
|
27
|
+
describe('Callback Handling', () => {
|
|
28
|
+
test('it fires onChange callback on change', async () => {
|
|
29
|
+
const mockedHandleChange = jest.fn();
|
|
30
|
+
|
|
31
|
+
const { getByLabelText } = render(
|
|
32
|
+
<SelectInput
|
|
33
|
+
value="chocolate"
|
|
34
|
+
id="testId"
|
|
35
|
+
onChange={mockedHandleChange}
|
|
36
|
+
placeholder="Test Placeholder"
|
|
37
|
+
label="onchange test"
|
|
38
|
+
options={selectOptions}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await selectEvent.select(getByLabelText('onchange test'), 'Vanilla');
|
|
43
|
+
|
|
44
|
+
expect(mockedHandleChange).toBeCalledTimes(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('it fires onFocus callback on focus', () => {
|
|
48
|
+
const mockedHandleChange = jest.fn();
|
|
49
|
+
const mockedHandleFocus = jest.fn();
|
|
50
|
+
|
|
51
|
+
render(
|
|
52
|
+
<SelectInput
|
|
53
|
+
id="testId"
|
|
54
|
+
label="Test"
|
|
55
|
+
value="chocolate"
|
|
56
|
+
onChange={mockedHandleChange}
|
|
57
|
+
onFocus={mockedHandleFocus}
|
|
58
|
+
placeholder="Test Placeholder"
|
|
59
|
+
options={selectOptions}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
fireEvent.focus(screen.getByRole('combobox'));
|
|
64
|
+
|
|
65
|
+
expect(mockedHandleFocus).toBeCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('it fires onBlur callback on blur', () => {
|
|
69
|
+
const mockedHandleChange = jest.fn();
|
|
70
|
+
const mockedHandleBlur = jest.fn();
|
|
71
|
+
|
|
72
|
+
render(
|
|
73
|
+
<SelectInput
|
|
74
|
+
id="testId"
|
|
75
|
+
label="Test"
|
|
76
|
+
value="chocolate"
|
|
77
|
+
onChange={mockedHandleChange}
|
|
78
|
+
onBlur={mockedHandleBlur}
|
|
79
|
+
placeholder="Test Placeholder"
|
|
80
|
+
options={selectOptions}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
fireEvent.blur(screen.getByRole('combobox'));
|
|
85
|
+
|
|
86
|
+
expect(mockedHandleBlur).toBeCalledTimes(1);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('States', () => {
|
|
91
|
+
describe('Hidden label, with a placeholder', () => {
|
|
92
|
+
test('it renders input without a visual label, and with a placeholder', () => {
|
|
93
|
+
const mockedHandleChange = jest.fn();
|
|
94
|
+
|
|
95
|
+
render(
|
|
96
|
+
<SelectInput
|
|
97
|
+
id="testId"
|
|
98
|
+
label="hidden label"
|
|
99
|
+
value="chocolate"
|
|
100
|
+
hideLabel
|
|
101
|
+
onChange={mockedHandleChange}
|
|
102
|
+
placeholder="Test Placeholder"
|
|
103
|
+
options={selectOptions}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
expect(screen.queryByText('hidden label')).toBeNull();
|
|
107
|
+
expect(screen.getByText('Test Placeholder')).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
|
|
112
|
+
const mockedHandleChange = jest.fn();
|
|
113
|
+
|
|
114
|
+
render(
|
|
115
|
+
<SelectInput
|
|
116
|
+
id="testInput"
|
|
117
|
+
label="hidden label"
|
|
118
|
+
hideLabel
|
|
119
|
+
onChange={mockedHandleChange}
|
|
120
|
+
options={selectOptions}
|
|
121
|
+
value="chocolate"
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
const inputElement = screen.getByLabelText('hidden label');
|
|
125
|
+
expect(inputElement).not.toHaveAttribute('aria-labelledby');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('With a label, and no custom placeholder', () => {
|
|
129
|
+
test('it renders input with a label, and with a default placeholder', () => {
|
|
130
|
+
const mockedHandleChange = jest.fn();
|
|
131
|
+
|
|
132
|
+
render(
|
|
133
|
+
<SelectInput
|
|
134
|
+
id="testId"
|
|
135
|
+
onChange={mockedHandleChange}
|
|
136
|
+
label="Select Label"
|
|
137
|
+
options={selectOptions}
|
|
138
|
+
value="chocolate"
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
expect(screen.getByLabelText('Select Label')).toBeInTheDocument();
|
|
143
|
+
expect(screen.getByText('Select...')).toBeInTheDocument();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('assigns the "aria-labelledby" attribute and renders label correct id, when a label is provided', () => {
|
|
147
|
+
const mockedHandleChange = jest.fn();
|
|
148
|
+
render(
|
|
149
|
+
<SelectInput
|
|
150
|
+
id="testInput"
|
|
151
|
+
label="test label"
|
|
152
|
+
options={selectOptions}
|
|
153
|
+
value="chocolate"
|
|
154
|
+
onChange={mockedHandleChange}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
const inputElement = screen.getByLabelText('test label');
|
|
158
|
+
expect(inputElement).toHaveAttribute(
|
|
159
|
+
'aria-labelledby',
|
|
160
|
+
'testInputLabel'
|
|
161
|
+
);
|
|
162
|
+
expect(document.getElementById('testInputLabel')).toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('Single select, pre-selected', () => {
|
|
167
|
+
test('it renders with value pre-selected', () => {
|
|
168
|
+
const mockedHandleChange = jest.fn();
|
|
169
|
+
|
|
170
|
+
render(
|
|
171
|
+
<SelectInput
|
|
172
|
+
id="testId"
|
|
173
|
+
onChange={mockedHandleChange}
|
|
174
|
+
label="Select Label"
|
|
175
|
+
options={selectOptions}
|
|
176
|
+
value={selectOptions[2]}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
expect(screen.getByText('Vanilla')).toBeInTheDocument();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('Multi select, no selection', () => {
|
|
185
|
+
test('it renders input with a label, and with a default placeholder', () => {
|
|
186
|
+
const mockedHandleChange = jest.fn();
|
|
187
|
+
|
|
188
|
+
render(
|
|
189
|
+
<SelectInput
|
|
190
|
+
id="testId"
|
|
191
|
+
onChange={mockedHandleChange}
|
|
192
|
+
label="Select Label"
|
|
193
|
+
options={selectOptions}
|
|
194
|
+
isMulti
|
|
195
|
+
value={[]}
|
|
196
|
+
/>
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(screen.getByLabelText('Select Label')).toBeInTheDocument();
|
|
200
|
+
expect(screen.getByText('Select...')).toBeInTheDocument();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('Multi select, with multiple items selected', () => {
|
|
205
|
+
test('it renders input with a label, and with two items selected', () => {
|
|
206
|
+
const mockedHandleChange = jest.fn();
|
|
207
|
+
|
|
208
|
+
render(
|
|
209
|
+
<SelectInput
|
|
210
|
+
id="testId"
|
|
211
|
+
onChange={mockedHandleChange}
|
|
212
|
+
label="Select Label"
|
|
213
|
+
options={selectOptions}
|
|
214
|
+
isMulti
|
|
215
|
+
value={[selectOptions[0], selectOptions[2]]}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(screen.getByLabelText('Select Label')).toBeInTheDocument();
|
|
220
|
+
expect(screen.queryByText('Select...')).toBeNull();
|
|
221
|
+
expect(screen.getByText('Chocolate')).toBeInTheDocument();
|
|
222
|
+
expect(screen.getByText('Vanilla')).toBeInTheDocument();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('Is Required', () => {
|
|
227
|
+
test('it renders an asterisk in the label', () => {
|
|
228
|
+
const mockedHandleChange = jest.fn();
|
|
229
|
+
|
|
230
|
+
render(
|
|
231
|
+
<SelectInput
|
|
232
|
+
id="testId"
|
|
233
|
+
onChange={mockedHandleChange}
|
|
234
|
+
label="Select Label"
|
|
235
|
+
options={selectOptions}
|
|
236
|
+
value="chocolate"
|
|
237
|
+
isRequired
|
|
238
|
+
/>
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(
|
|
242
|
+
screen.getByText(
|
|
243
|
+
getByTextWithMarkup('Select Label *') as unknown as Matcher
|
|
244
|
+
)
|
|
245
|
+
).toBeInTheDocument();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('Is Disabled', () => {
|
|
250
|
+
test('it disables the input', () => {
|
|
251
|
+
const mockedHandleChange = jest.fn();
|
|
252
|
+
|
|
253
|
+
const { container } = render(
|
|
254
|
+
<SelectInput
|
|
255
|
+
id="testId"
|
|
256
|
+
onChange={mockedHandleChange}
|
|
257
|
+
label="Select Label"
|
|
258
|
+
options={selectOptions}
|
|
259
|
+
value="chocolate"
|
|
260
|
+
isDisabled
|
|
261
|
+
/>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const combobox = container.querySelector(
|
|
265
|
+
'.react-select__control[aria-disabled="true"]'
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(combobox).toBeInTheDocument();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('Is Invalid, with a helpful message', () => {
|
|
273
|
+
test('it renders the helpful message', () => {
|
|
274
|
+
const mockedHandleChange = jest.fn();
|
|
275
|
+
|
|
276
|
+
render(
|
|
277
|
+
<SelectInput
|
|
278
|
+
id="testId"
|
|
279
|
+
onChange={mockedHandleChange}
|
|
280
|
+
label="Select Label"
|
|
281
|
+
options={selectOptions}
|
|
282
|
+
error="Helpful message"
|
|
283
|
+
value="chocolate"
|
|
284
|
+
/>
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(screen.getByText('Helpful message')).toBeInTheDocument();
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe('Is Clearable', () => {
|
|
292
|
+
test('it does not render the X icon if input has value but is not clearable', () => {
|
|
293
|
+
const mockedHandleChange = jest.fn();
|
|
294
|
+
|
|
295
|
+
const { container } = render(
|
|
296
|
+
<SelectInput
|
|
297
|
+
id="testId"
|
|
298
|
+
onChange={mockedHandleChange}
|
|
299
|
+
label="Select Label"
|
|
300
|
+
options={selectOptions}
|
|
301
|
+
value={selectOptions[0]}
|
|
302
|
+
isClearable={false}
|
|
303
|
+
/>
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(
|
|
307
|
+
container.querySelector('.react-select__clear-indicator')
|
|
308
|
+
).not.toBeInTheDocument();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('it renders the X icon if input has value and is clearable', () => {
|
|
312
|
+
const mockedHandleChange = jest.fn();
|
|
313
|
+
|
|
314
|
+
const { container } = render(
|
|
315
|
+
<SelectInput
|
|
316
|
+
id="testId"
|
|
317
|
+
onChange={mockedHandleChange}
|
|
318
|
+
label="Select Label"
|
|
319
|
+
options={selectOptions}
|
|
320
|
+
value={selectOptions[0]}
|
|
321
|
+
isClearable
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
expect(
|
|
326
|
+
container.querySelector('.react-select__clear-indicator')
|
|
327
|
+
).toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe('Sizes', () => {
|
|
333
|
+
const mockedHandleChange = jest.fn();
|
|
334
|
+
const sizes: TextInputSize[] = ['sm', 'md', 'lg'];
|
|
335
|
+
|
|
336
|
+
const breakpoints = ['tablet', 'desktop', 'hd'];
|
|
337
|
+
|
|
338
|
+
sizes.forEach((size) => {
|
|
339
|
+
test(`it has a ${size} class applied to it`, () => {
|
|
340
|
+
const { container } = render(
|
|
341
|
+
<SelectInput
|
|
342
|
+
id="testId"
|
|
343
|
+
onChange={mockedHandleChange}
|
|
344
|
+
options={selectOptions}
|
|
345
|
+
value={selectOptions[0].value}
|
|
346
|
+
size={size}
|
|
347
|
+
label="size test"
|
|
348
|
+
/>
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
expect(container.children[0].getAttribute('class')).toContain(size);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
breakpoints.forEach((breakpoint) => {
|
|
355
|
+
test(`it applies responsive classes for breakpoint: ${breakpoint} and size: ${size}`, () => {
|
|
356
|
+
const { container } = render(
|
|
357
|
+
<SelectInput
|
|
358
|
+
id="testId"
|
|
359
|
+
onChange={mockedHandleChange}
|
|
360
|
+
options={selectOptions}
|
|
361
|
+
value={selectOptions[0].value}
|
|
362
|
+
size={{ [breakpoint]: size }}
|
|
363
|
+
label="size test"
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
expect(container.children[0].getAttribute('class')).toContain(
|
|
368
|
+
`size-${size}-${breakpoint}`
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('It applies responsive classes when multiple are applied', () => {
|
|
375
|
+
const { container } = render(
|
|
376
|
+
<SelectInput
|
|
377
|
+
id="testId"
|
|
378
|
+
onChange={mockedHandleChange}
|
|
379
|
+
options={selectOptions}
|
|
380
|
+
value={selectOptions[0].value}
|
|
381
|
+
size={{
|
|
382
|
+
base: 'sm',
|
|
383
|
+
tablet: 'md',
|
|
384
|
+
desktop: 'lg',
|
|
385
|
+
hd: 'sm',
|
|
386
|
+
}}
|
|
387
|
+
label="size test"
|
|
388
|
+
/>
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
expect(container.children[0].getAttribute('class')).toContain('size-sm');
|
|
392
|
+
expect(container.children[0].getAttribute('class')).toContain(
|
|
393
|
+
'size-md-tablet'
|
|
394
|
+
);
|
|
395
|
+
expect(container.children[0].getAttribute('class')).toContain(
|
|
396
|
+
'size-lg-desktop'
|
|
397
|
+
);
|
|
398
|
+
expect(container.children[0].getAttribute('class')).toContain(
|
|
399
|
+
'size-sm-hd'
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import React, { FC, FocusEvent, ReactNode, FocusEventHandler } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import Select, {
|
|
4
|
+
components,
|
|
5
|
+
ClearIndicatorProps,
|
|
6
|
+
OptionsOrGroups,
|
|
7
|
+
OnChangeValue,
|
|
8
|
+
} from 'react-select';
|
|
9
|
+
import { ResponsiveProp } from '../../types';
|
|
10
|
+
import { Z_INDEX_VALUES } from '../../lib/tokens';
|
|
11
|
+
import { generateResponsiveClasses } from '../../lib/generateResponsiveClasses';
|
|
12
|
+
import { Box } from '../Box/Box';
|
|
13
|
+
import { Icon } from '../Icon/Icon';
|
|
14
|
+
import { FormLabel } from '../FormLabel/FormLabel';
|
|
15
|
+
import { InputValidationMessage } from '../InputValidationMessage/InputValidationMessage';
|
|
16
|
+
import styles from './SelectInput.module.scss';
|
|
17
|
+
import { GroupBase } from 'react-select/dist/declarations/src/types';
|
|
18
|
+
|
|
19
|
+
type SelectOptions = any;
|
|
20
|
+
type SelectGroupOptions = GroupBase<SelectOptions>;
|
|
21
|
+
export type SelectInputOptions = OptionsOrGroups<
|
|
22
|
+
SelectOptions,
|
|
23
|
+
SelectGroupOptions
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
export type SimulatedEventPayloadType = {
|
|
27
|
+
target: {
|
|
28
|
+
name: string;
|
|
29
|
+
value: OnChangeValue<SelectInputOptions, boolean>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type TextInputSize =
|
|
34
|
+
| 'sm'
|
|
35
|
+
| 'md'
|
|
36
|
+
| 'lg'
|
|
37
|
+
| ResponsiveProp<'sm' | 'md' | 'lg'>;
|
|
38
|
+
|
|
39
|
+
export interface SelectInputProps {
|
|
40
|
+
/**
|
|
41
|
+
* The id attribute of the input.
|
|
42
|
+
*/
|
|
43
|
+
id: string;
|
|
44
|
+
/**
|
|
45
|
+
* Custom content to be displayed above the input. If the label is hidden, will be used to set aria-label attribute.
|
|
46
|
+
*/
|
|
47
|
+
label: string;
|
|
48
|
+
/**
|
|
49
|
+
* Callback function to call on change event.
|
|
50
|
+
*/
|
|
51
|
+
onChange: (event: SimulatedEventPayloadType) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Options for dropdown list.
|
|
54
|
+
*/
|
|
55
|
+
options: SelectInputOptions;
|
|
56
|
+
/**
|
|
57
|
+
* The value(s) of select.
|
|
58
|
+
*/
|
|
59
|
+
value: any | any[]; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
60
|
+
/**
|
|
61
|
+
* Autofocus select input on render.
|
|
62
|
+
*/
|
|
63
|
+
autoFocus?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Additional classes to add.
|
|
66
|
+
*/
|
|
67
|
+
className?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Mark the input field as invalid and display a validation message.
|
|
70
|
+
* Pass a string or node to render a validation message below the input.
|
|
71
|
+
*/
|
|
72
|
+
error?: ReactNode;
|
|
73
|
+
/**
|
|
74
|
+
* Additional clarifying text to help describe the input
|
|
75
|
+
*/
|
|
76
|
+
helpText?: ReactNode;
|
|
77
|
+
/**
|
|
78
|
+
* Visually hide the label.
|
|
79
|
+
*/
|
|
80
|
+
hideLabel?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* If the input value is clearable programmatically.
|
|
83
|
+
*/
|
|
84
|
+
isClearable?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* If the input should be disabled and not focusable.
|
|
87
|
+
*/
|
|
88
|
+
isDisabled?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Is multi select enabled.
|
|
91
|
+
*/
|
|
92
|
+
isMulti?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* The required and aria-required attributes on the input
|
|
95
|
+
*/
|
|
96
|
+
isRequired?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Select 'name' attribute.
|
|
99
|
+
*/
|
|
100
|
+
name?: string;
|
|
101
|
+
/**
|
|
102
|
+
* A ref to portal the dropdown menu to. This is useful when the dropdown is located in a smaller container
|
|
103
|
+
* and we want to avoid cutting off the menu.
|
|
104
|
+
*/
|
|
105
|
+
menuPortalTarget?: HTMLElement;
|
|
106
|
+
/**
|
|
107
|
+
* Callback function to call on blur event.
|
|
108
|
+
*/
|
|
109
|
+
onBlur?: (event: FocusEvent<HTMLElement>) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Callback function to call on focus event.
|
|
112
|
+
*/
|
|
113
|
+
onFocus?: (event: FocusEvent<HTMLElement>) => void;
|
|
114
|
+
/**
|
|
115
|
+
* Placeholder for input.
|
|
116
|
+
*/
|
|
117
|
+
placeholder?: string;
|
|
118
|
+
/**
|
|
119
|
+
* Visual indicator that the field is required, that gets appended to the label
|
|
120
|
+
*/
|
|
121
|
+
requiredIndicator?: ReactNode;
|
|
122
|
+
/**
|
|
123
|
+
* The size of the text input.
|
|
124
|
+
*/
|
|
125
|
+
size?: TextInputSize;
|
|
126
|
+
/**
|
|
127
|
+
* Additional props to be spread. These will be applied specifically to
|
|
128
|
+
* the `react-select` component that powers the select. For full docs on
|
|
129
|
+
* react-select props, [Click Here](https://react-select.com/props)
|
|
130
|
+
*/
|
|
131
|
+
[x: string]: any; // eslint-disable-line
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const SelectInput: FC<SelectInputProps> = ({
|
|
135
|
+
id,
|
|
136
|
+
label,
|
|
137
|
+
onChange,
|
|
138
|
+
options,
|
|
139
|
+
value,
|
|
140
|
+
autoFocus = false,
|
|
141
|
+
className = '',
|
|
142
|
+
error = false,
|
|
143
|
+
helpText,
|
|
144
|
+
hideLabel = false,
|
|
145
|
+
isClearable = false,
|
|
146
|
+
isDisabled = false,
|
|
147
|
+
isMulti = false,
|
|
148
|
+
isRequired = false,
|
|
149
|
+
menuPortalTarget = null,
|
|
150
|
+
name = '',
|
|
151
|
+
onFocus = null,
|
|
152
|
+
onBlur = null,
|
|
153
|
+
placeholder = undefined,
|
|
154
|
+
requiredIndicator = ' *',
|
|
155
|
+
size = 'md',
|
|
156
|
+
...restProps
|
|
157
|
+
}) => {
|
|
158
|
+
const handleChange = (values: OnChangeValue<SelectInputOptions, boolean>) => {
|
|
159
|
+
const simulatedEventPayloadType: SimulatedEventPayloadType = {
|
|
160
|
+
target: {
|
|
161
|
+
name,
|
|
162
|
+
value: values,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
onChange(simulatedEventPayloadType);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleFocus: FocusEventHandler<HTMLInputElement> = (e) => {
|
|
170
|
+
if (onFocus) onFocus(e);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
|
|
174
|
+
if (onBlur) onBlur(e);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const responsiveClasses = generateResponsiveClasses('size', size);
|
|
178
|
+
|
|
179
|
+
const wrapperClasses = classNames(
|
|
180
|
+
'hyphen-components__variables__form-control',
|
|
181
|
+
'select-input-wrapper',
|
|
182
|
+
className,
|
|
183
|
+
...responsiveClasses.map((c) => styles[c]),
|
|
184
|
+
{
|
|
185
|
+
[styles.disabled]: isDisabled,
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const inputClasses = classNames('react-select', { [styles.error]: error });
|
|
190
|
+
|
|
191
|
+
const labelProps = {
|
|
192
|
+
inputId: id,
|
|
193
|
+
helpText,
|
|
194
|
+
className: styles['select-input-label'],
|
|
195
|
+
isDisabled,
|
|
196
|
+
isFieldRequired: isRequired,
|
|
197
|
+
requiredIndicator,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const ClearIndicator = (
|
|
201
|
+
props: ClearIndicatorProps<
|
|
202
|
+
OptionsOrGroups<SelectOptions, SelectGroupOptions>
|
|
203
|
+
>
|
|
204
|
+
) => (
|
|
205
|
+
<components.ClearIndicator {...props}>
|
|
206
|
+
<Icon name="remove" />
|
|
207
|
+
</components.ClearIndicator>
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Box width="100%" className={wrapperClasses}>
|
|
212
|
+
{label && !hideLabel && <FormLabel {...labelProps}>{label}</FormLabel>}
|
|
213
|
+
<Select
|
|
214
|
+
{...restProps}
|
|
215
|
+
inputId={id}
|
|
216
|
+
aria-label={label}
|
|
217
|
+
components={{ ClearIndicator }}
|
|
218
|
+
aria-labelledby={label && !hideLabel ? `${id}Label` : undefined}
|
|
219
|
+
className={inputClasses}
|
|
220
|
+
classNamePrefix="react-select"
|
|
221
|
+
placeholder={placeholder}
|
|
222
|
+
isClearable={isClearable}
|
|
223
|
+
isDisabled={isDisabled}
|
|
224
|
+
isMulti={isMulti}
|
|
225
|
+
menuPortalTarget={menuPortalTarget}
|
|
226
|
+
name={name}
|
|
227
|
+
autoFocus={autoFocus}
|
|
228
|
+
options={options}
|
|
229
|
+
onChange={handleChange}
|
|
230
|
+
onFocus={handleFocus}
|
|
231
|
+
onBlur={handleBlur}
|
|
232
|
+
styles={{
|
|
233
|
+
menuPortal: (base) => ({
|
|
234
|
+
...base,
|
|
235
|
+
zIndex: Number(Z_INDEX_VALUES.popover),
|
|
236
|
+
}),
|
|
237
|
+
}}
|
|
238
|
+
value={value}
|
|
239
|
+
/>
|
|
240
|
+
{error && typeof error !== 'boolean' && (
|
|
241
|
+
<InputValidationMessage>{error}</InputValidationMessage>
|
|
242
|
+
)}
|
|
243
|
+
</Box>
|
|
244
|
+
);
|
|
245
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import { SelectInputInset } from './SelectInputInset';
|
|
3
|
+
import * as Stories from './SelectInputInset.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={Stories} />
|
|
6
|
+
|
|
7
|
+
# SelectInputInset
|
|
8
|
+
|
|
9
|
+
Use `SelectInputInset` to create a floating label input field, but unlike `TextInputInset` they always show the `label` in the floated state. It is a controlled component, which means that you need to provide a value and an `onChange` handler to update the value.
|
|
10
|
+
|
|
11
|
+
<Canvas withSource="open" of={Stories.Default} />
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
<ArgTypes of={SelectInputInset} />
|
|
16
|
+
|
|
17
|
+
### Required
|
|
18
|
+
|
|
19
|
+
Use the `isRequired` prop to display an `*` after the label and set the `required` and `aria-required` on the underlying input element. You can also use the `requiredIndicator` prop to customize the indicator that appears next to the label.
|
|
20
|
+
|
|
21
|
+
<Canvas of={Stories.Required} />
|
|
22
|
+
|
|
23
|
+
### Help Text
|
|
24
|
+
|
|
25
|
+
Use `helpText` to provide additional information about the input field.
|
|
26
|
+
|
|
27
|
+
<Canvas of={Stories.HelpText} />
|
|
28
|
+
|
|
29
|
+
### Validation Error
|
|
30
|
+
|
|
31
|
+
Use the `error` prop to mark the input as invalid. `error` accepts a `boolean`, `string`, or `node`.
|
|
32
|
+
|
|
33
|
+
<Canvas of={Stories.ValidationError} />
|
|
34
|
+
|
|
35
|
+
### Disabled
|
|
36
|
+
|
|
37
|
+
Add `isDisabled`to give it a grayed out appearance, remove pointer events, and prevent focusing.
|
|
38
|
+
|
|
39
|
+
<Canvas of={Stories.Disabled} />
|
|
40
|
+
|
|
41
|
+
### Clearable
|
|
42
|
+
|
|
43
|
+
Use the `onClear` prop to display a clear icon (x) when the input has a value. `onClear` will fire
|
|
44
|
+
a callback function when the clear icon is clicked, which can then be handled to clear the value.
|
|
45
|
+
|
|
46
|
+
<Canvas of={Stories.Clearable} />
|
|
47
|
+
|
|
48
|
+
### Sizes
|
|
49
|
+
|
|
50
|
+
Two sizes, `md` and `lg` are available, with `md` being the default.
|
|
51
|
+
|
|
52
|
+
<Canvas of={Stories.Sizes} />
|
|
53
|
+
|
|
54
|
+
## Component Design Tokens
|
|
55
|
+
|
|
56
|
+
This component shares component design tokens with all form controls. For a complete list of tokens, see the [Theming Form Controls documentation](/docs/theming-form-controls--custom-theme-form).
|