@onewelcome/react-lib-components 0.1.0-alpha → 0.1.3-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 (240) 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 +4 -5
  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 +9 -7
  9. package/dist/Form/FormControl/FormControl.d.ts +6 -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 +8 -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 +3 -5
  29. package/dist/Notifications/BaseModal/BaseModal.d.ts +17 -0
  30. package/dist/Notifications/BaseModal/BaseModalActions/BaseModalActions.d.ts +5 -0
  31. package/dist/Notifications/BaseModal/BaseModalContent/BaseModalContent.d.ts +8 -0
  32. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalContext.d.ts +0 -0
  33. package/dist/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +8 -0
  34. package/dist/Notifications/Dialog/Dialog.d.ts +19 -0
  35. package/dist/Notifications/Dialog/DialogActions/DialogActions.d.ts +6 -0
  36. package/dist/Notifications/Dialog/DialogTitle/DialogTitle.d.ts +6 -0
  37. package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +13 -0
  38. package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +13 -0
  39. package/dist/{Modal → Notifications/Modal}/Modal.d.ts +0 -0
  40. package/dist/{Modal → Notifications/Modal}/ModalActions/ModalActions.d.ts +0 -0
  41. package/dist/{Modal → Notifications/Modal}/ModalContent/ModalContent.d.ts +0 -0
  42. package/dist/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.d.ts +0 -0
  43. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.d.ts +0 -0
  44. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.d.ts +0 -0
  45. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.d.ts +1 -1
  46. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.d.ts +0 -0
  47. package/dist/{Snackbar → Notifications/Snackbar}/interfaces.d.ts +0 -0
  48. package/dist/{Snackbar → Notifications/Snackbar}/useSnackbar.d.ts +0 -0
  49. package/dist/Pagination/Pagination.d.ts +19 -0
  50. package/dist/Popover/Popover.d.ts +3 -3
  51. package/dist/Tabs/Tab.d.ts +11 -0
  52. package/dist/Tabs/TabButton.d.ts +10 -0
  53. package/dist/Tabs/TabPanel.d.ts +8 -0
  54. package/dist/Tabs/Tabs.d.ts +9 -0
  55. package/dist/TextEllipsis/TextEllipsis.d.ts +6 -0
  56. package/dist/Tiles/Tile.d.ts +6 -7
  57. package/dist/Tiles/Tiles.d.ts +3 -3
  58. package/dist/Tooltip/Tooltip.d.ts +3 -3
  59. package/dist/Typography/Typography.d.ts +6 -4
  60. package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +3 -3
  61. package/dist/Wizard/WizardSteps/WizardSteps.d.ts +3 -3
  62. package/dist/_BaseStyling_/BaseStyling.d.ts +9 -0
  63. package/dist/hooks/useRepeater.d.ts +10 -0
  64. package/dist/hooks/useSpacing.d.ts +2 -2
  65. package/dist/hooks/useWrapper.d.ts +1 -1
  66. package/dist/index.d.ts +12 -7
  67. package/dist/interfaces.d.ts +2 -11
  68. package/dist/react-lib-components.cjs.development.js +1861 -1287
  69. package/dist/react-lib-components.cjs.development.js.map +1 -1
  70. package/dist/react-lib-components.cjs.production.min.js +1 -1
  71. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  72. package/dist/react-lib-components.esm.js +1858 -1289
  73. package/dist/react-lib-components.esm.js.map +1 -1
  74. package/dist/util/helper.d.ts +6 -1
  75. package/package.json +30 -24
  76. package/src/Breadcrumbs/Breadcrumbs.tsx +39 -37
  77. package/src/Button/BaseButton.test.tsx +65 -19
  78. package/src/Button/BaseButton.tsx +2 -3
  79. package/src/Button/Button.test.tsx +63 -17
  80. package/src/Button/Button.tsx +15 -4
  81. package/src/Button/IconButton.test.tsx +57 -22
  82. package/src/Button/IconButton.tsx +21 -12
  83. package/src/ContextMenu/ContextMenu.test.tsx +27 -1
  84. package/src/ContextMenu/ContextMenu.tsx +70 -65
  85. package/src/Form/Checkbox/Checkbox.module.scss +4 -0
  86. package/src/Form/Checkbox/Checkbox.test.tsx +28 -2
  87. package/src/Form/Checkbox/Checkbox.tsx +132 -117
  88. package/src/Form/Fieldset/Fieldset.module.scss +11 -1
  89. package/src/Form/Fieldset/Fieldset.test.tsx +30 -4
  90. package/src/Form/Fieldset/Fieldset.tsx +101 -43
  91. package/src/Form/FormControl/FormControl.test.tsx +27 -1
  92. package/src/Form/FormControl/FormControl.tsx +37 -37
  93. package/src/Form/FormGroup/FormGroup.test.tsx +27 -1
  94. package/src/Form/FormGroup/FormGroup.tsx +64 -58
  95. package/src/Form/FormHelperText/FormHelperText.test.tsx +27 -1
  96. package/src/Form/FormHelperText/FormHelperText.tsx +20 -16
  97. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +78 -0
  98. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +61 -55
  99. package/src/Form/Input/Input.module.scss +34 -15
  100. package/src/Form/Input/Input.test.tsx +27 -1
  101. package/src/Form/Input/Input.tsx +88 -47
  102. package/src/Form/Label/Label.test.tsx +27 -1
  103. package/src/Form/Label/Label.tsx +18 -14
  104. package/src/Form/Radio/Radio.module.scss +4 -0
  105. package/src/Form/Radio/Radio.test.tsx +28 -2
  106. package/src/Form/Radio/Radio.tsx +98 -80
  107. package/src/Form/Select/Option.test.tsx +27 -1
  108. package/src/Form/Select/Option.tsx +49 -42
  109. package/src/Form/Select/Select.module.scss +5 -1
  110. package/src/Form/Select/Select.test.tsx +224 -30
  111. package/src/Form/Select/Select.tsx +248 -182
  112. package/src/Form/Textarea/Textarea.module.scss +2 -1
  113. package/src/Form/Textarea/Textarea.test.tsx +28 -2
  114. package/src/Form/Textarea/Textarea.tsx +44 -29
  115. package/src/Form/Toggle/Toggle.module.scss +9 -0
  116. package/src/Form/Toggle/Toggle.test.tsx +27 -1
  117. package/src/Form/Toggle/Toggle.tsx +25 -12
  118. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +28 -2
  119. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +45 -48
  120. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +17 -1
  121. package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +89 -1
  122. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +134 -74
  123. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.test.tsx +1 -1
  124. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +64 -59
  125. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +1 -1
  126. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +43 -1
  127. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +55 -44
  128. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +5 -7
  129. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +43 -1
  130. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +100 -85
  131. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +1 -1
  132. package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +27 -1
  133. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +76 -71
  134. package/src/Form/form.interfaces.ts +4 -3
  135. package/src/Icon/Icon.module.scss +4 -0
  136. package/src/Icon/Icon.test.tsx +30 -2
  137. package/src/Icon/Icon.tsx +5 -5
  138. package/src/Link/Link.test.tsx +27 -1
  139. package/src/Link/Link.tsx +4 -6
  140. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.module.scss +0 -0
  141. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.test.tsx +35 -16
  142. package/src/Notifications/BaseModal/BaseModal.tsx +105 -0
  143. package/src/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.module.scss +0 -0
  144. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +42 -0
  145. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +16 -0
  146. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.module.scss +0 -0
  147. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.test.tsx +27 -1
  148. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +36 -0
  149. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContext.ts +0 -0
  150. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.module.scss +0 -0
  151. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.test.tsx +29 -1
  152. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +30 -0
  153. package/src/{Dialog → Notifications/Dialog}/Dialog.module.scss +0 -0
  154. package/src/{Dialog → Notifications/Dialog}/Dialog.test.tsx +52 -17
  155. package/src/Notifications/Dialog/Dialog.tsx +113 -0
  156. package/src/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.module.scss +0 -0
  157. package/src/Notifications/Dialog/DialogActions/DialogActions.test.tsx +51 -0
  158. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +24 -0
  159. package/src/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.module.scss +0 -0
  160. package/src/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +44 -0
  161. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +20 -0
  162. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +95 -0
  163. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +55 -0
  164. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +162 -0
  165. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +61 -0
  166. package/src/{Modal → Notifications/Modal}/Modal.test.tsx +0 -0
  167. package/src/{Modal → Notifications/Modal}/Modal.tsx +0 -0
  168. package/src/{Modal → Notifications/Modal}/ModalActions/ModalActions.tsx +0 -0
  169. package/src/{Modal → Notifications/Modal}/ModalContent/ModalContent.tsx +0 -0
  170. package/src/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.tsx +0 -0
  171. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.module.scss +0 -0
  172. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.test.tsx +0 -0
  173. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.tsx +0 -0
  174. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.module.scss +1 -1
  175. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.test.tsx +0 -0
  176. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.tsx +6 -7
  177. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.test.tsx +0 -0
  178. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.tsx +2 -2
  179. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.tsx +0 -0
  180. package/src/{Snackbar → Notifications/Snackbar}/interfaces.ts +0 -0
  181. package/src/{Snackbar → Notifications/Snackbar}/useSnackbar.ts +0 -0
  182. package/src/Pagination/Pagination.module.scss +120 -0
  183. package/src/Pagination/Pagination.test.tsx +176 -0
  184. package/src/Pagination/Pagination.tsx +205 -0
  185. package/src/Popover/Popover.test.tsx +3 -3
  186. package/src/Popover/Popover.tsx +3 -3
  187. package/src/Tabs/Tab.test.tsx +71 -0
  188. package/src/Tabs/Tab.tsx +17 -0
  189. package/src/Tabs/TabButton.module.scss +36 -0
  190. package/src/Tabs/TabButton.test.tsx +77 -0
  191. package/src/Tabs/TabButton.tsx +58 -0
  192. package/src/Tabs/TabPanel.module.scss +7 -0
  193. package/src/Tabs/TabPanel.test.tsx +76 -0
  194. package/src/Tabs/TabPanel.tsx +27 -0
  195. package/src/Tabs/Tabs.module.scss +41 -0
  196. package/src/Tabs/Tabs.test.tsx +268 -0
  197. package/src/Tabs/Tabs.tsx +149 -0
  198. package/src/TextEllipsis/TextEllipsis.module.scss +18 -0
  199. package/src/TextEllipsis/TextEllipsis.test.tsx +80 -0
  200. package/src/TextEllipsis/TextEllipsis.tsx +55 -0
  201. package/src/Tiles/Tile.module.scss +1 -1
  202. package/src/Tiles/Tile.test.tsx +48 -12
  203. package/src/Tiles/Tile.tsx +68 -34
  204. package/src/Tiles/Tiles.test.tsx +38 -10
  205. package/src/Tiles/Tiles.tsx +42 -39
  206. package/src/Tooltip/Tooltip.test.tsx +27 -1
  207. package/src/Tooltip/Tooltip.tsx +104 -92
  208. package/src/Typography/Typography.test.tsx +27 -1
  209. package/src/Typography/Typography.tsx +66 -68
  210. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +67 -62
  211. package/src/Wizard/Wizard.tsx +2 -2
  212. package/src/Wizard/WizardActions/WizardActions.tsx +3 -3
  213. package/src/Wizard/WizardSteps/WizardSteps.tsx +24 -21
  214. package/src/_BaseStyling_/BaseStyling.tsx +19 -1
  215. package/src/hooks/usePosition.test.tsx +3 -3
  216. package/src/hooks/useRepeater.test.tsx +139 -0
  217. package/src/hooks/useRepeater.ts +34 -0
  218. package/src/hooks/useSpacing.ts +1 -1
  219. package/src/hooks/useWrapper.ts +7 -2
  220. package/src/index.ts +20 -8
  221. package/src/interfaces.ts +2 -12
  222. package/src/util/helper.test.tsx +38 -1
  223. package/src/util/helper.tsx +21 -0
  224. package/dist/BaseModal/BaseModal.d.ts +0 -16
  225. package/dist/BaseModal/BaseModalActions/BaseModalActions.d.ts +0 -5
  226. package/dist/BaseModal/BaseModalContent/BaseModalContent.d.ts +0 -8
  227. package/dist/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +0 -8
  228. package/dist/Dialog/Dialog.d.ts +0 -18
  229. package/dist/Dialog/DialogActions/DialogActions.d.ts +0 -6
  230. package/dist/Dialog/DialogTitle/DialogTitle.d.ts +0 -6
  231. package/src/BaseModal/BaseModal.tsx +0 -113
  232. package/src/BaseModal/BaseModalActions/BaseModalActions.test.tsx +0 -17
  233. package/src/BaseModal/BaseModalActions/BaseModalActions.tsx +0 -14
  234. package/src/BaseModal/BaseModalContent/BaseModalContent.tsx +0 -35
  235. package/src/BaseModal/BaseModalHeader/BaseModalHeader.tsx +0 -28
  236. package/src/Dialog/Dialog.tsx +0 -96
  237. package/src/Dialog/DialogActions/DialogActions.test.tsx +0 -25
  238. package/src/Dialog/DialogActions/DialogActions.tsx +0 -21
  239. package/src/Dialog/DialogTitle/DialogTitle.test.tsx +0 -18
  240. package/src/Dialog/DialogTitle/DialogTitle.tsx +0 -18
@@ -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
+ });
@@ -0,0 +1,149 @@
1
+ import React, {
2
+ ComponentPropsWithRef,
3
+ createRef,
4
+ MutableRefObject,
5
+ useEffect,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import { generateID } from '../util/helper';
10
+ import { Props as TabProps } from './Tab';
11
+ import { TabButton } from './TabButton';
12
+ import { TabPanel } from './TabPanel';
13
+ import classes from './Tabs.module.scss';
14
+
15
+ export interface Props extends ComponentPropsWithRef<'div'> {
16
+ children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[];
17
+ selected?: number;
18
+ 'aria-label'?: string;
19
+ onTabChange?: (index: number) => void;
20
+ }
21
+
22
+ export const Tabs = ({
23
+ children,
24
+ selected = 0,
25
+ 'aria-label': ariaLabel,
26
+ onTabChange,
27
+ className,
28
+ ...rest
29
+ }: Props) => {
30
+ const indicatorRef = useRef<HTMLDivElement>(null);
31
+ const [indicatorPosition, setIndicatorPosition] = useState({ left: 0, top: 0 });
32
+ const [indicatorWidth, setIndicatorWidth] = useState(0);
33
+
34
+ const [max] = useState(React.Children.count(children) - 1);
35
+ const min = 0;
36
+
37
+ const [selectedTab, setSelectedTab] = useState(Math.min(max, Math.max(min, selected)));
38
+ const [focussedTab, setFocussedTab] = useState(-1);
39
+ const [tabIds] = useState([...Array(max)].map(() => generateID()));
40
+ const [tabPanelIds] = useState([...Array(max)].map(() => generateID()));
41
+
42
+ useEffect(() => {
43
+ setSelectedTab(Math.min(max, Math.max(min, selected)));
44
+ setFocussedTab(-1);
45
+ }, [selected]);
46
+
47
+ useEffect(() => {
48
+ calculateIndicatorPosition();
49
+ }, [selectedTab]);
50
+
51
+ const calculateIndicatorPosition = () => {
52
+ const selectedTabButton = (
53
+ tabButtons[selectedTab].ref as MutableRefObject<HTMLButtonElement | null>
54
+ ).current as HTMLButtonElement;
55
+
56
+ setIndicatorPosition({
57
+ left: selectedTabButton.offsetLeft,
58
+ top:
59
+ selectedTabButton.offsetTop +
60
+ selectedTabButton.offsetHeight -
61
+ indicatorRef.current!.offsetHeight,
62
+ });
63
+ setIndicatorWidth(selectedTabButton.offsetWidth);
64
+ };
65
+
66
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
67
+ // do not show focus unless we came here before
68
+ let currentFocussedTab = focussedTab === -1 ? selectedTab : focussedTab;
69
+
70
+ switch (e.code) {
71
+ case 'ArrowLeft':
72
+ setFocussedTab(currentFocussedTab === min ? max : currentFocussedTab - 1);
73
+ break;
74
+ case 'ArrowRight':
75
+ setFocussedTab(currentFocussedTab === max ? min : currentFocussedTab + 1);
76
+ break;
77
+ case 'Home':
78
+ setFocussedTab(min);
79
+ break;
80
+ case 'End':
81
+ setFocussedTab(max);
82
+ break;
83
+ case 'Space':
84
+ case 'Enter':
85
+ activateTab(currentFocussedTab);
86
+ break;
87
+ }
88
+ };
89
+
90
+ const handleBlur = () => {
91
+ setFocussedTab(selectedTab);
92
+ };
93
+
94
+ const activateTab = (index: number) => {
95
+ setSelectedTab(index);
96
+ setFocussedTab(index);
97
+ onTabChange && onTabChange(index);
98
+ };
99
+
100
+ const tabButtons = React.Children.map(children, (child, index) =>
101
+ React.createElement(TabButton, {
102
+ ref: child.props.buttonRef || createRef<HTMLButtonElement>(),
103
+ key: generateID(),
104
+ tabId: tabIds[index],
105
+ tabPanelId: tabPanelIds[index],
106
+ selected: selectedTab === index,
107
+ focussed: focussedTab === index,
108
+ onTabButtonClick: () => activateTab(index),
109
+ children: child.props.title,
110
+ })
111
+ );
112
+
113
+ const tabPanels = React.Children.map(children, (child, index) =>
114
+ React.createElement(TabPanel, {
115
+ ref: child.props.panelRef || createRef<HTMLDivElement>(),
116
+ key: generateID(),
117
+ selected: selectedTab === index,
118
+ tabId: tabIds[index],
119
+ tabPanelId: tabPanelIds[index],
120
+ children: child.props.children,
121
+ })
122
+ );
123
+
124
+ return (
125
+ <div {...rest} className={`${classes['tabs']} ${className ?? ''}`}>
126
+ <div
127
+ role="tablist"
128
+ onKeyDown={handleKeyDown}
129
+ onBlur={handleBlur}
130
+ aria-label={ariaLabel}
131
+ className={classes['tablist']}
132
+ >
133
+ <div className={classes['tabdivider']} />
134
+ {tabButtons}
135
+ <div
136
+ className={classes['indicator']}
137
+ ref={indicatorRef}
138
+ aria-hidden="true"
139
+ tabIndex={-1}
140
+ style={{
141
+ width: indicatorWidth,
142
+ ...indicatorPosition,
143
+ }}
144
+ />
145
+ </div>
146
+ <div className={classes['tabpanels']}>{tabPanels}</div>
147
+ </div>
148
+ );
149
+ };
@@ -0,0 +1,18 @@
1
+ .text-ellipsis {
2
+ overflow: hidden;
3
+ text-overflow: ellipsis;
4
+ white-space: nowrap;
5
+ }
6
+
7
+ .popover {
8
+ max-width: min(75ch, 90%);
9
+ padding: 0.6875rem 0.6875rem 0.6875rem 0.875rem;
10
+ border-radius: 0.25rem;
11
+
12
+ box-shadow: 0rem 0.1875rem 0.375rem rgba(0, 0, 0, 0.29);
13
+ white-space: pre-wrap;
14
+
15
+ transition-property: opacity;
16
+ transform: scale(1);
17
+ z-index: 1;
18
+ }
@@ -0,0 +1,80 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import user from '@testing-library/user-event';
4
+ import { Props, TextEllipsis } from './TextEllipsis';
5
+
6
+ const defaultParams: Props = {};
7
+
8
+ const createTextEllipsis = (params?: (defaultParams: Props) => Props) => {
9
+ let parameters: Props = defaultParams;
10
+ if (params) {
11
+ parameters = params(defaultParams);
12
+ }
13
+ const queries = render(
14
+ <TextEllipsis {...parameters} data-testid="TextEllipsis">
15
+ content
16
+ </TextEllipsis>
17
+ );
18
+ const textEllipsis = queries.getByTestId('TextEllipsis');
19
+
20
+ return {
21
+ ...queries,
22
+ textEllipsis,
23
+ };
24
+ };
25
+
26
+ describe('TextEllipsis should render', () => {
27
+ it('renders without crashing', () => {
28
+ const { textEllipsis, getAllByText } = createTextEllipsis();
29
+
30
+ expect(textEllipsis).toBeDefined();
31
+ expect(getAllByText('content')).toHaveLength(2);
32
+ });
33
+
34
+ it('does not show popover when full text is visible', () => {
35
+ const { textEllipsis, getByRole } = createTextEllipsis();
36
+
37
+ expect(getByRole('tooltip', { hidden: true })).toHaveAttribute('data-hidden', 'true');
38
+ user.hover(textEllipsis);
39
+ expect(getByRole('tooltip', { hidden: true })).toHaveAttribute('data-hidden', 'true');
40
+ });
41
+
42
+ it('shows popover with full text when base text has ellipsis', () => {
43
+ const { textEllipsis, getByRole } = createTextEllipsis();
44
+
45
+ Object.defineProperty(textEllipsis, 'offsetWidth', { configurable: true, value: 1 });
46
+ Object.defineProperty(textEllipsis, 'scrollWidth', { configurable: true, value: 2 });
47
+
48
+ expect(getByRole('tooltip', { hidden: true })).toHaveAttribute('data-hidden', 'true');
49
+ user.hover(textEllipsis);
50
+ expect(getByRole('tooltip', { hidden: true })).toHaveAttribute('data-hidden', 'false');
51
+ user.unhover(textEllipsis);
52
+ expect(getByRole('tooltip', { hidden: true })).toHaveAttribute('data-hidden', 'true');
53
+ });
54
+ });
55
+
56
+ describe('ref should work', () => {
57
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
58
+ const ExampleComponent = ({
59
+ propagateRef,
60
+ }: {
61
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
62
+ }) => {
63
+ const ref = useRef(null);
64
+
65
+ useEffect(() => {
66
+ if (ref.current) {
67
+ propagateRef && propagateRef(ref);
68
+ }
69
+ }, [ref]);
70
+
71
+ return <TextEllipsis data-ref="testing" ref={ref} />;
72
+ };
73
+
74
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
75
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
76
+ };
77
+
78
+ render(<ExampleComponent propagateRef={refCheck} />);
79
+ });
80
+ });
@@ -0,0 +1,55 @@
1
+ import React, { useRef, MouseEventHandler, useState, ComponentPropsWithRef } from 'react';
2
+ import { Popover } from '../Popover/Popover';
3
+ import classes from './TextEllipsis.module.scss';
4
+
5
+ export interface Props extends ComponentPropsWithRef<'div'> {
6
+ children?: string;
7
+ popoverClassName?: string;
8
+ }
9
+
10
+ export const TextEllipsis = React.forwardRef<HTMLDivElement, Props>(
11
+ ({ children, popoverClassName, className, ...rest }: Props, ref) => {
12
+ const [showPopover, setShowPopover] = useState(false);
13
+ const textContainer = useRef<HTMLDivElement>(null);
14
+
15
+ const ellipsisVisible = () => {
16
+ if (
17
+ textContainer.current &&
18
+ textContainer.current.offsetWidth < textContainer.current.scrollWidth
19
+ ) {
20
+ return true;
21
+ }
22
+ return false;
23
+ };
24
+
25
+ const onMouseEnter: MouseEventHandler<HTMLDivElement> = () => {
26
+ ellipsisVisible() && setShowPopover(true);
27
+ };
28
+
29
+ const onMouseLeave: MouseEventHandler<HTMLDivElement> = () => {
30
+ ellipsisVisible() && setShowPopover(false);
31
+ };
32
+
33
+ return (
34
+ <div
35
+ {...rest}
36
+ onMouseEnter={onMouseEnter}
37
+ onMouseLeave={onMouseLeave}
38
+ className={`${classes['text-ellipsis']} ${className ?? ''}`}
39
+ ref={ref || textContainer}
40
+ >
41
+ {children}
42
+ <Popover
43
+ aria-hidden={true}
44
+ data-hidden={!showPopover}
45
+ show={showPopover}
46
+ role="tooltip"
47
+ anchorEl={textContainer}
48
+ className={`${classes.popover} ${popoverClassName ?? ''}`}
49
+ >
50
+ {children}
51
+ </Popover>
52
+ </div>
53
+ );
54
+ }
55
+ );
@@ -7,6 +7,7 @@
7
7
  padding: 0 0 2rem;
8
8
  background-color: #fff;
9
9
  transition: box-shadow 0.2s ease-in-out;
10
+ font-family: var(--font-family);
10
11
 
11
12
  &:hover {
12
13
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.29);
@@ -14,7 +15,6 @@
14
15
 
15
16
  header {
16
17
  display: flex;
17
- justify-content: space-between;
18
18
  align-items: center;
19
19
 
20
20
  .icon {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { Tile, Props } from './Tile';
3
3
  import { render, getByRole } from '@testing-library/react';
4
4
  import { Icon, Icons } from '../Icon/Icon';
@@ -10,7 +10,6 @@ import userEvent from '@testing-library/user-event';
10
10
  const onShow = jest.fn();
11
11
  const onClose = jest.fn();
12
12
  const contextMenuItemOnClick = jest.fn();
13
- const iconClick = jest.fn();
14
13
 
15
14
  const contextMenu = (
16
15
  <ContextMenu
@@ -38,8 +37,8 @@ const contextMenu = (
38
37
 
39
38
  const defaultParams: Props = {
40
39
  title: 'tile',
41
- iconProps: { icon: Icons.Bell, onClick: iconClick, 'data-testid': 'icon' },
42
- menu: contextMenu,
40
+ enabled: true,
41
+ tileAction: contextMenu,
43
42
  };
44
43
 
45
44
  const createTile = (params?: (defaultParams: Props) => Props) => {
@@ -54,25 +53,36 @@ const createTile = (params?: (defaultParams: Props) => Props) => {
54
53
  );
55
54
  const tile = queries.getByTestId('tile');
56
55
  const menutrigger = queries.getByTestId('contextmenu-trigger');
57
- const icon = queries.getByTestId('icon');
58
56
 
59
57
  return {
60
58
  ...queries,
61
59
  tile,
62
60
  menutrigger,
63
- icon,
64
61
  };
65
62
  };
66
63
 
67
64
  describe('Tile should render', () => {
68
- it('renders without crashing', () => {
69
- const { tile, icon } = createTile();
65
+ it('renders without crashing and enabled', () => {
66
+ const { tile } = createTile();
70
67
 
71
- userEvent.click(icon);
72
-
73
- expect(iconClick).toHaveBeenCalled();
68
+ expect(tile.querySelector('.icon-checkmark')).toBeTruthy();
69
+ expect(tile.querySelector('.icon-forbidden')).toBeFalsy();
74
70
  expect(tile).toBeDefined();
75
71
  });
72
+
73
+ it('renders disabled', () => {
74
+ const { tile } = createTile((defaultParams) => ({ ...defaultParams, enabled: false }));
75
+
76
+ expect(tile.querySelector('.icon-checkmark')).toBeFalsy();
77
+ expect(tile.querySelector('.icon-forbidden')).toBeTruthy();
78
+ });
79
+
80
+ it('renders no status', () => {
81
+ const { tile } = createTile((defaultParams) => ({ ...defaultParams, enabled: undefined }));
82
+
83
+ expect(tile.querySelector('.icon-checkmark')).toBeFalsy();
84
+ expect(tile.querySelector('.icon-forbidden')).toBeFalsy();
85
+ });
76
86
  });
77
87
 
78
88
  describe("should throw errors since we don't pass props", () => {
@@ -85,7 +95,7 @@ describe("should throw errors since we don't pass props", () => {
85
95
 
86
96
  try {
87
97
  // @ts-ignore: mandatory props (test for non-typescript react projects)
88
- render(<Tile imageProps={{ src: 'test', alt: 'test' }} />);
98
+ render(<Tile imageProps={{ src: 'test' }} />);
89
99
  } catch (e: any) {
90
100
  actual = e.message;
91
101
  }
@@ -127,3 +137,29 @@ describe('contextmenu', () => {
127
137
  expect(contextMenuItemOnClick).toHaveBeenCalledTimes(3);
128
138
  });
129
139
  });
140
+
141
+ describe('ref should work', () => {
142
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
143
+ const ExampleComponent = ({
144
+ propagateRef,
145
+ }: {
146
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
147
+ }) => {
148
+ const ref = useRef(null);
149
+
150
+ useEffect(() => {
151
+ if (ref.current) {
152
+ propagateRef && propagateRef(ref);
153
+ }
154
+ }, [ref]);
155
+
156
+ return <Tile {...defaultParams} data-ref="testing" ref={ref} />;
157
+ };
158
+
159
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
160
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
161
+ };
162
+
163
+ render(<ExampleComponent propagateRef={refCheck} />);
164
+ });
165
+ });