@onewelcome/react-lib-components 0.1.1-alpha → 0.1.4-alpha

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.
Files changed (180) hide show
  1. package/README.md +16 -1
  2. package/dist/Breadcrumbs/Breadcrumbs.d.ts +3 -3
  3. package/dist/Button/BaseButton.d.ts +3 -4
  4. package/dist/Button/Button.d.ts +3 -4
  5. package/dist/Button/IconButton.d.ts +3 -4
  6. package/dist/ContextMenu/ContextMenu.d.ts +3 -3
  7. package/dist/Form/Checkbox/Checkbox.d.ts +5 -5
  8. package/dist/Form/Fieldset/Fieldset.d.ts +4 -4
  9. package/dist/Form/FormControl/FormControl.d.ts +5 -5
  10. package/dist/Form/FormGroup/FormGroup.d.ts +4 -4
  11. package/dist/Form/FormHelperText/FormHelperText.d.ts +4 -5
  12. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +8 -12
  13. package/dist/Form/Input/Input.d.ts +7 -6
  14. package/dist/Form/Label/Label.d.ts +4 -5
  15. package/dist/Form/Radio/Radio.d.ts +5 -5
  16. package/dist/Form/Select/Option.d.ts +3 -4
  17. package/dist/Form/Select/Select.d.ts +4 -4
  18. package/dist/Form/Textarea/Textarea.d.ts +9 -5
  19. package/dist/Form/Toggle/Toggle.d.ts +3 -3
  20. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +4 -3
  21. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +5 -5
  22. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +4 -4
  23. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +7 -4
  24. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +3 -3
  25. package/dist/Form/Wrapper/Wrapper/Wrapper.d.ts +6 -6
  26. package/dist/Form/form.interfaces.d.ts +4 -3
  27. package/dist/Icon/Icon.d.ts +4 -4
  28. package/dist/Link/Link.d.ts +4 -6
  29. package/dist/Notifications/BaseModal/BaseModal.d.ts +3 -4
  30. package/dist/Notifications/BaseModal/BaseModalActions/BaseModalActions.d.ts +3 -3
  31. package/dist/Notifications/BaseModal/BaseModalContent/BaseModalContent.d.ts +3 -3
  32. package/dist/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +3 -3
  33. package/dist/Notifications/Dialog/Dialog.d.ts +3 -3
  34. package/dist/Notifications/Dialog/DialogActions/DialogActions.d.ts +3 -3
  35. package/dist/Notifications/Dialog/DialogTitle/DialogTitle.d.ts +3 -3
  36. package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +5 -4
  37. package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +3 -1
  38. package/dist/Pagination/Pagination.d.ts +19 -0
  39. package/dist/Popover/Popover.d.ts +3 -3
  40. package/dist/Tabs/Tab.d.ts +11 -0
  41. package/dist/Tabs/TabButton.d.ts +10 -0
  42. package/dist/Tabs/TabPanel.d.ts +8 -0
  43. package/dist/Tabs/Tabs.d.ts +9 -0
  44. package/dist/TextEllipsis/TextEllipsis.d.ts +6 -0
  45. package/dist/Tiles/Tile.d.ts +3 -3
  46. package/dist/Tiles/Tiles.d.ts +3 -3
  47. package/dist/Tooltip/Tooltip.d.ts +3 -3
  48. package/dist/Typography/Typography.d.ts +6 -4
  49. package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +3 -3
  50. package/dist/Wizard/WizardSteps/WizardSteps.d.ts +3 -3
  51. package/dist/_BaseStyling_/BaseStyling.d.ts +9 -0
  52. package/dist/hooks/useRepeater.d.ts +10 -0
  53. package/dist/hooks/useSpacing.d.ts +2 -2
  54. package/dist/hooks/useWrapper.d.ts +1 -1
  55. package/dist/index.d.ts +6 -0
  56. package/dist/interfaces.d.ts +2 -11
  57. package/dist/react-lib-components.cjs.development.js +2395 -1696
  58. package/dist/react-lib-components.cjs.development.js.map +1 -1
  59. package/dist/react-lib-components.cjs.production.min.js +1 -1
  60. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  61. package/dist/react-lib-components.esm.js +2391 -1698
  62. package/dist/react-lib-components.esm.js.map +1 -1
  63. package/dist/util/helper.d.ts +6 -1
  64. package/package.json +30 -24
  65. package/src/Breadcrumbs/Breadcrumbs.tsx +39 -37
  66. package/src/Button/BaseButton.test.tsx +65 -19
  67. package/src/Button/BaseButton.tsx +2 -3
  68. package/src/Button/Button.test.tsx +63 -17
  69. package/src/Button/Button.tsx +15 -4
  70. package/src/Button/IconButton.test.tsx +57 -22
  71. package/src/Button/IconButton.tsx +14 -9
  72. package/src/ContextMenu/ContextMenu.test.tsx +27 -1
  73. package/src/ContextMenu/ContextMenu.tsx +70 -65
  74. package/src/Form/Checkbox/Checkbox.test.tsx +28 -2
  75. package/src/Form/Checkbox/Checkbox.tsx +132 -122
  76. package/src/Form/Fieldset/Fieldset.test.tsx +28 -2
  77. package/src/Form/Fieldset/Fieldset.tsx +96 -50
  78. package/src/Form/FormControl/FormControl.test.tsx +27 -1
  79. package/src/Form/FormControl/FormControl.tsx +36 -39
  80. package/src/Form/FormGroup/FormGroup.test.tsx +51 -1
  81. package/src/Form/FormGroup/FormGroup.tsx +64 -58
  82. package/src/Form/FormHelperText/FormHelperText.test.tsx +27 -1
  83. package/src/Form/FormHelperText/FormHelperText.tsx +20 -16
  84. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +78 -0
  85. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +61 -55
  86. package/src/Form/Input/Input.module.scss +34 -15
  87. package/src/Form/Input/Input.test.tsx +27 -1
  88. package/src/Form/Input/Input.tsx +88 -47
  89. package/src/Form/Label/Label.test.tsx +27 -1
  90. package/src/Form/Label/Label.tsx +18 -14
  91. package/src/Form/Radio/Radio.test.tsx +28 -2
  92. package/src/Form/Radio/Radio.tsx +98 -90
  93. package/src/Form/Select/Option.test.tsx +27 -1
  94. package/src/Form/Select/Option.tsx +49 -42
  95. package/src/Form/Select/Select.module.scss +5 -1
  96. package/src/Form/Select/Select.test.tsx +224 -30
  97. package/src/Form/Select/Select.tsx +248 -182
  98. package/src/Form/Textarea/Textarea.module.scss +2 -1
  99. package/src/Form/Textarea/Textarea.test.tsx +28 -2
  100. package/src/Form/Textarea/Textarea.tsx +44 -29
  101. package/src/Form/Toggle/Toggle.module.scss +9 -0
  102. package/src/Form/Toggle/Toggle.test.tsx +27 -1
  103. package/src/Form/Toggle/Toggle.tsx +25 -12
  104. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +27 -1
  105. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +45 -48
  106. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +17 -1
  107. package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +89 -1
  108. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +134 -74
  109. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +64 -59
  110. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +1 -1
  111. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +43 -1
  112. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +54 -44
  113. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +5 -7
  114. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +43 -1
  115. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +100 -85
  116. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +1 -1
  117. package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +27 -1
  118. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +76 -71
  119. package/src/Form/form.interfaces.ts +4 -3
  120. package/src/Icon/Icon.module.scss +4 -0
  121. package/src/Icon/Icon.test.tsx +30 -2
  122. package/src/Icon/Icon.tsx +5 -5
  123. package/src/Link/Link.test.tsx +27 -1
  124. package/src/Link/Link.tsx +10 -7
  125. package/src/Notifications/BaseModal/BaseModal.test.tsx +27 -1
  126. package/src/Notifications/BaseModal/BaseModal.tsx +59 -54
  127. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +26 -1
  128. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +11 -9
  129. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.test.tsx +27 -1
  130. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +27 -26
  131. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.test.tsx +29 -1
  132. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +18 -16
  133. package/src/Notifications/Dialog/Dialog.test.tsx +39 -1
  134. package/src/Notifications/Dialog/Dialog.tsx +84 -78
  135. package/src/Notifications/Dialog/DialogActions/DialogActions.test.tsx +27 -1
  136. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +15 -12
  137. package/src/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +28 -2
  138. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +13 -11
  139. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +41 -1
  140. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +43 -36
  141. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +52 -1
  142. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +8 -3
  143. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +1 -1
  144. package/src/Pagination/Pagination.module.scss +120 -0
  145. package/src/Pagination/Pagination.test.tsx +176 -0
  146. package/src/Pagination/Pagination.tsx +205 -0
  147. package/src/Popover/Popover.tsx +3 -3
  148. package/src/Tabs/Tab.test.tsx +71 -0
  149. package/src/Tabs/Tab.tsx +17 -0
  150. package/src/Tabs/TabButton.module.scss +36 -0
  151. package/src/Tabs/TabButton.test.tsx +77 -0
  152. package/src/Tabs/TabButton.tsx +58 -0
  153. package/src/Tabs/TabPanel.module.scss +7 -0
  154. package/src/Tabs/TabPanel.test.tsx +76 -0
  155. package/src/Tabs/TabPanel.tsx +27 -0
  156. package/src/Tabs/Tabs.module.scss +41 -0
  157. package/src/Tabs/Tabs.test.tsx +268 -0
  158. package/src/Tabs/Tabs.tsx +149 -0
  159. package/src/TextEllipsis/TextEllipsis.module.scss +18 -0
  160. package/src/TextEllipsis/TextEllipsis.test.tsx +80 -0
  161. package/src/TextEllipsis/TextEllipsis.tsx +55 -0
  162. package/src/Tiles/Tile.test.tsx +27 -1
  163. package/src/Tiles/Tile.tsx +59 -62
  164. package/src/Tiles/Tiles.test.tsx +27 -1
  165. package/src/Tiles/Tiles.tsx +42 -39
  166. package/src/Tooltip/Tooltip.test.tsx +27 -1
  167. package/src/Tooltip/Tooltip.tsx +104 -92
  168. package/src/Typography/Typography.test.tsx +27 -1
  169. package/src/Typography/Typography.tsx +66 -68
  170. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +67 -62
  171. package/src/Wizard/WizardSteps/WizardSteps.tsx +24 -21
  172. package/src/_BaseStyling_/BaseStyling.tsx +19 -1
  173. package/src/hooks/useRepeater.test.tsx +139 -0
  174. package/src/hooks/useRepeater.ts +34 -0
  175. package/src/hooks/useSpacing.ts +1 -1
  176. package/src/hooks/useWrapper.ts +7 -2
  177. package/src/index.ts +15 -1
  178. package/src/interfaces.ts +2 -12
  179. package/src/util/helper.test.tsx +38 -1
  180. package/src/util/helper.tsx +21 -0
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ export interface Props {
4
+ title: string;
5
+ children?: React.ReactNode;
6
+ selected?: boolean;
7
+ focussed?: boolean;
8
+ buttonRef?: React.RefObject<HTMLButtonElement>;
9
+ panelRef?: React.RefObject<HTMLDivElement>;
10
+ onTabButtonClick?: () => void;
11
+ }
12
+
13
+ export const Tab = (args: Props) => {
14
+ return (
15
+ <div {...args}>{`A <Tab /> component should only be used inside the <Tabs /> component.`}</div>
16
+ );
17
+ };
@@ -0,0 +1,36 @@
1
+ .tabbutton {
2
+ border: 0;
3
+ border-radius: 0;
4
+ font-size: var(--font-size);
5
+ line-height: 1.5;
6
+ margin: 0;
7
+ padding: 0.25rem 0 1rem 0;
8
+ min-height: 2.5rem;
9
+ cursor: pointer;
10
+ font-family: var(--font-family);
11
+ background-color: transparent;
12
+ white-space: nowrap;
13
+ position: relative;
14
+ font-weight: bold;
15
+ color: transparent;
16
+
17
+ &.focussed:not(:focus-visible) {
18
+ outline: none;
19
+ }
20
+
21
+ span {
22
+ color: var(--tab-text-color);
23
+ font-weight: normal;
24
+ position: absolute;
25
+ left: 0;
26
+ }
27
+
28
+ &.selected {
29
+ color: var(--tab-text-color);
30
+
31
+ span {
32
+ color: transparent;
33
+ visibility: hidden;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import { Props, TabButton } from './TabButton';
3
+ import { render } from '@testing-library/react';
4
+
5
+ const defaultParams: Props = {};
6
+
7
+ const createTabButton = (params?: (defaultParams: Props) => Props) => {
8
+ let parameters: Props = defaultParams;
9
+ if (params) {
10
+ parameters = params(defaultParams);
11
+ }
12
+ const queries = render(
13
+ <TabButton {...parameters} data-testid="tabbutton">
14
+ tab content
15
+ </TabButton>
16
+ );
17
+ const tabButton = queries.getByTestId('tabbutton');
18
+
19
+ return {
20
+ ...queries,
21
+ tabButton,
22
+ };
23
+ };
24
+
25
+ describe('Tab should render', () => {
26
+ it('renders without crashing', () => {
27
+ const { tabButton } = createTabButton();
28
+
29
+ expect(tabButton).toBeTruthy();
30
+ });
31
+
32
+ it('should be selected when prop selected is passed', () => {
33
+ const { tabButton } = createTabButton((defaultParams) => ({
34
+ ...defaultParams,
35
+ selected: true,
36
+ }));
37
+
38
+ expect(tabButton).toHaveAttribute('aria-selected', 'true');
39
+ expect(tabButton).toHaveAttribute('tabIndex', '0');
40
+ });
41
+
42
+ it('should be focussed when prop focussed is passed', () => {
43
+ const { tabButton } = createTabButton((defaultParams) => ({
44
+ ...defaultParams,
45
+ focussed: true,
46
+ }));
47
+
48
+ expect(tabButton).toHaveClass('focussed');
49
+ });
50
+
51
+ it('should set accessibility attributes when prop tabPanelId is passed', () => {
52
+ const { tabButton } = createTabButton((defaultParams) => ({
53
+ ...defaultParams,
54
+ tabPanelId: 'fakeId',
55
+ }));
56
+
57
+ expect(tabButton).toHaveAttribute('aria-controls', 'fakeId');
58
+ });
59
+
60
+ it('should set id attribute when prop tabId is passed', () => {
61
+ const { tabButton } = createTabButton((defaultParams) => ({
62
+ ...defaultParams,
63
+ tabId: 'fakeId',
64
+ }));
65
+
66
+ expect(tabButton).toHaveAttribute('id', 'fakeId');
67
+ });
68
+
69
+ it('should set class when prop className is passed', () => {
70
+ const { tabButton } = createTabButton((defaultParams) => ({
71
+ ...defaultParams,
72
+ className: 'customclass',
73
+ }));
74
+
75
+ expect(tabButton).toHaveClass('customclass');
76
+ });
77
+ });
@@ -0,0 +1,58 @@
1
+ import React, { ComponentPropsWithRef, useEffect } from 'react';
2
+ import classes from './TabButton.module.scss';
3
+
4
+ export interface Props extends ComponentPropsWithRef<'button'> {
5
+ children?: string;
6
+ selected?: boolean;
7
+ focussed?: boolean;
8
+ tabId?: string;
9
+ tabPanelId?: string;
10
+ onTabButtonClick?: () => void;
11
+ }
12
+
13
+ export const TabButton = React.forwardRef<HTMLButtonElement, Props>(
14
+ (
15
+ {
16
+ children,
17
+ selected = false,
18
+ focussed = false,
19
+ tabId,
20
+ tabPanelId,
21
+ className,
22
+ onTabButtonClick,
23
+ ...rest
24
+ }: Props,
25
+ ref
26
+ ) => {
27
+ useEffect(() => {
28
+ if (focussed && ref) {
29
+ (ref as React.MutableRefObject<HTMLButtonElement>).current.focus();
30
+ }
31
+ }, [focussed]);
32
+
33
+ const classNames = [classes['tabbutton']];
34
+
35
+ selected && classNames.push(classes['selected']);
36
+ focussed && !selected && classNames.push(classes['focussed']);
37
+ className && classNames.push(className);
38
+
39
+ return (
40
+ <button
41
+ {...rest}
42
+ aria-selected={selected}
43
+ key={tabId}
44
+ className={classNames.join(' ')}
45
+ ref={ref}
46
+ role="tab"
47
+ tabIndex={selected ? 0 : -1}
48
+ type="button"
49
+ aria-controls={tabPanelId}
50
+ id={tabId}
51
+ onClick={onTabButtonClick}
52
+ >
53
+ <span aria-hidden="true">{children}</span>
54
+ {children}
55
+ </button>
56
+ );
57
+ }
58
+ );
@@ -0,0 +1,7 @@
1
+ .tabpanel {
2
+ display: none;
3
+
4
+ &.selected {
5
+ display: block;
6
+ }
7
+ }
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+ import { TabPanel, Props } from './TabPanel';
3
+ import { render } from '@testing-library/react';
4
+
5
+ const defaultParams: Props = {
6
+ tabId: 'fakeTabId',
7
+ tabPanelId: 'fakeTabPanelId',
8
+ children: [],
9
+ };
10
+
11
+ const createTabPanel = (params?: (defaultParams: Props) => Props) => {
12
+ let parameters: Props = defaultParams;
13
+ if (params) {
14
+ parameters = params(defaultParams);
15
+ }
16
+ const queries = render(
17
+ <TabPanel {...parameters} data-testid="tabpanel">
18
+ tabpanel content
19
+ </TabPanel>
20
+ );
21
+ const tabpanel = queries.getByTestId('tabpanel');
22
+
23
+ return {
24
+ ...queries,
25
+ tabpanel,
26
+ };
27
+ };
28
+
29
+ describe('TabPanel should render', () => {
30
+ it('renders without crashing', () => {
31
+ const { tabpanel } = createTabPanel();
32
+
33
+ expect(tabpanel).toBeTruthy();
34
+
35
+ expect(tabpanel).toHaveAttribute('aria-hidden', 'true');
36
+ expect(tabpanel).toHaveAttribute('hidden');
37
+ });
38
+
39
+ it('should be visible when prop selected is passed', () => {
40
+ const { tabpanel } = createTabPanel((defaultParams) => ({
41
+ ...defaultParams,
42
+ selected: true,
43
+ }));
44
+
45
+ expect(tabpanel).toHaveClass('selected');
46
+ expect(tabpanel).toHaveAttribute('aria-hidden', 'false');
47
+ expect(tabpanel).not.toHaveAttribute('hidden');
48
+ });
49
+
50
+ it('should set id when prop tabPanelId is passed', () => {
51
+ const { tabpanel } = createTabPanel((defaultParams) => ({
52
+ ...defaultParams,
53
+ tabPanelId: 'fakeId',
54
+ }));
55
+
56
+ expect(tabpanel).toHaveAttribute('id', 'fakeId');
57
+ });
58
+
59
+ it('should set accessibility attributes when prop tabId is passed', () => {
60
+ const { tabpanel } = createTabPanel((defaultParams) => ({
61
+ ...defaultParams,
62
+ tabId: 'fakeId',
63
+ }));
64
+
65
+ expect(tabpanel).toHaveAttribute('aria-labelledby', 'fakeId');
66
+ });
67
+
68
+ it('should set class when prop className is passed', () => {
69
+ const { tabpanel } = createTabPanel((defaultParams) => ({
70
+ ...defaultParams,
71
+ className: 'customclass',
72
+ }));
73
+
74
+ expect(tabpanel).toHaveClass('customclass');
75
+ });
76
+ });
@@ -0,0 +1,27 @@
1
+ import React, { ComponentPropsWithRef } from 'react';
2
+ import classes from './TabPanel.module.scss';
3
+
4
+ export interface Props extends ComponentPropsWithRef<'div'> {
5
+ children: React.ReactNode;
6
+ tabId: string;
7
+ tabPanelId: string;
8
+ selected?: boolean;
9
+ }
10
+
11
+ export const TabPanel = React.forwardRef<HTMLDivElement, Props>(
12
+ ({ children, tabId, tabPanelId, selected = false, className, ...rest }: Props, ref) => (
13
+ <div
14
+ {...rest}
15
+ ref={ref}
16
+ aria-labelledby={tabId}
17
+ className={`${classes['tabpanel']} ${selected ? classes['selected'] : ''} ${className ?? ''}`}
18
+ id={tabPanelId}
19
+ role="tabpanel"
20
+ tabIndex={0}
21
+ hidden={!selected || undefined}
22
+ aria-hidden={!selected}
23
+ >
24
+ {children}
25
+ </div>
26
+ )
27
+ );
@@ -0,0 +1,41 @@
1
+ $focus-width: 5px;
2
+
3
+ .tabs {
4
+ background-color: var(--tabs-background-color);
5
+ padding: 1rem 1.25rem;
6
+ border-radius: 0.5rem;
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 1rem;
10
+ }
11
+
12
+ .tablist {
13
+ display: flex;
14
+ flex-wrap: nowrap;
15
+ overflow-x: auto;
16
+ gap: 2rem;
17
+ position: relative;
18
+ margin: -$focus-width;
19
+ padding: $focus-width;
20
+ z-index: 0;
21
+ }
22
+
23
+ .indicator {
24
+ position: absolute;
25
+ height: 2px;
26
+ background-color: var(--color-primary);
27
+ transition-property: left, width;
28
+ transition-duration: 0.2s;
29
+ transition-timing-function: ease-in-out;
30
+ }
31
+
32
+ .tabdivider {
33
+ border-bottom-style: var(--tablist-border-style);
34
+ border-bottom-width: var(--tablist-border-width);
35
+ border-bottom-color: var(--tablist-border-color);
36
+ width: calc(100% - ($focus-width * 2));
37
+ position: absolute;
38
+ bottom: $focus-width;
39
+ left: $focus-width;
40
+ z-index: -1;
41
+ }
@@ -0,0 +1,268 @@
1
+ import React from 'react';
2
+ import { Tabs, Props } from './Tabs';
3
+ import { render } from '@testing-library/react';
4
+ import { TabButton as Tab } from './TabButton';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ const defaultParams: Props = {
8
+ 'aria-label': 'Entertainment',
9
+ children: [
10
+ <Tab title="Tab1">Tabpanel1 content</Tab>,
11
+ <Tab title="Tab2">Tabpanel2 content</Tab>,
12
+ <Tab title="Tab3">Tabpanel3 content</Tab>,
13
+ ],
14
+ };
15
+
16
+ const createTabs = (params?: (defaultParams: Props) => Props) => {
17
+ let parameters: Props = defaultParams;
18
+ if (params) {
19
+ parameters = params(defaultParams);
20
+ }
21
+ const queries = render(<Tabs {...parameters} data-testid="tabs"></Tabs>);
22
+ const tabs = queries.getByTestId('tabs');
23
+
24
+ return {
25
+ ...queries,
26
+ tabs,
27
+ };
28
+ };
29
+
30
+ describe('Tabs should render', () => {
31
+ it('renders without crashing', () => {
32
+ const { tabs } = createTabs();
33
+
34
+ expect(tabs).toBeDefined();
35
+ });
36
+
37
+ it('renders with properties passed', () => {
38
+ const { tabs } = createTabs((defaultParams) => ({
39
+ ...defaultParams,
40
+ className: 'testclass',
41
+ }));
42
+
43
+ expect(tabs).toHaveClass('testclass');
44
+ });
45
+
46
+ it('switches to tab and tabpanel when selected property is changed', () => {
47
+ const { tabs } = createTabs((defaultParams) => ({
48
+ ...defaultParams,
49
+ selected: 2,
50
+ }));
51
+
52
+ const tablist = tabs.firstChild as HTMLDivElement;
53
+ const tabpanels = tabs.lastChild as HTMLDivElement;
54
+ const tabButtons = tablist.querySelectorAll('.tabbutton');
55
+
56
+ const tab1 = tabButtons[0] as HTMLButtonElement;
57
+ const tab3 = tabButtons[2] as HTMLButtonElement;
58
+
59
+ const tabpanel1 = tabpanels.firstChild as HTMLDivElement;
60
+ const tabpanel3 = tabpanels.lastChild as HTMLDivElement;
61
+
62
+ expect(tab1).not.toHaveClass('selected');
63
+ expect(tab3).toHaveClass('selected');
64
+
65
+ expect(tabpanel1).not.toHaveClass('selected');
66
+ expect(tabpanel3).toHaveClass('selected');
67
+ });
68
+
69
+ it('switches to tab and tabpanel when tab is clicked', () => {
70
+ const { tabs } = createTabs((defaultParams) => ({
71
+ ...defaultParams,
72
+ }));
73
+
74
+ const tablist = tabs.firstChild as HTMLDivElement;
75
+ const tabpanels = tabs.lastChild as HTMLDivElement;
76
+ let tabButtons = tablist.querySelectorAll('.tabbutton');
77
+
78
+ let tab1 = tabButtons[0] as HTMLButtonElement;
79
+ let tab3 = tabButtons[2] as HTMLButtonElement;
80
+
81
+ let tabpanel1 = tabpanels.firstChild as HTMLDivElement;
82
+ let tabpanel3 = tabpanels.lastChild as HTMLDivElement;
83
+
84
+ expect(tab1).toHaveClass('selected');
85
+ expect(tab3).not.toHaveClass('selected');
86
+
87
+ expect(tabpanel1).toHaveClass('selected');
88
+ expect(tabpanel3).not.toHaveClass('selected');
89
+
90
+ userEvent.click(tab3);
91
+
92
+ tabButtons = tablist.querySelectorAll('.tabbutton');
93
+
94
+ tab1 = tabButtons[0] as HTMLButtonElement;
95
+ tab3 = tabButtons[2] as HTMLButtonElement;
96
+
97
+ tabpanel1 = tabpanels.firstChild as HTMLDivElement;
98
+ tabpanel3 = tabpanels.lastChild as HTMLDivElement;
99
+
100
+ expect(tab1).not.toHaveClass('selected');
101
+ expect(tab3).toHaveClass('selected');
102
+
103
+ expect(tabpanel1).not.toHaveClass('selected');
104
+ expect(tabpanel3).toHaveClass('selected');
105
+ });
106
+
107
+ it('triggers the onTabChange when switching tabs', () => {
108
+ const onTabChangeHandler = jest.fn();
109
+
110
+ const { tabs } = createTabs((defaultParams) => ({
111
+ ...defaultParams,
112
+ onTabChange: onTabChangeHandler,
113
+ }));
114
+
115
+ const tablist = tabs.firstChild as HTMLDivElement;
116
+ const tabButtons = tablist.querySelectorAll('.tabbutton');
117
+
118
+ let tab3 = tabButtons[2] as HTMLButtonElement;
119
+
120
+ userEvent.click(tab3);
121
+
122
+ expect(onTabChangeHandler).toHaveBeenCalled();
123
+ });
124
+ });
125
+
126
+ describe('Tabs should not render other children then tab components', () => {
127
+ it('renders no tabs', () => {
128
+ const queries = render(
129
+ <Tabs data-testid="tabs">
130
+ <div>wrong component 1</div>
131
+ <div>wrong component 2</div>
132
+ </Tabs>
133
+ );
134
+
135
+ const tabs = queries.getByTestId('tabs');
136
+
137
+ expect(tabs).toBeDefined();
138
+ expect(tabs).toBeEmptyDOMElement;
139
+ });
140
+ });
141
+
142
+ describe('Tabs should comply with accessibility rules', () => {
143
+ it('traverse through tabs with keyboard', async () => {
144
+ const { tabs } = createTabs();
145
+
146
+ const tablist = tabs.firstChild as HTMLDivElement;
147
+ const tabpanels = tabs.lastChild as HTMLDivElement;
148
+ let tabButtons = tablist.querySelectorAll('.tabbutton');
149
+
150
+ let tab1 = tabButtons[0] as HTMLButtonElement;
151
+ let tab3 = tabButtons[2] as HTMLButtonElement;
152
+
153
+ let tabpanel1 = tabpanels.firstChild as HTMLDivElement;
154
+ let tabpanel3 = tabpanels.lastChild as HTMLDivElement;
155
+
156
+ tab1.focus();
157
+
158
+ tabButtons = tablist.querySelectorAll('.tabbutton');
159
+ tab1 = tabButtons[0] as HTMLButtonElement;
160
+ tab3 = tabButtons[2] as HTMLButtonElement;
161
+ tabpanel1 = tabpanels.firstChild as HTMLDivElement;
162
+ tabpanel3 = tabpanels.lastChild as HTMLDivElement;
163
+
164
+ expect(tab1).toHaveFocus();
165
+ expect(tab1).toHaveClass('selected');
166
+ expect(tab1).not.toHaveClass('focussed');
167
+ expect(tab3).not.toHaveClass('selected');
168
+ expect(tab3).not.toHaveClass('focussed');
169
+ expect(tabpanel1).toHaveClass('selected');
170
+ expect(tabpanel3).not.toHaveClass('selected');
171
+
172
+ userEvent.keyboard('[ArrowLeft]');
173
+ userEvent.keyboard('[Space]');
174
+
175
+ tabButtons = tablist.querySelectorAll('.tabbutton');
176
+ tab1 = tabButtons[0] as HTMLButtonElement;
177
+ tab3 = tabButtons[2] as HTMLButtonElement;
178
+ tabpanel1 = tabpanels.firstChild as HTMLDivElement;
179
+ tabpanel3 = tabpanels.lastChild as HTMLDivElement;
180
+
181
+ expect(tab3).toHaveFocus();
182
+ expect(tab3).toHaveClass('selected');
183
+ expect(tab1).not.toHaveClass('selected');
184
+ expect(tab1).not.toHaveClass('focussed');
185
+ expect(tabpanel3).toHaveClass('selected');
186
+ expect(tabpanel1).not.toHaveClass('selected');
187
+
188
+ userEvent.keyboard('[ArrowRight]');
189
+ userEvent.keyboard('[Enter]');
190
+
191
+ tabButtons = tablist.querySelectorAll('.tabbutton');
192
+ tab1 = tabButtons[0] as HTMLButtonElement;
193
+ tab3 = tabButtons[2] as HTMLButtonElement;
194
+ tabpanel1 = tabpanels.firstChild as HTMLDivElement;
195
+ tabpanel3 = tabpanels.lastChild as HTMLDivElement;
196
+
197
+ expect(tab1).toHaveFocus();
198
+ expect(tab1).toHaveClass('selected');
199
+ expect(tab3).not.toHaveClass('selected');
200
+ expect(tab3).not.toHaveClass('focussed');
201
+ expect(tabpanel1).toHaveClass('selected');
202
+ expect(tabpanel3).not.toHaveClass('selected');
203
+
204
+ userEvent.keyboard('[End]');
205
+
206
+ tabButtons = tablist.querySelectorAll('.tabbutton');
207
+ tab1 = tabButtons[0] as HTMLButtonElement;
208
+ tab3 = tabButtons[2] as HTMLButtonElement;
209
+
210
+ expect(tab3).toHaveFocus();
211
+ expect(tab3).toHaveClass('focussed');
212
+ expect(tab1).not.toHaveClass('focussed');
213
+
214
+ userEvent.keyboard('[Home]');
215
+
216
+ tabButtons = tablist.querySelectorAll('.tabbutton');
217
+ tab1 = tabButtons[0] as HTMLButtonElement;
218
+ tab3 = tabButtons[2] as HTMLButtonElement;
219
+
220
+ expect(tab1).toHaveFocus();
221
+ expect(tab1).toHaveClass('selected');
222
+ expect(tab3).not.toHaveClass('focussed');
223
+
224
+ // should have no effect
225
+ userEvent.keyboard('a');
226
+
227
+ tabButtons = tablist.querySelectorAll('.tabbutton');
228
+ tab1 = tabButtons[0] as HTMLButtonElement;
229
+ tab3 = tabButtons[2] as HTMLButtonElement;
230
+
231
+ expect(tab1).toHaveFocus();
232
+ expect(tab1).toHaveClass('selected');
233
+ expect(tab3).not.toHaveClass('focussed');
234
+
235
+ userEvent.keyboard('[ArrowRight]');
236
+
237
+ tabButtons = tablist.querySelectorAll('.tabbutton');
238
+ tab1 = tabButtons[0] as HTMLButtonElement;
239
+ tab3 = tabButtons[2] as HTMLButtonElement;
240
+
241
+ expect(tab1).not.toHaveFocus();
242
+ expect(tab3).not.toHaveFocus();
243
+ expect(tab1).not.toHaveClass('focussed');
244
+ expect(tab3).not.toHaveClass('focussed');
245
+
246
+ userEvent.keyboard('[ArrowLeft]');
247
+
248
+ tabButtons = tablist.querySelectorAll('.tabbutton');
249
+ tab1 = tabButtons[0] as HTMLButtonElement;
250
+ tab3 = tabButtons[2] as HTMLButtonElement;
251
+
252
+ expect(tab1).toHaveFocus();
253
+ expect(tab1).toHaveClass('selected');
254
+ expect(tab3).not.toHaveClass('focussed');
255
+
256
+ userEvent.keyboard('[ArrowLeft]');
257
+
258
+ tabButtons = tablist.querySelectorAll('.tabbutton');
259
+ tab1 = tabButtons[0] as HTMLButtonElement;
260
+ tab3 = tabButtons[2] as HTMLButtonElement;
261
+
262
+ tab3.blur();
263
+
264
+ expect(tab1).not.toHaveFocus();
265
+ expect(tab3).not.toHaveFocus();
266
+ expect(tab1).toHaveClass('selected');
267
+ });
268
+ });