@telus-uds/components-base 3.30.0 → 3.32.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 (45) hide show
  1. package/CHANGELOG.md +31 -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 +65 -12
  7. package/lib/cjs/ColourToggle/ColourToggle.js +40 -11
  8. package/lib/cjs/ColourToggle/constants.js +15 -0
  9. package/lib/cjs/ColourToggle/dictionary.js +15 -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 +66 -13
  21. package/lib/esm/ColourToggle/ColourToggle.js +41 -12
  22. package/lib/esm/ColourToggle/constants.js +8 -0
  23. package/lib/esm/ColourToggle/dictionary.js +9 -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 +2 -2
  31. package/package.json +2 -2
  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 +50 -10
  38. package/src/ColourToggle/constants.js +10 -0
  39. package/src/ColourToggle/dictionary.js +6 -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
@@ -15,9 +15,7 @@ import FlexGridCol from '../FlexGrid/Col';
15
15
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
16
16
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
17
17
  const GRID_COLUMNS = 12;
18
- const isOverlayColor = color => {
19
- return color && typeof color === 'string' && color.startsWith('rgba(');
20
- };
18
+ const isOverlayColor = color => color && typeof color === 'string' && color.startsWith('rgba(');
21
19
  const setBackgroundImage = _ref => {
22
20
  let {
23
21
  src,
@@ -26,7 +24,8 @@ const setBackgroundImage = _ref => {
26
24
  backgroundImagePosition,
27
25
  backgroundImageAlign,
28
26
  content,
29
- cardStyle
27
+ cardStyle,
28
+ testID
30
29
  } = _ref;
31
30
  const borderRadius = cardStyle?.borderRadius || 0;
32
31
  const borderWidth = cardStyle?.borderWidth || 0;
@@ -86,6 +85,7 @@ const setBackgroundImage = _ref => {
86
85
  style: [staticStyles.imageBackground, backgroundImageStyle],
87
86
  role: "img",
88
87
  "aria-label": alt,
88
+ testID: testID,
89
89
  children: content
90
90
  });
91
91
  }
@@ -100,7 +100,8 @@ const setBackgroundImage = _ref => {
100
100
  style: [staticStyles.containImage, positionStyles],
101
101
  accessible: true,
102
102
  accessibilityLabel: alt,
103
- accessibilityIgnoresInvertColors: true
103
+ accessibilityIgnoresInvertColors: true,
104
+ testID: testID
104
105
  }), /*#__PURE__*/_jsx(View, {
105
106
  style: staticStyles.contentOverlay,
106
107
  children: content
@@ -116,6 +117,7 @@ const setBackgroundImage = _ref => {
116
117
  style: staticStyles.imageBackground,
117
118
  accessible: true,
118
119
  accessibilityLabel: alt,
120
+ testID: testID,
119
121
  children: content
120
122
  });
121
123
  };
@@ -124,22 +126,13 @@ const selectPaddedContentStyles = _ref2 => {
124
126
  paddingTop,
125
127
  paddingBottom,
126
128
  paddingLeft,
127
- paddingRight,
128
- borderWidth,
129
- borderColor,
130
- borderRadius,
131
- hasInteractiveBorder
129
+ paddingRight
132
130
  } = _ref2;
133
131
  return {
134
132
  paddingTop,
135
133
  paddingBottom,
136
134
  paddingLeft,
137
- paddingRight,
138
- ...(hasInteractiveBorder ? {
139
- borderWidth,
140
- borderColor,
141
- borderRadius
142
- } : {})
135
+ paddingRight
143
136
  };
144
137
  };
145
138
  const selectInteractiveOverlayStyles = _ref3 => {
@@ -158,12 +151,31 @@ const selectInteractiveOverlayStyles = _ref3 => {
158
151
  backgroundColor,
159
152
  borderRadius: adjustedBorderRadius,
160
153
  pointerEvents: 'none',
161
- zIndex: 1
154
+ zIndex: 3
155
+ };
156
+ };
157
+ const selectOuterContainerStyles = _ref4 => {
158
+ let {
159
+ containerStyle,
160
+ backgroundColor,
161
+ borderRadius,
162
+ borderColor,
163
+ borderWidth,
164
+ hasGradientToken
165
+ } = _ref4;
166
+ return {
167
+ ...containerStyle,
168
+ backgroundColor,
169
+ borderRadius,
170
+ ...(hasGradientToken ? {} : {
171
+ borderColor,
172
+ borderWidth
173
+ })
162
174
  };
163
175
  };
164
176
 
165
177
  // Ensure explicit selection of tokens
166
- export const selectStyles = _ref4 => {
178
+ export const selectStyles = function (_ref5) {
167
179
  let {
168
180
  flex,
169
181
  backgroundColor,
@@ -184,7 +196,10 @@ export const selectStyles = _ref4 => {
184
196
  gradient,
185
197
  maxHeight,
186
198
  overflowY
187
- } = _ref4;
199
+ } = _ref5;
200
+ let {
201
+ hasBgImage = false
202
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
188
203
  const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web';
189
204
  let backgroundImageValue = null;
190
205
  if (hasGradient) {
@@ -213,7 +228,11 @@ export const selectStyles = _ref4 => {
213
228
  ...(gradient && Platform.OS === 'web' ? {
214
229
  backgroundImage: backgroundImageValue,
215
230
  backgroundOrigin: `border-box`,
216
- boxShadow: `inset 0 1000px ${boxShadowColor}`,
231
+ // bgImage child fills the content-box and replaces what the inset shadow
232
+ // used to cover, so we omit it to avoid hiding the image.
233
+ ...(hasBgImage ? {} : {
234
+ boxShadow: `inset 0 1000px ${boxShadowColor}`
235
+ }),
217
236
  border: `${borderWidth}px solid transparent`
218
237
  } : {}),
219
238
  ...(backgroundGradient && Platform.OS === 'web' ? {
@@ -230,7 +249,7 @@ export const selectStyles = _ref4 => {
230
249
  * A themeless base component for Card which components can apply theme tokens to. Not
231
250
  * intended to be used in apps or sites directly: build themed components on top of this.
232
251
  */
233
- const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
252
+ const CardBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
234
253
  let {
235
254
  children,
236
255
  tokens,
@@ -238,15 +257,14 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
238
257
  backgroundImage,
239
258
  fullBleedContent,
240
259
  cardState,
260
+ testID,
241
261
  ...rest
242
- } = _ref5;
262
+ } = _ref6;
243
263
  const resolvedTokens = typeof tokens === 'function' ? tokens(cardState) : tokens;
244
- const tokensToUse = backgroundImage && backgroundImage.src ? {
245
- ...resolvedTokens,
246
- gradient: undefined,
247
- backgroundGradient: undefined
248
- } : resolvedTokens;
249
- const cardStyle = selectStyles(tokensToUse);
264
+ const hasBgImage = Boolean(backgroundImage && backgroundImage.src);
265
+ const cardStyle = selectStyles(resolvedTokens, {
266
+ hasBgImage
267
+ });
250
268
  const props = selectProps(rest);
251
269
  let content = children;
252
270
  const {
@@ -279,31 +297,29 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
279
297
  ...containerStyle
280
298
  } = cardStyle;
281
299
  const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight;
282
- const hasInteractiveBorder = borderWidth && borderWidth > 0;
283
300
  const hasInteractiveOverlay = isOverlayColor(backgroundColor);
284
- const paddedContent = hasPadding || hasInteractiveBorder ? /*#__PURE__*/_jsx(View, {
301
+ const outerBackgroundColor = hasInteractiveOverlay ? undefined : backgroundColor;
302
+ const hasGradientToken = Boolean(resolvedTokens.gradient) && Platform.OS === 'web';
303
+ const paddedContent = hasPadding ? /*#__PURE__*/_jsx(View, {
285
304
  style: selectPaddedContentStyles({
286
305
  paddingTop,
287
306
  paddingBottom,
288
307
  paddingLeft,
289
- paddingRight,
290
- borderWidth,
291
- borderColor,
292
- borderRadius,
293
- hasInteractiveBorder
308
+ paddingRight
294
309
  }),
295
310
  children: children
296
311
  }) : children;
297
312
  const contentWithOverlay = /*#__PURE__*/_jsxs(_Fragment, {
298
- children: [hasInteractiveOverlay && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
313
+ children: [/*#__PURE__*/_jsx(View, {
314
+ style: staticStyles.contentOverlay,
315
+ children: paddedContent
316
+ }), hasInteractiveOverlay && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
299
317
  style: selectInteractiveOverlayStyles({
300
318
  backgroundColor,
301
319
  borderRadius,
302
320
  borderWidth
303
- })
304
- }), /*#__PURE__*/_jsx(View, {
305
- style: staticStyles.contentOverlay,
306
- children: paddedContent
321
+ }),
322
+ testID: testID && `${testID}-card-base-bg-overlay`
307
323
  })]
308
324
  });
309
325
  content = setBackgroundImage({
@@ -314,17 +330,23 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
314
330
  backgroundImageAlign,
315
331
  content: contentWithOverlay,
316
332
  cardStyle: {
317
- ...containerStyle,
318
- borderRadius
319
- }
333
+ borderRadius,
334
+ borderWidth
335
+ },
336
+ testID: testID && `${testID}-card-base-bg-image`
320
337
  });
321
338
  return /*#__PURE__*/_jsx(View, {
322
- style: {
323
- ...containerStyle,
324
- borderRadius
325
- },
339
+ style: selectOuterContainerStyles({
340
+ containerStyle,
341
+ backgroundColor: outerBackgroundColor,
342
+ borderRadius,
343
+ borderColor,
344
+ borderWidth,
345
+ hasGradientToken
346
+ }),
326
347
  dataSet: dataSet,
327
348
  ref: ref,
349
+ testID: testID,
328
350
  ...props,
329
351
  children: content
330
352
  });
@@ -364,6 +386,7 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
364
386
  style: containerStyle,
365
387
  dataSet: dataSet,
366
388
  ref: ref,
389
+ testID: testID,
367
390
  ...props,
368
391
  children: /*#__PURE__*/_jsx(FlexGrid, {
369
392
  children: /*#__PURE__*/_jsx(FlexGridRow, {
@@ -380,6 +403,7 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
380
403
  style: cardStyle,
381
404
  dataSet: dataSet,
382
405
  ref: ref,
406
+ testID: testID,
383
407
  ...props,
384
408
  children: content
385
409
  });
@@ -422,6 +446,10 @@ CardBase.propTypes = {
422
446
  ...selectedSystemPropTypes,
423
447
  children: PropTypes.node,
424
448
  tokens: getTokensPropType('Card'),
449
+ /**
450
+ * Identifier for testing purposes.
451
+ */
452
+ testID: PropTypes.string,
425
453
  /**
426
454
  * Apply background image to the card.
427
455
  */
@@ -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';
8
9
  import { getTokensPropType } from '../utils';
9
10
  import Tooltip from '../Tooltip';
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
- 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
+ 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,6 +170,24 @@ 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
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
139
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
+ });
140
193
  export default ColourBubble;
@@ -2,10 +2,12 @@ import React from 'react';
2
2
  import View from "react-native-web/dist/exports/View";
3
3
  import PropTypes from 'prop-types';
4
4
  import { useThemeTokensCallback } from '../ThemeProvider';
5
- import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils';
5
+ import { a11yProps, getTokensPropType, selectSystemProps, useCopy, variantProp, viewProps } from '../utils';
6
6
  import { StackWrap } from '../StackView';
7
7
  import Typography from '../Typography';
8
8
  import ColourBubble from './ColourBubble';
9
+ import { UNAVAILABLE_VARIANT, DICTIONARY_CONTENT_SHAPE } from './constants';
10
+ import DEFAULT_COLOUR_TOGGLE_DICTIONARY from './dictionary';
9
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
11
13
  const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
@@ -16,37 +18,49 @@ const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
16
18
  items,
17
19
  onChange,
18
20
  showTooltips,
21
+ copy = 'en',
22
+ dictionary = DEFAULT_COLOUR_TOGGLE_DICTIONARY,
19
23
  ...rest
20
24
  } = _ref;
21
25
  const [currentColourId, setCurrentColourId] = React.useState(defaultColourId);
22
26
  const getTokens = useThemeTokensCallback('ColourToggle', tokens, variant);
27
+ const getCopy = useCopy({
28
+ dictionary,
29
+ copy
30
+ });
23
31
  const {
24
32
  space
25
33
  } = getTokens();
26
- const {
27
- colourName: currentColourName = ''
28
- } = items.find(item => item.id === currentColourId) || '';
34
+ const currentItem = items.find(_ref2 => {
35
+ let {
36
+ id
37
+ } = _ref2;
38
+ return id === currentColourId;
39
+ });
29
40
  return /*#__PURE__*/_jsxs(View, {
30
41
  ref: ref,
31
42
  ...selectProps(rest),
32
43
  children: [/*#__PURE__*/_jsx(Typography, {
33
- children: currentColourName
44
+ children: currentItem?.unavailable ? `${currentItem.colourName} ${getCopy('unavailable')}` : currentItem?.colourName ?? ''
34
45
  }), /*#__PURE__*/_jsx(StackWrap, {
35
46
  space: space,
36
47
  accessibilityRole: "radiogroup",
37
- children: items.map((_ref2, index) => {
48
+ children: items.map((_ref3, index) => {
38
49
  let {
39
50
  id,
40
51
  colourHexCode,
41
- colourName
42
- } = _ref2;
52
+ colourName,
53
+ unavailable,
54
+ disabled
55
+ } = _ref3;
43
56
  const colourBubbleId = id || `ColourBubble[${index}]`;
44
57
  const handleChangeColour = event => {
45
58
  setCurrentColourId(id);
46
59
  onChange?.(event, {
47
60
  id,
48
61
  colourHexCode,
49
- colourName
62
+ colourName,
63
+ unavailable
50
64
  });
51
65
  };
52
66
  return /*#__PURE__*/_jsx(ColourBubble, {
@@ -56,7 +70,9 @@ const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
56
70
  colourHexCode: colourHexCode,
57
71
  colourName: colourName,
58
72
  onPress: handleChangeColour,
59
- showTooltip: showTooltips
73
+ showTooltip: showTooltips,
74
+ unavailable: unavailable,
75
+ isDisabled: !!disabled
60
76
  }, colourBubbleId);
61
77
  })
62
78
  })]
@@ -83,7 +99,9 @@ ColourToggle.propTypes = {
83
99
  items: PropTypes.arrayOf(PropTypes.exact({
84
100
  colourHexCode: PropTypes.string,
85
101
  colourName: PropTypes.string,
86
- id: PropTypes.string
102
+ id: PropTypes.string,
103
+ unavailable: PropTypes.oneOf(Object.values(UNAVAILABLE_VARIANT)),
104
+ disabled: PropTypes.bool
87
105
  })),
88
106
  /**
89
107
  * 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
@@ -92,6 +110,17 @@ ColourToggle.propTypes = {
92
110
  /**
93
111
  * When true, displays each colour's name as a tooltip on hover (web only).
94
112
  */
95
- showTooltips: PropTypes.bool
113
+ showTooltips: PropTypes.bool,
114
+ /**
115
+ * Select English or French copy.
116
+ */
117
+ copy: PropTypes.oneOf(['en', 'fr']),
118
+ /**
119
+ * Override default labels.
120
+ */
121
+ dictionary: PropTypes.shape({
122
+ en: DICTIONARY_CONTENT_SHAPE,
123
+ fr: DICTIONARY_CONTENT_SHAPE
124
+ })
96
125
  };
97
126
  export default ColourToggle;
@@ -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,9 @@
1
+ const DEFAULT_COLOUR_TOGGLE_DICTIONARY = {
2
+ en: {
3
+ unavailable: '(Out of stock)'
4
+ },
5
+ fr: {
6
+ unavailable: '(En rupture de stock)'
7
+ }
8
+ };
9
+ export default DEFAULT_COLOUR_TOGGLE_DICTIONARY;
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
5
  import Platform from "react-native-web/dist/exports/Platform";
6
+ import ScrollView from "react-native-web/dist/exports/ScrollView";
6
7
  import Portal from '../Portal';
7
8
  import { useThemeTokens } from '../ThemeProvider';
8
9
  import Card from '../Card';
@@ -60,7 +61,12 @@ const DropdownOverlay = /*#__PURE__*/React.forwardRef((_ref, ref) => {
60
61
  paddingLeft: paddingHorizontal,
61
62
  paddingRight: paddingHorizontal
62
63
  },
63
- children: children
64
+ children: Platform.OS !== 'web' ? /*#__PURE__*/_jsx(ScrollView, {
65
+ style: {
66
+ maxHeight
67
+ },
68
+ children: children
69
+ }) : children
64
70
  })
65
71
  });
66
72
  });
@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
5
  import Platform from "react-native-web/dist/exports/Platform";
6
- import { Portal } from '@gorhom/portal';
6
+ import { Portal as GorhomPortal } from '@gorhom/portal';
7
+ import LocalPortal from '../Portal/Portal';
7
8
  import { useCopy, copyPropTypes, getTokensPropType, variantProp } from '../utils';
8
9
  import { useViewport } from '../ViewportProvider';
9
10
  import { useThemeTokens } from '../ThemeProvider';
@@ -116,7 +117,12 @@ const ModalOverlay = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
116
117
  copy
117
118
  });
118
119
  const closeLabel = getCopy('closeButton');
119
- return /*#__PURE__*/_jsx(Portal, {
120
+
121
+ // On web, use the local Portal (always appends to document.body) to ensure correct
122
+ // absolute positioning in MFE/iframe contexts. @gorhom/portal may render its host
123
+ // outside the iframe's document, causing coordinate mismatches when scrolled.
124
+ const PortalComponent = Platform.OS === 'web' ? LocalPortal : GorhomPortal;
125
+ return /*#__PURE__*/_jsx(PortalComponent, {
120
126
  children: /*#__PURE__*/_jsx(View, {
121
127
  ref: containerRef,
122
128
  onLayout: onLayout,
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Portal } from '@gorhom/portal';
4
3
  import View from "react-native-web/dist/exports/View";
5
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
5
  import Dimensions from "react-native-web/dist/exports/Dimensions";
@@ -212,20 +211,6 @@ const MultiSelectFilter = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
212
211
  setIsOpen(false);
213
212
  onCancel();
214
213
  };
215
- const appRootRef = React.useRef(null);
216
- const [rootOffsets, setRootOffsets] = React.useState(null);
217
- React.useEffect(() => {
218
- if (rootOffsets) return;
219
- appRootRef.current?.measureInWindow((x, y) => {
220
- // Only set offsets if they are positive
221
- // this is because we want to avoid negative offsets that could cause
222
- // the dropdown to be positioned incorrectly in some situations
223
- if (y > 0) setRootOffsets({
224
- horizontal: x,
225
- vertical: y
226
- });
227
- });
228
- }, [isOpen, rootOffsets]);
229
214
  const {
230
215
  align,
231
216
  offsets
@@ -242,8 +227,7 @@ const MultiSelectFilter = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
242
227
  left: 'left'
243
228
  },
244
229
  offsets: {
245
- vertical: 4 - (rootOffsets?.vertical || 0),
246
- horizontal: -rootOffsets?.horizontal || 0
230
+ vertical: 4
247
231
  }
248
232
  }
249
233
  });
@@ -380,12 +364,7 @@ const MultiSelectFilter = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
380
364
  })]
381
365
  });
382
366
  return /*#__PURE__*/_jsxs(_Fragment, {
383
- children: [/*#__PURE__*/_jsx(Portal, {
384
- children: /*#__PURE__*/_jsx(View, {
385
- ref: appRootRef,
386
- style: styles.appRootRef
387
- })
388
- }), /*#__PURE__*/_jsx(ButtonDropdown, {
367
+ children: [/*#__PURE__*/_jsx(ButtonDropdown, {
389
368
  ref: sourceRef,
390
369
  ...pressHandlers,
391
370
  value: isOpen,
@@ -466,11 +445,6 @@ const styles = StyleSheet.create({
466
445
  },
467
446
  scrollContainer: {
468
447
  padding: 1
469
- },
470
- appRootRef: {
471
- position: 'absolute',
472
- top: 0,
473
- left: 0
474
448
  }
475
449
  });
476
450
 
@@ -272,7 +272,8 @@ const TextInputBase = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
272
272
  }, [element, pattern]);
273
273
  const handleChangeText = event => {
274
274
  const text = event.nativeEvent?.text ?? event.target?.value;
275
- let filteredText = isNumeric ? text?.replace(/[^\d]/g, '') || undefined : text;
275
+ // Do NOT use `|| undefined`: empty string is falsy and would break the controlled contract
276
+ let filteredText = isNumeric ? text?.replace(/[^\d]/g, '') ?? '' : text;
276
277
  if (type === 'card' && filteredText) {
277
278
  const formattedValue = filteredText.replace(/[^a-zA-Z0-9]/g, '');
278
279
  const regex = new RegExp(`([a-zA-Z0-9]{4})(?=[a-zA-Z0-9])`, 'g');
@@ -199,7 +199,11 @@ const Validator = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
199
199
 
200
200
  // Sync external value prop to internal state
201
201
  React.useEffect(() => {
202
- if (value && Number(value).toString() !== 'NaN') {
202
+ if (value === '') {
203
+ setCodes(Array(validatorsLength).fill(''));
204
+ return;
205
+ }
206
+ if (Number(value).toString() !== 'NaN') {
203
207
  const digits = value.split('').slice(0, validatorsLength);
204
208
  const newCodes = Array(validatorsLength).fill('');
205
209
  digits.forEach((digit, i) => {