@telus-uds/components-base 0.0.2-prerelease.9 → 1.1.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.
Files changed (293) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.storybook/main.js +4 -0
  3. package/.storybook/preview.js +37 -0
  4. package/.ultra.cache.json +1 -1
  5. package/CHANGELOG.md +50 -0
  6. package/README.md +4 -2
  7. package/__fixtures__/test-utils.js +25 -0
  8. package/__fixtures__/testTheme.js +4 -2
  9. package/__tests__/Button/ButtonGroup.test.jsx +4 -5
  10. package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
  11. package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
  12. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  13. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  14. package/__tests__/Link/LinkBase.test.jsx +0 -14
  15. package/__tests__/Radio/Radio.test.jsx +2 -2
  16. package/__tests__/Radio/RadioGroup.test.jsx +4 -5
  17. package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
  18. package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
  19. package/__tests__/Search/Search.test.jsx +9 -8
  20. package/__tests__/Select/Select.test.jsx +3 -2
  21. package/__tests__/Tabs/Tabs.test.jsx +1 -161
  22. package/__tests__/Tags/Tags.test.jsx +4 -5
  23. package/__tests__/TextInput/TextArea.test.jsx +3 -2
  24. package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
  25. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  26. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  27. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  28. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  29. package/__tests__/utils/children.test.jsx +128 -0
  30. package/__tests__/utils/input.test.js +1 -1
  31. package/__tests__/utils/semantics.test.jsx +43 -0
  32. package/babel.config.js +9 -16
  33. package/component-docs.json +10313 -0
  34. package/generate-component-docs.js +56 -0
  35. package/lib/A11yText/index.js +10 -5
  36. package/lib/ActivityIndicator/Spinner.js +16 -13
  37. package/lib/ActivityIndicator/Spinner.native.js +12 -8
  38. package/lib/Box/Box.js +103 -8
  39. package/lib/Button/Button.js +9 -8
  40. package/lib/Button/ButtonBase.js +14 -7
  41. package/lib/Button/ButtonGroup.js +25 -10
  42. package/lib/Button/ButtonLink.js +10 -7
  43. package/lib/Card/Card.js +2 -0
  44. package/lib/Card/CardBase.js +13 -5
  45. package/lib/Card/PressableCardBase.js +12 -8
  46. package/lib/Checkbox/Checkbox.js +25 -14
  47. package/lib/Checkbox/CheckboxGroup.js +22 -12
  48. package/lib/Divider/Divider.js +12 -7
  49. package/lib/ExpandCollapse/Accordion.js +10 -4
  50. package/lib/ExpandCollapse/Control.js +12 -6
  51. package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
  52. package/lib/ExpandCollapse/Panel.js +8 -7
  53. package/lib/Feedback/Feedback.js +10 -5
  54. package/lib/Fieldset/Fieldset.js +10 -5
  55. package/lib/Fieldset/FieldsetContainer.js +10 -5
  56. package/lib/Fieldset/FieldsetContainer.native.js +10 -5
  57. package/lib/Fieldset/Legend.js +10 -5
  58. package/lib/Fieldset/Legend.native.js +10 -5
  59. package/lib/FlexGrid/Col/Col.js +8 -5
  60. package/lib/FlexGrid/FlexGrid.js +31 -6
  61. package/lib/FlexGrid/Row/Row.js +12 -5
  62. package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
  63. package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
  64. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
  65. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
  66. package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  67. package/lib/HorizontalScroll/index.js +35 -0
  68. package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  69. package/lib/Icon/Icon.js +16 -9
  70. package/lib/Icon/IconText.js +8 -7
  71. package/lib/IconButton/IconButton.js +10 -5
  72. package/lib/InputLabel/InputLabel.js +33 -5
  73. package/lib/InputLabel/LabelContent.js +22 -12
  74. package/lib/InputLabel/LabelContent.native.js +23 -5
  75. package/lib/InputSupports/InputSupports.js +10 -5
  76. package/lib/Link/ChevronLink.js +12 -5
  77. package/lib/Link/InlinePressable.js +10 -4
  78. package/lib/Link/InlinePressable.native.js +5 -4
  79. package/lib/Link/Link.js +12 -5
  80. package/lib/Link/LinkBase.js +12 -5
  81. package/lib/Link/TextButton.js +10 -5
  82. package/lib/List/List.js +6 -6
  83. package/lib/List/ListItem.js +28 -33
  84. package/lib/List/index.js +15 -0
  85. package/lib/Modal/Modal.js +10 -5
  86. package/lib/Notification/Notification.js +21 -5
  87. package/lib/Pagination/PageButton.js +16 -11
  88. package/lib/Pagination/Pagination.js +12 -7
  89. package/lib/Pagination/SideButton.js +12 -7
  90. package/lib/Pagination/usePagination.js +2 -2
  91. package/lib/Progress/Progress.js +10 -5
  92. package/lib/Progress/ProgressBar.js +21 -10
  93. package/lib/Progress/ProgressBarBackground.js +12 -8
  94. package/lib/Radio/Radio.js +14 -13
  95. package/lib/Radio/RadioButton.js +20 -9
  96. package/lib/Radio/RadioGroup.js +24 -13
  97. package/lib/RadioCard/RadioCard.js +14 -10
  98. package/lib/RadioCard/RadioCardGroup.js +13 -12
  99. package/lib/Search/Search.js +29 -18
  100. package/lib/Select/Picker.js +11 -6
  101. package/lib/Select/Picker.native.js +21 -6
  102. package/lib/Select/Select.js +46 -4
  103. package/lib/SideNav/Item.js +10 -5
  104. package/lib/SideNav/ItemsGroup.js +10 -5
  105. package/lib/SideNav/SideNav.js +11 -7
  106. package/lib/Skeleton/Skeleton.js +15 -15
  107. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  108. package/lib/Spacer/Spacer.js +19 -7
  109. package/lib/StackView/StackView.js +26 -7
  110. package/lib/StackView/StackWrap.js +24 -13
  111. package/lib/StackView/StackWrapBox.js +34 -8
  112. package/lib/StackView/StackWrapGap.js +16 -7
  113. package/lib/StackView/common.js +4 -2
  114. package/lib/StackView/getStackedContent.js +2 -2
  115. package/lib/StepTracker/StepTracker.js +10 -5
  116. package/lib/Tabs/Tabs.js +26 -19
  117. package/lib/Tabs/TabsItem.js +16 -12
  118. package/lib/Tags/Tags.js +27 -11
  119. package/lib/TextInput/TextArea.js +7 -5
  120. package/lib/TextInput/TextInput.js +12 -6
  121. package/lib/TextInput/TextInputBase.js +12 -8
  122. package/lib/ThemeProvider/ThemeProvider.js +14 -10
  123. package/lib/ThemeProvider/useSetTheme.js +6 -1
  124. package/lib/ThemeProvider/utils/styles.js +2 -2
  125. package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
  126. package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
  127. package/lib/Tooltip/Backdrop.js +10 -2
  128. package/lib/Tooltip/Tooltip.js +5 -4
  129. package/lib/Typography/Typography.js +40 -24
  130. package/lib/index.js +36 -1
  131. package/lib/utils/a11y/index.js +13 -0
  132. package/lib/utils/a11y/propTypes.js +61 -0
  133. package/lib/utils/a11y/propTypes.native.js +47 -0
  134. package/lib/utils/a11y/semantics.js +173 -0
  135. package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
  136. package/lib/utils/children.js +55 -8
  137. package/lib/utils/input.js +27 -17
  138. package/lib/utils/propTypes.js +40 -68
  139. package/lib/utils/useCopy.js +1 -1
  140. package/lib/utils/useHash.js +8 -4
  141. package/lib/utils/useSpacingScale.js +1 -3
  142. package/lib/utils/useUniqueId.js +1 -1
  143. package/package.json +14 -6
  144. package/release-context.json +4 -4
  145. package/src/A11yText/index.jsx +6 -4
  146. package/src/ActivityIndicator/Spinner.jsx +5 -3
  147. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  148. package/src/Box/Box.jsx +125 -39
  149. package/src/Button/Button.jsx +7 -4
  150. package/src/Button/ButtonBase.jsx +86 -77
  151. package/src/Button/ButtonGroup.jsx +81 -69
  152. package/src/Button/ButtonLink.jsx +18 -13
  153. package/src/Card/Card.jsx +2 -2
  154. package/src/Card/CardBase.jsx +6 -4
  155. package/src/Card/PressableCardBase.jsx +71 -64
  156. package/src/Checkbox/Checkbox.jsx +118 -108
  157. package/src/Checkbox/CheckboxGroup.jsx +72 -62
  158. package/src/Divider/Divider.jsx +7 -4
  159. package/src/ExpandCollapse/Accordion.jsx +3 -2
  160. package/src/ExpandCollapse/Control.jsx +40 -43
  161. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  162. package/src/ExpandCollapse/Panel.jsx +69 -63
  163. package/src/Feedback/Feedback.jsx +36 -33
  164. package/src/Fieldset/Fieldset.jsx +63 -56
  165. package/src/Fieldset/FieldsetContainer.jsx +14 -5
  166. package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
  167. package/src/Fieldset/Legend.jsx +7 -2
  168. package/src/Fieldset/Legend.native.jsx +7 -2
  169. package/src/FlexGrid/Col/Col.jsx +139 -132
  170. package/src/FlexGrid/FlexGrid.jsx +79 -51
  171. package/src/FlexGrid/Row/Row.jsx +55 -48
  172. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  173. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  174. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
  175. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
  176. package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  177. package/src/HorizontalScroll/index.js +17 -0
  178. package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  179. package/src/Icon/Icon.jsx +37 -35
  180. package/src/Icon/IconText.jsx +22 -17
  181. package/src/IconButton/IconButton.jsx +49 -42
  182. package/src/InputLabel/InputLabel.jsx +53 -38
  183. package/src/InputLabel/LabelContent.jsx +14 -6
  184. package/src/InputLabel/LabelContent.native.jsx +11 -2
  185. package/src/InputSupports/InputSupports.jsx +29 -34
  186. package/src/Link/ChevronLink.jsx +26 -16
  187. package/src/Link/InlinePressable.jsx +5 -3
  188. package/src/Link/InlinePressable.native.jsx +5 -3
  189. package/src/Link/Link.jsx +22 -16
  190. package/src/Link/LinkBase.jsx +67 -58
  191. package/src/Link/TextButton.jsx +30 -23
  192. package/src/List/List.jsx +6 -7
  193. package/src/List/ListItem.jsx +70 -90
  194. package/src/List/index.js +5 -0
  195. package/src/Modal/Modal.jsx +9 -4
  196. package/src/Notification/Notification.jsx +58 -43
  197. package/src/Pagination/PageButton.jsx +37 -34
  198. package/src/Pagination/Pagination.jsx +88 -92
  199. package/src/Pagination/SideButton.jsx +44 -41
  200. package/src/Progress/Progress.jsx +5 -4
  201. package/src/Progress/ProgressBar.jsx +42 -29
  202. package/src/Progress/ProgressBarBackground.jsx +5 -3
  203. package/src/Radio/Radio.jsx +85 -78
  204. package/src/Radio/RadioButton.jsx +54 -43
  205. package/src/Radio/RadioGroup.jsx +74 -63
  206. package/src/RadioCard/RadioCard.jsx +75 -68
  207. package/src/RadioCard/RadioCardGroup.jsx +82 -75
  208. package/src/Search/Search.jsx +127 -106
  209. package/src/Select/Picker.jsx +49 -42
  210. package/src/Select/Picker.native.jsx +56 -49
  211. package/src/Select/Select.jsx +115 -72
  212. package/src/SideNav/Item.jsx +53 -46
  213. package/src/SideNav/ItemsGroup.jsx +50 -43
  214. package/src/SideNav/SideNav.jsx +68 -60
  215. package/src/Skeleton/Skeleton.jsx +9 -13
  216. package/src/Spacer/Spacer.jsx +11 -4
  217. package/src/StackView/StackView.jsx +47 -23
  218. package/src/StackView/StackWrap.jsx +14 -12
  219. package/src/StackView/StackWrapBox.jsx +62 -28
  220. package/src/StackView/StackWrapGap.jsx +46 -24
  221. package/src/StackView/common.jsx +3 -2
  222. package/src/StepTracker/StepTracker.jsx +73 -62
  223. package/src/Tabs/Tabs.jsx +70 -62
  224. package/src/Tabs/TabsItem.jsx +111 -103
  225. package/src/Tags/Tags.jsx +114 -102
  226. package/src/TextInput/TextArea.jsx +5 -4
  227. package/src/TextInput/TextInput.jsx +5 -4
  228. package/src/TextInput/TextInputBase.jsx +84 -77
  229. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  230. package/src/ThemeProvider/useSetTheme.js +4 -0
  231. package/src/ThemeProvider/utils/theme-tokens.js +28 -0
  232. package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
  233. package/src/Tooltip/Tooltip.jsx +134 -130
  234. package/src/Typography/Typography.jsx +67 -44
  235. package/src/index.js +3 -1
  236. package/src/utils/a11y/index.js +1 -0
  237. package/src/utils/a11y/propTypes.js +61 -0
  238. package/src/utils/a11y/propTypes.native.js +39 -0
  239. package/src/utils/a11y/semantics.js +162 -0
  240. package/src/utils/children.jsx +60 -7
  241. package/src/utils/input.js +20 -17
  242. package/src/utils/propTypes.js +30 -76
  243. package/src/utils/useCopy.js +1 -1
  244. package/src/utils/useHash.js +8 -3
  245. package/stories/A11yText/A11yText.stories.jsx +3 -3
  246. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +2 -2
  247. package/stories/Box/Box.stories.jsx +2 -2
  248. package/stories/Button/Button.stories.jsx +3 -3
  249. package/stories/Button/ButtonGroup.stories.jsx +2 -2
  250. package/stories/Button/ButtonLink.stories.jsx +2 -2
  251. package/stories/Card/Card.stories.jsx +2 -2
  252. package/stories/Checkbox/Checkbox.stories.jsx +2 -2
  253. package/stories/Divider/Divider.stories.jsx +2 -2
  254. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +3 -3
  255. package/stories/Feedback/Feedback.stories.jsx +2 -2
  256. package/stories/FlexGrid/01 FlexGrid.stories.jsx +2 -2
  257. package/stories/FlexGrid/02 Row.stories.jsx +2 -2
  258. package/stories/FlexGrid/03 Col.stories.jsx +2 -2
  259. package/stories/Icon/Icon.stories.jsx +2 -2
  260. package/stories/IconButton/IconButton.stories.jsx +2 -2
  261. package/stories/InputLabel/InputLabel.stories.jsx +2 -2
  262. package/stories/Link/ChevronLink.stories.jsx +2 -2
  263. package/stories/Link/Link.stories.jsx +2 -2
  264. package/stories/Link/TextButton.stories.jsx +2 -2
  265. package/stories/List/List.stories.jsx +2 -2
  266. package/stories/Modal/Modal.stories.jsx +2 -2
  267. package/stories/Notification/Notification.stories.jsx +2 -2
  268. package/stories/Pagination/Pagination.stories.jsx +2 -2
  269. package/stories/Progress/Progress.stories.jsx +2 -2
  270. package/stories/Radio/Radio.stories.jsx +2 -2
  271. package/stories/RadioCard/RadioCard.stories.jsx +2 -2
  272. package/stories/Search/Search.stories.jsx +2 -2
  273. package/stories/Select/Select.stories.jsx +2 -2
  274. package/stories/SideNav/SideNav.stories.jsx +2 -2
  275. package/stories/SideNav/SideNavItem.stories.jsx +2 -2
  276. package/stories/SideNav/SideNavItemsGroup.stories.jsx +2 -2
  277. package/stories/Skeleton/Skeleton.stories.jsx +3 -3
  278. package/stories/Spacer/Spacer.stories.jsx +2 -2
  279. package/stories/StackView/StackView.stories.jsx +2 -2
  280. package/stories/StackView/StackWrap.stories.jsx +2 -2
  281. package/stories/StepTracker/StepTracker.stories.jsx +2 -2
  282. package/stories/Tabs/Tabs.stories.jsx +2 -2
  283. package/stories/Tags/Tags.stories.jsx +2 -2
  284. package/stories/TextInput/TextArea.stories.jsx +2 -2
  285. package/stories/TextInput/TextInput.stories.jsx +2 -2
  286. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +2 -2
  287. package/stories/Tooltip/Tooltip.stories.jsx +2 -2
  288. package/stories/TooltipButton/TooltipButton.stories.jsx +2 -2
  289. package/stories/Typography/Typography.stories.jsx +2 -2
  290. package/stories/platform-supports.jsx +1 -1
  291. package/stories/supports.jsx +4 -5
  292. package/src/Tabs/HorizontalScroll.jsx +0 -165
  293. package/src/Tabs/TabsScrollButton.jsx +0 -100
@@ -174,15 +174,14 @@ describe('RadioCardGroup (controlled)', () => {
174
174
  expect(handleChange).toHaveBeenCalledTimes(0)
175
175
 
176
176
  const one = getByText('One')
177
- await fireEvent.press(one)
177
+ await fireEvent(one, 'press', { nativeEvent: 'example' })
178
178
  expect(handleChange).toHaveBeenCalledTimes(1)
179
-
180
- expect(handleChange.mock.calls[0][0]).toEqual('one')
179
+ expect(handleChange).toHaveBeenLastCalledWith('one', { nativeEvent: 'example' })
181
180
 
182
181
  const two = getByText('Two')
183
- await fireEvent.press(two)
182
+ await fireEvent(two, 'press', { nativeEvent: 'example2' })
184
183
  expect(handleChange).toHaveBeenCalledTimes(2)
185
- expect(handleChange.mock.calls[1][0]).toEqual('two')
184
+ expect(handleChange).toHaveBeenLastCalledWith('two', { nativeEvent: 'example2' })
186
185
  })
187
186
 
188
187
  it("Doesn't change its own selection if `checkedId` is passed", async () => {
@@ -3,6 +3,7 @@ import { render, fireEvent } from '@testing-library/react-native'
3
3
 
4
4
  import { Search } from '../../src'
5
5
  import Theme from '../../__fixtures__/Theme'
6
+ import { fireChange } from '../../__fixtures__/test-utils'
6
7
 
7
8
  describe('Search', () => {
8
9
  const commonProps = {
@@ -25,16 +26,16 @@ describe('Search', () => {
25
26
 
26
27
  expect(onSubmit).not.toHaveBeenCalled()
27
28
 
28
- fireEvent.press(getByA11yLabel('submit-button'))
29
+ fireEvent(getByA11yLabel('submit-button'), 'press', { nativeEvent: 'example' })
29
30
 
30
31
  expect(onSubmit).toHaveBeenCalledTimes(1)
31
- expect(onSubmit).toHaveBeenLastCalledWith('initial value')
32
+ expect(onSubmit).toHaveBeenLastCalledWith('initial value', { nativeEvent: 'example' })
32
33
 
33
- fireEvent.changeText(getByA11yLabel('input'), 'new value')
34
- fireEvent.press(getByA11yLabel('submit-button'))
34
+ fireChange(getByA11yLabel('input'), 'new value')
35
+ fireEvent(getByA11yLabel('submit-button'), 'press', { nativeEvent: 'example' })
35
36
 
36
37
  expect(onSubmit).toHaveBeenCalledTimes(2)
37
- expect(onSubmit).toHaveBeenLastCalledWith('new value')
38
+ expect(onSubmit).toHaveBeenLastCalledWith('new value', { nativeEvent: 'example' })
38
39
  })
39
40
 
40
41
  it('shows the clear button when text is entered', () => {
@@ -44,7 +45,7 @@ describe('Search', () => {
44
45
 
45
46
  expect(() => getByA11yLabel('clear-button')).toThrow()
46
47
 
47
- fireEvent.changeText(getByA11yLabel('input'), 'test value')
48
+ fireChange(getByA11yLabel('input'), 'test value')
48
49
 
49
50
  expect(() => getByA11yLabel('clear-button')).not.toThrow()
50
51
  })
@@ -63,10 +64,10 @@ describe('Search', () => {
63
64
  expect(onClear).not.toHaveBeenCalled()
64
65
  expect(onChange).not.toHaveBeenCalled()
65
66
 
66
- fireEvent.press(getByA11yLabel('clear-button'))
67
+ fireEvent(getByA11yLabel('clear-button'), 'press', { nativeEvent: 'example' })
67
68
 
68
69
  expect(onClear).toHaveBeenCalledTimes(1)
69
70
  expect(onChange).toHaveBeenCalledTimes(1)
70
- expect(onChange).toHaveBeenLastCalledWith('')
71
+ expect(onChange).toHaveBeenLastCalledWith('', { nativeEvent: 'example' })
71
72
  })
72
73
  })
@@ -64,10 +64,11 @@ describe('Select', () => {
64
64
 
65
65
  expect(onChange).not.toHaveBeenCalled()
66
66
 
67
- fireEvent.change(getByTestId('Test-Select'), { target: { value: 'item-1' } })
67
+ const event = { target: { value: 'item-1' } }
68
+ fireEvent.change(getByTestId('Test-Select'), event)
68
69
 
69
70
  expect(onChange).toHaveBeenCalledTimes(1)
70
- expect(onChange).toHaveBeenLastCalledWith('item-1')
71
+ expect(onChange).toHaveBeenLastCalledWith('item-1', event)
71
72
  })
72
73
 
73
74
  it("doesn't change value when readOnly", () => {
@@ -1,169 +1,9 @@
1
- import React, { useState } from 'react'
2
- import { Pressable, View, Text } from 'react-native'
1
+ import React from 'react'
3
2
  import { fireEvent, render } from '@testing-library/react-native'
4
3
 
5
4
  import { Tabs } from '../../src'
6
- import {
7
- getItemPositionLayoutHandler,
8
- useItemPositions,
9
- getItemPositionScrollTarget
10
- } from '../../src/Tabs/itemPositions'
11
5
  import Theme from '../../__fixtures__/Theme'
12
6
 
13
- // ScrollViewRef.scrollTo doesn't work in Jest - unit-test the utilities instead
14
- describe('Tabs getItemPositionScrollTarget', () => {
15
- const positionsWithoutSpacing = {
16
- 0: { start: 0, end: 100 },
17
- 1: { start: 100, end: 200 },
18
- 2: { start: 200, end: 300 }
19
- }
20
- const positions = {
21
- 0: { start: 0, end: 50 },
22
- 1: { start: 100, end: 150 },
23
- 2: { start: 200, end: 250 }
24
- }
25
- it('scrolls to the start of an item rather than chopping it', () => {
26
- const x = getItemPositionScrollTarget(150, 300, positionsWithoutSpacing)
27
- expect(x).toBe(100)
28
- })
29
- it('offsets the position by a button clearance', () => {
30
- const x = getItemPositionScrollTarget(150, 300, positionsWithoutSpacing, 20)
31
- expect(x).toBe(80)
32
- })
33
- it('scrolls exactly if landing on a gap', () => {
34
- const x = getItemPositionScrollTarget(170, 300, positions)
35
- expect(x).toBe(170)
36
- })
37
- it('applies button clearance if landing on a gap', () => {
38
- const x = getItemPositionScrollTarget(170, 300, positions, 40)
39
- expect(x).toBe(130)
40
- })
41
- it('does not apply button clearance on reaching the end', () => {
42
- const x = getItemPositionScrollTarget(300, 300, positions, 40)
43
- expect(x).toBe(300)
44
- })
45
- it('does not exceed the maximum', () => {
46
- const x = getItemPositionScrollTarget(320, 300, positions, 40)
47
- expect(x).toBe(300)
48
- })
49
- it('does not go below 0', () => {
50
- const x = getItemPositionScrollTarget(-420, 300, positions, 40)
51
- expect(x).toBe(0)
52
- })
53
- })
54
-
55
- // onLayout also doesn't work naturally in jest
56
- describe('Tabs getItemPositionLayoutHandler', () => {
57
- // Test component using useItemPositions with getItemPositionLayoutHandler
58
- // that can be rerendered manually and that outputs itemPositions value
59
- const MockComponent = () => {
60
- const [num, setNum] = useState()
61
- const [{ positions }] = useItemPositions()
62
- return (
63
- <View>
64
- <View testID="onlayout[0]" onLayout={getItemPositionLayoutHandler(positions, 0)} />
65
- <View testID="onlayout[1]" onLayout={getItemPositionLayoutHandler(positions, 1)} />
66
- <Text testID="output">{JSON.stringify(positions)}</Text>
67
- <Pressable testID="rerender" onPress={() => setNum(num + 1)}>
68
- <Text>{num}</Text>
69
- </Pressable>
70
- </View>
71
- )
72
- }
73
- it('gets width and x positions from onLayout events', async () => {
74
- const { getByTestId } = render(<MockComponent />)
75
-
76
- // onLayout doesn't work in jest but can be called directly as an event, but we
77
- // have to pass the event object. Can't test that it gets them correctly from style
78
- await fireEvent(getByTestId('onlayout[0]'), 'layout', {
79
- nativeEvent: {
80
- layout: {
81
- width: 200,
82
- x: 0
83
- }
84
- }
85
- })
86
- await fireEvent(getByTestId('onlayout[1]'), 'layout', {
87
- nativeEvent: {
88
- layout: {
89
- width: 100,
90
- x: 250
91
- }
92
- }
93
- })
94
- await fireEvent.press(getByTestId('rerender'))
95
- expect(getByTestId('output')).toHaveTextContent(
96
- '{"0":{"start":0,"end":200},"1":{"start":250,"end":350}}'
97
- )
98
- })
99
- it('does not cause a rerender from onLayout events alone', async () => {
100
- const { getByTestId } = render(<MockComponent />)
101
-
102
- expect(getByTestId('output')).toHaveTextContent('{}')
103
- await fireEvent(getByTestId('onlayout[0]'), 'layout', {
104
- nativeEvent: {
105
- layout: {
106
- width: 200,
107
- x: 0
108
- }
109
- }
110
- })
111
- await fireEvent(getByTestId('onlayout[1]'), 'layout', {
112
- nativeEvent: {
113
- layout: {
114
- width: 100,
115
- x: 250
116
- }
117
- }
118
- })
119
- expect(getByTestId('output')).toHaveTextContent('{}')
120
- })
121
- })
122
-
123
- describe('useItemPositions', () => {
124
- // Again we're limited in what we can test; we can't test natural onLayout
125
- // or page load timing, but we can affirm that calling `setIsReady` after layout
126
- // events causes a re-render that can access positions data
127
- const IsReadyMockComponent = () => {
128
- const [{ positions, setIsReady }] = useItemPositions()
129
- return (
130
- <View testID="container" onLayout={setIsReady}>
131
- <View testID="onlayout[0]" onLayout={getItemPositionLayoutHandler(positions, 0)} />
132
- <View testID="onlayout[1]" onLayout={getItemPositionLayoutHandler(positions, 1)} />
133
- <Text testID="output">{JSON.stringify(positions)}</Text>
134
- </View>
135
- )
136
- }
137
- it('causes a rerender when setIsReady is called', async () => {
138
- const { getByTestId } = render(<IsReadyMockComponent />)
139
-
140
- expect(getByTestId('output')).toHaveTextContent('{}')
141
- await fireEvent(getByTestId('onlayout[0]'), 'layout', {
142
- nativeEvent: {
143
- layout: {
144
- width: 200,
145
- x: 0
146
- }
147
- }
148
- })
149
- await fireEvent(getByTestId('onlayout[1]'), 'layout', {
150
- nativeEvent: {
151
- layout: {
152
- width: 100,
153
- x: 250
154
- }
155
- }
156
- })
157
- await fireEvent(getByTestId('container'), 'layout', {
158
- nativeEvent: { layout: {} }
159
- })
160
-
161
- expect(getByTestId('output')).toHaveTextContent(
162
- '{"0":{"start":0,"end":200},"1":{"start":250,"end":350}}'
163
- )
164
- })
165
- })
166
-
167
7
  describe('Tabs', () => {
168
8
  const items = [{ label: 'one' }, { label: 'two' }, { label: 'three' }, { label: 'four' }]
169
9
  const linkItems = items.map((item) => ({ ...item, href: 'https://telus.com' }))
@@ -238,15 +238,14 @@ describe('Tags (controlled)', () => {
238
238
  expect(handleChange).toHaveBeenCalledTimes(0)
239
239
 
240
240
  const one = getByText('One')
241
- await fireEvent.press(one)
241
+ await fireEvent(one, 'press', { nativeEvent: 'example' })
242
242
  expect(handleChange).toHaveBeenCalledTimes(1)
243
-
244
- expect(handleChange.mock.calls[0][0]).toEqual(['one'])
243
+ expect(handleChange).toHaveBeenLastCalledWith(['one'], { nativeEvent: 'example' })
245
244
 
246
245
  const two = getByText('Two')
247
- await fireEvent.press(two)
246
+ await fireEvent(two, 'press', { nativeEvent: 'example2' })
248
247
  expect(handleChange).toHaveBeenCalledTimes(2)
249
- expect(handleChange.mock.calls[1][0]).toEqual(['two'])
248
+ expect(handleChange).toHaveBeenLastCalledWith(['two'], { nativeEvent: 'example2' })
250
249
  })
251
250
 
252
251
  it("Doesn't change its own selection if `values` is passed", async () => {
@@ -1,9 +1,10 @@
1
1
  import React from 'react'
2
- import { fireEvent, render } from '@testing-library/react-native'
2
+ import { render } from '@testing-library/react-native'
3
3
 
4
4
  import { Platform } from 'react-native'
5
5
  import { TextArea } from '../../src'
6
6
  import Theme from '../../__fixtures__/Theme'
7
+ import { fireChange } from '../../__fixtures__/test-utils'
7
8
 
8
9
  describe('TextArea', () => {
9
10
  it('renders', () => {
@@ -26,7 +27,7 @@ describe('TextArea', () => {
26
27
 
27
28
  expect(textarea).toHaveStyle({ height: 20 })
28
29
 
29
- fireEvent.changeText(textarea, '\n\n\n')
30
+ fireChange(textarea, '\n\n\n')
30
31
 
31
32
  expect(textarea).toHaveStyle({ height: 60 })
32
33
  })
@@ -3,6 +3,7 @@ import React from 'react'
3
3
  import TextInputBase from '../../src/TextInput/TextInputBase'
4
4
  import Viewport from '../../__fixtures__/Viewport'
5
5
  import Theme from '../../__fixtures__/Theme'
6
+ import { getMockEvent, fireChange } from '../../__fixtures__/test-utils'
6
7
 
7
8
  // eslint-disable-next-line react/prop-types
8
9
  const Wrapper = ({ children }) => (
@@ -11,6 +12,9 @@ const Wrapper = ({ children }) => (
11
12
  </Viewport>
12
13
  )
13
14
 
15
+ const text = 'new value'
16
+ const changeEvent = getMockEvent({ text })
17
+
14
18
  describe('TextInputBaseBase', () => {
15
19
  it('triggers the interactive callbacks', () => {
16
20
  const onFocus = jest.fn()
@@ -73,9 +77,9 @@ describe('TextInputBaseBase', () => {
73
77
  const input = getByA11yLabel('Input label')
74
78
 
75
79
  expect(onChange).not.toHaveBeenCalled()
76
- fireEvent.changeText(input, 'new value')
80
+ fireChange(input, text)
77
81
  expect(onChange).toHaveBeenCalledTimes(1)
78
- expect(onChange).toHaveBeenLastCalledWith('new value')
82
+ expect(onChange).toHaveBeenLastCalledWith(text, changeEvent)
79
83
  })
80
84
 
81
85
  it('changes value when controlled', () => {
@@ -91,9 +95,10 @@ describe('TextInputBaseBase', () => {
91
95
  const input = getByA11yLabel('Input label')
92
96
 
93
97
  expect(onChange).not.toHaveBeenCalled()
94
- fireEvent.changeText(input, 'new value')
98
+
99
+ fireChange(input, text)
95
100
  expect(onChange).toHaveBeenCalledTimes(1)
96
- expect(onChange).toHaveBeenLastCalledWith('new value')
101
+ expect(onChange).toHaveBeenLastCalledWith(text, changeEvent)
97
102
  })
98
103
 
99
104
  it("doesn't change value when readOnly", () => {
@@ -114,7 +119,7 @@ describe('TextInputBaseBase', () => {
114
119
  const input = getByA11yLabel('Input label')
115
120
 
116
121
  expect(onChange).not.toHaveBeenCalled()
117
- fireEvent.changeText(input, 'new value')
122
+ fireChange(input, text)
118
123
  expect(onChange).not.toHaveBeenCalled()
119
124
  })
120
125
  })
@@ -0,0 +1,77 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { renderHook } from '@testing-library/react-hooks'
4
+ import semVerSatisfies from 'semver/functions/satisfies'
5
+ import ThemeProvider, { useTheme, useSetTheme } from '../../src/ThemeProvider'
6
+ import pkg from '../../package.json'
7
+
8
+ jest.mock('semver/functions/satisfies')
9
+
10
+ const expectedThemeTokensVersion = 'expected'
11
+ const actualThemeTokensVersion = 'actual'
12
+ pkg.dependencies['@telus-uds/system-theme-tokens'] = expectedThemeTokensVersion
13
+ const theme = {
14
+ metadata: {
15
+ name: 'tokens-test',
16
+ themeTokensVersion: actualThemeTokensVersion
17
+ },
18
+ components: {}
19
+ }
20
+
21
+ beforeEach(() => {
22
+ semVerSatisfies.mockReset()
23
+ })
24
+
25
+ describe('Uninitialized ThemeProvider context', () => {
26
+ test('throws on useTheme', () => {
27
+ const { result } = renderHook(useTheme)
28
+ expect(() => result.current).toThrow('Theme context used outside of ThemeProvider')
29
+ })
30
+
31
+ test('error useSetTheme hook without provider', () => {
32
+ const { result } = renderHook(useSetTheme)
33
+ expect(() => result.current).toThrow('Theme context used outside of ThemeProvider')
34
+ })
35
+ })
36
+
37
+ describe('ThemeProvider theme tokens version validation', () => {
38
+ const renderThemeHook = (hookFn) => {
39
+ const ThemeWrapper = ({ children }) => (
40
+ <ThemeProvider defaultTheme={theme}>{children}</ThemeProvider>
41
+ )
42
+ ThemeWrapper.propTypes = { children: PropTypes.node.isRequired }
43
+
44
+ return renderHook(hookFn, {
45
+ wrapper: ThemeWrapper
46
+ })
47
+ }
48
+
49
+ test('validates theme tokens version with semver', () => {
50
+ semVerSatisfies.mockImplementationOnce(() => true)
51
+
52
+ const { result } = renderThemeHook(useTheme)
53
+
54
+ expect(result.current).toEqual(theme)
55
+ expect(semVerSatisfies).toHaveBeenCalledWith(
56
+ actualThemeTokensVersion,
57
+ expectedThemeTokensVersion
58
+ )
59
+ })
60
+
61
+ test('warning if theme tokens version does not match', () => {
62
+ semVerSatisfies.mockImplementationOnce(() => false)
63
+
64
+ const { result } = renderThemeHook(useTheme)
65
+
66
+ expect(() => result.current).toThrow('Invalid UDS token schema version detected')
67
+ })
68
+
69
+ test('validates on every render', () => {
70
+ semVerSatisfies.mockImplementationOnce(() => true)
71
+
72
+ const { rerender } = renderThemeHook(useTheme)
73
+ rerender()
74
+
75
+ expect(semVerSatisfies).toHaveBeenCalledTimes(2)
76
+ })
77
+ })
@@ -4,10 +4,13 @@ import { renderHook } from '@testing-library/react-hooks'
4
4
 
5
5
  import ThemeProvider from '../../src/ThemeProvider'
6
6
  import { useThemeTokens, useThemeTokensCallback } from '../../src/ThemeProvider/useThemeTokens'
7
- import {
8
- doesThemeRuleApply,
9
- doesThemeConditionApply
10
- } from '../../src/ThemeProvider/utils/theme-tokens'
7
+ import * as tokenUtils from '../../src/ThemeProvider/utils/theme-tokens'
8
+
9
+ // Mock out the function that checks the theme version
10
+ const validateThemeTokensVersion = jest.spyOn(tokenUtils, 'validateThemeTokensVersion')
11
+ validateThemeTokensVersion.mockImplementation(() => undefined)
12
+
13
+ const { doesThemeRuleApply, doesThemeConditionApply } = tokenUtils
11
14
 
12
15
  const componentName = 'TestComponent'
13
16
 
@@ -48,7 +51,8 @@ const defaultTokens = {
48
51
 
49
52
  const theme = {
50
53
  metadata: {
51
- name: 'tokens-test'
54
+ name: 'tokens-test',
55
+ themeTokensVersion: 'test-version'
52
56
  },
53
57
  components: {
54
58
  [componentName]: {
@@ -0,0 +1,41 @@
1
+ import pkg from '../../../package.json'
2
+ import { validateThemeTokensVersion } from '../../../src/ThemeProvider/utils/theme-tokens'
3
+
4
+ function validate({ expectedVersion, actualVersion }) {
5
+ pkg.dependencies['@telus-uds/system-theme-tokens'] = expectedVersion
6
+ const theme = {
7
+ metadata: {
8
+ themeTokensVersion: actualVersion
9
+ }
10
+ }
11
+ validateThemeTokensVersion(theme)
12
+ }
13
+
14
+ describe('validateThemeTokensVersion', () => {
15
+ test('No error on matching version', async () => {
16
+ const expectedVersion = '1.2.3'
17
+ const actualVersion = '1.2.3'
18
+
19
+ validate({ expectedVersion, actualVersion })
20
+
21
+ // No errors, no problem...
22
+ })
23
+
24
+ test('Throws if theme version is old', async () => {
25
+ const expectedVersion = '^2.3.4'
26
+ const actualVersion = '2.3.3'
27
+
28
+ expect(() => validate({ expectedVersion, actualVersion })).toThrow(
29
+ 'Invalid UDS token schema version detected.'
30
+ )
31
+ })
32
+
33
+ test('Throws if theme version is new', async () => {
34
+ const expectedVersion = '^1.2.3'
35
+ const actualVersion = '2.3.4'
36
+
37
+ expect(() => validate({ expectedVersion, actualVersion })).toThrow(
38
+ 'Invalid UDS token schema version detected.'
39
+ )
40
+ })
41
+ })
@@ -63,9 +63,10 @@ describe('ToggleSwitch', () => {
63
63
  expect(handleChange).toHaveBeenCalledTimes(0)
64
64
  const toggleSwitch = getByA11yRole('switch')
65
65
  expect(queryAllByA11yState(checked)).toHaveLength(0)
66
- await fireEvent.press(toggleSwitch)
66
+
67
+ await fireEvent(toggleSwitch, 'press', { nativeEvent: 'example' })
67
68
  expect(handleChange).toHaveBeenCalledTimes(1)
68
- expect(handleChange.mock.calls[0][0]).toEqual(true)
69
+ expect(handleChange).toHaveBeenLastCalledWith(true, { nativeEvent: 'example' })
69
70
  expect(queryAllByA11yState(checked)).toHaveLength(0)
70
71
  })
71
72
  })
@@ -0,0 +1,128 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { render } from '@testing-library/react-native'
4
+ import { Text, View } from 'react-native'
5
+
6
+ import { wrapStringsInText } from '../../src/utils'
7
+ import A11yText from '../../src/A11yText'
8
+
9
+ const TestComponent = ({ children }) => (
10
+ <View testID="wrapper">{wrapStringsInText(children, { testID: 'text' })}</View>
11
+ )
12
+ TestComponent.propTypes = {
13
+ children: PropTypes.node
14
+ }
15
+
16
+ describe('wrapStringsInText', () => {
17
+ it('combines adjacent strings into one text', () => {
18
+ const secondString = 'Second string'
19
+ const { queryAllByTestId } = render(
20
+ <TestComponent>
21
+ First string
22
+ {secondString}
23
+ Third string
24
+ </TestComponent>
25
+ )
26
+ expect(queryAllByTestId('text')).toHaveLength(1)
27
+ })
28
+ it('combines adjacent strings and numbers into one `Text`', () => {
29
+ const { queryAllByTestId } = render(
30
+ <TestComponent>
31
+ First string
32
+ {12345}
33
+ Third string
34
+ </TestComponent>
35
+ )
36
+ expect(queryAllByTestId('text')).toHaveLength(1)
37
+ })
38
+ it('combines adjacent strings, numbers and A11yText into one `Text`', () => {
39
+ const { queryAllByTestId } = render(
40
+ <TestComponent>
41
+ First string
42
+ <A11yText text="Some inserted A11yText" />
43
+ Third string
44
+ </TestComponent>
45
+ )
46
+ expect(queryAllByTestId('text')).toHaveLength(1)
47
+ })
48
+ it('combines clusters seperated by another component into multiple `Text`s', () => {
49
+ const { queryAllByTestId } = render(
50
+ <TestComponent>
51
+ First string
52
+ {12345}
53
+ Third string
54
+ <View />
55
+ Fourth string
56
+ <A11yText text="Some inserted A11yText" />
57
+ Fifth string
58
+ <View />
59
+ Sixth string
60
+ {12345}
61
+ <A11yText text="Some inserted A11yText" />
62
+ Seventh string
63
+ </TestComponent>
64
+ )
65
+ expect(queryAllByTestId('text')).toHaveLength(3)
66
+ })
67
+ it('does not wrap elements that do not need wrapping', () => {
68
+ const { getByTestId, queryAllByTestId } = render(
69
+ <TestComponent>
70
+ Unwrapped text
71
+ {12345}
72
+ <View>
73
+ <Text>
74
+ Wrapped text inside View
75
+ {12345}
76
+ </Text>
77
+ </View>
78
+ More unwrapped text
79
+ {12345}
80
+ <Text>
81
+ Wrapped text separate from above unwrapped text
82
+ {12345}
83
+ </Text>
84
+ </TestComponent>
85
+ )
86
+ expect(queryAllByTestId('text')).toHaveLength(2)
87
+ expect(getByTestId('wrapper').children.map((child) => child.type)).toEqual([
88
+ Text,
89
+ View,
90
+ Text,
91
+ Text
92
+ ])
93
+ })
94
+ it('does not wrap a singular A11yText in `Text`', () => {
95
+ const { queryAllByTestId } = render(
96
+ <TestComponent>
97
+ <A11yText text="Some inserted A11yText" />
98
+ </TestComponent>
99
+ )
100
+ expect(queryAllByTestId('text')).toHaveLength(0)
101
+ })
102
+ it('does not wrap an A11yText in `Text` if it is sandwiched by non-text', () => {
103
+ const { queryAllByTestId } = render(
104
+ <TestComponent>
105
+ First string
106
+ {12345}
107
+ Third string
108
+ <View />
109
+ <A11yText text="Some inserted A11yText" />
110
+ <View />
111
+ Sixth string
112
+ {12345}
113
+ <A11yText text="Some inserted A11yText" />
114
+ Seventh string
115
+ </TestComponent>
116
+ )
117
+ expect(queryAllByTestId('text')).toHaveLength(2)
118
+ })
119
+ it('does not double-wrap already wrapped `Text`', () => {
120
+ const { queryAllByTestId } = render(
121
+ <TestComponent>
122
+ <Text>Already wrapped</Text>
123
+ <Text>Also already wrapped</Text>
124
+ </TestComponent>
125
+ )
126
+ expect(queryAllByTestId('text')).toHaveLength(0)
127
+ })
128
+ })
@@ -1,6 +1,6 @@
1
1
  import { renderHook, act } from '@testing-library/react-hooks'
2
2
 
3
- import { useInputValue, useMultipleInputValues } from '../../lib/utils'
3
+ import { useInputValue, useMultipleInputValues } from '../../src/utils'
4
4
 
5
5
  /**
6
6
  * Tests for errors thrown on incorrect props usage