@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,437 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, screen, within } from '@testing-library/react';
|
|
3
|
+
import { Button } from '../Button/Button';
|
|
4
|
+
import { Table } from './Table';
|
|
5
|
+
|
|
6
|
+
const columnConfig = [
|
|
7
|
+
{ heading: 'ID', dataKey: 'id' },
|
|
8
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
9
|
+
{ heading: 'Flavor', dataKey: 'flavor' },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const columnConfigSortable = [
|
|
13
|
+
{ heading: 'ID', dataKey: 'id', isSortable: true },
|
|
14
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
15
|
+
{ heading: 'Flavor', dataKey: 'flavor', isSortable: true },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const tableData = [
|
|
19
|
+
{ id: 1, color: 'red', flavor: 'vanilla' },
|
|
20
|
+
{ id: 2, color: 'blue', flavor: 'chocolate' },
|
|
21
|
+
{ id: 3, color: 'green', flavor: 'strawberry' },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const mockHandleSort = jest.fn();
|
|
25
|
+
|
|
26
|
+
describe('Table', () => {
|
|
27
|
+
describe('Callback Handling', () => {
|
|
28
|
+
describe('onSort', () => {
|
|
29
|
+
test('onSort event fires callback function', () => {
|
|
30
|
+
render(
|
|
31
|
+
<Table
|
|
32
|
+
columns={columnConfigSortable}
|
|
33
|
+
rows={tableData}
|
|
34
|
+
rowKey="id"
|
|
35
|
+
onSort={mockHandleSort}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const sortableColumnHeader = screen.getByText('ID');
|
|
40
|
+
|
|
41
|
+
fireEvent.click(sortableColumnHeader);
|
|
42
|
+
expect(mockHandleSort).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('States', () => {
|
|
48
|
+
describe('Default', () => {
|
|
49
|
+
test('it renders a table', () => {
|
|
50
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
51
|
+
|
|
52
|
+
const table = screen.getByRole('table');
|
|
53
|
+
|
|
54
|
+
expect(table).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('it renders a thead and tbody', () => {
|
|
58
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
59
|
+
|
|
60
|
+
const rows = screen.queryAllByRole('rowgroup');
|
|
61
|
+
|
|
62
|
+
expect(rows).toHaveLength(2);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test(`it renders the correct amount of headers: ${columnConfig.length}`, () => {
|
|
66
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
67
|
+
|
|
68
|
+
const rows = screen.queryAllByRole('columnheader');
|
|
69
|
+
|
|
70
|
+
expect(rows).toHaveLength(3);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test(`it renders 3 headings that match columns headings: ${[
|
|
74
|
+
...columnConfig.map((c) => `${c.heading}, `),
|
|
75
|
+
]}`, () => {
|
|
76
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
77
|
+
|
|
78
|
+
const idHeader = screen.getByText('ID');
|
|
79
|
+
const colorHeader = screen.getByText('Color');
|
|
80
|
+
const flavorHeader = screen.getByText('Flavor');
|
|
81
|
+
|
|
82
|
+
expect(idHeader).toBeInTheDocument();
|
|
83
|
+
expect(colorHeader).toBeInTheDocument();
|
|
84
|
+
expect(flavorHeader).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test(`it renders correct amount of rows based on data: ${tableData.length} + 1 (header row)`, () => {
|
|
88
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
89
|
+
|
|
90
|
+
const rows = screen.queryAllByRole('row');
|
|
91
|
+
|
|
92
|
+
expect(rows).toHaveLength(4);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test(`it renders correct amount of cells based on columns and rows: ${
|
|
96
|
+
columnConfig.length + tableData.length
|
|
97
|
+
}`, () => {
|
|
98
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
99
|
+
|
|
100
|
+
const cells = screen.queryAllByRole('cell');
|
|
101
|
+
|
|
102
|
+
expect(cells).toHaveLength(9);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('it renders the cell content', () => {
|
|
106
|
+
render(<Table columns={columnConfig} rows={tableData} rowKey="id" />);
|
|
107
|
+
const tableRowArrays = tableData.map((row) =>
|
|
108
|
+
Object.values(row).map((v) => v.toString())
|
|
109
|
+
);
|
|
110
|
+
const cellData = tableRowArrays.reduce(
|
|
111
|
+
(target, current) => [...current, ...target],
|
|
112
|
+
[]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
cellData.map((value) =>
|
|
116
|
+
expect(screen.getByText(value)).toBeInTheDocument()
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('Loading', () => {
|
|
122
|
+
test('it renders a loading indicator', () => {
|
|
123
|
+
render(
|
|
124
|
+
<Table
|
|
125
|
+
columns={columnConfig}
|
|
126
|
+
rows={tableData}
|
|
127
|
+
rowKey="id"
|
|
128
|
+
isLoading
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const spinner = screen.getByTestId('spinner-testid');
|
|
133
|
+
|
|
134
|
+
expect(spinner).toBeInTheDocument();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('Sortable', () => {
|
|
139
|
+
test('it renders 2 sortable table headers', () => {
|
|
140
|
+
render(
|
|
141
|
+
<Table columns={columnConfigSortable} rows={tableData} rowKey="id" />
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const sortableHeaders = screen.getAllByTestId(
|
|
145
|
+
'tableHeaderCellSortNone-testid'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(sortableHeaders).toHaveLength(2);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('it passes the sorted column properly to the correct th element', () => {
|
|
152
|
+
render(
|
|
153
|
+
<Table
|
|
154
|
+
columns={columnConfigSortable}
|
|
155
|
+
rows={tableData}
|
|
156
|
+
rowKey="id"
|
|
157
|
+
sortedColumn={{ dataKey: 'flavor', sortDirection: 'ascending' }}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const headingSpan = screen.getByText('Flavor');
|
|
162
|
+
expect(headingSpan).toBeInTheDocument();
|
|
163
|
+
const headingElement = headingSpan.closest('th');
|
|
164
|
+
expect(headingElement).toBeInTheDocument();
|
|
165
|
+
if (headingElement) {
|
|
166
|
+
const { getByTestId } = within(headingElement);
|
|
167
|
+
expect(
|
|
168
|
+
getByTestId('tableHeaderCellSortAsc-testid')
|
|
169
|
+
).toBeInTheDocument();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('Scrollable', () => {
|
|
175
|
+
test('It renders the table container limited by width/height attributes', () => {
|
|
176
|
+
render(
|
|
177
|
+
<Table
|
|
178
|
+
columns={columnConfig}
|
|
179
|
+
rows={tableData}
|
|
180
|
+
rowKey="id"
|
|
181
|
+
isScrollable={{ x: true, y: true }}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const tableContainer = screen.getByTestId('tableContainerDiv-testid');
|
|
186
|
+
|
|
187
|
+
expect(tableContainer).toHaveClass(
|
|
188
|
+
'scrollable',
|
|
189
|
+
'scrollable-x',
|
|
190
|
+
'scrollable-y'
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('Custom Actions', () => {
|
|
196
|
+
test('if action is triggered, function is called', () => {
|
|
197
|
+
const mockHandleClick = jest.fn();
|
|
198
|
+
const tableDataWithClickableButton = [
|
|
199
|
+
{ id: 1, color: null, flavor: <Button onClick={mockHandleClick}>Click me</Button> }, // eslint-disable-line
|
|
200
|
+
{ id: 2, color: 'blue', flavor: 'chocolate' },
|
|
201
|
+
{ id: 3, color: 'green', flavor: 'strawberry' },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
render(
|
|
205
|
+
<Table
|
|
206
|
+
columns={columnConfig}
|
|
207
|
+
rows={tableDataWithClickableButton}
|
|
208
|
+
rowKey="id"
|
|
209
|
+
/>
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const button = screen.getByText('Click me');
|
|
213
|
+
|
|
214
|
+
fireEvent.click(button);
|
|
215
|
+
expect(mockHandleClick).toHaveBeenCalledTimes(1);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('Empty Cell Placeholder', () => {
|
|
220
|
+
test('it renders the placeholder in the empty cell', () => {
|
|
221
|
+
const tableDataWithMissingCellContent = [
|
|
222
|
+
{ id: 1, color: null, flavor: 'vanilla' },
|
|
223
|
+
{ id: 2, color: 'blue', flavor: 'chocolate' },
|
|
224
|
+
{ id: 3, color: 'green', flavor: 'strawberry' },
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
render(
|
|
228
|
+
<Table
|
|
229
|
+
columns={columnConfig}
|
|
230
|
+
rows={tableDataWithMissingCellContent}
|
|
231
|
+
emptyCellPlaceholder="--"
|
|
232
|
+
rowKey="id"
|
|
233
|
+
/>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const placeholder = screen.getByText('--');
|
|
237
|
+
|
|
238
|
+
expect(placeholder).toBeInTheDocument();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('Custom width in column config', () => {
|
|
243
|
+
test('it renders columns with fixed width based on prop.', () => {
|
|
244
|
+
render(
|
|
245
|
+
<Table
|
|
246
|
+
columns={[...columnConfig.map((col) => ({ ...col, width: 100 }))]}
|
|
247
|
+
rows={tableData}
|
|
248
|
+
rowKey="id"
|
|
249
|
+
/>
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const tableHeaderCell = screen.getByText('Flavor').closest('th');
|
|
253
|
+
expect(tableHeaderCell).toHaveStyle({ width: '100px' });
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('Column with no corresponding data key', () => {
|
|
258
|
+
test('it renders a column despite not matching a data key in the rows collection', () => {
|
|
259
|
+
const columnConfigNoKey = [
|
|
260
|
+
{ heading: 'ID', dataKey: 'id', isSortable: true },
|
|
261
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
262
|
+
{ heading: 'Flavor', isSortable: true },
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
render(
|
|
266
|
+
<Table columns={columnConfigNoKey} rows={tableData} rowKey="id" />
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const tableHeaderCell = screen.getByText('Flavor');
|
|
270
|
+
expect(tableHeaderCell).toBeInTheDocument();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('Column with render method', () => {
|
|
275
|
+
test('it renders a column with a custom render method', () => {
|
|
276
|
+
const columnConfigRender = [
|
|
277
|
+
{ heading: 'ID', dataKey: 'id', isSortable: true },
|
|
278
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
279
|
+
{
|
|
280
|
+
heading: 'Flavor',
|
|
281
|
+
isSortable: true,
|
|
282
|
+
render: () => <button type="submit">Submit</button>,
|
|
283
|
+
},
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
render(
|
|
287
|
+
<Table columns={columnConfigRender} rows={tableData} rowKey="id" />
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const submitButtons = screen.getAllByText('Submit');
|
|
291
|
+
expect(submitButtons).toHaveLength(tableData.length);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('Column with render method based on row', () => {
|
|
296
|
+
test('it renders a column with a custom render method', () => {
|
|
297
|
+
const columnConfigRender = [
|
|
298
|
+
{ heading: 'ID', dataKey: 'id', isSortable: true },
|
|
299
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
300
|
+
{
|
|
301
|
+
heading: 'Flavor',
|
|
302
|
+
dataKey: 'flavor',
|
|
303
|
+
render: (cell: any) => <button type="submit">{cell}</button>,
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
render(
|
|
308
|
+
<Table columns={columnConfigRender} rows={tableData} rowKey="id" />
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const submitButton = screen.getByText('vanilla');
|
|
312
|
+
expect(submitButton).toBeInTheDocument();
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('Text alignment of columns', () => {
|
|
317
|
+
test('it renders column cells with text aligned based on global align prop', () => {
|
|
318
|
+
const { rerender } = render(
|
|
319
|
+
<Table
|
|
320
|
+
columns={columnConfig}
|
|
321
|
+
rows={tableData}
|
|
322
|
+
rowKey="id"
|
|
323
|
+
align="right"
|
|
324
|
+
/>
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const cellsRight = screen.queryAllByRole('cell');
|
|
328
|
+
cellsRight.forEach((cell) => {
|
|
329
|
+
expect(cell).toHaveClass('align-right');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
rerender(
|
|
333
|
+
<Table
|
|
334
|
+
columns={columnConfig}
|
|
335
|
+
rows={tableData}
|
|
336
|
+
rowKey="id"
|
|
337
|
+
align="center"
|
|
338
|
+
/>
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const cellsCenter = screen.queryAllByRole('cell');
|
|
342
|
+
cellsCenter.forEach((cell) => {
|
|
343
|
+
expect(cell).toHaveClass('align-center');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('It renders column cells with alignment based on column align prop', () => {
|
|
348
|
+
const columnConfigWithAlign = [
|
|
349
|
+
{ heading: 'ID', dataKey: 'id' },
|
|
350
|
+
{ heading: 'Color', dataKey: 'color', align: 'left' as const },
|
|
351
|
+
{ heading: 'Flavor', dataKey: 'flavor', align: 'right' as const },
|
|
352
|
+
];
|
|
353
|
+
|
|
354
|
+
const tableDataAlign = [
|
|
355
|
+
{ id: 1, color: 'red', flavor: 'vanilla' },
|
|
356
|
+
{ id: 2, color: 'blue', flavor: 'chocolate' },
|
|
357
|
+
{ id: 3, color: 'green', flavor: 'strawberry' },
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
render(
|
|
361
|
+
<Table
|
|
362
|
+
columns={columnConfigWithAlign}
|
|
363
|
+
rows={tableDataAlign}
|
|
364
|
+
rowKey="id"
|
|
365
|
+
align="center"
|
|
366
|
+
/>
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
const cells = screen.queryAllByRole('cell');
|
|
370
|
+
// Checking cell classes based on where they are in the table.
|
|
371
|
+
cells.forEach((cell, index) => {
|
|
372
|
+
// First column
|
|
373
|
+
if (index === 0 || index === 3 || index === 6) {
|
|
374
|
+
expect(cell).toHaveClass('align-center');
|
|
375
|
+
}
|
|
376
|
+
// Second Column
|
|
377
|
+
if (index === 1 || index === 4 || index === 7) {
|
|
378
|
+
expect(cell).not.toHaveClass('align-center');
|
|
379
|
+
expect(cell).not.toHaveClass('align-right');
|
|
380
|
+
}
|
|
381
|
+
// Third Column
|
|
382
|
+
if (index === 2 || index === 5 || index === 8) {
|
|
383
|
+
expect(cell).toHaveClass('align-right');
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe('Custom cell classes', () => {
|
|
390
|
+
test('It renders columns with classes passed in the column config', () => {
|
|
391
|
+
const columnConfigClasses = [
|
|
392
|
+
{ heading: 'ID', dataKey: 'id', headerClassName: 'header-class' },
|
|
393
|
+
{ heading: 'Color', dataKey: 'color', cellClassName: 'cell-class' },
|
|
394
|
+
{ heading: 'Flavor', dataKey: 'flavor', align: 'right' as const },
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
render(
|
|
398
|
+
<Table columns={columnConfigClasses} rows={tableData} rowKey="id" />
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const headerCells = screen.queryAllByRole('columnheader');
|
|
402
|
+
expect(headerCells[0]).toHaveClass('header-class'); // Header(th) for first column.
|
|
403
|
+
|
|
404
|
+
const cells = screen.queryAllByRole('cell');
|
|
405
|
+
// Checking cell classes based on where they are in the table.
|
|
406
|
+
cells.forEach((cell, index) => {
|
|
407
|
+
// Second Column
|
|
408
|
+
if (index === 1 || index === 4 || index === 7) {
|
|
409
|
+
expect(cell).toHaveClass('cell-class');
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
describe('Sticky Column', () => {
|
|
416
|
+
test('if a column is sticky, it renders the row header elements', () => {
|
|
417
|
+
const columnConfigStickyColumn = [
|
|
418
|
+
{ heading: 'ID', dataKey: 'id', sticky: 'left' as const },
|
|
419
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
420
|
+
{ heading: 'Flavor', dataKey: 'flavor' },
|
|
421
|
+
];
|
|
422
|
+
|
|
423
|
+
render(
|
|
424
|
+
<Table
|
|
425
|
+
columns={columnConfigStickyColumn}
|
|
426
|
+
rows={tableData}
|
|
427
|
+
rowKey="id"
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
const rowHeaders = screen.getAllByRole('rowheader');
|
|
432
|
+
|
|
433
|
+
expect(rowHeaders).toHaveLength(3);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Column, Row, EventWithColumnKey } from '../../types';
|
|
4
|
+
import { Spinner } from '../Spinner/Spinner';
|
|
5
|
+
import styles from './Table.module.scss';
|
|
6
|
+
import { TableBody } from './TableBody/TableBody';
|
|
7
|
+
import { TableHead } from './TableHead/TableHead';
|
|
8
|
+
|
|
9
|
+
export interface TableProps {
|
|
10
|
+
/**
|
|
11
|
+
* Columns for the table. See Column definition below for details.
|
|
12
|
+
*/
|
|
13
|
+
columns: Column[];
|
|
14
|
+
/**
|
|
15
|
+
* The data rows to be displayed
|
|
16
|
+
*/
|
|
17
|
+
rows: Row[];
|
|
18
|
+
/**
|
|
19
|
+
* Key that represents a unique value for a row. This is necessary in
|
|
20
|
+
* order to supply React with a node key on each row.
|
|
21
|
+
*/
|
|
22
|
+
rowKey: string;
|
|
23
|
+
/**
|
|
24
|
+
* Additional classes to add.
|
|
25
|
+
*/
|
|
26
|
+
className?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Text alignment for all table cells. Can be superseded by passing the same prop into the `Column` object
|
|
29
|
+
* for a specific column.
|
|
30
|
+
*/
|
|
31
|
+
align?: 'left' | 'right' | 'center';
|
|
32
|
+
/**
|
|
33
|
+
* Global placeholder for empty cells. Can be overwritten by setting the same attribute
|
|
34
|
+
* in the `Column` config.
|
|
35
|
+
*/
|
|
36
|
+
emptyCellPlaceholder?: string | number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Enable a hover state on table rows.
|
|
39
|
+
*/
|
|
40
|
+
hoverableRows?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Remove borders around table, thead, tbody, td, etc.
|
|
43
|
+
*/
|
|
44
|
+
isBorderless?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Make Table more compact by cutting cell padding in half.
|
|
47
|
+
*/
|
|
48
|
+
isCompact?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* If table scrolls vertically, header will remain stuck to the top of the table, and not scroll away.
|
|
51
|
+
*/
|
|
52
|
+
hasStickyHeader?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Set to true if data is loading.
|
|
55
|
+
*/
|
|
56
|
+
isLoading?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Set the maximum width and height and enable scrolling within the container when the table grows
|
|
59
|
+
* past those values. Useful for when we want to render a large table but not force the parent container
|
|
60
|
+
* to grow and instead make the user scroll. Set values to boolean `true` to enable `overflow: scroll` on the table
|
|
61
|
+
* without specifying a width/height
|
|
62
|
+
*/
|
|
63
|
+
isScrollable?: {
|
|
64
|
+
x?: boolean;
|
|
65
|
+
y?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Adds zebra-striping to any table row within the table body.
|
|
69
|
+
*/
|
|
70
|
+
isStriped?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Callback function to fire on sorting one of the table headers.
|
|
73
|
+
*/
|
|
74
|
+
onSort?: (event: EventWithColumnKey) => void;
|
|
75
|
+
/**
|
|
76
|
+
* The key of the sorted column and its sort direction.
|
|
77
|
+
*/
|
|
78
|
+
sortedColumn?: {
|
|
79
|
+
dataKey: string | undefined;
|
|
80
|
+
sortDirection: 'none' | 'ascending' | 'descending' | undefined;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Control the `table-layout` css property for the table.
|
|
84
|
+
*/
|
|
85
|
+
useFixedTableLayout?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Truncate overflow inside column based on column width. Can be overwritten on specific columns,
|
|
88
|
+
* by passing `truncateOverflow` value on a specific Column
|
|
89
|
+
*/
|
|
90
|
+
truncateOverflow?: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const Table: FC<TableProps> = ({
|
|
94
|
+
columns,
|
|
95
|
+
rows,
|
|
96
|
+
rowKey,
|
|
97
|
+
align = 'left',
|
|
98
|
+
className = undefined,
|
|
99
|
+
emptyCellPlaceholder = undefined,
|
|
100
|
+
hoverableRows = false,
|
|
101
|
+
isBorderless = false,
|
|
102
|
+
isCompact = false,
|
|
103
|
+
hasStickyHeader = false,
|
|
104
|
+
isLoading = false,
|
|
105
|
+
isScrollable = undefined,
|
|
106
|
+
isStriped = false,
|
|
107
|
+
onSort = undefined,
|
|
108
|
+
sortedColumn = undefined,
|
|
109
|
+
useFixedTableLayout = false,
|
|
110
|
+
truncateOverflow = false,
|
|
111
|
+
}) => {
|
|
112
|
+
const containerClasses = classNames(styles.container, {
|
|
113
|
+
[styles['full-height']]: !!isScrollable?.y,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const scrollContainerClasses = classNames(
|
|
117
|
+
styles['scroll-container'],
|
|
118
|
+
{
|
|
119
|
+
[styles.scrollable]: !!isScrollable?.x || !!isScrollable?.y,
|
|
120
|
+
[styles['scrollable-x']]: !!isScrollable?.x,
|
|
121
|
+
[styles['scrollable-y']]: !!isScrollable?.y,
|
|
122
|
+
},
|
|
123
|
+
className
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const tableClasses = classNames(styles.table, {
|
|
127
|
+
[styles.fixed]: useFixedTableLayout,
|
|
128
|
+
[styles.striped]: isStriped,
|
|
129
|
+
[styles.borderless]: isBorderless,
|
|
130
|
+
[styles.compact]: isCompact,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className={containerClasses}>
|
|
135
|
+
{isLoading && (
|
|
136
|
+
<div className={styles['loading-mask']}>
|
|
137
|
+
<Spinner size="xl" />
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
<div
|
|
141
|
+
className={scrollContainerClasses}
|
|
142
|
+
data-testid="tableContainerDiv-testid"
|
|
143
|
+
>
|
|
144
|
+
<table className={tableClasses}>
|
|
145
|
+
<TableHead
|
|
146
|
+
columns={columns}
|
|
147
|
+
align={align}
|
|
148
|
+
onSort={onSort}
|
|
149
|
+
isBorderless={isBorderless}
|
|
150
|
+
isCompact={isCompact}
|
|
151
|
+
sortedColumn={sortedColumn}
|
|
152
|
+
truncateOverflow={truncateOverflow}
|
|
153
|
+
hasStickyHeader={hasStickyHeader}
|
|
154
|
+
/>
|
|
155
|
+
<TableBody
|
|
156
|
+
rows={rows}
|
|
157
|
+
columns={columns}
|
|
158
|
+
rowKey={rowKey}
|
|
159
|
+
align={align}
|
|
160
|
+
isStriped={isStriped}
|
|
161
|
+
emptyCellPlaceholder={emptyCellPlaceholder}
|
|
162
|
+
hoverableRows={hoverableRows}
|
|
163
|
+
truncateOverflow={truncateOverflow}
|
|
164
|
+
isBorderless={isBorderless}
|
|
165
|
+
isCompact={isCompact}
|
|
166
|
+
/>
|
|
167
|
+
</table>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.table-body {
|
|
2
|
+
&.striped {
|
|
3
|
+
tr:nth-of-type(odd) {
|
|
4
|
+
th,
|
|
5
|
+
td {
|
|
6
|
+
background-color: var(--color-background-secondary);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&.hover {
|
|
12
|
+
tr:hover {
|
|
13
|
+
th,
|
|
14
|
+
td {
|
|
15
|
+
background-color: var(--color-background-secondary);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { TableBody } from './TableBody';
|
|
4
|
+
|
|
5
|
+
const columns = [
|
|
6
|
+
{ heading: 'ID', dataKey: 'id' },
|
|
7
|
+
{ heading: 'Color', dataKey: 'color' },
|
|
8
|
+
{ heading: 'Flavor', dataKey: 'flavor' },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const rows = [
|
|
12
|
+
{ id: 1, color: 'red', flavor: 'vanilla' },
|
|
13
|
+
{ id: 2, color: 'blue', flavor: 'chocolate' },
|
|
14
|
+
{ id: 3, color: 'green', flavor: 'strawberry' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
describe('TableBody', () => {
|
|
18
|
+
test("It renders with striped rows if passed 'isStriped' prop", () => {
|
|
19
|
+
render(<TableBody columns={columns} rows={rows} rowKey="id" isStriped />);
|
|
20
|
+
|
|
21
|
+
const tableBody = screen.getByRole('rowgroup');
|
|
22
|
+
expect(tableBody).toHaveClass('striped');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('It renders with a custom class when passed as a prop', () => {
|
|
26
|
+
render(
|
|
27
|
+
<TableBody
|
|
28
|
+
columns={columns}
|
|
29
|
+
rows={rows}
|
|
30
|
+
rowKey="id"
|
|
31
|
+
className="my-custom-class"
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const tableBody = screen.getByRole('rowgroup');
|
|
36
|
+
expect(tableBody).toHaveClass('my-custom-class');
|
|
37
|
+
});
|
|
38
|
+
});
|