@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,195 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, screen } from '@testing-library/react';
|
|
3
|
+
import { TextareaInput } from './TextareaInput';
|
|
4
|
+
|
|
5
|
+
const baseProps = {
|
|
6
|
+
name: 'firstName',
|
|
7
|
+
id: 'firstName',
|
|
8
|
+
label: 'first name',
|
|
9
|
+
value: 'hello',
|
|
10
|
+
onChange: () => null,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function getByTextWithMarkup(text: string) {
|
|
14
|
+
// eslint-disable-next-line
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
return (content, element) => {
|
|
17
|
+
const hasText = (node: Element) => node.textContent === text;
|
|
18
|
+
const elementHasText = hasText(element);
|
|
19
|
+
// eslint-disable-next-line
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
const childrenDontHaveText = Array.from(element.children).every(
|
|
22
|
+
(child) => !hasText(child as Element)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return elementHasText && childrenDontHaveText;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('TextareaInput', () => {
|
|
30
|
+
describe('Callback Handling', () => {
|
|
31
|
+
describe('onChange', () => {
|
|
32
|
+
test('onChange event fires callback function', () => {
|
|
33
|
+
const mockedHandleChange = jest.fn(() => null);
|
|
34
|
+
|
|
35
|
+
render(<TextareaInput {...baseProps} onChange={mockedHandleChange} />);
|
|
36
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
37
|
+
|
|
38
|
+
fireEvent.change(inputElement, { target: { value: 'good bye' } });
|
|
39
|
+
expect(mockedHandleChange).toHaveBeenCalledTimes(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('Input value is updated properly when upper state changes', () => {
|
|
43
|
+
let value = 'hello';
|
|
44
|
+
const mockedHandleChange = jest.fn((event) => {
|
|
45
|
+
value = event.target.value;
|
|
46
|
+
});
|
|
47
|
+
const { rerender } = render(
|
|
48
|
+
<TextareaInput
|
|
49
|
+
{...baseProps}
|
|
50
|
+
value={value}
|
|
51
|
+
onChange={mockedHandleChange}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const inputElement = screen.getByDisplayValue(
|
|
56
|
+
'hello'
|
|
57
|
+
) as HTMLInputElement;
|
|
58
|
+
expect(inputElement.value).toBe('hello');
|
|
59
|
+
|
|
60
|
+
fireEvent.change(inputElement, { target: { value: 'good bye' } });
|
|
61
|
+
expect(mockedHandleChange).toHaveBeenCalledTimes(1);
|
|
62
|
+
|
|
63
|
+
rerender(
|
|
64
|
+
<TextareaInput
|
|
65
|
+
{...baseProps}
|
|
66
|
+
value={value}
|
|
67
|
+
onChange={mockedHandleChange}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
expect(inputElement.value).toBe('good bye');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('onFocus', () => {
|
|
75
|
+
test('Input fires onFocus callback', () => {
|
|
76
|
+
const mockedHandleFocus = jest.fn();
|
|
77
|
+
render(<TextareaInput {...baseProps} onFocus={mockedHandleFocus} />);
|
|
78
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
79
|
+
fireEvent.focus(inputElement);
|
|
80
|
+
expect(mockedHandleFocus).toBeCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('onBlur', () => {
|
|
85
|
+
test('Input fires onBlur callback', () => {
|
|
86
|
+
const mockedHandleBlur = jest.fn();
|
|
87
|
+
render(<TextareaInput {...baseProps} onBlur={mockedHandleBlur} />);
|
|
88
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
89
|
+
fireEvent.focus(inputElement);
|
|
90
|
+
fireEvent.blur(inputElement);
|
|
91
|
+
expect(mockedHandleBlur).toBeCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('States', () => {
|
|
97
|
+
describe('Label', () => {
|
|
98
|
+
test('it renders a label', () => {
|
|
99
|
+
render(<TextareaInput {...baseProps} />);
|
|
100
|
+
|
|
101
|
+
const labelElement = screen.getByText(baseProps.label);
|
|
102
|
+
|
|
103
|
+
expect(labelElement).toBeInTheDocument();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Autofocused', () => {
|
|
108
|
+
test('Input autofocuses if "autoFocus" prop is set to true', () => {
|
|
109
|
+
render(<TextareaInput {...baseProps} autoFocus />);
|
|
110
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
111
|
+
expect(document.activeElement).toEqual(inputElement);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('Autocomplete', () => {
|
|
116
|
+
test('Input correctly assigns autocomplete value of "on" when bool true is provided', () => {
|
|
117
|
+
render(<TextareaInput {...baseProps} autoComplete />);
|
|
118
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
119
|
+
expect(inputElement).toHaveAttribute('autocomplete', 'on');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('Input correctly assigns autocomplete value of "off" when bool false is provided', () => {
|
|
123
|
+
render(<TextareaInput {...baseProps} autoComplete={false} />);
|
|
124
|
+
const inputElement = screen.getByDisplayValue('hello');
|
|
125
|
+
expect(inputElement).toHaveAttribute('autocomplete', 'off');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('Input correctly assigns autocomplete specific value when provided', () => {
|
|
129
|
+
render(<TextareaInput {...baseProps} autoComplete="email" />);
|
|
130
|
+
const inputElement = screen.getByDisplayValue('hello');
|
|
131
|
+
expect(inputElement).toHaveAttribute('autocomplete', 'email');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('Required', () => {
|
|
136
|
+
test('it correctly assigns the "aria-required" attribute when "isRequired" prop is true', () => {
|
|
137
|
+
render(<TextareaInput {...baseProps} isRequired />);
|
|
138
|
+
|
|
139
|
+
const inputElement = screen.getByDisplayValue('hello');
|
|
140
|
+
|
|
141
|
+
expect(inputElement).toHaveAttribute('aria-required', 'true');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("it's label renders an asterisk indicating that it's required", () => {
|
|
145
|
+
render(<TextareaInput {...baseProps} isRequired />);
|
|
146
|
+
|
|
147
|
+
const labelElement = screen.getByText(
|
|
148
|
+
getByTextWithMarkup(`${baseProps.label} *`)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(labelElement).toBeInTheDocument();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('Error', () => {
|
|
156
|
+
test('Input correctly displays error message if provided', () => {
|
|
157
|
+
render(<TextareaInput {...baseProps} error="You silly goose" />);
|
|
158
|
+
|
|
159
|
+
const validationMessageElement = screen.getByText('You silly goose');
|
|
160
|
+
|
|
161
|
+
expect(validationMessageElement).toBeInTheDocument();
|
|
162
|
+
expect(validationMessageElement).toHaveTextContent('You silly goose');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('Max Length', () => {
|
|
167
|
+
test('Input correctly passes maxlength property if prop is passed', async () => {
|
|
168
|
+
render(<TextareaInput {...baseProps} value="" maxLength={3} />);
|
|
169
|
+
|
|
170
|
+
const inputElement = screen.getByLabelText(baseProps.label);
|
|
171
|
+
expect(inputElement).toBeInTheDocument();
|
|
172
|
+
expect(inputElement).toHaveAttribute('maxlength');
|
|
173
|
+
expect(inputElement.getAttribute('maxlength')).toBe('3');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('Aria-labelledby', () => {
|
|
178
|
+
test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
|
|
179
|
+
render(<TextareaInput {...baseProps} />);
|
|
180
|
+
const inputElement = screen.getByDisplayValue(baseProps.value);
|
|
181
|
+
expect(inputElement).toHaveAttribute(
|
|
182
|
+
'aria-labelledby',
|
|
183
|
+
`${baseProps.id}Label`
|
|
184
|
+
);
|
|
185
|
+
expect(document.getElementById(baseProps.id)).toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
|
|
189
|
+
render(<TextareaInput {...baseProps} hideLabel />);
|
|
190
|
+
const inputElement = screen.getByLabelText(baseProps.label);
|
|
191
|
+
expect(inputElement).not.toHaveAttribute('aria-labelledby');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import React, { FC, ChangeEvent, FocusEvent, ReactNode } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { ResponsiveProp } from '../../types';
|
|
4
|
+
import { Box, BoxProps } from '../Box/Box';
|
|
5
|
+
import { FormLabel } from '../FormLabel/FormLabel';
|
|
6
|
+
import { InputValidationMessage } from '../InputValidationMessage/InputValidationMessage';
|
|
7
|
+
import { getAutoCompleteValue } from '../../lib/getAutoCompleteValue';
|
|
8
|
+
import { generateResponsiveClasses } from '../../lib/generateResponsiveClasses';
|
|
9
|
+
import styles from './TextareaInput.module.scss';
|
|
10
|
+
|
|
11
|
+
export type TextareaInputSize = 'sm' | 'md' | 'lg';
|
|
12
|
+
export interface TextareaInputProps extends Omit<BoxProps, 'as' | 'width'> {
|
|
13
|
+
/**
|
|
14
|
+
* The input's id attribute. Used to programmatically tie the input with its label.
|
|
15
|
+
*/
|
|
16
|
+
id: string;
|
|
17
|
+
/**
|
|
18
|
+
* Custom content to be displayed above the input. If the label is hidden, will be used to set aria-label attribute.
|
|
19
|
+
*/
|
|
20
|
+
label: string;
|
|
21
|
+
/**
|
|
22
|
+
* Callback function to call on change event.
|
|
23
|
+
*/
|
|
24
|
+
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
25
|
+
/**
|
|
26
|
+
* The text value of the input. Required since our Input is a controlled component.
|
|
27
|
+
*/
|
|
28
|
+
value: string;
|
|
29
|
+
/**
|
|
30
|
+
* The input's 'autocomplete' attribute.
|
|
31
|
+
*/
|
|
32
|
+
autoComplete?: boolean | string;
|
|
33
|
+
/**
|
|
34
|
+
* Automatically focus the input when the page is loaded.
|
|
35
|
+
*/
|
|
36
|
+
autoFocus?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Custom class to be added to standard input classes.
|
|
39
|
+
*/
|
|
40
|
+
className?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Mark the input field as invalid and display a validation message.
|
|
43
|
+
* Pass a string or node to render a validation message below the input.
|
|
44
|
+
*/
|
|
45
|
+
error?: ReactNode;
|
|
46
|
+
/**
|
|
47
|
+
* Additional clarifying text to help describe the input
|
|
48
|
+
*/
|
|
49
|
+
helpText?: ReactNode;
|
|
50
|
+
/**
|
|
51
|
+
* Visually hide the label.
|
|
52
|
+
*/
|
|
53
|
+
hideLabel?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The input's disabled attribute
|
|
56
|
+
*/
|
|
57
|
+
isDisabled?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The required and aria-required attributes on the input
|
|
60
|
+
*/
|
|
61
|
+
isRequired?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The input's 'maxlength' attribute.
|
|
64
|
+
* NOTE: initializing the input with a value longer than the desired maxlength will not trim this value.
|
|
65
|
+
*/
|
|
66
|
+
maxLength?: number;
|
|
67
|
+
/**
|
|
68
|
+
* The input's 'name' attribute.
|
|
69
|
+
*/
|
|
70
|
+
name?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Callback function to call on blur event.
|
|
73
|
+
*/
|
|
74
|
+
onBlur?: (event: FocusEvent<HTMLTextAreaElement>) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Callback function to call on focus event.
|
|
77
|
+
*/
|
|
78
|
+
onFocus?: (event: FocusEvent<HTMLTextAreaElement>) => void;
|
|
79
|
+
/**
|
|
80
|
+
* The input placeholder attribute.
|
|
81
|
+
*/
|
|
82
|
+
placeholder?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Visual indicator that the field is required, that gets appended to the label
|
|
85
|
+
*/
|
|
86
|
+
requiredIndicator?: ReactNode;
|
|
87
|
+
/**
|
|
88
|
+
* Textarea resize behavior
|
|
89
|
+
*/
|
|
90
|
+
resize?: 'vertical' | 'horizontal' | 'none' | 'both';
|
|
91
|
+
/**
|
|
92
|
+
* number of visible text lines for the control.
|
|
93
|
+
*/
|
|
94
|
+
rows?: number;
|
|
95
|
+
/**
|
|
96
|
+
* The size of the text input.
|
|
97
|
+
*/
|
|
98
|
+
size?: TextareaInputSize | ResponsiveProp<TextareaInputSize>;
|
|
99
|
+
/**
|
|
100
|
+
* Additional props to be spread to rendered element
|
|
101
|
+
*/
|
|
102
|
+
[x: string]: any; // eslint-disable-line
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const TextareaInput: FC<TextareaInputProps> = ({
|
|
106
|
+
id,
|
|
107
|
+
label,
|
|
108
|
+
onChange,
|
|
109
|
+
value,
|
|
110
|
+
autoComplete = false,
|
|
111
|
+
autoFocus = false,
|
|
112
|
+
className = undefined,
|
|
113
|
+
error = false,
|
|
114
|
+
helpText,
|
|
115
|
+
hideLabel = false,
|
|
116
|
+
isDisabled = false,
|
|
117
|
+
isRequired = false,
|
|
118
|
+
maxLength = undefined,
|
|
119
|
+
name = undefined,
|
|
120
|
+
onBlur = undefined,
|
|
121
|
+
onFocus = undefined,
|
|
122
|
+
placeholder = '',
|
|
123
|
+
requiredIndicator = ' *',
|
|
124
|
+
resize = 'vertical',
|
|
125
|
+
rows = 3,
|
|
126
|
+
size = 'md',
|
|
127
|
+
...restProps
|
|
128
|
+
}) => {
|
|
129
|
+
const responsiveClasses = generateResponsiveClasses('size', size);
|
|
130
|
+
|
|
131
|
+
const inputWrapperClasses = classNames(
|
|
132
|
+
'hyphen-components__variables__form-control',
|
|
133
|
+
styles['textarea-input-wrapper'],
|
|
134
|
+
...responsiveClasses.map((c) => styles[c]),
|
|
135
|
+
{
|
|
136
|
+
[styles.error]: error,
|
|
137
|
+
[styles.disabled]: isDisabled,
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const inputProps = {
|
|
142
|
+
'aria-required': isRequired,
|
|
143
|
+
'aria-invalid': !!error,
|
|
144
|
+
'aria-label': label,
|
|
145
|
+
'aria-labelledby': label && !hideLabel ? `${id}Label` : undefined,
|
|
146
|
+
autoComplete: getAutoCompleteValue(autoComplete),
|
|
147
|
+
autoFocus,
|
|
148
|
+
className: classNames(styles[`textarea-resize-${resize}`]),
|
|
149
|
+
disabled: isDisabled,
|
|
150
|
+
id,
|
|
151
|
+
maxLength,
|
|
152
|
+
name,
|
|
153
|
+
onBlur,
|
|
154
|
+
onChange,
|
|
155
|
+
onFocus,
|
|
156
|
+
placeholder,
|
|
157
|
+
required: isRequired,
|
|
158
|
+
rows,
|
|
159
|
+
value,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const labelProps = {
|
|
163
|
+
inputId: id,
|
|
164
|
+
helpText,
|
|
165
|
+
className: styles['textarea-input-label'],
|
|
166
|
+
isDisabled,
|
|
167
|
+
isFieldRequired: isRequired,
|
|
168
|
+
requiredIndicator,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<Box width="100%" className={className} {...restProps}>
|
|
173
|
+
{label && !hideLabel && <FormLabel {...labelProps}>{label}</FormLabel>}
|
|
174
|
+
<Box display="block" className={inputWrapperClasses}>
|
|
175
|
+
<Box as="textarea" {...inputProps} />
|
|
176
|
+
</Box>
|
|
177
|
+
{error && error !== true && (
|
|
178
|
+
<InputValidationMessage>{error}</InputValidationMessage>
|
|
179
|
+
)}
|
|
180
|
+
</Box>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import { TextareaInputInset } from './TextareaInputInset';
|
|
3
|
+
import * as Stories from './TextareaInputInset.stories';
|
|
4
|
+
|
|
5
|
+
<Meta of={Stories} />
|
|
6
|
+
|
|
7
|
+
# TextareaInputInset
|
|
8
|
+
|
|
9
|
+
Use `TextareaInputInset` to create a floating label input field. 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={TextareaInputInset} />
|
|
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
|
+
### Sizes
|
|
42
|
+
|
|
43
|
+
Two sizes, `md` and `lg` are available, with `md` being the default.
|
|
44
|
+
|
|
45
|
+
<Canvas of={Stories.Sizes} />
|
|
46
|
+
|
|
47
|
+
## Resize
|
|
48
|
+
|
|
49
|
+
The `resize` prop accepts `horizontal`, `vertical`, `both` or `none` to control the users ability to resize the input.
|
|
50
|
+
|
|
51
|
+
<Canvas of={Stories.Resize} />
|
|
52
|
+
|
|
53
|
+
## Component Design Tokens
|
|
54
|
+
|
|
55
|
+
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).
|