@telus-uds/components-base 0.0.2-prerelease.3 → 0.0.2-prerelease.7

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 (266) hide show
  1. package/.ultra.cache.json +1 -0
  2. package/CHANGELOG.md +55 -0
  3. package/__fixtures__/testTheme.js +528 -42
  4. package/__tests__/Button/ButtonBase.test.jsx +3 -32
  5. package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
  6. package/__tests__/Divider/Divider.test.jsx +26 -5
  7. package/__tests__/Feedback/Feedback.test.jsx +42 -0
  8. package/__tests__/FlexGrid/Col.test.jsx +5 -0
  9. package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
  10. package/__tests__/List/List.test.jsx +60 -0
  11. package/__tests__/Radio/Radio.test.jsx +87 -0
  12. package/__tests__/Select/Select.test.jsx +93 -0
  13. package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
  14. package/__tests__/Spacer/Spacer.test.jsx +63 -0
  15. package/__tests__/StackView/StackView.test.jsx +216 -0
  16. package/__tests__/StackView/StackWrap.test.jsx +47 -0
  17. package/__tests__/StackView/getStackedContent.test.jsx +295 -0
  18. package/__tests__/Tags/Tags.test.jsx +328 -0
  19. package/__tests__/TextInput/TextArea.test.jsx +34 -0
  20. package/__tests__/TextInput/TextInputBase.test.jsx +120 -0
  21. package/__tests__/Tooltip/Tooltip.test.jsx +65 -0
  22. package/__tests__/Tooltip/getTooltipPosition.test.js +79 -0
  23. package/__tests__/utils/useCopy.test.js +31 -0
  24. package/__tests__/utils/useResponsiveProp.test.jsx +202 -0
  25. package/__tests__/utils/{spacing.test.jsx → useSpacingScale.test.jsx} +1 -1
  26. package/__tests__/utils/useUniqueId.test.js +31 -0
  27. package/jest.config.js +8 -2
  28. package/lib/Box/Box.js +7 -2
  29. package/lib/Button/Button.js +10 -3
  30. package/lib/Button/ButtonBase.js +79 -75
  31. package/lib/Button/ButtonGroup.js +24 -49
  32. package/lib/Button/ButtonLink.js +5 -0
  33. package/lib/Checkbox/Checkbox.js +308 -0
  34. package/lib/Checkbox/CheckboxInput.native.js +6 -0
  35. package/lib/Checkbox/CheckboxInput.web.js +57 -0
  36. package/lib/Checkbox/index.js +2 -0
  37. package/lib/Divider/Divider.js +40 -2
  38. package/lib/Feedback/Feedback.js +132 -0
  39. package/lib/Feedback/index.js +2 -0
  40. package/lib/Icon/Icon.js +9 -6
  41. package/lib/Icon/IconText.js +72 -0
  42. package/lib/Icon/index.js +2 -1
  43. package/lib/InputLabel/InputLabel.js +88 -0
  44. package/lib/InputLabel/LabelContent.native.js +8 -0
  45. package/lib/InputLabel/LabelContent.web.js +17 -0
  46. package/lib/InputLabel/index.js +2 -0
  47. package/lib/InputSupports/InputSupports.js +90 -0
  48. package/lib/InputSupports/index.js +2 -0
  49. package/lib/InputSupports/propTypes.js +55 -0
  50. package/lib/Link/ChevronLink.js +35 -10
  51. package/lib/Link/InlinePressable.native.js +78 -0
  52. package/lib/Link/InlinePressable.web.js +32 -0
  53. package/lib/Link/Link.js +11 -10
  54. package/lib/Link/LinkBase.js +69 -124
  55. package/lib/Link/TextButton.js +20 -9
  56. package/lib/Link/index.js +2 -1
  57. package/lib/List/List.js +52 -0
  58. package/lib/List/ListItem.js +207 -0
  59. package/lib/List/index.js +2 -0
  60. package/lib/Pagination/PageButton.js +3 -26
  61. package/lib/Pagination/SideButton.js +32 -42
  62. package/lib/Radio/Radio.js +291 -0
  63. package/lib/Radio/RadioInput.native.js +6 -0
  64. package/lib/Radio/RadioInput.web.js +59 -0
  65. package/lib/Radio/index.js +2 -0
  66. package/lib/Select/Group.native.js +14 -0
  67. package/lib/Select/Group.web.js +18 -0
  68. package/lib/Select/Item.native.js +9 -0
  69. package/lib/Select/Item.web.js +15 -0
  70. package/lib/Select/Picker.native.js +87 -0
  71. package/lib/Select/Picker.web.js +63 -0
  72. package/lib/Select/Select.js +272 -0
  73. package/lib/Select/index.js +6 -0
  74. package/lib/Skeleton/Skeleton.js +119 -0
  75. package/lib/Skeleton/index.js +2 -0
  76. package/lib/Spacer/Spacer.js +98 -0
  77. package/lib/Spacer/index.js +2 -0
  78. package/lib/StackView/StackView.js +107 -0
  79. package/lib/StackView/StackWrap.js +32 -0
  80. package/lib/StackView/StackWrap.native.js +3 -0
  81. package/lib/StackView/StackWrapBox.js +90 -0
  82. package/lib/StackView/StackWrapGap.js +50 -0
  83. package/lib/StackView/common.js +30 -0
  84. package/lib/StackView/getStackedContent.js +111 -0
  85. package/lib/StackView/index.js +5 -0
  86. package/lib/Tags/Tags.js +217 -0
  87. package/lib/Tags/index.js +2 -0
  88. package/lib/TextInput/TextArea.js +82 -0
  89. package/lib/TextInput/TextInput.js +54 -0
  90. package/lib/TextInput/TextInputBase.js +229 -0
  91. package/lib/TextInput/index.js +3 -0
  92. package/lib/TextInput/propTypes.js +31 -0
  93. package/lib/ThemeProvider/useThemeTokens.js +54 -3
  94. package/lib/ToggleSwitch/ToggleSwitch.js +1 -1
  95. package/lib/Tooltip/Backdrop.native.js +35 -0
  96. package/lib/Tooltip/Backdrop.web.js +52 -0
  97. package/lib/Tooltip/Tooltip.js +315 -0
  98. package/lib/Tooltip/dictionary.js +8 -0
  99. package/lib/Tooltip/getTooltipPosition.js +164 -0
  100. package/lib/Tooltip/index.js +2 -0
  101. package/lib/TooltipButton/TooltipButton.js +64 -0
  102. package/lib/TooltipButton/index.js +2 -0
  103. package/lib/Typography/Typography.js +4 -23
  104. package/lib/ViewportProvider/ViewportProvider.js +25 -0
  105. package/lib/ViewportProvider/index.js +2 -43
  106. package/lib/ViewportProvider/useViewport.js +3 -0
  107. package/lib/ViewportProvider/useViewportListener.js +43 -0
  108. package/lib/index.js +15 -1
  109. package/lib/utils/a11y/index.js +1 -0
  110. package/lib/utils/a11y/textSize.js +33 -0
  111. package/lib/utils/index.js +7 -1
  112. package/lib/utils/info/index.js +7 -0
  113. package/lib/utils/info/platform/index.js +11 -0
  114. package/lib/utils/info/platform/platform.android.js +1 -0
  115. package/lib/utils/info/platform/platform.ios.js +1 -0
  116. package/lib/utils/info/platform/platform.native.js +4 -0
  117. package/lib/utils/info/platform/platform.web.js +1 -0
  118. package/lib/utils/info/versions.js +5 -0
  119. package/lib/utils/input.js +3 -1
  120. package/lib/utils/pressability.js +92 -0
  121. package/lib/utils/propTypes.js +77 -8
  122. package/lib/utils/useCopy.js +16 -0
  123. package/lib/utils/useResponsiveProp.js +47 -0
  124. package/lib/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +30 -9
  125. package/lib/utils/useUniqueId.js +12 -0
  126. package/package.json +7 -5
  127. package/release-context.json +4 -4
  128. package/src/Box/Box.jsx +4 -2
  129. package/src/Button/Button.jsx +6 -3
  130. package/src/Button/ButtonBase.jsx +72 -75
  131. package/src/Button/ButtonGroup.jsx +22 -39
  132. package/src/Button/ButtonLink.jsx +11 -2
  133. package/src/Checkbox/Checkbox.jsx +275 -0
  134. package/src/Checkbox/CheckboxInput.native.jsx +6 -0
  135. package/src/Checkbox/CheckboxInput.web.jsx +55 -0
  136. package/src/Checkbox/index.js +3 -0
  137. package/src/Divider/Divider.jsx +38 -3
  138. package/src/Feedback/Feedback.jsx +108 -0
  139. package/src/Feedback/index.js +3 -0
  140. package/src/Icon/Icon.jsx +11 -6
  141. package/src/Icon/IconText.jsx +63 -0
  142. package/src/Icon/index.js +2 -1
  143. package/src/InputLabel/InputLabel.jsx +99 -0
  144. package/src/InputLabel/LabelContent.native.jsx +6 -0
  145. package/src/InputLabel/LabelContent.web.jsx +13 -0
  146. package/src/InputLabel/index.js +3 -0
  147. package/src/InputSupports/InputSupports.jsx +86 -0
  148. package/src/InputSupports/index.js +3 -0
  149. package/src/InputSupports/propTypes.js +44 -0
  150. package/src/Link/ChevronLink.jsx +28 -7
  151. package/src/Link/InlinePressable.native.jsx +73 -0
  152. package/src/Link/InlinePressable.web.jsx +37 -0
  153. package/src/Link/Link.jsx +17 -13
  154. package/src/Link/LinkBase.jsx +62 -139
  155. package/src/Link/TextButton.jsx +25 -11
  156. package/src/Link/index.js +2 -1
  157. package/src/List/List.jsx +47 -0
  158. package/src/List/ListItem.jsx +187 -0
  159. package/src/List/index.js +3 -0
  160. package/src/Pagination/PageButton.jsx +3 -17
  161. package/src/Pagination/SideButton.jsx +27 -38
  162. package/src/Radio/Radio.jsx +270 -0
  163. package/src/Radio/RadioInput.native.jsx +6 -0
  164. package/src/Radio/RadioInput.web.jsx +57 -0
  165. package/src/Radio/index.js +3 -0
  166. package/src/Select/Group.native.jsx +14 -0
  167. package/src/Select/Group.web.jsx +15 -0
  168. package/src/Select/Item.native.jsx +10 -0
  169. package/src/Select/Item.web.jsx +11 -0
  170. package/src/Select/Picker.native.jsx +95 -0
  171. package/src/Select/Picker.web.jsx +67 -0
  172. package/src/Select/Select.jsx +265 -0
  173. package/src/Select/index.js +8 -0
  174. package/src/Skeleton/Skeleton.jsx +101 -0
  175. package/src/Skeleton/index.js +3 -0
  176. package/src/Spacer/Spacer.jsx +91 -0
  177. package/src/Spacer/index.js +3 -0
  178. package/src/StackView/StackView.jsx +104 -0
  179. package/src/StackView/StackWrap.jsx +33 -0
  180. package/src/StackView/StackWrap.native.jsx +4 -0
  181. package/src/StackView/StackWrapBox.jsx +93 -0
  182. package/src/StackView/StackWrapGap.jsx +49 -0
  183. package/src/StackView/common.jsx +28 -0
  184. package/src/StackView/getStackedContent.jsx +106 -0
  185. package/src/StackView/index.js +6 -0
  186. package/src/Tags/Tags.jsx +206 -0
  187. package/src/Tags/index.js +3 -0
  188. package/src/TextInput/TextArea.jsx +78 -0
  189. package/src/TextInput/TextInput.jsx +52 -0
  190. package/src/TextInput/TextInputBase.jsx +220 -0
  191. package/src/TextInput/index.js +4 -0
  192. package/src/TextInput/propTypes.js +29 -0
  193. package/src/ThemeProvider/useThemeTokens.js +54 -3
  194. package/src/ToggleSwitch/ToggleSwitch.jsx +1 -1
  195. package/src/Tooltip/Backdrop.native.jsx +33 -0
  196. package/src/Tooltip/Backdrop.web.jsx +60 -0
  197. package/src/Tooltip/Tooltip.jsx +294 -0
  198. package/src/Tooltip/dictionary.js +8 -0
  199. package/src/Tooltip/getTooltipPosition.js +161 -0
  200. package/src/Tooltip/index.js +3 -0
  201. package/src/TooltipButton/TooltipButton.jsx +53 -0
  202. package/src/TooltipButton/index.js +3 -0
  203. package/src/Typography/Typography.jsx +4 -19
  204. package/src/ViewportProvider/ViewportProvider.jsx +21 -0
  205. package/src/ViewportProvider/index.jsx +2 -41
  206. package/src/ViewportProvider/useViewport.js +5 -0
  207. package/src/ViewportProvider/useViewportListener.js +43 -0
  208. package/src/index.js +15 -1
  209. package/src/utils/a11y/index.js +1 -0
  210. package/src/utils/a11y/textSize.js +30 -0
  211. package/src/utils/index.js +8 -1
  212. package/src/utils/info/index.js +8 -0
  213. package/src/utils/info/platform/index.js +11 -0
  214. package/src/utils/info/platform/platform.android.js +1 -0
  215. package/src/utils/info/platform/platform.ios.js +1 -0
  216. package/src/utils/info/platform/platform.native.js +4 -0
  217. package/src/utils/info/platform/platform.web.js +1 -0
  218. package/src/utils/info/versions.js +6 -0
  219. package/src/utils/input.js +2 -1
  220. package/src/utils/pressability.js +92 -0
  221. package/src/utils/propTypes.js +97 -13
  222. package/src/utils/useCopy.js +13 -0
  223. package/src/utils/useResponsiveProp.js +50 -0
  224. package/src/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +25 -10
  225. package/src/utils/useUniqueId.js +14 -0
  226. package/stories/A11yText/A11yText.stories.jsx +11 -5
  227. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
  228. package/stories/Box/Box.stories.jsx +29 -2
  229. package/stories/Button/Button.stories.jsx +21 -20
  230. package/stories/Button/ButtonGroup.stories.jsx +2 -1
  231. package/stories/Button/ButtonLink.stories.jsx +6 -4
  232. package/stories/Card/Card.stories.jsx +13 -1
  233. package/stories/Checkbox/Checkbox.stories.jsx +71 -0
  234. package/stories/Divider/Divider.stories.jsx +26 -2
  235. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
  236. package/stories/Feedback/Feedback.stories.jsx +96 -0
  237. package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
  238. package/stories/Icon/Icon.stories.jsx +11 -3
  239. package/stories/InputLabel/InputLabel.stories.jsx +42 -0
  240. package/stories/Link/ChevronLink.stories.jsx +20 -4
  241. package/stories/Link/Link.stories.jsx +39 -3
  242. package/stories/Link/TextButton.stories.jsx +24 -2
  243. package/stories/List/List.stories.jsx +117 -0
  244. package/stories/Pagination/Pagination.stories.jsx +28 -14
  245. package/stories/Radio/Radio.stories.jsx +113 -0
  246. package/stories/Select/Select.stories.jsx +55 -0
  247. package/stories/SideNav/SideNav.stories.jsx +17 -2
  248. package/stories/Skeleton/Skeleton.stories.jsx +36 -0
  249. package/stories/Spacer/Spacer.stories.jsx +38 -0
  250. package/stories/StackView/StackView.stories.jsx +75 -0
  251. package/stories/StackView/StackWrap.stories.jsx +64 -0
  252. package/stories/Tags/Tags.stories.jsx +69 -0
  253. package/stories/TextInput/TextArea.stories.jsx +100 -0
  254. package/stories/TextInput/TextInput.stories.jsx +103 -0
  255. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
  256. package/stories/Tooltip/Tooltip.stories.jsx +81 -0
  257. package/stories/TooltipButton/TooltipButton.stories.jsx +11 -0
  258. package/stories/Typography/Typography.stories.jsx +12 -3
  259. package/stories/platform-supports.web.jsx +1 -1
  260. package/stories/supports.jsx +110 -14
  261. package/lib/Pagination/useCopy.js +0 -10
  262. package/lib/utils/spacing/index.js +0 -2
  263. package/lib/utils/spacing/utils.js +0 -32
  264. package/src/Pagination/useCopy.js +0 -7
  265. package/src/utils/spacing/index.js +0 -3
  266. package/src/utils/spacing/utils.js +0 -28
@@ -0,0 +1,328 @@
1
+ import React from 'react'
2
+ import { render, fireEvent } from '@testing-library/react-native'
3
+ import { toHaveTextContent } from '@testing-library/jest-native'
4
+
5
+ import Theme from '../../__fixtures__/Theme'
6
+ import Tags from '../../src/Tags'
7
+
8
+ const items = [
9
+ { label: 'One', id: 'one', accessibilityLabel: 'Option one' },
10
+ { label: 'Two', id: 'two', accessibilityLabel: 'Option two' },
11
+ { label: 'Three', id: 'three', accessibilityLabel: 'Option three' },
12
+ { label: 'Four', id: 'four', accessibilityLabel: 'Option four' }
13
+ ]
14
+ const checked = { checked: true }
15
+
16
+ // expect().toHaveTextContent doesn't work on arrays, expect().toContain etc doesn't match elements
17
+ const containsText = (queryResult, text) =>
18
+ queryResult.some((testInstance) => toHaveTextContent(testInstance, text).pass)
19
+
20
+ describe('Tags', () => {
21
+ // eslint-disable-next-line no-console
22
+ const consoleError = console.error
23
+ beforeEach(() => {
24
+ // eslint-disable-next-line no-console
25
+ console.error = () => {}
26
+ })
27
+ afterEach(() => {
28
+ // eslint-disable-next-line no-console
29
+ console.error = consoleError
30
+ })
31
+
32
+ it('Throws if has `values` without `onChange`', () => {
33
+ expect(() =>
34
+ render(
35
+ <Theme>
36
+ <Tags items={items} values={['one']} />
37
+ </Theme>
38
+ )
39
+ ).toThrow(/values.+without.+onChange/)
40
+ })
41
+ it("Doesn't throw if has `values` without `onChange` and is read only", () => {
42
+ expect(() =>
43
+ render(
44
+ <Theme>
45
+ <Tags items={items} values={['one']} readOnly />
46
+ </Theme>
47
+ )
48
+ ).not.toThrow()
49
+ })
50
+ it('Throws if has both `values` and `initialValues`', () => {
51
+ expect(() =>
52
+ render(
53
+ <Theme>
54
+ <Tags items={items} values={['one']} initialValues={['one']} onChange={() => {}} />
55
+ </Theme>
56
+ )
57
+ ).toThrow(/both(?=.*initialValues)(?=.*values){2}/)
58
+ })
59
+ })
60
+
61
+ describe('Tags (uncontrolled)', () => {
62
+ // These tests are almost the same as ButtonGroup while Tags-specific behaviours are still TBC
63
+ it('Selects one and only one item if maxValues is passed', async () => {
64
+ const { getByText, queryAllByA11yState } = render(
65
+ <Theme>
66
+ <Tags items={items} maxValues={1} />
67
+ </Theme>
68
+ )
69
+
70
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
71
+
72
+ const one = getByText('One')
73
+ await fireEvent.press(one)
74
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
75
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
76
+
77
+ const two = getByText('Two')
78
+ await fireEvent.press(two)
79
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
80
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
81
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
82
+ })
83
+
84
+ it('Deselects if the selected item is pressed', async () => {
85
+ const { getByText, queryAllByA11yState } = render(
86
+ <Theme>
87
+ <Tags items={items} />
88
+ </Theme>
89
+ )
90
+
91
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
92
+
93
+ const three = getByText('Three')
94
+ await fireEvent.press(three)
95
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
96
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
97
+
98
+ await fireEvent.press(three)
99
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
100
+ expect(containsText(queryAllByA11yState(checked), 'Three')).not.toBeTruthy()
101
+ })
102
+
103
+ it('Selects <= two items when maxValues === 2', async () => {
104
+ const { getByText, queryAllByA11yState } = render(
105
+ <Theme>
106
+ <Tags items={items} maxValues={2} />
107
+ </Theme>
108
+ )
109
+
110
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
111
+
112
+ const one = getByText('One')
113
+ await fireEvent.press(one)
114
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
115
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
116
+
117
+ const two = getByText('Two')
118
+ await fireEvent.press(two)
119
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
120
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
121
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
122
+
123
+ const three = getByText('Three')
124
+ await fireEvent.press(three)
125
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
126
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
127
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
128
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
129
+
130
+ const four = getByText('Four')
131
+ await fireEvent.press(four)
132
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
133
+ expect(containsText(queryAllByA11yState(checked), 'Four')).toBeTruthy()
134
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
135
+ expect(containsText(queryAllByA11yState(checked), 'Two')).not.toBeTruthy()
136
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
137
+ })
138
+
139
+ it('Is accessible as checkboxes by default', async () => {
140
+ const { queryAllByA11yRole, queryAllByA11yState } = render(
141
+ <Theme>
142
+ <Tags items={items} />
143
+ </Theme>
144
+ )
145
+
146
+ expect(queryAllByA11yRole('radioGroup')).toHaveLength(0)
147
+ expect(queryAllByA11yRole('radio')).toHaveLength(0)
148
+
149
+ const checks = queryAllByA11yRole('checkbox')
150
+ expect(checks).toHaveLength(4)
151
+
152
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
153
+ await fireEvent.press(checks[0])
154
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
155
+ })
156
+
157
+ it('Selects unlimited items by default', async () => {
158
+ const { getByText, queryAllByA11yState } = render(
159
+ <Theme>
160
+ <Tags items={items} />
161
+ </Theme>
162
+ )
163
+
164
+ expect(queryAllByA11yState(checked)).toHaveLength(0)
165
+
166
+ const one = getByText('One')
167
+ await fireEvent.press(one)
168
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
169
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
170
+
171
+ const two = getByText('Two')
172
+ await fireEvent.press(two)
173
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
174
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
175
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
176
+
177
+ const three = getByText('Three')
178
+ await fireEvent.press(three)
179
+ expect(queryAllByA11yState(checked)).toHaveLength(3)
180
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
181
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
182
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
183
+
184
+ const four = getByText('Four')
185
+ await fireEvent.press(four)
186
+ expect(queryAllByA11yState(checked)).toHaveLength(4)
187
+ expect(containsText(queryAllByA11yState(checked), 'Four')).toBeTruthy()
188
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
189
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
190
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
191
+ })
192
+
193
+ it('Auto-selects any provided initialValues', async () => {
194
+ const { getByText, queryAllByA11yState } = render(
195
+ <Theme>
196
+ <Tags items={items} initialValues={['one', 'two']} maxValues={3} />
197
+ </Theme>
198
+ )
199
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
200
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
201
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
202
+
203
+ const three = getByText('Three')
204
+ await fireEvent.press(three)
205
+
206
+ expect(queryAllByA11yState(checked)).toHaveLength(3)
207
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
208
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
209
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
210
+
211
+ const four = getByText('Four')
212
+ await fireEvent.press(four)
213
+ expect(queryAllByA11yState(checked)).toHaveLength(3)
214
+ expect(containsText(queryAllByA11yState(checked), 'Four')).toBeTruthy()
215
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
216
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
217
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
218
+
219
+ const two = getByText('Two')
220
+ await fireEvent.press(two)
221
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
222
+ expect(containsText(queryAllByA11yState(checked), 'Four')).toBeTruthy()
223
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
224
+ expect(containsText(queryAllByA11yState(checked), 'Two')).not.toBeTruthy()
225
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
226
+ })
227
+ })
228
+
229
+ describe('Tags (controlled)', () => {
230
+ it('Calls onChange handler on press in controlled mode, providing pressed id', async () => {
231
+ const handleChange = jest.fn((arg) => arg)
232
+ const { getByText } = render(
233
+ <Theme>
234
+ <Tags items={items} values={[]} onChange={handleChange} />
235
+ </Theme>
236
+ )
237
+
238
+ expect(handleChange).toHaveBeenCalledTimes(0)
239
+
240
+ const one = getByText('One')
241
+ await fireEvent.press(one)
242
+ expect(handleChange).toHaveBeenCalledTimes(1)
243
+
244
+ expect(handleChange.mock.calls[0][0]).toEqual(['one'])
245
+
246
+ const two = getByText('Two')
247
+ await fireEvent.press(two)
248
+ expect(handleChange).toHaveBeenCalledTimes(2)
249
+ expect(handleChange.mock.calls[1][0]).toEqual(['two'])
250
+ })
251
+
252
+ it("Doesn't change its own selection if `values` is passed", async () => {
253
+ const { getByText, queryAllByA11yState } = render(
254
+ <Theme>
255
+ <Tags items={items} values={['one']} onChange={() => {}} />
256
+ </Theme>
257
+ )
258
+
259
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
260
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
261
+
262
+ const one = getByText('One')
263
+ await fireEvent.press(one)
264
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
265
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
266
+
267
+ const two = getByText('Two')
268
+ await fireEvent.press(two)
269
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
270
+ expect(containsText(queryAllByA11yState(checked), 'Two')).not.toBeTruthy()
271
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
272
+ })
273
+
274
+ it('Does not render an invalid `values` invalidly', async () => {
275
+ const { queryAllByA11yState } = render(
276
+ <Theme>
277
+ <Tags items={items} values={['one', 'two', 'three']} maxValues={2} onChange={() => {}} />
278
+ </Theme>
279
+ )
280
+ expect(queryAllByA11yState(checked)).toHaveLength(2)
281
+ expect(containsText(queryAllByA11yState(checked), 'One')).not.toBeTruthy()
282
+ expect(containsText(queryAllByA11yState(checked), 'Two')).toBeTruthy()
283
+ expect(containsText(queryAllByA11yState(checked), 'Three')).toBeTruthy()
284
+ })
285
+ })
286
+
287
+ describe('Tags (read-only)', () => {
288
+ it("Doesn't call onChange handler when read-only", async () => {
289
+ const handleChange = jest.fn((arg) => arg)
290
+ const { getByText } = render(
291
+ <Theme>
292
+ <Tags items={items} values={[]} onChange={handleChange} readOnly />
293
+ </Theme>
294
+ )
295
+
296
+ expect(handleChange).toHaveBeenCalledTimes(0)
297
+
298
+ const one = getByText('One')
299
+ await fireEvent.press(one)
300
+ expect(handleChange).toHaveBeenCalledTimes(0)
301
+
302
+ const two = getByText('Two')
303
+ await fireEvent.press(two)
304
+ expect(handleChange).toHaveBeenCalledTimes(0)
305
+ })
306
+
307
+ it("Doesn't change its selection on press", async () => {
308
+ const { getByText, queryAllByA11yState } = render(
309
+ <Theme>
310
+ <Tags items={items} values={['one']} readOnly />
311
+ </Theme>
312
+ )
313
+
314
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
315
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
316
+
317
+ const one = getByText('One')
318
+ await fireEvent.press(one)
319
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
320
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
321
+
322
+ const two = getByText('Two')
323
+ await fireEvent.press(two)
324
+ expect(queryAllByA11yState(checked)).toHaveLength(1)
325
+ expect(containsText(queryAllByA11yState(checked), 'Two')).not.toBeTruthy()
326
+ expect(containsText(queryAllByA11yState(checked), 'One')).toBeTruthy()
327
+ })
328
+ })
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import { fireEvent, render } from '@testing-library/react-native'
3
+
4
+ import { Platform } from 'react-native'
5
+ import { TextArea } from '../../src'
6
+ import Theme from '../../__fixtures__/Theme'
7
+
8
+ describe('TextArea', () => {
9
+ it('renders', () => {
10
+ // at least one called test is required in a suite
11
+ render(<TextArea />, { wrapper: Theme })
12
+ })
13
+
14
+ // TODO: this won't be called until cross-platform Jest tests are configured
15
+ // see https://github.com/telus/universal-design-system/issues/319
16
+ if (Platform.OS === 'web') {
17
+ it('grows in size on input', () => {
18
+ const { getByTestId } = render(
19
+ <TextArea tokens={{ minLines: 1, maxLines: 5, lineHeight: 20 }} testID="textarea" />,
20
+ {
21
+ wrapper: Theme
22
+ }
23
+ )
24
+
25
+ const textarea = getByTestId('textarea')
26
+
27
+ expect(textarea).toHaveStyle({ height: 20 })
28
+
29
+ fireEvent.changeText(textarea, '\n\n\n')
30
+
31
+ expect(textarea).toHaveStyle({ height: 60 })
32
+ })
33
+ }
34
+ })
@@ -0,0 +1,120 @@
1
+ import { fireEvent, render } from '@testing-library/react-native'
2
+ import React from 'react'
3
+ import TextInputBase from '../../src/TextInput/TextInputBase'
4
+ import Viewport from '../../__fixtures__/Viewport'
5
+ import Theme from '../../__fixtures__/Theme'
6
+
7
+ // eslint-disable-next-line react/prop-types
8
+ const Wrapper = ({ children }) => (
9
+ <Viewport viewport="xs">
10
+ <Theme>{children}</Theme>
11
+ </Viewport>
12
+ )
13
+
14
+ describe('TextInputBaseBase', () => {
15
+ it('triggers the interactive callbacks', () => {
16
+ const onFocus = jest.fn()
17
+ const onBlur = jest.fn()
18
+ const onMouseOver = jest.fn()
19
+ const onMouseOut = jest.fn()
20
+
21
+ const { getByA11yLabel } = render(
22
+ <TextInputBase
23
+ accessibilityLabel="Input label"
24
+ onFocus={onFocus}
25
+ onBlur={onBlur}
26
+ onMouseOver={onMouseOver}
27
+ onMouseOut={onMouseOut}
28
+ />,
29
+ {
30
+ wrapper: Wrapper
31
+ }
32
+ )
33
+
34
+ const input = getByA11yLabel('Input label')
35
+
36
+ expect(onFocus).not.toHaveBeenCalled()
37
+ fireEvent(input, 'focus')
38
+ expect(onFocus).toHaveBeenCalledTimes(1)
39
+
40
+ expect(onBlur).not.toHaveBeenCalled()
41
+ fireEvent(input, 'blur')
42
+ expect(onBlur).toHaveBeenCalledTimes(1)
43
+
44
+ expect(onMouseOver).not.toHaveBeenCalled()
45
+ fireEvent(input, 'mouseOver')
46
+ expect(onMouseOver).toHaveBeenCalledTimes(1)
47
+
48
+ expect(onMouseOut).not.toHaveBeenCalled()
49
+ fireEvent(input, 'mouseOut')
50
+ expect(onMouseOut).toHaveBeenCalledTimes(1)
51
+ })
52
+
53
+ it("can't be edited when inactive", () => {
54
+ const { getByA11yLabel } = render(<TextInputBase accessibilityLabel="Input label" inactive />, {
55
+ wrapper: Wrapper
56
+ })
57
+
58
+ const input = getByA11yLabel('Input label')
59
+
60
+ expect(input).toHaveProp('editable', false)
61
+ })
62
+
63
+ it('changes value when uncontrolled', () => {
64
+ const onChange = jest.fn()
65
+
66
+ const { getByA11yLabel } = render(
67
+ <TextInputBase accessibilityLabel="Input label" onChange={onChange} />,
68
+ {
69
+ wrapper: Wrapper
70
+ }
71
+ )
72
+
73
+ const input = getByA11yLabel('Input label')
74
+
75
+ expect(onChange).not.toHaveBeenCalled()
76
+ fireEvent.changeText(input, 'new value')
77
+ expect(onChange).toHaveBeenCalledTimes(1)
78
+ expect(onChange).toHaveBeenLastCalledWith('new value')
79
+ })
80
+
81
+ it('changes value when controlled', () => {
82
+ const onChange = jest.fn()
83
+
84
+ const { getByA11yLabel } = render(
85
+ <TextInputBase accessibilityLabel="Input label" onChange={onChange} value="initial value" />,
86
+ {
87
+ wrapper: Wrapper
88
+ }
89
+ )
90
+
91
+ const input = getByA11yLabel('Input label')
92
+
93
+ expect(onChange).not.toHaveBeenCalled()
94
+ fireEvent.changeText(input, 'new value')
95
+ expect(onChange).toHaveBeenCalledTimes(1)
96
+ expect(onChange).toHaveBeenLastCalledWith('new value')
97
+ })
98
+
99
+ it("doesn't change value when readOnly", () => {
100
+ const onChange = jest.fn()
101
+
102
+ const { getByA11yLabel } = render(
103
+ <TextInputBase
104
+ accessibilityLabel="Input label"
105
+ onChange={onChange}
106
+ value="initial value"
107
+ readOnly
108
+ />,
109
+ {
110
+ wrapper: Wrapper
111
+ }
112
+ )
113
+
114
+ const input = getByA11yLabel('Input label')
115
+
116
+ expect(onChange).not.toHaveBeenCalled()
117
+ fireEvent.changeText(input, 'new value')
118
+ expect(onChange).not.toHaveBeenCalled()
119
+ })
120
+ })
@@ -0,0 +1,65 @@
1
+ import React from 'react'
2
+ import { fireEvent, render } from '@testing-library/react-native'
3
+
4
+ import { Text } from 'react-native'
5
+ import { Tooltip } from '../../src'
6
+
7
+ import Theme from '../../__fixtures__/Theme'
8
+ import Viewport from '../../__fixtures__/Viewport'
9
+
10
+ const wrapper = ({ children }) => (
11
+ <Theme>
12
+ <Viewport>{children}</Viewport>
13
+ </Theme>
14
+ )
15
+
16
+ const content = 'tooltip content'
17
+
18
+ describe('Tooltip', () => {
19
+ it('renders content after control is pressed', () => {
20
+ const { queryByText, getByA11yRole } = render(<Tooltip content={content} />, { wrapper })
21
+
22
+ expect(queryByText(content)).toBeFalsy()
23
+
24
+ fireEvent.press(getByA11yRole('button'))
25
+
26
+ expect(queryByText(content)).not.toBeFalsy()
27
+ })
28
+
29
+ it("renders custom control, and renders content after it's pressed", () => {
30
+ const { queryByTestId, getByA11yRole, queryByText } = render(
31
+ <Tooltip content={content}>
32
+ <Text testID="control" />
33
+ </Tooltip>,
34
+ { wrapper }
35
+ )
36
+
37
+ expect(queryByTestId('control')).not.toBeFalsy()
38
+
39
+ expect(queryByText(content)).toBeFalsy()
40
+
41
+ fireEvent.press(getByA11yRole('button'))
42
+
43
+ expect(queryByText(content)).not.toBeFalsy()
44
+ })
45
+
46
+ it("renders custom control render function, and renders content after it's pressed", () => {
47
+ const { queryByTestId, getByA11yRole, queryByText } = render(
48
+ <Tooltip content={content} variant={{ inverse: true }}>
49
+ {(pressableState, variant) => (
50
+ <Text testID="control">{variant.inverse ? 'Inverse' : 'Default'}</Text>
51
+ )}
52
+ </Tooltip>,
53
+ { wrapper }
54
+ )
55
+
56
+ expect(queryByTestId('control')).not.toBeFalsy()
57
+ expect(queryByText('Inverse')).not.toBeFalsy()
58
+
59
+ expect(queryByText(content)).toBeFalsy()
60
+
61
+ fireEvent.press(getByA11yRole('button'))
62
+
63
+ expect(queryByText(content)).not.toBeFalsy()
64
+ })
65
+ })
@@ -0,0 +1,79 @@
1
+ import getTooltipPosition from '../../lib/Tooltip/getTooltipPosition'
2
+
3
+ const windowDimensions = {
4
+ width: 320,
5
+ height: 400
6
+ }
7
+
8
+ const tooltipDimensions = {
9
+ width: 200,
10
+ height: 40
11
+ }
12
+
13
+ describe('getTooltipPosition()', () => {
14
+ it('automatically places the tooltip within viewport', () => {
15
+ const controlLayout = {
16
+ right: { x: 20, y: 20 }, // control in top-left corner
17
+ left: { x: windowDimensions.width - 20, y: 20 }, // control in top-right corner
18
+ above: { x: windowDimensions.width / 2, y: windowDimensions.height - 20 }, // control in bottom center
19
+ below: { x: windowDimensions.width / 2, y: 20 } // control in top center
20
+ }
21
+
22
+ Object.keys(controlLayout).forEach((position) => {
23
+ const result = getTooltipPosition('auto', {
24
+ controlLayout: { width: 20, height: 20, ...controlLayout[position] },
25
+ tooltipDimensions,
26
+ windowDimensions
27
+ })
28
+
29
+ expect(result.position).toBe(position)
30
+ })
31
+ })
32
+
33
+ it('places the tooltip in the position from prop if that fits within viewport', () => {
34
+ const controlLayout = {
35
+ right: { x: 20, y: 20 }, // control in top-left corner
36
+ left: { x: windowDimensions.width - 20, y: 20 }, // control in top-right corner
37
+ above: { x: windowDimensions.width / 2, y: windowDimensions.height - 20 }, // control in bottom center
38
+ below: { x: windowDimensions.width / 2, y: 20 } // control in top center
39
+ }
40
+
41
+ Object.keys(controlLayout).forEach((position) => {
42
+ const positionProp = position // this would normally come from the component's prop
43
+
44
+ const result = getTooltipPosition(positionProp, {
45
+ controlLayout: { width: 20, height: 20, ...controlLayout[position] },
46
+ tooltipDimensions,
47
+ windowDimensions
48
+ })
49
+
50
+ expect(result.position).toBe(position)
51
+ })
52
+ })
53
+
54
+ it("places the tooltip in a different position if prop wouldn't fit in the viewport", () => {
55
+ const controlLayout = {
56
+ right: { x: 20, y: 20 }, // control in top-left corner
57
+ left: { x: windowDimensions.width - 20, y: 20 }, // control in top-right corner
58
+ above: { x: windowDimensions.width / 2, y: windowDimensions.height - 20 }, // control in bottom center
59
+ below: { x: windowDimensions.width / 2, y: 20 } // control in top center
60
+ }
61
+
62
+ const positionProp = {
63
+ right: 'left',
64
+ left: 'right',
65
+ above: 'below',
66
+ below: 'above'
67
+ }
68
+
69
+ Object.keys(controlLayout).forEach((position) => {
70
+ const result = getTooltipPosition(positionProp[position], {
71
+ controlLayout: { width: 20, height: 20, ...controlLayout[position] },
72
+ tooltipDimensions,
73
+ windowDimensions
74
+ })
75
+
76
+ expect(result.position).toBe(position)
77
+ })
78
+ })
79
+ })
@@ -0,0 +1,31 @@
1
+ import { renderHook } from '@testing-library/react-hooks'
2
+ import useCopy from '../../lib/utils/useCopy'
3
+
4
+ const dictionary = {
5
+ en: {
6
+ 'translation key': 'english version'
7
+ },
8
+ fr: {
9
+ 'translation key': 'french version'
10
+ }
11
+ }
12
+
13
+ describe('useCopy hook', () => {
14
+ it('returns a correct english translation', () => {
15
+ const { result } = renderHook(() => useCopy({ dictionary, copy: 'en' }))
16
+
17
+ expect(result.current('translation key')).toBe('english version')
18
+ })
19
+
20
+ it('returns a correct french translation', () => {
21
+ const { result } = renderHook(() => useCopy({ dictionary, copy: 'fr' }))
22
+
23
+ expect(result.current('translation key')).toBe('french version')
24
+ })
25
+
26
+ it('returns undefined when no translation is found', () => {
27
+ const { result } = renderHook(() => useCopy({ dictionary, copy: 'en' }))
28
+
29
+ expect(result.current('non-existing key')).toBe(undefined)
30
+ })
31
+ })