@telus-uds/components-base 0.0.2-prerelease.1 → 0.0.2-prerelease.5

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 (161) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/__fixtures__/testTheme.js +264 -84
  3. package/__tests__/Box/Box.test.jsx +81 -58
  4. package/__tests__/Card/Card.test.jsx +63 -0
  5. package/__tests__/Divider/Divider.test.jsx +26 -5
  6. package/__tests__/Feedback/Feedback.test.jsx +42 -0
  7. package/__tests__/FlexGrid/Col.test.jsx +5 -0
  8. package/__tests__/Pagination/Pagination.test.jsx +160 -0
  9. package/__tests__/Spacer/Spacer.test.jsx +63 -0
  10. package/__tests__/StackView/StackView.test.jsx +242 -0
  11. package/__tests__/StackView/StackWrap.test.jsx +47 -0
  12. package/__tests__/StackView/getStackedContent.test.jsx +295 -0
  13. package/__tests__/TextInput/TextInput.test.jsx +146 -0
  14. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +5 -3
  15. package/__tests__/utils/spacing.test.jsx +273 -0
  16. package/__tests__/utils/useUniqueId.test.js +31 -0
  17. package/babel.config.json +8 -0
  18. package/jest.config.js +7 -6
  19. package/lib/A11yInfoProvider/index.js +2 -2
  20. package/lib/A11yText/index.js +1 -3
  21. package/lib/ActivityIndicator/Spinner.web.js +3 -5
  22. package/lib/Box/Box.js +117 -82
  23. package/lib/Button/Button.js +1 -3
  24. package/lib/Button/ButtonBase.js +9 -21
  25. package/lib/Button/ButtonGroup.js +14 -25
  26. package/lib/Button/ButtonLink.js +1 -3
  27. package/lib/Card/Card.js +103 -0
  28. package/lib/Card/index.js +2 -0
  29. package/lib/Divider/Divider.js +40 -4
  30. package/lib/ExpandCollapse/Accordion.js +1 -3
  31. package/lib/ExpandCollapse/Control.js +3 -5
  32. package/lib/ExpandCollapse/Panel.js +2 -4
  33. package/lib/Feedback/Feedback.js +110 -0
  34. package/lib/Feedback/index.js +2 -0
  35. package/lib/FlexGrid/Col/Col.js +3 -5
  36. package/lib/FlexGrid/FlexGrid.js +1 -3
  37. package/lib/FlexGrid/Row/Row.js +1 -3
  38. package/lib/FlexGrid/providers/GutterContext.js +1 -1
  39. package/lib/Icon/Icon.js +1 -1
  40. package/lib/InputLabel/InputLabel.js +86 -0
  41. package/lib/InputLabel/LabelContent.native.js +8 -0
  42. package/lib/InputLabel/LabelContent.web.js +17 -0
  43. package/lib/InputLabel/index.js +2 -0
  44. package/lib/Link/ChevronLink.js +1 -3
  45. package/lib/Link/Link.js +1 -3
  46. package/lib/Link/LinkBase.js +11 -7
  47. package/lib/Link/TextButton.js +1 -3
  48. package/lib/Pagination/PageButton.js +85 -0
  49. package/lib/Pagination/Pagination.js +118 -0
  50. package/lib/Pagination/SideButton.js +108 -0
  51. package/lib/Pagination/dictionary.js +18 -0
  52. package/lib/Pagination/index.js +2 -0
  53. package/lib/Pagination/useCopy.js +10 -0
  54. package/lib/Pagination/usePagination.js +70 -0
  55. package/lib/SideNav/Item.js +4 -6
  56. package/lib/SideNav/ItemsGroup.js +11 -11
  57. package/lib/SideNav/SideNav.js +2 -4
  58. package/lib/Spacer/Spacer.js +98 -0
  59. package/lib/Spacer/index.js +2 -0
  60. package/lib/StackView/StackView.js +105 -0
  61. package/lib/StackView/StackWrap.js +32 -0
  62. package/lib/StackView/StackWrap.native.js +3 -0
  63. package/lib/StackView/StackWrapBox.js +85 -0
  64. package/lib/StackView/StackWrapGap.js +45 -0
  65. package/lib/StackView/common.js +30 -0
  66. package/lib/StackView/getStackedContent.js +111 -0
  67. package/lib/StackView/index.js +5 -0
  68. package/lib/TextInput/TextInput.js +337 -0
  69. package/lib/TextInput/index.js +2 -0
  70. package/lib/ThemeProvider/ThemeProvider.js +2 -2
  71. package/lib/ThemeProvider/useThemeTokens.js +34 -6
  72. package/lib/ThemeProvider/utils/theme-tokens.js +37 -9
  73. package/lib/ToggleSwitch/ToggleSwitch.js +17 -47
  74. package/lib/Typography/Typography.js +1 -7
  75. package/lib/ViewportProvider/index.js +1 -1
  76. package/lib/index.js +8 -1
  77. package/lib/utils/index.js +2 -1
  78. package/lib/utils/input.js +3 -1
  79. package/lib/utils/propTypes.js +103 -8
  80. package/lib/utils/spacing/index.js +2 -0
  81. package/lib/utils/spacing/useSpacingScale.js +102 -0
  82. package/lib/utils/spacing/utils.js +32 -0
  83. package/lib/utils/useUniqueId.js +12 -0
  84. package/package.json +6 -9
  85. package/release-context.json +4 -4
  86. package/src/Box/Box.jsx +117 -80
  87. package/src/Button/ButtonBase.jsx +8 -21
  88. package/src/Button/ButtonGroup.jsx +13 -17
  89. package/src/Card/Card.jsx +101 -0
  90. package/src/Card/index.js +3 -0
  91. package/src/Divider/Divider.jsx +38 -3
  92. package/src/ExpandCollapse/Control.jsx +2 -3
  93. package/src/Feedback/Feedback.jsx +99 -0
  94. package/src/Feedback/index.js +3 -0
  95. package/src/FlexGrid/Col/Col.jsx +4 -2
  96. package/src/Icon/Icon.jsx +2 -1
  97. package/src/InputLabel/InputLabel.jsx +99 -0
  98. package/src/InputLabel/LabelContent.native.jsx +6 -0
  99. package/src/InputLabel/LabelContent.web.jsx +13 -0
  100. package/src/InputLabel/index.js +3 -0
  101. package/src/Link/LinkBase.jsx +9 -3
  102. package/src/Pagination/PageButton.jsx +80 -0
  103. package/src/Pagination/Pagination.jsx +135 -0
  104. package/src/Pagination/SideButton.jsx +93 -0
  105. package/src/Pagination/dictionary.js +18 -0
  106. package/src/Pagination/index.js +3 -0
  107. package/src/Pagination/useCopy.js +7 -0
  108. package/src/Pagination/usePagination.js +69 -0
  109. package/src/SideNav/Item.jsx +3 -3
  110. package/src/SideNav/ItemsGroup.jsx +11 -13
  111. package/src/Spacer/Spacer.jsx +91 -0
  112. package/src/Spacer/index.js +3 -0
  113. package/src/StackView/StackView.jsx +103 -0
  114. package/src/StackView/StackWrap.jsx +33 -0
  115. package/src/StackView/StackWrap.native.jsx +4 -0
  116. package/src/StackView/StackWrapBox.jsx +82 -0
  117. package/src/StackView/StackWrapGap.jsx +39 -0
  118. package/src/StackView/common.jsx +28 -0
  119. package/src/StackView/getStackedContent.jsx +106 -0
  120. package/src/StackView/index.js +6 -0
  121. package/src/TextInput/TextInput.jsx +325 -0
  122. package/src/TextInput/index.js +3 -0
  123. package/src/ThemeProvider/useThemeTokens.js +34 -7
  124. package/src/ThemeProvider/utils/theme-tokens.js +37 -8
  125. package/src/ToggleSwitch/ToggleSwitch.jsx +23 -43
  126. package/src/Typography/Typography.jsx +0 -4
  127. package/src/index.js +8 -1
  128. package/src/utils/index.js +1 -0
  129. package/src/utils/input.js +2 -1
  130. package/src/utils/propTypes.js +105 -16
  131. package/src/utils/spacing/index.js +3 -0
  132. package/src/utils/spacing/useSpacingScale.js +93 -0
  133. package/src/utils/spacing/utils.js +28 -0
  134. package/src/utils/useUniqueId.js +14 -0
  135. package/stories/A11yText/A11yText.stories.jsx +11 -5
  136. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
  137. package/stories/Box/Box.stories.jsx +46 -17
  138. package/stories/Button/Button.stories.jsx +17 -21
  139. package/stories/Button/ButtonGroup.stories.jsx +2 -1
  140. package/stories/Button/ButtonLink.stories.jsx +6 -4
  141. package/stories/Card/Card.stories.jsx +62 -0
  142. package/stories/Divider/Divider.stories.jsx +26 -2
  143. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
  144. package/stories/Feedback/Feedback.stories.jsx +97 -0
  145. package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
  146. package/stories/Icon/Icon.stories.jsx +11 -3
  147. package/stories/InputLabel/InputLabel.stories.jsx +37 -0
  148. package/stories/Link/ChevronLink.stories.jsx +20 -4
  149. package/stories/Link/Link.stories.jsx +24 -3
  150. package/stories/Link/TextButton.stories.jsx +24 -3
  151. package/stories/Pagination/Pagination.stories.jsx +64 -0
  152. package/stories/SideNav/SideNav.stories.jsx +17 -2
  153. package/stories/Spacer/Spacer.stories.jsx +33 -0
  154. package/stories/StackView/StackView.stories.jsx +65 -0
  155. package/stories/StackView/StackWrap.stories.jsx +52 -0
  156. package/stories/TextInput/TextInput.stories.jsx +103 -0
  157. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
  158. package/stories/Typography/Typography.stories.jsx +12 -3
  159. package/stories/platform-supports.web.jsx +1 -1
  160. package/stories/supports.jsx +113 -13
  161. package/babel.config.js +0 -3
@@ -0,0 +1,273 @@
1
+ import React from 'react'
2
+ import { renderHook } from '@testing-library/react-hooks'
3
+ import { useSpacingScale } from '../../src/utils/spacing'
4
+ import Theme from '../../__fixtures__/Theme'
5
+ import Viewport from '../../__fixtures__/Viewport'
6
+
7
+ describe('useSpacingScale', () => {
8
+ // values from testTheme.js
9
+ const expectedDefaultScale = [0, 4, 8, 16, 24, 32, 36]
10
+ const expectedCompactScale = [0, 2, 4, 6, 8, 10, 12]
11
+ const expectedResponsiveScale = {
12
+ xs: [0, 1, 2, 3, 4, 5, 6],
13
+ sm: [0, 3, 4, 5, 6, 7, 8],
14
+ md: [0, 3, 4, 5, 6, 7, 8],
15
+ lg: [0, 7, 8, 9, 10, 11, 12],
16
+ xl: [0, 7, 8, 9, 10, 11, 12]
17
+ }
18
+
19
+ const wrapper = ({ children, viewport }) => (
20
+ <Viewport viewport={viewport}>
21
+ <Theme>{children}</Theme>
22
+ </Viewport>
23
+ )
24
+
25
+ it.each(expectedDefaultScale.map((item, index) => [item, index]))(
26
+ 'returns the expected size index %# from the theme spacing scale',
27
+ (expectedSize, index) => {
28
+ const { result } = renderHook(() => useSpacingScale(index), {
29
+ wrapper
30
+ })
31
+ expect(result.current).toBe(expectedSize)
32
+ }
33
+ )
34
+ it.each(expectedCompactScale.map((item, index) => [item, index]))(
35
+ 'returns the expected size index %# from the theme spacing scale in a variant',
36
+ (expectedSize, index) => {
37
+ const { result } = renderHook(
38
+ () => useSpacingScale({ space: index, options: { variant: { compact: true } } }),
39
+ {
40
+ wrapper
41
+ }
42
+ )
43
+ expect(result.current).toBe(expectedSize)
44
+ }
45
+ )
46
+ it.each(Object.entries(expectedResponsiveScale))(
47
+ 'returns appropriate sizes from a responsive theme variant at %p viewport',
48
+ (viewport, expectedValues) => {
49
+ const { result, rerender } = renderHook(({ spacingValue }) => useSpacingScale(spacingValue), {
50
+ wrapper
51
+ })
52
+ expectedValues.forEach((expectedSize, index) => {
53
+ rerender({
54
+ viewport,
55
+ spacingValue: { space: index, options: { variant: { responsive: true } } }
56
+ })
57
+ expect(result.current).toBe(expectedSize)
58
+ })
59
+ }
60
+ )
61
+
62
+ it('returns the 0 index when passed a nullish spacing value', () => {
63
+ const { result, rerender } = renderHook((spacingValue) => useSpacingScale(spacingValue), {
64
+ wrapper
65
+ })
66
+ // implicit undefined
67
+ expect(result.current).toBe(expectedDefaultScale[0])
68
+ // explicit undefined
69
+ rerender(undefined)
70
+ expect(result.current).toBe(expectedDefaultScale[0])
71
+ rerender(null)
72
+ expect(result.current).toBe(expectedDefaultScale[0])
73
+
74
+ // confim this test isn't a false positive misuing rerender etc
75
+ rerender(1)
76
+ expect(result.current).toBe(expectedDefaultScale[1])
77
+ })
78
+ it('returns the 0 index when passed a nullish spacing value in a variant', () => {
79
+ const { result, rerender } = renderHook((spacingValue) => useSpacingScale(spacingValue), {
80
+ wrapper,
81
+ initialProps: { options: { variant: { compact: true } } }
82
+ })
83
+ // implicit undefined
84
+ expect(result.current).toBe(expectedCompactScale[0])
85
+ // explicit undefined
86
+ rerender({ space: undefined, options: { variant: { compact: true } } })
87
+ expect(result.current).toBe(expectedCompactScale[0])
88
+ rerender({ space: null, options: { variant: { compact: true } } })
89
+ expect(result.current).toBe(expectedCompactScale[0])
90
+
91
+ // confim this test isn't a false positive misuing rerender etc
92
+ rerender({ space: 1, options: { variant: { compact: true } } })
93
+ expect(result.current).toBe(expectedCompactScale[1])
94
+ })
95
+
96
+ it('returns the highest index when passed an out-of-bounds spacing index', () => {
97
+ const { result } = renderHook(() => useSpacingScale(99), {
98
+ wrapper
99
+ })
100
+ expect(result.current).toBe(expectedDefaultScale[expectedDefaultScale.length - 1])
101
+ })
102
+ it('returns the highest index when passed an out-of-bounds spacing index in a variant', () => {
103
+ const { result } = renderHook(
104
+ () => useSpacingScale({ space: 99, options: { variant: { compact: true } } }),
105
+ {
106
+ wrapper
107
+ }
108
+ )
109
+ expect(result.current).toBe(expectedCompactScale[expectedDefaultScale.length - 1])
110
+ })
111
+
112
+ it('returns viewport indexes that explicitly match current viewport', () => {
113
+ const spacingValue = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
114
+ const { rerender, result } = renderHook((args) => useSpacingScale(args.spacingValue), {
115
+ wrapper,
116
+ initialProps: { viewport: 'xs', spacingValue }
117
+ })
118
+ expect(result.current).toBe(expectedDefaultScale[1])
119
+ rerender({ viewport: 'sm', spacingValue })
120
+ expect(result.current).toBe(expectedDefaultScale[2])
121
+ rerender({ viewport: 'md', spacingValue })
122
+ expect(result.current).toBe(expectedDefaultScale[3])
123
+ rerender({ viewport: 'lg', spacingValue })
124
+ expect(result.current).toBe(expectedDefaultScale[4])
125
+ rerender({ viewport: 'xl', spacingValue })
126
+ expect(result.current).toBe(expectedDefaultScale[5])
127
+ })
128
+ it('returns viewport indexes that explicitly match current viewport in a variant', () => {
129
+ const spacingValue = {
130
+ xs: 1,
131
+ sm: 2,
132
+ md: 3,
133
+ lg: 4,
134
+ xl: 5,
135
+ options: { variant: { compact: true } }
136
+ }
137
+ const { rerender, result } = renderHook((args) => useSpacingScale(args.spacingValue), {
138
+ wrapper,
139
+ initialProps: { viewport: 'xs', spacingValue }
140
+ })
141
+ expect(result.current).toBe(expectedCompactScale[1])
142
+ rerender({ viewport: 'sm', spacingValue })
143
+ expect(result.current).toBe(expectedCompactScale[2])
144
+ rerender({ viewport: 'md', spacingValue })
145
+ expect(result.current).toBe(expectedCompactScale[3])
146
+ rerender({ viewport: 'lg', spacingValue })
147
+ expect(result.current).toBe(expectedCompactScale[4])
148
+ rerender({ viewport: 'xl', spacingValue })
149
+ expect(result.current).toBe(expectedCompactScale[5])
150
+ })
151
+
152
+ it('returns viewport indexes that implicitly match current viewport', () => {
153
+ const spacingValue = { xs: 1, md: 3 }
154
+ const { rerender, result } = renderHook((args) => useSpacingScale(args.spacingValue), {
155
+ wrapper,
156
+ initialProps: { viewport: 'xs', spacingValue }
157
+ })
158
+ expect(result.current).toBe(expectedDefaultScale[1])
159
+ rerender({ viewport: 'sm', spacingValue })
160
+ expect(result.current).toBe(expectedDefaultScale[1])
161
+ rerender({ viewport: 'md', spacingValue })
162
+ expect(result.current).toBe(expectedDefaultScale[3])
163
+ rerender({ viewport: 'lg', spacingValue })
164
+ expect(result.current).toBe(expectedDefaultScale[3])
165
+ rerender({ viewport: 'xl', spacingValue })
166
+ expect(result.current).toBe(expectedDefaultScale[3])
167
+ })
168
+ it('returns viewport indexes that implicitly match current viewport in a variant', () => {
169
+ const spacingValue = { xs: 1, md: 3, options: { variant: { compact: true } } }
170
+ const { rerender, result } = renderHook((args) => useSpacingScale(args.spacingValue), {
171
+ wrapper,
172
+ initialProps: { viewport: 'xs', spacingValue }
173
+ })
174
+ expect(result.current).toBe(expectedCompactScale[1])
175
+ rerender({ viewport: 'sm', spacingValue })
176
+ expect(result.current).toBe(expectedCompactScale[1])
177
+ rerender({ viewport: 'md', spacingValue })
178
+ expect(result.current).toBe(expectedCompactScale[3])
179
+ rerender({ viewport: 'lg', spacingValue })
180
+ expect(result.current).toBe(expectedCompactScale[3])
181
+ rerender({ viewport: 'xl', spacingValue })
182
+ expect(result.current).toBe(expectedCompactScale[3])
183
+ })
184
+
185
+ it('chooses smallest provided viewport index if viewport is unavailable', () => {
186
+ const spacingValue = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
187
+ const { result } = renderHook((args) => useSpacingScale(args.spacingValue), {
188
+ wrapper,
189
+ initialProps: { viewport: null, spacingValue }
190
+ })
191
+ expect(result.current).toBe(expectedDefaultScale[1])
192
+ })
193
+ it('chooses 0 index if current viewport matches no provided index', () => {
194
+ const spacingValue = { lg: 4 }
195
+ const { result, rerender } = renderHook((args) => useSpacingScale(args.spacingValue), {
196
+ wrapper,
197
+ initialProps: { viewport: 'xs', spacingValue }
198
+ })
199
+ expect(result.current).toBe(expectedDefaultScale[0])
200
+ rerender({ viewport: 'sm', spacingValue })
201
+ expect(result.current).toBe(expectedDefaultScale[0])
202
+ rerender({ viewport: 'md', spacingValue })
203
+ expect(result.current).toBe(expectedDefaultScale[0])
204
+ rerender({ viewport: 'lg', spacingValue })
205
+ expect(result.current).toBe(expectedDefaultScale[4])
206
+ rerender({ viewport: 'xl', spacingValue })
207
+ expect(result.current).toBe(expectedDefaultScale[4])
208
+ })
209
+
210
+ it('subtracts a provided value from a spacing scale value', () => {
211
+ const spacingValue = { viewport: 'xs', space: 2, options: { subtract: 3 } }
212
+ const { result } = renderHook(() => useSpacingScale(spacingValue), {
213
+ wrapper
214
+ })
215
+ expect(result.current).toBe(expectedDefaultScale[2] - 3)
216
+ })
217
+ it('subtracts a provided value from a spacing scale value in a variant', () => {
218
+ const spacingValue = {
219
+ space: 4,
220
+ options: { subtract: 3, variant: { compact: true } }
221
+ }
222
+ const { result } = renderHook(() => useSpacingScale(spacingValue), {
223
+ wrapper
224
+ })
225
+ expect(result.current).toBe(expectedCompactScale[4] - 3)
226
+ })
227
+ it('returns 0 if a subtraction would result in a negative number', () => {
228
+ const spacingValue = { space: 2, options: { subtract: 999 } }
229
+ const { result } = renderHook((args) => useSpacingScale(args.spacingValue), {
230
+ wrapper,
231
+ initialProps: { spacingValue }
232
+ })
233
+ expect(result.current).toBe(0)
234
+ })
235
+
236
+ it('gives an exact amount if `size` option is passed', () => {
237
+ const size = 123456
238
+ const spacingValue = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5, options: { size } }
239
+ const compactSpacingValue = { ...spacingValue, options: { size, variant: { compact: true } } }
240
+ const { result, rerender } = renderHook((args) => useSpacingScale(args.spacingValue), {
241
+ wrapper,
242
+ initialProps: { viewport: 'xs', spacingValue }
243
+ })
244
+ expect(result.current).toBe(size)
245
+ rerender({ viewport: 'sm', spacingValue })
246
+ expect(result.current).toBe(size)
247
+ rerender({ viewport: 'md', spacingValue })
248
+ expect(result.current).toBe(size)
249
+ rerender({ viewport: 'lg', spacingValue })
250
+ expect(result.current).toBe(size)
251
+ rerender({ viewport: 'xl', spacingValue })
252
+ expect(result.current).toBe(size)
253
+ rerender({ viewport: 'xs', spacingValue: compactSpacingValue })
254
+ expect(result.current).toBe(size)
255
+ rerender({ viewport: 'sm', spacingValue: compactSpacingValue })
256
+ expect(result.current).toBe(size)
257
+ rerender({ viewport: 'md', spacingValue: compactSpacingValue })
258
+ expect(result.current).toBe(size)
259
+ rerender({ viewport: 'lg', spacingValue: compactSpacingValue })
260
+ expect(result.current).toBe(size)
261
+ rerender({ viewport: 'xl', spacingValue: compactSpacingValue })
262
+ expect(result.current).toBe(size)
263
+ })
264
+ it('subtracts from size if `subtract` and `size` options are provided', () => {
265
+ const size = 123456
266
+ const subtract = 12345
267
+ const spacingValue = { options: { size, subtract } }
268
+ const { result } = renderHook(() => useSpacingScale(spacingValue), {
269
+ wrapper
270
+ })
271
+ expect(result.current).toBe(size - subtract)
272
+ })
273
+ })
@@ -0,0 +1,31 @@
1
+ import { renderHook } from '@testing-library/react-hooks'
2
+ import useUniqueId from '../../lib/utils/useUniqueId'
3
+
4
+ describe('useUniqueId hook', () => {
5
+ it('returns the same id for each re-render, but a different id for a new instance', () => {
6
+ const { result, rerender } = renderHook(() => useUniqueId('prefix'))
7
+
8
+ expect(result.current).toBe('prefix-1')
9
+
10
+ rerender()
11
+
12
+ expect(result.current).toBe('prefix-1')
13
+
14
+ // has to be done within the same test case, as we can't ensure order in which tests are being run in parallel
15
+ const { result: result2, rerender: rerender2 } = renderHook(() => useUniqueId('prefix'))
16
+
17
+ expect(result.current).toBe('prefix-1')
18
+ expect(result2.current).toBe('prefix-2')
19
+
20
+ // ensure that instances' rerendering doesn't affect one another
21
+ rerender2()
22
+
23
+ expect(result.current).toBe('prefix-1')
24
+ expect(result2.current).toBe('prefix-2')
25
+
26
+ rerender()
27
+
28
+ expect(result.current).toBe('prefix-1')
29
+ expect(result2.current).toBe('prefix-2')
30
+ })
31
+ })
@@ -0,0 +1,8 @@
1
+ {
2
+ "presets": [["@babel/preset-react", { "useBuiltIns": true }]],
3
+ "env": {
4
+ "test": {
5
+ "presets": ["module:metro-react-native-babel-preset"]
6
+ }
7
+ }
8
+ }
package/jest.config.js CHANGED
@@ -1,22 +1,23 @@
1
- const path = require('path')
1
+ const preset = require('react-native/jest-preset')
2
2
  const displayName = require('./package.json').name.split('@telus-uds/').pop()
3
3
 
4
+ // cherry-pick the bits of the preset we want to avoid it overriding the fix we have on L14
4
5
  module.exports = {
6
+ haste: preset.haste,
5
7
  displayName,
6
- preset: 'react-native',
8
+ setupFiles: preset.setupFiles,
7
9
  transform: {
8
- // Required in addition to be preset since preset omits jsx extension
9
10
  // Use babel-jest instead of react-native/jest/preprocessor.js so that the coverage report is correct as per:
10
11
  // https://github.com/facebook/react-native/issues/20404#issuecomment-593392763
11
12
  // https://kulshekhar.github.io/ts-jest/docs/guides/react-native/#jest-config
12
- '\\.(jsx)$': 'babel-jest'
13
+ // __dirname here tells babel to look in components-base for babel root when running from monorepo root
14
+ '\\.(js|jsx)$': ['babel-jest', { cwd: __dirname }]
13
15
  },
14
16
  setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
15
- moduleDirectories: ['node_modules', path.join(__dirname, 'src')],
16
17
  moduleNameMapper: {
17
18
  '.+\\.(otf|svg|png|jpg)$': 'identity-obj-proxy'
18
19
  },
19
-
20
+ transformIgnorePatterns: preset.transformIgnorePatterns,
20
21
  // Count everything in src when calculating coverage
21
22
  collectCoverageFrom: ['src/**/*.{js,jsx}']
22
23
  }
@@ -1,8 +1,8 @@
1
1
  import React, { createContext, useContext, useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { AccessibilityInfo, Platform } from 'react-native';
4
- const ScreenReaderContext = createContext(false);
5
- const ReducedMotionContext = createContext(false);
4
+ const ScreenReaderContext = /*#__PURE__*/createContext(false);
5
+ const ReducedMotionContext = /*#__PURE__*/createContext(false);
6
6
 
7
7
  const A11yInfoProvider = ({
8
8
  children
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import { Platform, StyleSheet, View } from 'react-native';
5
3
  import PropTypes from 'prop-types';
@@ -26,7 +24,7 @@ const A11yText = ({
26
24
  accessibilityRole: heading ? 'header' : 'text',
27
25
  ...rest
28
26
  });
29
- return /*#__PURE__*/React.createElement(View, _extends({
27
+ return /*#__PURE__*/React.createElement(View, Object.assign({
30
28
  style: styles.invisible
31
29
  }, a11y));
32
30
  };
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import { DURATION, MIN_EMPTY_ANGLE, MIN_STROKE_ANGLE, BEZIER, propTypes } from './shared';
5
3
  const SVG_RADIUS = 20;
@@ -31,7 +29,7 @@ const Spinner = ({
31
29
  "aria-valuetext": label,
32
30
  role: "progressbar",
33
31
  "aria-busy": true
34
- }, /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("animateTransform", _extends({
32
+ }, /*#__PURE__*/React.createElement("g", null, /*#__PURE__*/React.createElement("animateTransform", Object.assign({
35
33
  attributeName: "transform",
36
34
  type: "rotate",
37
35
  values: `-180 24 24;${360 + MIN_STROKE_ANGLE - 180} 24 24`
@@ -45,10 +43,10 @@ const Spinner = ({
45
43
  r: "20",
46
44
  strokeDasharray: [MIN_SVG_LENGTH, SVG_CIRCUMFERENCE],
47
45
  strokeDashoffset: 0
48
- }, /*#__PURE__*/React.createElement("animate", _extends({
46
+ }, /*#__PURE__*/React.createElement("animate", Object.assign({
49
47
  attributeName: "stroke-dashoffset",
50
48
  values: `0;-10;${MIN_SVG_LENGTH - SVG_CIRCUMFERENCE}`
51
- }, animationProps, bezierProps)), /*#__PURE__*/React.createElement("animate", _extends({
49
+ }, animationProps, bezierProps)), /*#__PURE__*/React.createElement("animate", Object.assign({
52
50
  attributeName: "stroke-dasharray",
53
51
  values: `${MIN_SVG_LENGTH}, 200;${MAX_SVG_LENGTH}, 200;${MIN_SVG_LENGTH}, 200`
54
52
  }, animationProps, bezierProps)))));
package/lib/Box/Box.js CHANGED
@@ -1,111 +1,146 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import PropTypes from 'prop-types';
5
3
  import { View, ScrollView } from 'react-native';
6
- import { useViewport } from '../ViewportProvider';
7
- import { useTheme, useThemeTokens } from '../ThemeProvider';
8
- import { levelsPropType, responsivePropTypeFactory, variantProp, getTokensPropType } from '../utils/propTypes';
9
- const propToStyleMap = {
10
- top: 'paddingTop',
11
- below: 'marginBottom',
12
- bottom: 'paddingBottom',
13
- left: 'paddingLeft',
14
- right: 'paddingRight',
15
- vertical: 'paddingVertical',
16
- horizontal: 'paddingHorizontal'
17
- };
18
-
19
- const transformPropToStyle = key => propToStyleMap[key];
4
+ import { useThemeTokens } from '../ThemeProvider';
5
+ import { a11yProps, spacingProps, variantProp, getTokensPropType, useSpacingScale } from '../utils';
6
+ /**
7
+ * @typedef {import('../utils/propTypes.js').SpacingValue} SpacingValue
8
+ * @typedef {import('../utils/propTypes.js').SpacingIndex} SpacingIndex
9
+ * @typedef {import('../utils/propTypes.js').SpacingObject} SpacingObject
10
+ * @typedef {import('../utils/propTypes.js').SpacingOptions} SpacingOptions
11
+ */
20
12
 
21
- const getStyleByType = (currentViewPort, styleProp, levels) => {
22
- if (typeof styleProp === 'undefined') return null;
23
- if (typeof styleProp === 'number') return levels[currentViewPort][styleProp];
24
- const spacingValue = styleProp[currentViewPort];
25
- return levels[currentViewPort][spacingValue];
26
- };
13
+ const selectBoxStyles = tokens => {
14
+ const styles = {
15
+ backgroundColor: tokens.backgroundColor
16
+ };
17
+ const paddings = ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']; // Only set on styles if token provided because we spread this object after the spacing scale values
27
18
 
28
- const getBaseStyle = (currentViewPort, styleProps, levels) => {
29
- const styleObject = {};
30
- Object.entries(styleProps).forEach(([key, styleProp]) => {
31
- const styleKey = transformPropToStyle(key);
32
- styleObject[styleKey] = getStyleByType(currentViewPort, styleProp, levels);
19
+ paddings.forEach(side => {
20
+ if (tokens[side]) {
21
+ styles[side] = tokens[side];
22
+ }
33
23
  });
34
- return styleObject;
24
+ return styles;
35
25
  };
36
26
  /**
37
- * A layout utility component
27
+ * A layout utility component. Use Box to create space (padding) around content.
28
+ *
29
+ * ## Spacing
30
+ *
31
+ * For most simple uses, pass a number to Box's `space` prop referring to an index in the theme's
32
+ * spacing scale. For example, for a box with the theme's smallest non-zero amount of padding on all sides:
33
+ *
34
+ * ```jsx
35
+ * <Box space={1}>
36
+ * ```
37
+ *
38
+ * #### Targetting specific sides
39
+ *
40
+ * Box allows spacing value props to be assigned to any side or all sides using the following props:
41
+ *
42
+ * - `space` sets the default for all sides, which is overridden by other props below
43
+ * - `horizontal` sets defaults for `left` and `right` (overriding `space` there if it is set)
44
+ * - `vertical` sets defaults for `top` and `bottom` (overriding `space` there if it is set)
45
+ * - `left` sets the left side padding, inside the box's bounds
46
+ * - `right` sets the right side padding, inside the box's bounds
47
+ * - `top` sets the top side padding, inside the box's bounds
48
+ * - `bottom` sets the bottom side padding, inside the box's bounds
49
+ *
50
+ * Box only controls spacing within its bounds. If space is needed around a box outside of its bounds,
51
+ * use `Spacer`, `StackView` or wrap the `Box` inside another `Box`.
52
+ *
53
+ * #### Viewport-specific spacing
54
+ *
55
+ * Responsive behaviours may be set by passing any of the above props an object keyed by viewports.
38
56
  *
39
- * > TODO decide on the API we want to offer here
57
+ * For example, if a theme's spacing scale is [0, 4, 8...], this below will have padding of 4px on top,
58
+ * bottom, and right, and its left padding will be 0px on xs, sm and md viewports and 8px on lg and xl viewports:
59
+ *
60
+ * ```jsx
61
+ * <Box space={1} left={{ xs: 0, lg: 2 }} />`
62
+ * ```
63
+ *
64
+ * #### Subtracting from spacing
65
+ *
66
+ * A parent may sometimes need to reduce the spacing size on one or more sides of a `Box` by some variable.
67
+ * For example, the parent may have a border on one side and want to reduce the spacing of a child box
68
+ * by the width of that border on that side. This can be achieved using the `subtract` option of the
69
+ * spacing object, for example:
70
+ *
71
+ * ```jsx
72
+ * <Box space={2} left={{ space: 2, options: { subtract: themeTokens.borderWidthLeft } }} />`
73
+ * ```
74
+ *
75
+ * See `useSpacingScale` hook for other spacing value options and documentation.
76
+ *
77
+ * ## Theming
78
+ *
79
+ * Box is intended for layout, so minimal theming is supported. Use components like `Card` for
80
+ * more sophisticated theming.
81
+ *
82
+ * ## Scroll
83
+ *
84
+ * If passed, the box will be scrollable. If an object is passed, it will be passed to React Native's
85
+ * `ScrollView` component as props.
86
+ *
87
+ * When building native iOS and Android apps, it is important to remember to ensure any screen containing
88
+ * text content is inside a scrollable box, as screens are not scrollable by default and even very
89
+ * short text will require scrolling on small devices at the highest accessibility text scaling settings.
40
90
  */
41
91
 
42
92
 
43
93
  const Box = ({
94
+ space,
95
+ horizontal = space,
96
+ vertical = space,
97
+ top = vertical,
98
+ bottom = vertical,
99
+ left = horizontal,
100
+ right = horizontal,
44
101
  children,
45
- top,
46
- below,
47
- bottom,
48
- left,
49
- right,
50
- vertical,
51
- horizontal,
52
102
  variant,
53
103
  tokens,
54
- scroll = false,
55
- scrollProps = null
104
+ scroll,
105
+ testID,
106
+ ...rest
56
107
  }) => {
57
- const theme = useTheme(); // TODO: integrate level resolution with other tokens as part of
58
- // https://github.com/telus/universal-design-system/issues/267
59
-
60
- const {
61
- levels
62
- } = theme.components.Box; // TODO: update Box logic to use viewport as an appearance context and make levels themable
63
- // https://github.com/telus/universal-design-system/issues/87
64
-
65
- const viewport = useViewport();
66
- const {
67
- backgroundColor
68
- } = useThemeTokens('Box', tokens, variant);
69
- const bkgStyle = {
70
- backgroundColor
108
+ const a11y = a11yProps.select(rest);
109
+ const themeTokens = useThemeTokens('Box', tokens, variant);
110
+ const styles = {
111
+ paddingLeft: useSpacingScale(left),
112
+ paddingRight: useSpacingScale(right),
113
+ paddingTop: useSpacingScale(top),
114
+ paddingBottom: useSpacingScale(bottom),
115
+ ...selectBoxStyles(themeTokens)
71
116
  };
72
- const styleProps = {
73
- below,
74
- bottom,
75
- left,
76
- right,
77
- top,
78
- vertical,
79
- horizontal
80
- };
81
- const baseStyle = getBaseStyle(viewport, styleProps, levels);
82
- const styles = [baseStyle, bkgStyle];
83
117
 
84
118
  if (scroll) {
85
- return /*#__PURE__*/React.createElement(ScrollView, _extends({
86
- contentContainerStyle: styles
87
- }, scrollProps), children);
119
+ const scrollProps = typeof scroll === 'object' ? scroll : {};
120
+ scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle];
121
+ return /*#__PURE__*/React.createElement(ScrollView, Object.assign({}, scrollProps, a11y, {
122
+ testID: testID
123
+ }), children);
88
124
  }
89
125
 
90
- return /*#__PURE__*/React.createElement(View, {
91
- style: styles
92
- }, children);
93
- }; // TODO apply props as separate
94
-
126
+ return /*#__PURE__*/React.createElement(View, Object.assign({}, a11y, {
127
+ style: styles,
128
+ testID: testID
129
+ }), children);
130
+ };
95
131
 
96
- const responsiveLevelsPropType = responsivePropTypeFactory(levelsPropType);
97
132
  Box.propTypes = {
98
- below: responsiveLevelsPropType,
99
- bottom: responsiveLevelsPropType,
100
- left: responsiveLevelsPropType,
101
- right: responsiveLevelsPropType,
102
- top: responsiveLevelsPropType,
103
- vertical: responsiveLevelsPropType,
104
- horizontal: responsiveLevelsPropType,
105
- scroll: PropTypes.bool,
106
- scrollProps: PropTypes.object,
133
+ space: spacingProps.types.spacingValue,
134
+ vertical: spacingProps.types.spacingValue,
135
+ horizontal: spacingProps.types.spacingValue,
136
+ bottom: spacingProps.types.spacingValue,
137
+ left: spacingProps.types.spacingValue,
138
+ right: spacingProps.types.spacingValue,
139
+ top: spacingProps.types.spacingValue,
140
+ scroll: PropTypes.oneOfType([PropTypes.bool, ScrollView.propTypes ? PropTypes.shape(ScrollView.propTypes) : PropTypes.object]),
107
141
  variant: variantProp.propType,
108
142
  tokens: getTokensPropType('Box'),
143
+ testID: PropTypes.string,
109
144
  children: PropTypes.node.isRequired
110
145
  };
111
146
  export default Box;
@@ -1,5 +1,3 @@
1
- function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
-
3
1
  import React from 'react';
4
2
  import ButtonBase from './ButtonBase';
5
3
  import buttonPropTypes from './propTypes';
@@ -8,7 +6,7 @@ import { a11yProps } from '../utils/propTypes';
8
6
  const Button = ({
9
7
  accessibilityRole = 'button',
10
8
  ...props
11
- }) => /*#__PURE__*/React.createElement(ButtonBase, _extends({}, props, {
9
+ }) => /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, props, {
12
10
  accessibilityRole: accessibilityRole
13
11
  }));
14
12