@telus-uds/components-base 1.14.2 → 1.16.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 (161) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/__tests17__/A11yText/A11yText.test.jsx +34 -0
  3. package/__tests17__/ActivityIndicator/ActivityIndicator.test.jsx +68 -0
  4. package/__tests17__/ActivityIndicator/__snapshots__/ActivityIndicator.test.jsx.snap +299 -0
  5. package/__tests17__/Box/Box.test.jsx +111 -0
  6. package/__tests17__/Button/Button.test.jsx +86 -0
  7. package/__tests17__/Button/ButtonBase.test.jsx +82 -0
  8. package/__tests17__/Button/ButtonGroup.test.jsx +347 -0
  9. package/__tests17__/Button/ButtonLink.test.jsx +61 -0
  10. package/__tests17__/Card/Card.test.jsx +63 -0
  11. package/__tests17__/Carousel/Carousel.test.jsx +128 -0
  12. package/__tests17__/Carousel/CarouselTabs.test.jsx +142 -0
  13. package/__tests17__/Checkbox/Checkbox.test.jsx +94 -0
  14. package/__tests17__/Checkbox/CheckboxGroup.test.jsx +246 -0
  15. package/__tests17__/Divider/Divider.test.jsx +91 -0
  16. package/__tests17__/ExpandCollapse/ExpandCollapse.test.jsx +109 -0
  17. package/__tests17__/Feedback/Feedback.test.jsx +42 -0
  18. package/__tests17__/FlexGrid/Col.test.jsx +261 -0
  19. package/__tests17__/FlexGrid/FlexGrid.test.jsx +136 -0
  20. package/__tests17__/FlexGrid/Row.test.jsx +273 -0
  21. package/__tests17__/HorizontalScroll/HorizontalScroll.test.jsx +165 -0
  22. package/__tests17__/Icon/Icon.test.jsx +61 -0
  23. package/__tests17__/IconButton/IconButton.test.jsx +52 -0
  24. package/__tests17__/InputLabel/InputLabel.test.jsx +28 -0
  25. package/__tests17__/InputLabel/__snapshots__/InputLabel.test.jsx.snap +3 -0
  26. package/__tests17__/InputSupports/InputSupports.test.jsx +60 -0
  27. package/__tests17__/Link/Link.test.jsx +63 -0
  28. package/__tests17__/Link/TextButton.test.jsx +35 -0
  29. package/__tests17__/List/List.test.jsx +82 -0
  30. package/__tests17__/Modal/Modal.test.jsx +47 -0
  31. package/__tests17__/Notification/Notification.test.jsx +20 -0
  32. package/__tests17__/Pagination/Pagination.test.jsx +160 -0
  33. package/__tests17__/Progress/Progress.test.jsx +79 -0
  34. package/__tests17__/Radio/Radio.test.jsx +87 -0
  35. package/__tests17__/Radio/RadioGroup.test.jsx +220 -0
  36. package/__tests17__/RadioCard/RadioCard.test.jsx +87 -0
  37. package/__tests17__/RadioCard/RadioCardGroup.test.jsx +246 -0
  38. package/__tests17__/Search/Search.test.jsx +87 -0
  39. package/__tests17__/Select/Select.test.jsx +94 -0
  40. package/__tests17__/SideNav/SideNav.test.jsx +110 -0
  41. package/__tests17__/Skeleton/Skeleton.test.jsx +61 -0
  42. package/__tests17__/SkipLink/SkipLink.test.jsx +61 -0
  43. package/__tests17__/Spacer/Spacer.test.jsx +63 -0
  44. package/__tests17__/StackView/StackView.test.jsx +211 -0
  45. package/__tests17__/StackView/StackWrap.test.jsx +47 -0
  46. package/__tests17__/StackView/getStackedContent.test.jsx +295 -0
  47. package/__tests17__/StepTracker/StepTracker.test.jsx +108 -0
  48. package/__tests17__/Tabs/Tabs.test.jsx +49 -0
  49. package/__tests17__/Tags/Tags.test.jsx +327 -0
  50. package/__tests17__/TextInput/TextArea.test.jsx +35 -0
  51. package/__tests17__/TextInput/TextInputBase.test.jsx +125 -0
  52. package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +80 -0
  53. package/__tests17__/ThemeProvider/useThemeTokens.test.jsx +514 -0
  54. package/__tests17__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  55. package/__tests17__/ToggleSwitch/ToggleSwitch.test.jsx +82 -0
  56. package/__tests17__/ToggleSwitch/ToggleSwitchGroup.test.jsx +192 -0
  57. package/__tests17__/Tooltip/Tooltip.test.jsx +65 -0
  58. package/__tests17__/Tooltip/getTooltipPosition.test.js +79 -0
  59. package/__tests17__/Typography/typography.test.jsx +90 -0
  60. package/__tests17__/utils/children.test.jsx +128 -0
  61. package/__tests17__/utils/containUniqueFields.test.js +25 -0
  62. package/__tests17__/utils/input.test.js +375 -0
  63. package/__tests17__/utils/props.test.js +36 -0
  64. package/__tests17__/utils/semantics.test.jsx +34 -0
  65. package/__tests17__/utils/useCopy.test.js +42 -0
  66. package/__tests17__/utils/useResponsiveProp.test.jsx +202 -0
  67. package/__tests17__/utils/useSpacingScale.test.jsx +273 -0
  68. package/__tests17__/utils/useUniqueId.test.js +31 -0
  69. package/component-docs.json +120 -85
  70. package/lib/A11yInfoProvider/index.js +14 -5
  71. package/lib/Button/ButtonGroup.js +3 -2
  72. package/lib/Carousel/Carousel.js +18 -2
  73. package/lib/Carousel/CarouselTabs/CarouselTabs.js +6 -7
  74. package/lib/Checkbox/Checkbox.js +9 -6
  75. package/lib/ExpandCollapse/Control.js +6 -5
  76. package/lib/ExpandCollapse/Panel.js +5 -4
  77. package/lib/List/ListItem.js +10 -236
  78. package/lib/List/ListItemBase.js +162 -0
  79. package/lib/List/ListItemContent.js +85 -0
  80. package/lib/List/ListItemMark.js +158 -0
  81. package/lib/List/PressableListItemBase.js +147 -0
  82. package/lib/Notification/Notification.js +2 -1
  83. package/lib/Pagination/Pagination.js +4 -3
  84. package/lib/Radio/Radio.js +9 -6
  85. package/lib/RadioCard/RadioCard.js +9 -6
  86. package/lib/Select/Select.js +1 -0
  87. package/lib/Skeleton/Skeleton.js +18 -13
  88. package/lib/Skeleton/useSkeletonNativeAnimation.js +4 -2
  89. package/lib/Tabs/Tabs.js +12 -3
  90. package/lib/Tags/Tags.js +3 -3
  91. package/lib/TextInput/TextInput.js +5 -4
  92. package/lib/ToggleSwitch/ToggleSwitch.js +24 -19
  93. package/lib/ViewportProvider/useViewportListener.js +11 -5
  94. package/lib/utils/hasOwnProperty.js +18 -0
  95. package/lib/utils/props/a11yProps.js +171 -1
  96. package/lib/utils/props/getPropSelector.js +47 -5
  97. package/lib/utils/ssr.js +116 -1
  98. package/lib/utils/useResponsiveProp.js +5 -3
  99. package/lib/utils/withLinkRouter.js +3 -5
  100. package/lib-module/A11yInfoProvider/index.js +14 -4
  101. package/lib-module/Button/ButtonGroup.js +3 -2
  102. package/lib-module/Carousel/Carousel.js +16 -2
  103. package/lib-module/Carousel/CarouselTabs/CarouselTabs.js +7 -6
  104. package/lib-module/Checkbox/Checkbox.js +9 -6
  105. package/lib-module/ExpandCollapse/Control.js +6 -5
  106. package/lib-module/ExpandCollapse/Panel.js +5 -4
  107. package/lib-module/List/ListItem.js +13 -235
  108. package/lib-module/List/ListItemBase.js +139 -0
  109. package/lib-module/List/ListItemContent.js +66 -0
  110. package/lib-module/List/ListItemMark.js +143 -0
  111. package/lib-module/List/PressableListItemBase.js +117 -0
  112. package/lib-module/Notification/Notification.js +2 -1
  113. package/lib-module/Pagination/Pagination.js +5 -3
  114. package/lib-module/Radio/Radio.js +9 -6
  115. package/lib-module/RadioCard/RadioCard.js +9 -6
  116. package/lib-module/Select/Select.js +1 -0
  117. package/lib-module/Skeleton/Skeleton.js +15 -13
  118. package/lib-module/Skeleton/useSkeletonNativeAnimation.js +3 -2
  119. package/lib-module/Tabs/Tabs.js +13 -4
  120. package/lib-module/Tags/Tags.js +3 -3
  121. package/lib-module/TextInput/TextInput.js +5 -4
  122. package/lib-module/ToggleSwitch/ToggleSwitch.js +24 -19
  123. package/lib-module/ViewportProvider/useViewportListener.js +10 -4
  124. package/lib-module/utils/hasOwnProperty.js +11 -0
  125. package/lib-module/utils/props/a11yProps.js +169 -1
  126. package/lib-module/utils/props/getPropSelector.js +44 -5
  127. package/lib-module/utils/ssr.js +106 -0
  128. package/lib-module/utils/useResponsiveProp.js +3 -4
  129. package/lib-module/utils/withLinkRouter.js +3 -5
  130. package/package.json +12 -17
  131. package/src/A11yInfoProvider/index.jsx +20 -4
  132. package/src/Button/ButtonGroup.jsx +4 -2
  133. package/src/Carousel/Carousel.jsx +15 -2
  134. package/src/Carousel/CarouselTabs/CarouselTabs.jsx +5 -3
  135. package/src/Checkbox/Checkbox.jsx +7 -3
  136. package/src/ExpandCollapse/Control.jsx +8 -5
  137. package/src/ExpandCollapse/Panel.jsx +7 -5
  138. package/src/List/ListItem.jsx +12 -191
  139. package/src/List/ListItemBase.jsx +118 -0
  140. package/src/List/ListItemContent.jsx +52 -0
  141. package/src/List/ListItemMark.jsx +99 -0
  142. package/src/List/PressableListItemBase.jsx +102 -0
  143. package/src/Notification/Notification.jsx +1 -1
  144. package/src/Pagination/Pagination.jsx +6 -1
  145. package/src/Radio/Radio.jsx +7 -3
  146. package/src/RadioCard/RadioCard.jsx +7 -3
  147. package/src/Select/Select.jsx +1 -1
  148. package/src/Skeleton/Skeleton.jsx +25 -19
  149. package/src/Skeleton/useSkeletonNativeAnimation.js +3 -3
  150. package/src/Tabs/Tabs.jsx +19 -2
  151. package/src/Tags/Tags.jsx +3 -3
  152. package/src/TextInput/TextInput.jsx +4 -4
  153. package/src/ToggleSwitch/ToggleSwitch.jsx +3 -3
  154. package/src/ViewportProvider/useViewportListener.js +10 -5
  155. package/src/utils/hasOwnProperty.js +11 -0
  156. package/src/utils/props/a11yProps.js +107 -1
  157. package/src/utils/props/getPropSelector.js +45 -4
  158. package/src/utils/ssr.jsx +124 -0
  159. package/src/utils/useResponsiveProp.js +3 -3
  160. package/src/utils/withLinkRouter.jsx +1 -3
  161. package/src/utils/ssr.js +0 -35
@@ -33,10 +33,16 @@ const useViewportListenerCSR = setViewport => {
33
33
  return setViewport(viewports.select(window.width));
34
34
  };
35
35
 
36
- const listener = Dimensions.addEventListener('change', onChange); // From RN 0.65.0, Dimensions.removeEventListener is deprecated for `remove` on addEventListener return value;
37
- // however, that is not available in RN <=0.64.X, therefore not in any Expo release as of 2021 (Expo SDK 43).
38
-
39
- return (listener === null || listener === void 0 ? void 0 : listener.remove) || (() => Dimensions.removeEventListener('change', onChange)); // setViewport is a function from `useState` so it is stable and won't make the effect re-run
36
+ const listener = Dimensions.addEventListener('change', onChange);
37
+ return () => {
38
+ if (typeof (listener === null || listener === void 0 ? void 0 : listener.remove) === 'function') {
39
+ // Can't just return listener.remove because listener.emitter disappears, causing an internal error.
40
+ // See https://github.com/facebook/react-native/issues/34508
41
+ listener.remove(); // From RN 0.65.0, Dimensions.removeEventListener is deprecated for `remove` on addEventListener return value
42
+ } else if (typeof Dimensions.removeEventListener === 'function') {
43
+ Dimensions.removeEventListener('change', onChange);
44
+ }
45
+ };
40
46
  }, [setViewport]);
41
47
  }; // Window is a defined global object in both Web and Native client-side, and undefined in SSR
42
48
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Linter disallows object instance prototype methods like someObject.hasOwnProperty(key),
3
+ * but we can use this instead.
4
+ *
5
+ * @param {object} object
6
+ * @param {String} key
7
+ * @returns {Boolean}
8
+ */
9
+ export default function hasOwnProperty(object, key) {
10
+ return Object.prototype.hasOwnProperty.call(object, key);
11
+ }
@@ -83,6 +83,167 @@ const a11yPropTypes = { ...nativeA11yPropTypes,
83
83
  accessibilityValueNow: PropTypes.number,
84
84
  accessibilityValueText: PropTypes.string
85
85
  };
86
+ const a11yPropTypesByPlatform = Platform.select({
87
+ // React Native Web adds many a11y props that alias aria-* attributes
88
+ // Types based on https://necolas.github.io/react-native-web/docs/accessibility/
89
+ web: a11yPropTypes,
90
+ // Ignore web-only props in native builds
91
+ default: nativeA11yPropTypes
92
+ }); // These RNW-only props only exist in RNW >=0.18. Catch them and map them according to platform
93
+ // so all props work on RN, RNW >=0.18 and RNW <=0.18, regardless of which they were written for:
94
+ // - On native, bundle them into objects, like `accessibilityValue: { max: 100 }`
95
+ // - On web, split them into both of:
96
+ // - The appropriate aria-* attr, like `aria-valuenow`, which will work regardless of RNW version
97
+ // - The corresponding RNW >=0.18 prop, like `accessibilityValueNow`, which in some cases does more
98
+ // than just add the aria-* (e.g. `accessibilityDisabled` adds `disabled` if element supports it,
99
+ // and future releases might add more features here).
100
+
101
+ const rwnPropMappings = {
102
+ // Former accessibilityValue props.
103
+ accessibilityValueMax: value => Platform.select({
104
+ web: {
105
+ 'aria-valuemax': value,
106
+ accessibilityValueMax: value
107
+ },
108
+ default: {
109
+ accessibilityValue: {
110
+ max: value
111
+ }
112
+ }
113
+ }),
114
+ accessibilityValueMin: value => Platform.select({
115
+ web: {
116
+ 'aria-valuemin': value,
117
+ accessibilityValueMin: value
118
+ },
119
+ default: {
120
+ accessibilityValue: {
121
+ min: value
122
+ }
123
+ }
124
+ }),
125
+ accessibilityValueNow: value => Platform.select({
126
+ web: {
127
+ 'aria-valuenow': value,
128
+ accessibilityValueNow: value
129
+ },
130
+ default: {
131
+ accessibilityValue: {
132
+ now: value
133
+ }
134
+ }
135
+ }),
136
+ accessibilityValueText: value => Platform.select({
137
+ web: {
138
+ 'aria-valuetext': value,
139
+ accessibilityValueText: value
140
+ },
141
+ default: {
142
+ accessibilityValue: {
143
+ text: value
144
+ }
145
+ }
146
+ }),
147
+ // Former accessibilityState props
148
+ accessibilityBusy: value => Platform.select({
149
+ web: {
150
+ 'aria-busy': value,
151
+ accessibilityBusy: value
152
+ },
153
+ default: {
154
+ accessibilityState: {
155
+ busy: value
156
+ }
157
+ }
158
+ }),
159
+ accessibilityChecked: value => Platform.select({
160
+ web: {
161
+ 'aria-checked': value,
162
+ accessibilityChecked: value
163
+ },
164
+ default: {
165
+ accessibilityState: {
166
+ checked: value
167
+ }
168
+ }
169
+ }),
170
+ accessibilityDisabled: value => Platform.select({
171
+ web: {
172
+ 'aria-disabled': value,
173
+ // RNW >= 0.18 maps `accessibilityDisabled` to `disabled` attr if element supports it
174
+ accessibilityDisabled: value,
175
+ // As of RNW 0.18.9, Pressable doesn't support `accessibilityDisabled`, only `disabled`,
176
+ // but everything else supports `accessibilityDisabled` but not `disabled`.
177
+ disabled: value
178
+ },
179
+ default: {
180
+ accessibilityState: {
181
+ disabled: value
182
+ }
183
+ }
184
+ }),
185
+ accessibilityExpanded: value => Platform.select({
186
+ web: {
187
+ 'aria-expanded': value,
188
+ accessibilityExpanded: value
189
+ },
190
+ default: {
191
+ accessibilityState: {
192
+ expanded: value
193
+ }
194
+ }
195
+ }),
196
+ accessibilitySelected: value => Platform.select({
197
+ web: {
198
+ 'aria-selected': value,
199
+ accessibilitySelected: value
200
+ },
201
+ default: {
202
+ accessibilityState: {
203
+ selected: value
204
+ }
205
+ }
206
+ })
207
+ };
208
+
209
+ if (Platform.OS === 'web') {
210
+ const mapIfDefined = (value, fn) => value === undefined ? undefined : fn(value); // On Web only, these React Native object props need manual mapping in RNW >=0.18
211
+ // which dropped support for the React Native shape of these props.
212
+ // Re-use our RNW 0.18 prop mappings to support both RNW <0.18 (aria-*) and
213
+ // new features added in >=0.18 (e.g. for accessibilityDisabled).
214
+
215
+
216
+ rwnPropMappings.accessibilityValue = function () {
217
+ let {
218
+ max,
219
+ min,
220
+ now,
221
+ text
222
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
223
+ return { ...mapIfDefined(max, rwnPropMappings.accessibilityValueMax),
224
+ ...mapIfDefined(min, rwnPropMappings.accessibilityValueMin),
225
+ ...mapIfDefined(now, rwnPropMappings.accessibilityValueNow),
226
+ ...mapIfDefined(text, rwnPropMappings.accessibilityValueText)
227
+ };
228
+ };
229
+
230
+ rwnPropMappings.accessibilityState = function () {
231
+ let {
232
+ busy,
233
+ checked,
234
+ disabled,
235
+ expanded,
236
+ selected
237
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
238
+ return { ...mapIfDefined(busy, rwnPropMappings.accessibilityBusy),
239
+ ...mapIfDefined(checked, rwnPropMappings.accessibilityChecked),
240
+ ...mapIfDefined(disabled, rwnPropMappings.accessibilityDisabled),
241
+ ...mapIfDefined(expanded, rwnPropMappings.accessibilityExpanded),
242
+ ...mapIfDefined(selected, rwnPropMappings.accessibilitySelected)
243
+ };
244
+ };
245
+ }
246
+
86
247
  export default {
87
248
  /**
88
249
  * Proptypes for recognised React Native accessiblity (a11y) props.
@@ -96,7 +257,14 @@ export default {
96
257
  * Where components accept React Native a11y props, pass { ...rest } from its props to this,
97
258
  * then spread the returned object into the component's props (usually its outer container).
98
259
  */
99
- select: getPropSelector(a11yPropTypes, /^aria-/),
260
+ select: getPropSelector( // Allow all React Native accessibility props
261
+ a11yPropTypesByPlatform, // Allow any `aria-*` attribute on web; ignore them on native
262
+ Platform.OS === 'web' && /^aria-/, // For the props added and deprecated in React Native Web 0.18, convert them to
263
+ // a form that is platform-appropriate and RNW-version safe
264
+ (key, value) => {
265
+ const rnwPropMapper = rwnPropMappings[key];
266
+ return rnwPropMapper ? rnwPropMapper(value) : undefined;
267
+ }),
100
268
 
101
269
  /**
102
270
  * Use this to disable focus for elements which are visually hidden but still rendered.
@@ -1,9 +1,48 @@
1
- export default function getPropSelector(propTypes, regexp) {
2
- const keys = Object.keys(propTypes);
1
+ import merge from 'lodash.merge';
2
+ import hasOwnProperty from '../hasOwnProperty';
3
+ /**
4
+ * @callback PropSelectorCallback - a callback called for each prop passed to a component
5
+ * @param {string} key - the key for the prop to be tested
6
+ * @param {*} value - the value of the prop being passed in to the component
7
+ * @returns {object|undefined}
8
+ */
9
+
10
+ /**
11
+ * @param {PropSelectorCallback} callback
12
+ * @param {object} items
13
+ * @param {string} key
14
+ * @param {*} value
15
+ * @returns {object|undefined}
16
+ */
17
+
18
+ const applyCallback = (callback, items, key, value) => {
19
+ // If there's no callback, continue and look up keys as normal
20
+ if (typeof callback !== 'function') return undefined;
21
+ const newItems = callback(key, value); // If the callback doesn't return anything, continue and look up keys as normal
22
+
23
+ if (!newItems) return undefined; // If the callback returns items, merge them in, deep merging props that are objects
24
+
25
+ return merge({}, items, newItems);
26
+ };
27
+ /**
28
+ * Generates a function to filter an object of props down to a subset of allowed props, with
29
+ * optional prop alteration and re-mapping via an optional callback.
30
+ *
31
+ * @param {object} propTypes - an object where every defined key is a valid prop
32
+ * @param {*} [regexp] - an optional regular expression where any match is a valid prop
33
+ * @param {PropSelectorCallback} callback - optional function taking `(key, value)` returning either undefined or an object of new props to merge in
34
+ * @returns {object} - valid props for this component
35
+ */
36
+
37
+
38
+ export default function getPropSelector(propTypes, regexp, callback) {
3
39
  return props => Object.entries(props).reduce((items, _ref) => {
4
40
  let [key, value] = _ref;
5
- return keys.includes(key) || regexp && regexp.test(key) ? { ...items,
6
- [key]: value
7
- } : items;
41
+ return (// If there's a callback and it matches something, applyCallback merges it in; return that
42
+ applyCallback(callback, items, key, value) || ( // If there's no callback match, check if this prop is valid and merge it in if it is
43
+ hasOwnProperty(propTypes, key) || regexp && regexp.test(key) ? { ...items,
44
+ [key]: value
45
+ } : items)
46
+ );
8
47
  }, {});
9
48
  }
@@ -1,9 +1,113 @@
1
+ import React from 'react';
1
2
  import AppRegistry from "react-native-web/dist/exports/AppRegistry";
2
3
  /** @typedef {import('react').ComponentType} ReactComponent */
3
4
 
4
5
  /** @typedef {import('react').ReactElement} ReactElement */
5
6
 
6
7
  /**
8
+ * Returns object with `renderApp` and `getStyles` functions.
9
+ * Weave these into your app's server-side render process:
10
+ *
11
+ * - Call `renderApp` first to do the actual server-side render
12
+ * - After the render is complete, call `getStyles`
13
+ * - Include the style tags returned by `getStyles` in the SSR <head>
14
+ *
15
+ * @param {string} [appName] - optional unique identifier if ssrStyles is called multiple times for multiple apps
16
+ * @param {object} [options] -
17
+ * - `styleGetters`: optional array of additional style getter functions to call after render
18
+ * - `collectStyles`: optional function, takes the rendered app, returns either the same or a new app element
19
+ * @param {boolean} [options.styleGetters]
20
+ * @param {(ReactElement) => ReactElement} [options.collectStyles]
21
+ */
22
+
23
+ import { jsx as _jsx } from "react/jsx-runtime";
24
+ export const ssrStyles = function () {
25
+ let appName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'UDS app';
26
+ let {
27
+ styleGetters = [],
28
+ collectStyles
29
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
30
+ let hasAppRendered = false;
31
+ return {
32
+ /**
33
+ * Server-side-renders the provided app in a way that supports collecting
34
+ * styles for Styled Components and React Native Web stylesheets.
35
+ *
36
+ * @param {ReactComponent} App - the root component for the app
37
+ * @param {object} [props] - props for this render e.g. page routing props
38
+ * @param {object} [options] -
39
+ * - `renderedByRNW`: pass as true if the main render is by AppRegistry.runApplication from React Native
40
+ * - `WrapperComponent`: Component rendered with no props around app content and inside root tag
41
+ * @param {boolean} [options.renderedByRNW]
42
+ * @param {ReactComponent} [options.WrapperComponent]
43
+ *
44
+ * @returns {ReactElement} - the rendered app output
45
+ */
46
+ renderApp: function (App, props) {
47
+ let {
48
+ renderedByRNW = false,
49
+ WrapperComponent
50
+ } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
51
+ AppRegistry.registerComponent(appName, () => App); // AppRegistry.getApplication renders the app in a container, and collects styles.
52
+
53
+ const {
54
+ element,
55
+ getStyleElement
56
+ } = AppRegistry.getApplication(appName, {
57
+ WrapperComponent,
58
+ initialProps: props
59
+ });
60
+ let renderedApp = // React Native Web's AppRegistry.getApplication assumes the app is rendered using
61
+ // AppRegistry.runApplication and wraps it in <AppContainer>, which wraps the entire app
62
+ // in two outer containers resembling <View style={{ flex: 1 }} pointerEvents="box-none">.
63
+ // So, use that IF user says AppRegistry.runApplication will do the client-side render.
64
+ renderedByRNW && element || // If the live app is not rendered using AppRegistry.runApplication, we need to
65
+ // re-render it without the <AppContainer> wrapper, to avoid SSR mismatch errors.
66
+ // Default to this as many platforms (e.g. NextJS) will use their own renderers.
67
+ WrapperComponent ? /*#__PURE__*/_jsx(WrapperComponent, {
68
+ children: /*#__PURE__*/_jsx(App, { ...props
69
+ })
70
+ }) : /*#__PURE__*/_jsx(App, { ...props
71
+ });
72
+
73
+ const getRNWStyle = () => getStyleElement({
74
+ key: 'react-native-stylesheet'
75
+ });
76
+
77
+ styleGetters.push(getRNWStyle);
78
+
79
+ if (typeof collectStyles === 'function') {
80
+ renderedApp = collectStyles(renderedApp);
81
+ }
82
+
83
+ hasAppRendered = true;
84
+ return renderedApp;
85
+ },
86
+
87
+ /**
88
+ * Turns styles collected during renderApp into an array of React elements of
89
+ * HTML <style> tags ready for insertion into the SSR HTML's <head>.
90
+ *
91
+ * Must be called after `renderApp` has completed.
92
+ *
93
+ * @param {...ReactElement} existingStyles - any existing style tag elements to merge in
94
+ * @returns {ReactElement[]} - flat array of <style> React elements
95
+ */
96
+ getStyles: function () {
97
+ if (!hasAppRendered) throw new Error('Called getStyles before renderApp in ssrStyles');
98
+
99
+ for (var _len = arguments.length, existingStyles = new Array(_len), _key = 0; _key < _len; _key++) {
100
+ existingStyles[_key] = arguments[_key];
101
+ }
102
+
103
+ return [...existingStyles, ...styleGetters.flatMap(getter => getter())];
104
+ }
105
+ };
106
+ };
107
+ export default ssrStyles;
108
+ /**
109
+ * @deprecated - use ssrStyles instead
110
+ *
7
111
  * Registers the app's root component with React Native Web and generates
8
112
  * the main <style> tag containing React Native Web stylesheet styles.
9
113
  *
@@ -21,6 +125,8 @@ export const getReactNativeWebSSRStyles = function (AppRoot) {
21
125
  return [getStyleElement()];
22
126
  };
23
127
  /**
128
+ * @deprecated - use ssrStyles instead
129
+ *
24
130
  * Gets style tags for each currently supported CSS-in-JS library and returns
25
131
  * them alongside any existing style tags.
26
132
  *
@@ -1,9 +1,8 @@
1
1
  import { viewports } from '@telus-uds/system-constants';
2
2
  import { useViewport } from '../ViewportProvider';
3
+ import hasOwnProperty from './hasOwnProperty';
3
4
 
4
- const hasOwn = (objectProp, key) => Object.prototype.hasOwnProperty.call(objectProp, key);
5
-
6
- const hasResponsiveProperties = objectProp => viewports.keys.some(key => hasOwn(objectProp, key));
5
+ const hasResponsiveProperties = objectProp => viewports.keys.some(key => hasOwnProperty(objectProp, key));
7
6
  /**
8
7
  * Resolves a prop which may be a responsive object with keys for viewports.
9
8
  *
@@ -21,7 +20,7 @@ export const resolveResponsiveProp = (prop, viewport, defaultValue) => {
21
20
  if (!prop || typeof prop !== 'object' || !hasResponsiveProperties(prop)) return prop;
22
21
  const value = viewports.keys.includes(viewport) ? // If there's a current viewport, return the closest match at or below it
23
22
  viewports.inherit(prop)[viewport] : // If no current viewport is available, default to smallest viewport
24
- prop[viewports.keys.find(key => hasOwn(prop, key))];
23
+ prop[viewports.keys.find(key => hasOwnProperty(prop, key))];
25
24
  return value === undefined ? defaultValue : value;
26
25
  };
27
26
  /**
@@ -1,9 +1,6 @@
1
1
  import React, { forwardRef } from 'react';
2
- import PropTypes from 'prop-types'; // Prototype-safe alternative to (linter-forbidden) someObject.hasOwnProperty()
3
-
4
- import { jsx as _jsx } from "react/jsx-runtime";
5
-
6
- const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop);
2
+ import PropTypes from 'prop-types';
3
+ import hasOwnProperty from './hasOwnProperty';
7
4
  /**
8
5
  * Higher-order component that has no effect unless an additional prop `LinkRouter` is passed.
9
6
  * This may be used to provide custom wrappers for integrations with third party libraries.
@@ -33,6 +30,7 @@ const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(ob
33
30
  * ```
34
31
  */
35
32
 
33
+ import { jsx as _jsx } from "react/jsx-runtime";
36
34
 
37
35
  const withLinkRouter = Component => {
38
36
  const wrappedComponent = /*#__PURE__*/forwardRef((_ref, ref) => {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "airbnb-prop-types": "^2.16.0",
11
11
  "@telus-uds/system-constants": "^1.0.4",
12
- "@telus-uds/system-theme-tokens": "^2.4.0",
12
+ "@telus-uds/system-theme-tokens": "^2.5.0",
13
13
  "lodash.debounce": "^4.0.8",
14
14
  "lodash.merge": "^4.6.2",
15
15
  "prop-types": "^15.7.2",
@@ -18,17 +18,14 @@
18
18
  },
19
19
  "description": "Base components",
20
20
  "devDependencies": {
21
- "@storybook/addon-a11y": "^6.5.6",
22
- "@storybook/addon-essentials": "^6.5.6",
23
- "@storybook/cli": "^6.5.6",
24
- "@storybook/react": "^6.5.6",
25
- "@storybook/builder-webpack5": "^6.5.6",
26
- "@storybook/manager-webpack5": "^6.5.6",
27
21
  "@telus-uds/browserslist-config": "^1.0.4",
28
22
  "@testing-library/jest-native": "^4.0.1",
29
- "@testing-library/react-hooks": "^7.0.1",
30
- "@testing-library/react-native": "^7.2.0",
31
- "react-test-renderer": "^16.3.2",
23
+ "@testing-library/react": "^13.3.0",
24
+ "@testing-library/react-native": "11.0.0",
25
+ "react-test-renderer": "~18.0.0",
26
+ "@testing-library/react-hooks": "~7.0.1",
27
+ "@testing-library/react-native-17": "npm:@testing-library/react-native@7.2.0",
28
+ "react-test-renderer-17": "npm:react-test-renderer@17.0.2",
32
29
  "webpack": "5.x"
33
30
  },
34
31
  "directories": {
@@ -44,10 +41,10 @@
44
41
  "module": "lib-module/index.js",
45
42
  "name": "@telus-uds/components-base",
46
43
  "peerDependencies": {
47
- "react": "^17.0.2",
48
- "react-dom": "^17.0.2",
44
+ "react": "^17.0.2 || ^18.0.0",
45
+ "react-dom": "^17.0.2 || ^18.0.0",
49
46
  "react-native": "*",
50
- "react-native-web": "~0.17.5"
47
+ "react-native-web": "~0.17.7 || ~0.18.7"
51
48
  },
52
49
  "react-native": "src/index.js",
53
50
  "repository": {
@@ -63,13 +60,11 @@
63
60
  "build:main": "babel src -d lib",
64
61
  "build:module": "babel src -d lib-module --env-name module",
65
62
  "build:code": "npm run build:main && npm run build:module",
66
- "build:docs": "babel-node --plugins=@nearform/babel-plugin-react-docgen generate-component-docs.js",
67
- "storybook": "start-storybook",
68
- "build-storybook": "build-storybook"
63
+ "build:docs": "babel-node --plugins=@nearform/babel-plugin-react-docgen generate-component-docs.js"
69
64
  },
70
65
  "sideEffects": false,
71
66
  "standard-engine": {
72
67
  "skip": true
73
68
  },
74
- "version": "1.14.2"
69
+ "version": "1.16.0"
75
70
  }
@@ -16,8 +16,14 @@ const A11yInfoProvider = ({ children }) => {
16
16
  return () => {}
17
17
  }
18
18
 
19
- AccessibilityInfo.addEventListener('reduceMotionChanged', setReduceMotionEnabled)
20
- AccessibilityInfo.addEventListener('screenReaderChanged', setScreenReaderEnabled)
19
+ const motionSubscription = AccessibilityInfo.addEventListener(
20
+ 'reduceMotionChanged',
21
+ setReduceMotionEnabled
22
+ )
23
+ const screenReaderSubscription = AccessibilityInfo.addEventListener(
24
+ 'screenReaderChanged',
25
+ setScreenReaderEnabled
26
+ )
21
27
 
22
28
  const setInitialA11yInfo = async () => {
23
29
  const [initialReduceMotionEnabled, initialScreenReaderEnabled] = await Promise.all([
@@ -38,8 +44,18 @@ const A11yInfoProvider = ({ children }) => {
38
44
  }
39
45
 
40
46
  return () => {
41
- AccessibilityInfo.removeEventListener('reduceMotionChanged', setReduceMotionEnabled)
42
- AccessibilityInfo.removeEventListener('screenReaderChanged', setScreenReaderEnabled)
47
+ // From react-native 0.65, AccessibilityInfo.removeEventListener is deprecated for `remove` on addEventListener return value
48
+ if (typeof motionSubscription?.remove === 'function') {
49
+ motionSubscription?.remove()
50
+ } else if (typeof AccessibilityInfo.removeEventListener === 'function') {
51
+ AccessibilityInfo.removeEventListener('reduceMotionChanged', setReduceMotionEnabled)
52
+ }
53
+
54
+ if (typeof screenReaderSubscription?.remove === 'function') {
55
+ screenReaderSubscription?.remove()
56
+ } else if (typeof AccessibilityInfo.removeEventListener === 'function') {
57
+ AccessibilityInfo.removeEventListener('screenReaderChanged', setScreenReaderEnabled)
58
+ }
43
59
  }
44
60
  }, [])
45
61
 
@@ -141,8 +141,10 @@ const ButtonGroup = forwardRef(
141
141
  tokens={getButtonTokens}
142
142
  selected={isSelected}
143
143
  inactive={inactive}
144
- {...itemA11y}
145
- {...selectItemProps(itemRest)}
144
+ {...selectItemProps({
145
+ ...itemRest,
146
+ ...itemA11y
147
+ })}
146
148
  >
147
149
  {label}
148
150
  </ButtonBase>
@@ -22,6 +22,8 @@ import SkipLink from '../SkipLink'
22
22
  import A11yText from '../A11yText'
23
23
  import CarouselStepTracker from './CarouselStepTracker'
24
24
  import CarouselThumbnailNavigation from './CarouselThumbnailNavigation'
25
+ import CarouselTabsPanel from './CarouselTabs/CarouselTabsPanel'
26
+ import CarouselTabsPanelItem from './CarouselTabs/CarouselTabsPanelItem'
25
27
  import dictionary from './dictionary'
26
28
 
27
29
  const staticStyles = StyleSheet.create({
@@ -150,7 +152,8 @@ const Carousel = React.forwardRef(
150
152
  onAnimationEnd,
151
153
  onIndexChanged,
152
154
  skipLinkHref,
153
- refocus,
155
+ tabs,
156
+ refocus = Boolean(tabs),
154
157
  title = 'carousel',
155
158
  springConfig = undefined,
156
159
  thumbnails = undefined,
@@ -177,6 +180,7 @@ const Carousel = React.forwardRef(
177
180
  nextIcon,
178
181
  showPreviousNextNavigation,
179
182
  showPanelNavigation,
183
+ showPanelTabs,
180
184
  spaceBetweenSlideAndPreviousNextNavigation
181
185
  } = themeTokens
182
186
  const [activeIndex, setActiveIndex] = React.useState(0)
@@ -415,6 +419,9 @@ const Carousel = React.forwardRef(
415
419
  [activeIndex, childrenArray.length, itemLabel, getCopy, title]
416
420
  )
417
421
 
422
+ const activePanelNavigation =
423
+ tabs && showPanelTabs ? <CarouselTabsPanel items={tabs} /> : panelNavigation
424
+
418
425
  return (
419
426
  <CarouselProvider
420
427
  activeIndex={activeIndex}
@@ -511,7 +518,7 @@ const Carousel = React.forwardRef(
511
518
  </View>
512
519
  )}
513
520
  </View>
514
- {showPanelNavigation ? panelNavigation : null}
521
+ {showPanelNavigation ? activePanelNavigation : null}
515
522
  </CarouselProvider>
516
523
  )
517
524
  }
@@ -587,6 +594,12 @@ Carousel.propTypes = {
587
594
  * with the ID of a focusable element immediately after the Carousel, e.g. `'#section-2-heading'`.
588
595
  */
589
596
  skipLinkHref: PropTypes.string,
597
+ /**
598
+ * If provided, defaults the navigation panel to a CarouselTabsPanel element passing each item as props for one tab.
599
+ *
600
+ * Be careful to ensure that the order of each element in the items array matches the order of each child in the Carousel.
601
+ */
602
+ tabs: PropTypes.arrayOf(PropTypes.shape(CarouselTabsPanelItem.propTypes || {})),
590
603
  /**
591
604
  * If true, whenever a new slide comes into view, the focus of the Carousel switches to the start.
592
605
  *
@@ -3,13 +3,15 @@ import PropTypes from 'prop-types'
3
3
 
4
4
  import { useResponsiveProp } from '../../utils'
5
5
  import Carousel from '../Carousel'
6
- import CarouselTabsPanel from './CarouselTabsPanel'
7
6
 
7
+ /**
8
+ * @deprecated Please use Carousel and pass the `tabs` prop.
9
+ */
8
10
  const CarouselTabs = forwardRef(({ items, refocus = true, ...carouselProps }, ref) => {
9
- const panelNavigation = useResponsiveProp({ md: <CarouselTabsPanel items={items} /> })
11
+ const tabs = useResponsiveProp({ md: items })
10
12
 
11
13
  return (
12
- <Carousel refocus={refocus} {...carouselProps} ref={ref} panelNavigation={panelNavigation}>
14
+ <Carousel refocus={refocus} {...carouselProps} ref={ref} tabs={tabs}>
13
15
  {items.map(({ title, content }) => (
14
16
  <React.Fragment key={title}>{content}</React.Fragment>
15
17
  ))}
@@ -176,6 +176,12 @@ const Checkbox = forwardRef(
176
176
  const inputId = id ?? uniqueId
177
177
  const { themeOptions } = useTheme()
178
178
 
179
+ const selectedProps = selectProps({
180
+ accessibilityRole: 'checkbox',
181
+ accessibilityState: { checked: isChecked, disabled: inactive },
182
+ ...rest
183
+ })
184
+
179
185
  return (
180
186
  <View style={staticStyles.wrapper} ref={ref}>
181
187
  <StackView direction={feedbackPosition === 'top' ? 'column-reverse' : 'column'} space={0}>
@@ -183,9 +189,7 @@ const Checkbox = forwardRef(
183
189
  disabled={inactive}
184
190
  onKeyDown={handleKeyDown}
185
191
  onPress={handleChange}
186
- accessibilityRole="checkbox"
187
- accessibilityState={{ checked: isChecked, disabled: inactive }}
188
- {...selectProps(rest)}
192
+ {...selectedProps}
189
193
  >
190
194
  {({ focused: focus, hovered: hover, pressed }) => {
191
195
  const { icon: IconComponent, ...stateTokens } = getTokens({ focus, hover, pressed })