@telus-uds/components-base 4.0.0-alpha.2 → 4.0.0-beta.1

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 (120) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/lib/cjs/Autocomplete/Autocomplete.js +13 -1
  3. package/lib/cjs/Autocomplete/constants.js +1 -1
  4. package/lib/cjs/Button/ButtonGroup.js +91 -23
  5. package/lib/cjs/Card/CardBase.js +75 -47
  6. package/lib/cjs/ColourToggle/ColourBubble.js +66 -13
  7. package/lib/cjs/ColourToggle/ColourToggle.js +41 -11
  8. package/lib/cjs/ColourToggle/constants.js +15 -0
  9. package/lib/cjs/ColourToggle/dictionary.js +14 -0
  10. package/lib/cjs/Listbox/ListboxOverlay.js +7 -1
  11. package/lib/cjs/MultiSelectFilter/ModalOverlay.js +7 -1
  12. package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +2 -28
  13. package/lib/cjs/TextInput/TextInputBase.js +2 -1
  14. package/lib/cjs/Validator/Validator.js +5 -1
  15. package/lib/cjs/utils/useOverlaidPosition.js +83 -42
  16. package/lib/esm/Autocomplete/Autocomplete.js +13 -1
  17. package/lib/esm/Autocomplete/constants.js +1 -1
  18. package/lib/esm/Button/ButtonGroup.js +91 -23
  19. package/lib/esm/Card/CardBase.js +75 -47
  20. package/lib/esm/ColourToggle/ColourBubble.js +67 -14
  21. package/lib/esm/ColourToggle/ColourToggle.js +41 -11
  22. package/lib/esm/ColourToggle/constants.js +8 -0
  23. package/lib/esm/ColourToggle/dictionary.js +8 -0
  24. package/lib/esm/Listbox/ListboxOverlay.js +7 -1
  25. package/lib/esm/MultiSelectFilter/ModalOverlay.js +8 -2
  26. package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +2 -28
  27. package/lib/esm/TextInput/TextInputBase.js +2 -1
  28. package/lib/esm/Validator/Validator.js +5 -1
  29. package/lib/esm/utils/useOverlaidPosition.js +83 -42
  30. package/lib/package.json +3 -3
  31. package/package.json +3 -3
  32. package/src/Autocomplete/Autocomplete.jsx +11 -2
  33. package/src/Autocomplete/constants.js +1 -1
  34. package/src/Button/ButtonGroup.jsx +93 -24
  35. package/src/Card/CardBase.jsx +94 -75
  36. package/src/ColourToggle/ColourBubble.jsx +62 -7
  37. package/src/ColourToggle/ColourToggle.jsx +43 -9
  38. package/src/ColourToggle/constants.js +10 -0
  39. package/src/ColourToggle/dictionary.js +4 -0
  40. package/src/Listbox/ListboxOverlay.jsx +6 -2
  41. package/src/MultiSelectFilter/ModalOverlay.jsx +9 -3
  42. package/src/MultiSelectFilter/MultiSelectFilter.jsx +1 -31
  43. package/src/TextInput/TextInputBase.jsx +2 -1
  44. package/src/Validator/Validator.jsx +5 -1
  45. package/src/utils/useOverlaidPosition.js +84 -34
  46. package/types/A11yInfoProvider.d.ts +9 -0
  47. package/types/A11yText.d.ts +13 -0
  48. package/types/ActionCard.d.ts +19 -0
  49. package/types/ActivityIndicator.d.ts +13 -0
  50. package/types/Autocomplete.d.ts +27 -0
  51. package/types/Badge.d.ts +15 -4
  52. package/types/BaseProvider.d.ts +26 -0
  53. package/types/Box.d.ts +13 -3
  54. package/types/Button.d.ts +35 -0
  55. package/types/Card.d.ts +19 -0
  56. package/types/CardGroup.d.ts +23 -0
  57. package/types/Carousel.d.ts +25 -0
  58. package/types/Checkbox.d.ts +15 -2
  59. package/types/CheckboxCard.d.ts +20 -0
  60. package/types/CheckboxCardGroup.d.ts +10 -4
  61. package/types/ChevronLink.d.ts +9 -2
  62. package/types/ColourToggle.d.ts +16 -0
  63. package/types/Common.d.ts +7 -3
  64. package/types/Divider.d.ts +13 -1
  65. package/types/DownloadApp.d.ts +19 -0
  66. package/types/ExpandCollapse.d.ts +16 -9
  67. package/types/ExpandCollapseMini.d.ts +16 -0
  68. package/types/Feedback.d.ts +16 -0
  69. package/types/Fieldset.d.ts +17 -0
  70. package/types/FileUpload.d.ts +6 -0
  71. package/types/FlexGrid.d.ts +57 -0
  72. package/types/Footnote.d.ts +18 -0
  73. package/types/HorizontalScroll.d.ts +15 -0
  74. package/types/HorizontalScrollButton.d.ts +17 -1
  75. package/types/Icon.d.ts +13 -2
  76. package/types/IconButton.d.ts +19 -0
  77. package/types/InputLabel.d.ts +12 -0
  78. package/types/InputSupports.d.ts +19 -0
  79. package/types/Link.d.ts +20 -4
  80. package/types/List.d.ts +11 -4
  81. package/types/Listbox.d.ts +13 -3
  82. package/types/Modal.d.ts +25 -0
  83. package/types/MultiSelectFilter.d.ts +24 -0
  84. package/types/Notification.d.ts +19 -0
  85. package/types/OrderedList.d.ts +17 -0
  86. package/types/Pagination.d.ts +25 -0
  87. package/types/Portal.d.ts +5 -1
  88. package/types/PriceLockup.d.ts +28 -0
  89. package/types/ProductCard.d.ts +20 -0
  90. package/types/ProductCardGroup.d.ts +21 -0
  91. package/types/Progress.d.ts +22 -0
  92. package/types/QuickLinks.d.ts +27 -0
  93. package/types/QuickLinksFeature.d.ts +27 -0
  94. package/types/Radio.d.ts +22 -0
  95. package/types/RadioCard.d.ts +21 -0
  96. package/types/Responsive.d.ts +14 -0
  97. package/types/Scroll.d.ts +2 -2
  98. package/types/Search.d.ts +15 -2
  99. package/types/Select.d.ts +12 -4
  100. package/types/SideNav.d.ts +38 -0
  101. package/types/Skeleton.d.ts +17 -0
  102. package/types/SkipLink.d.ts +14 -0
  103. package/types/Spacer.d.ts +8 -1
  104. package/types/StackView.d.ts +8 -2
  105. package/types/Status.d.ts +2 -3
  106. package/types/StepTracker.d.ts +16 -0
  107. package/types/TabBar.d.ts +25 -0
  108. package/types/Tabs.d.ts +12 -3
  109. package/types/Tags.d.ts +24 -0
  110. package/types/TextButton.d.ts +11 -4
  111. package/types/TextInput.d.ts +38 -0
  112. package/types/ThemeProvider.d.ts +29 -0
  113. package/types/Timeline.d.ts +16 -0
  114. package/types/ToggleSwitch.d.ts +11 -2
  115. package/types/ToolTip.d.ts +7 -3
  116. package/types/TooltipButton.d.ts +20 -0
  117. package/types/Typography.d.ts +8 -2
  118. package/types/Validator.d.ts +16 -0
  119. package/types/ViewportProvider.d.ts +12 -0
  120. package/types/index.d.ts +197 -50
@@ -21,6 +21,8 @@ import { getPressHandlersWithArgs } from '../utils/pressability';
21
21
  import { jsx as _jsx } from "react/jsx-runtime";
22
22
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
23
23
  const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]);
24
+ const EQUAL_WIDTH = 'equal';
25
+ const RESPONSIVE_WIDTH = 'responsive';
24
26
  export const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
25
27
  let {
26
28
  variant,
@@ -65,31 +67,90 @@ export const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
65
67
  const isMobileNonContained = Platform.OS !== 'web' && (!variant || variant?.style !== 'contained');
66
68
  const themeButtonTokensCallback = useThemeTokensCallback('ButtonGroupItem', tokens, variant);
67
69
  const gapValue = useSpacingScale(gap || space);
70
+ const isWeb = Platform.OS === 'web';
71
+ const buttonWidthVariant = variant?.width;
68
72
  const getButtonTokens = useCallback(state => {
69
73
  const themeButtonTokens = themeButtonTokensCallback(state);
70
74
  const shouldUseTransparentBackground = isMobileNonContained && !state.selected && !state.pressed && !state.hover && !state.focus;
75
+ let widthStyle;
76
+ switch (buttonWidthVariant) {
77
+ case EQUAL_WIDTH:
78
+ widthStyle = staticStyles.equalWidth;
79
+ break;
80
+ case RESPONSIVE_WIDTH:
81
+ widthStyle = staticStyles.responsiveWidth;
82
+ break;
83
+ // no default
84
+ }
71
85
  return {
72
86
  ...themeButtonTokens,
73
- ...(variant?.width === 'equal' && staticStyles.equalWidth),
87
+ ...widthStyle,
74
88
  ...(shouldUseTransparentBackground && {
75
89
  backgroundColor: 'transparent'
76
90
  }),
77
91
  alignSelf: themeButtonTokens.width ? 'flex-start' : 'center'
78
92
  };
79
- }, [themeButtonTokensCallback, isMobileNonContained, variant?.width]);
80
- const fieldsetStyles = useMemo(() => ({
81
- ...staticStyles.fieldsetBase,
82
- borderRadius,
83
- backgroundColor: isMobileNonContained ? 'transparent' : backgroundColor || 'transparent',
84
- padding,
85
- width: variant?.width === 'equal' ? '100%' : 'auto'
86
- }), [borderRadius, backgroundColor, padding, variant?.width, isMobileNonContained]);
87
- const viewStyles = useMemo(() => ({
88
- ...staticStyles.viewBase,
89
- flexDirection: direction === 'column' ? 'column' : 'row',
90
- gap: gapValue || 0,
91
- justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start'
92
- }), [direction, gapValue, variant?.width]);
93
+ }, [themeButtonTokensCallback, isMobileNonContained, buttonWidthVariant]);
94
+ const fieldsetStyles = useMemo(() => {
95
+ let fieldSetBase;
96
+ switch (buttonWidthVariant) {
97
+ case EQUAL_WIDTH:
98
+ case RESPONSIVE_WIDTH:
99
+ fieldSetBase = staticStyles.fieldSetResponsive;
100
+ break;
101
+ default:
102
+ fieldSetBase = staticStyles.fieldsetBase;
103
+ break;
104
+ }
105
+ return {
106
+ ...fieldSetBase,
107
+ borderRadius,
108
+ backgroundColor: isMobileNonContained ? 'transparent' : backgroundColor || 'transparent',
109
+ padding
110
+ };
111
+ }, [borderRadius, backgroundColor, padding, isMobileNonContained, buttonWidthVariant]);
112
+ const buttonsContainerStyles = useMemo(() => {
113
+ let flexWrap;
114
+ if (isWeb) {
115
+ flexWrap = 'wrap';
116
+ } else {
117
+ switch (buttonWidthVariant) {
118
+ case EQUAL_WIDTH:
119
+ case RESPONSIVE_WIDTH:
120
+ flexWrap = 'nowrap';
121
+ break;
122
+ default:
123
+ flexWrap = 'wrap';
124
+ break;
125
+ }
126
+ }
127
+ let justifyContent;
128
+ switch (buttonWidthVariant) {
129
+ case EQUAL_WIDTH:
130
+ justifyContent = 'space-evenly';
131
+ break;
132
+ default:
133
+ justifyContent = 'flex-start';
134
+ break;
135
+ }
136
+ let viewBaseStyle;
137
+ switch (buttonWidthVariant) {
138
+ case EQUAL_WIDTH:
139
+ case RESPONSIVE_WIDTH:
140
+ viewBaseStyle = {};
141
+ break;
142
+ default:
143
+ viewBaseStyle = staticStyles.viewBase;
144
+ break;
145
+ }
146
+ return {
147
+ ...viewBaseStyle,
148
+ flexWrap,
149
+ flexDirection: direction === 'column' ? 'column' : 'row',
150
+ gap: gapValue || 0,
151
+ justifyContent
152
+ };
153
+ }, [direction, gapValue, buttonWidthVariant, isWeb]);
93
154
  const {
94
155
  currentValues,
95
156
  toggleOneValue
@@ -111,7 +172,7 @@ export const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
111
172
  }
112
173
 
113
174
  // Some web screenreaders e.g. MacOS Voiceover don't handle radiogroups properly unless radio is direct child of radiogroup
114
- const innerRole = Platform.OS === 'web' && accessibilityRole === 'radiogroup' ? accessibilityRole : undefined;
175
+ const buttonsWrapperRole = Platform.OS === 'web' && accessibilityRole === 'radiogroup' ? accessibilityRole : undefined;
115
176
  return /*#__PURE__*/_jsx(Fieldset, {
116
177
  ...systemProps,
117
178
  ref: ref,
@@ -129,8 +190,8 @@ export const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
129
190
  style: fieldsetStyles,
130
191
  ...selectProps(rest),
131
192
  children: /*#__PURE__*/_jsx(View, {
132
- accessibilityRole: innerRole,
133
- style: viewStyles,
193
+ accessibilityRole: buttonsWrapperRole,
194
+ style: buttonsContainerStyles,
134
195
  children: items.map((_ref2, index) => {
135
196
  let {
136
197
  label,
@@ -300,17 +361,24 @@ ButtonGroup.propTypes = {
300
361
  copy: PropTypes.oneOf(['en', 'fr'])
301
362
  };
302
363
  const staticStyles = StyleSheet.create({
364
+ equalWidth: {
365
+ width: '100%',
366
+ flex: 1
367
+ },
368
+ responsiveWidth: {
369
+ width: 'auto'
370
+ },
303
371
  fieldsetBase: {
304
372
  alignSelf: 'flex-start',
305
- display: 'inline'
373
+ display: 'inline',
374
+ width: 'auto'
375
+ },
376
+ fieldSetResponsive: {
377
+ width: '100%'
306
378
  },
307
379
  viewBase: {
308
380
  flexWrap: 'wrap',
309
381
  alignContent: 'stretch',
310
382
  alignItems: 'center'
311
- },
312
- equalWidth: {
313
- width: '100%',
314
- flex: 1
315
383
  }
316
384
  });
@@ -20,9 +20,7 @@ import { FlexGridCol } from '../FlexGrid/FlexGridCol/FlexGridCol';
20
20
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
21
21
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
22
22
  const GRID_COLUMNS = 12;
23
- const isOverlayColor = color => {
24
- return color && typeof color === 'string' && color.startsWith('rgba(');
25
- };
23
+ const isOverlayColor = color => color && typeof color === 'string' && color.startsWith('rgba(');
26
24
  const setBackgroundImage = _ref => {
27
25
  let {
28
26
  src,
@@ -31,7 +29,8 @@ const setBackgroundImage = _ref => {
31
29
  backgroundImagePosition,
32
30
  backgroundImageAlign,
33
31
  content,
34
- cardStyle
32
+ cardStyle,
33
+ testID
35
34
  } = _ref;
36
35
  const borderRadius = cardStyle?.borderRadius || 0;
37
36
  const borderWidth = cardStyle?.borderWidth || 0;
@@ -91,6 +90,7 @@ const setBackgroundImage = _ref => {
91
90
  style: [staticStyles.imageBackground, backgroundImageStyle],
92
91
  role: "img",
93
92
  "aria-label": alt,
93
+ testID: testID,
94
94
  children: content
95
95
  });
96
96
  }
@@ -105,7 +105,8 @@ const setBackgroundImage = _ref => {
105
105
  style: [staticStyles.containImage, positionStyles],
106
106
  accessible: true,
107
107
  accessibilityLabel: alt,
108
- accessibilityIgnoresInvertColors: true
108
+ accessibilityIgnoresInvertColors: true,
109
+ testID: testID
109
110
  }), /*#__PURE__*/_jsx(View, {
110
111
  style: staticStyles.contentOverlay,
111
112
  children: content
@@ -121,6 +122,7 @@ const setBackgroundImage = _ref => {
121
122
  style: staticStyles.imageBackground,
122
123
  accessible: true,
123
124
  accessibilityLabel: alt,
125
+ testID: testID,
124
126
  children: content
125
127
  });
126
128
  };
@@ -129,22 +131,13 @@ const selectPaddedContentStyles = _ref2 => {
129
131
  paddingTop,
130
132
  paddingBottom,
131
133
  paddingLeft,
132
- paddingRight,
133
- borderWidth,
134
- borderColor,
135
- borderRadius,
136
- hasInteractiveBorder
134
+ paddingRight
137
135
  } = _ref2;
138
136
  return {
139
137
  paddingTop,
140
138
  paddingBottom,
141
139
  paddingLeft,
142
- paddingRight,
143
- ...(hasInteractiveBorder ? {
144
- borderWidth,
145
- borderColor,
146
- borderRadius
147
- } : {})
140
+ paddingRight
148
141
  };
149
142
  };
150
143
  const selectInteractiveOverlayStyles = _ref3 => {
@@ -163,12 +156,31 @@ const selectInteractiveOverlayStyles = _ref3 => {
163
156
  backgroundColor,
164
157
  borderRadius: adjustedBorderRadius,
165
158
  pointerEvents: 'none',
166
- zIndex: 1
159
+ zIndex: 3
160
+ };
161
+ };
162
+ const selectOuterContainerStyles = _ref4 => {
163
+ let {
164
+ containerStyle,
165
+ backgroundColor,
166
+ borderRadius,
167
+ borderColor,
168
+ borderWidth,
169
+ hasGradientToken
170
+ } = _ref4;
171
+ return {
172
+ ...containerStyle,
173
+ backgroundColor,
174
+ borderRadius,
175
+ ...(hasGradientToken ? {} : {
176
+ borderColor,
177
+ borderWidth
178
+ })
167
179
  };
168
180
  };
169
181
 
170
182
  // Ensure explicit selection of tokens
171
- export const selectStyles = _ref4 => {
183
+ export const selectStyles = function (_ref5) {
172
184
  let {
173
185
  flex,
174
186
  backgroundColor,
@@ -189,7 +201,10 @@ export const selectStyles = _ref4 => {
189
201
  gradient,
190
202
  maxHeight,
191
203
  overflowY
192
- } = _ref4;
204
+ } = _ref5;
205
+ let {
206
+ hasBgImage = false
207
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
193
208
  const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web';
194
209
  let backgroundImageValue = null;
195
210
  if (hasGradient) {
@@ -218,7 +233,11 @@ export const selectStyles = _ref4 => {
218
233
  ...(gradient && Platform.OS === 'web' ? {
219
234
  backgroundImage: backgroundImageValue,
220
235
  backgroundOrigin: `border-box`,
221
- boxShadow: `inset 0 1000px ${boxShadowColor}`,
236
+ // bgImage child fills the content-box and replaces what the inset shadow
237
+ // used to cover, so we omit it to avoid hiding the image.
238
+ ...(hasBgImage ? {} : {
239
+ boxShadow: `inset 0 1000px ${boxShadowColor}`
240
+ }),
222
241
  border: `${borderWidth}px solid transparent`
223
242
  } : {}),
224
243
  ...(backgroundGradient && Platform.OS === 'web' ? {
@@ -235,7 +254,7 @@ export const selectStyles = _ref4 => {
235
254
  * A themeless base component for Card which components can apply theme tokens to. Not
236
255
  * intended to be used in apps or sites directly: build themed components on top of this.
237
256
  */
238
- export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
257
+ export const CardBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
239
258
  let {
240
259
  children,
241
260
  tokens,
@@ -243,15 +262,14 @@ export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
243
262
  backgroundImage,
244
263
  fullBleedContent,
245
264
  cardState,
265
+ testID,
246
266
  ...rest
247
- } = _ref5;
267
+ } = _ref6;
248
268
  const resolvedTokens = typeof tokens === 'function' ? tokens(cardState) : tokens;
249
- const tokensToUse = backgroundImage && backgroundImage.src ? {
250
- ...resolvedTokens,
251
- gradient: undefined,
252
- backgroundGradient: undefined
253
- } : resolvedTokens;
254
- const cardStyle = selectStyles(tokensToUse);
269
+ const hasBgImage = Boolean(backgroundImage && backgroundImage.src);
270
+ const cardStyle = selectStyles(resolvedTokens, {
271
+ hasBgImage
272
+ });
255
273
  const props = selectProps(rest);
256
274
  let content = children;
257
275
  const {
@@ -284,31 +302,29 @@ export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
284
302
  ...containerStyle
285
303
  } = cardStyle;
286
304
  const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight;
287
- const hasInteractiveBorder = borderWidth && borderWidth > 0;
288
305
  const hasInteractiveOverlay = isOverlayColor(backgroundColor);
289
- const paddedContent = hasPadding || hasInteractiveBorder ? /*#__PURE__*/_jsx(View, {
306
+ const outerBackgroundColor = hasInteractiveOverlay ? undefined : backgroundColor;
307
+ const hasGradientToken = Boolean(resolvedTokens.gradient) && Platform.OS === 'web';
308
+ const paddedContent = hasPadding ? /*#__PURE__*/_jsx(View, {
290
309
  style: selectPaddedContentStyles({
291
310
  paddingTop,
292
311
  paddingBottom,
293
312
  paddingLeft,
294
- paddingRight,
295
- borderWidth,
296
- borderColor,
297
- borderRadius,
298
- hasInteractiveBorder
313
+ paddingRight
299
314
  }),
300
315
  children: children
301
316
  }) : children;
302
317
  const contentWithOverlay = /*#__PURE__*/_jsxs(_Fragment, {
303
- children: [hasInteractiveOverlay && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
318
+ children: [/*#__PURE__*/_jsx(View, {
319
+ style: staticStyles.contentOverlay,
320
+ children: paddedContent
321
+ }), hasInteractiveOverlay && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
304
322
  style: selectInteractiveOverlayStyles({
305
323
  backgroundColor,
306
324
  borderRadius,
307
325
  borderWidth
308
- })
309
- }), /*#__PURE__*/_jsx(View, {
310
- style: staticStyles.contentOverlay,
311
- children: paddedContent
326
+ }),
327
+ testID: testID && `${testID}-card-base-bg-overlay`
312
328
  })]
313
329
  });
314
330
  content = setBackgroundImage({
@@ -319,17 +335,23 @@ export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
319
335
  backgroundImageAlign,
320
336
  content: contentWithOverlay,
321
337
  cardStyle: {
322
- ...containerStyle,
323
- borderRadius
324
- }
338
+ borderRadius,
339
+ borderWidth
340
+ },
341
+ testID: testID && `${testID}-card-base-bg-image`
325
342
  });
326
343
  return /*#__PURE__*/_jsx(View, {
327
- style: {
328
- ...containerStyle,
329
- borderRadius
330
- },
344
+ style: selectOuterContainerStyles({
345
+ containerStyle,
346
+ backgroundColor: outerBackgroundColor,
347
+ borderRadius,
348
+ borderColor,
349
+ borderWidth,
350
+ hasGradientToken
351
+ }),
331
352
  dataSet: dataSet,
332
353
  ref: ref,
354
+ testID: testID,
333
355
  ...props,
334
356
  children: content
335
357
  });
@@ -369,6 +391,7 @@ export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
369
391
  style: containerStyle,
370
392
  dataSet: dataSet,
371
393
  ref: ref,
394
+ testID: testID,
372
395
  ...props,
373
396
  children: /*#__PURE__*/_jsx(FlexGrid, {
374
397
  children: /*#__PURE__*/_jsx(FlexGridRow, {
@@ -385,6 +408,7 @@ export const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
385
408
  style: cardStyle,
386
409
  dataSet: dataSet,
387
410
  ref: ref,
411
+ testID: testID,
388
412
  ...props,
389
413
  children: content
390
414
  });
@@ -427,6 +451,10 @@ CardBase.propTypes = {
427
451
  ...selectedSystemPropTypes,
428
452
  children: PropTypes.node,
429
453
  tokens: getTokensPropType('Card'),
454
+ /**
455
+ * Identifier for testing purposes.
456
+ */
457
+ testID: PropTypes.string,
430
458
  /**
431
459
  * Apply background image to the card.
432
460
  */
@@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import Pressable from "react-native-web/dist/exports/Pressable";
5
5
  import Platform from "react-native-web/dist/exports/Platform";
6
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
7
  import { resolvePressableTokens } from '../utils/pressability';
7
8
  import { applyShadowToken } from '../ThemeProvider/utils/styles';
8
9
  import { getTokensPropType } from '../utils/props/tokens';
9
10
  import { Tooltip } from '../Tooltip/Tooltip.native';
10
- import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { UNAVAILABLE_VARIANT } from './constants';
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
13
  const selectGeneralBubbleTokens = _ref => {
12
14
  let {
13
15
  outerBubbleHeight,
@@ -63,37 +65,70 @@ const selectBorderBubbleTokens = _ref3 => {
63
65
  borderRadius: bubbleBorderRadius
64
66
  };
65
67
  };
66
- export const ColourBubble = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
68
+ const selectOverlayStyles = _ref4 => {
67
69
  let {
68
- tokens = {},
70
+ overlayColor,
71
+ innerBubbleBorderRadius
72
+ } = _ref4;
73
+ return {
74
+ borderRadius: innerBubbleBorderRadius,
75
+ backgroundColor: overlayColor
76
+ };
77
+ };
78
+ const selectSlashStyles = (_ref5, unavailable) => {
79
+ let {
80
+ slashLightColor,
81
+ slashDarkColor,
82
+ slashWidth,
83
+ slashOffset
84
+ } = _ref5;
85
+ return {
86
+ width: slashWidth,
87
+ backgroundColor: unavailable === UNAVAILABLE_VARIANT.DARK ? slashDarkColor : slashLightColor,
88
+ marginLeft: slashOffset
89
+ };
90
+ };
91
+ export const ColourBubble = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
92
+ let {
93
+ tokens = () => ({}),
69
94
  id,
70
95
  colourHexCode,
71
96
  colourName,
72
97
  isSelected,
73
98
  onPress,
74
- showTooltip
75
- } = _ref4;
99
+ showTooltip,
100
+ unavailable,
101
+ isDisabled
102
+ } = _ref6;
76
103
  const defaultTokens = tokens({
77
104
  selected: isSelected
78
105
  });
79
- const resolveColourBubbleTokens = pressState => resolvePressableTokens(tokens, pressState, {});
80
- const themeTokens = React.useMemo(() => tokens(), [tokens]);
81
- const pressable = /*#__PURE__*/_jsx(Pressable, {
106
+ const resolveColourBubbleTokens = pressState => resolvePressableTokens(tokens, isDisabled ? {} : pressState, {});
107
+ const themeTokens = tokens();
108
+ const pressable = /*#__PURE__*/_jsxs(Pressable, {
82
109
  style: state => [selectGeneralBubbleTokens(resolveColourBubbleTokens(state)), isSelected && selectBorderBubbleTokens(defaultTokens)],
83
110
  onPress: onPress,
111
+ disabled: isDisabled,
112
+ focusable: !isDisabled,
84
113
  accessible: true,
85
114
  accessibilityRole: "radio",
86
115
  accessibilityLabel: colourName,
87
116
  accessibilityState: {
88
- checked: isSelected
117
+ checked: isSelected,
118
+ disabled: isDisabled
89
119
  },
90
120
  ref: ref,
91
121
  testID: id,
92
- children: /*#__PURE__*/_jsx(View, {
122
+ children: [/*#__PURE__*/_jsx(View, {
93
123
  style: [selectInnerBubbleTokens(themeTokens), {
94
124
  backgroundColor: colourHexCode
95
- }]
96
- })
125
+ }],
126
+ children: unavailable && /*#__PURE__*/_jsx(View, {
127
+ style: [StyleSheet.absoluteFillObject, selectOverlayStyles(themeTokens)]
128
+ })
129
+ }), unavailable && /*#__PURE__*/_jsx(View, {
130
+ style: [staticStyles.indicator, selectSlashStyles(themeTokens, unavailable)]
131
+ })]
97
132
  });
98
133
  if (showTooltip) {
99
134
  return /*#__PURE__*/_jsx(Tooltip, {
@@ -135,5 +170,23 @@ ColourBubble.propTypes = {
135
170
  /**
136
171
  * When true, wraps the bubble in a Tooltip that displays the colourName on hover (web only).
137
172
  */
138
- showTooltip: PropTypes.bool
139
- };
173
+ showTooltip: PropTypes.bool,
174
+ /**
175
+ * When set, renders a diagonal indicator over the bubble. Accepts either 'dark' or 'light' to determine the colour of the indicator.
176
+ */
177
+ unavailable: PropTypes.oneOf(Object.values(UNAVAILABLE_VARIANT)),
178
+ /**
179
+ * When true, disables interaction on the bubble.
180
+ */
181
+ isDisabled: PropTypes.bool
182
+ };
183
+ const staticStyles = StyleSheet.create({
184
+ indicator: {
185
+ position: 'absolute',
186
+ top: '50%',
187
+ height: 2,
188
+ transform: [{
189
+ rotate: '-45deg'
190
+ }]
191
+ }
192
+ });
@@ -7,9 +7,12 @@ import { getTokensPropType } from '../utils/props/tokens';
7
7
  import { selectSystemProps } from '../utils/props/selectSystemProps';
8
8
  import { variantProp } from '../utils/props/variantProp';
9
9
  import { viewProps } from '../utils/props/viewProps';
10
+ import { useCopy } from '../utils/useCopy';
10
11
  import { StackWrap } from '../StackView/StackWrap';
11
12
  import { Typography } from '../Typography/Typography';
12
13
  import { ColourBubble } from './ColourBubble';
14
+ import { UNAVAILABLE_VARIANT, DICTIONARY_CONTENT_SHAPE } from './constants';
15
+ import { DEFAULT_COLOUR_TOGGLE_DICTIONARY } from './dictionary';
13
16
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
17
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
15
18
  export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
@@ -20,37 +23,49 @@ export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
20
23
  items,
21
24
  onChange,
22
25
  showTooltips,
26
+ copy = 'en',
27
+ dictionary = DEFAULT_COLOUR_TOGGLE_DICTIONARY,
23
28
  ...rest
24
29
  } = _ref;
25
30
  const [currentColourId, setCurrentColourId] = React.useState(defaultColourId);
26
31
  const getTokens = useThemeTokensCallback('ColourToggle', tokens, variant);
32
+ const getCopy = useCopy({
33
+ dictionary,
34
+ copy
35
+ });
27
36
  const {
28
37
  space
29
38
  } = getTokens();
30
- const {
31
- colourName: currentColourName = ''
32
- } = items.find(item => item.id === currentColourId) || '';
39
+ const currentItem = items.find(_ref2 => {
40
+ let {
41
+ id
42
+ } = _ref2;
43
+ return id === currentColourId;
44
+ });
33
45
  return /*#__PURE__*/_jsxs(View, {
34
46
  ref: ref,
35
47
  ...selectProps(rest),
36
48
  children: [/*#__PURE__*/_jsx(Typography, {
37
- children: currentColourName
49
+ children: currentItem?.unavailable ? `${currentItem.colourName} ${getCopy('unavailable')}` : currentItem?.colourName ?? ''
38
50
  }), /*#__PURE__*/_jsx(StackWrap, {
39
51
  space: space,
40
52
  accessibilityRole: "radiogroup",
41
- children: items.map((_ref2, index) => {
53
+ children: items.map((_ref3, index) => {
42
54
  let {
43
55
  id,
44
56
  colourHexCode,
45
- colourName
46
- } = _ref2;
57
+ colourName,
58
+ unavailable,
59
+ disabled
60
+ } = _ref3;
47
61
  const colourBubbleId = id || `ColourBubble[${index}]`;
48
62
  const handleChangeColour = event => {
49
63
  setCurrentColourId(id);
50
64
  onChange?.(event, {
51
65
  id,
52
66
  colourHexCode,
53
- colourName
67
+ colourName,
68
+ unavailable
54
69
  });
55
70
  };
56
71
  return /*#__PURE__*/_jsx(ColourBubble, {
@@ -60,7 +75,9 @@ export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
60
75
  colourHexCode: colourHexCode,
61
76
  colourName: colourName,
62
77
  onPress: handleChangeColour,
63
- showTooltip: showTooltips
78
+ showTooltip: showTooltips,
79
+ unavailable: unavailable,
80
+ isDisabled: !!disabled
64
81
  }, colourBubbleId);
65
82
  })
66
83
  })]
@@ -87,7 +104,9 @@ ColourToggle.propTypes = {
87
104
  items: PropTypes.arrayOf(PropTypes.exact({
88
105
  colourHexCode: PropTypes.string,
89
106
  colourName: PropTypes.string,
90
- id: PropTypes.string
107
+ id: PropTypes.string,
108
+ unavailable: PropTypes.oneOf(Object.values(UNAVAILABLE_VARIANT)),
109
+ disabled: PropTypes.bool
91
110
  })),
92
111
  /**
93
112
  * If provided, this function is called when the current selection of the color is changed of all currently `items`. Receives two parameters: item object selected and the event
@@ -96,5 +115,16 @@ ColourToggle.propTypes = {
96
115
  /**
97
116
  * When true, displays each colour's name as a tooltip on hover (web only).
98
117
  */
99
- showTooltips: PropTypes.bool
118
+ showTooltips: PropTypes.bool,
119
+ /**
120
+ * Select English or French copy.
121
+ */
122
+ copy: PropTypes.oneOf(['en', 'fr']),
123
+ /**
124
+ * Override default labels.
125
+ */
126
+ dictionary: PropTypes.shape({
127
+ en: DICTIONARY_CONTENT_SHAPE,
128
+ fr: DICTIONARY_CONTENT_SHAPE
129
+ })
100
130
  };
@@ -0,0 +1,8 @@
1
+ import PropTypes from 'prop-types';
2
+ export const UNAVAILABLE_VARIANT = {
3
+ DARK: 'dark',
4
+ LIGHT: 'light'
5
+ };
6
+ export const DICTIONARY_CONTENT_SHAPE = PropTypes.shape({
7
+ unavailable: PropTypes.string
8
+ });
@@ -0,0 +1,8 @@
1
+ export const DEFAULT_COLOUR_TOGGLE_DICTIONARY = {
2
+ en: {
3
+ unavailable: '(Out of stock)'
4
+ },
5
+ fr: {
6
+ unavailable: '(En rupture de stock)'
7
+ }
8
+ };