@telus-uds/components-web 4.18.2 → 4.19.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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,23 @@
1
1
  # Change Log - @telus-uds/components-web
2
2
 
3
- This log was last generated on Fri, 20 Feb 2026 23:53:11 GMT and should not be manually modified.
3
+ This log was last generated on Tue, 03 Mar 2026 23:02:55 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 4.19.0
8
+
9
+ Tue, 03 Mar 2026 23:02:55 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - `Card`: add padding feature (sergio.ramirez@telus.com)
14
+ - Spinner: Added a new prop called `persistChildrenState`, to be used only when overlaying children, ensuring the children's state is kept after the Spinner is triggered. (kevin.ruiz@telus.com)
15
+ - Bump @telus-uds/components-base to v3.28.2
16
+
17
+ ### Patches
18
+
19
+ - `Autocomplete`: add the ability to show all values and filter them (josue.higueroscalderon@telus.com)
20
+
7
21
  ## 4.18.2
8
22
 
9
23
  Fri, 20 Feb 2026 23:53:11 GMT
@@ -185,6 +185,52 @@ const FocusBorder = /*#__PURE__*/_styledComponents.default.div.withConfig({
185
185
  zIndex: 2
186
186
  };
187
187
  });
188
+ const FullBleedPaddingWrapper = /*#__PURE__*/_styledComponents.default.div.withConfig({
189
+ displayName: "Card__FullBleedPaddingWrapper",
190
+ componentId: "components-web__sc-1elbtwd-4"
191
+ })(_ref4 => {
192
+ let {
193
+ paddingTop,
194
+ paddingBottom,
195
+ paddingLeft,
196
+ paddingRight
197
+ } = _ref4;
198
+ return {
199
+ paddingTop,
200
+ paddingBottom,
201
+ paddingLeft,
202
+ paddingRight,
203
+ display: 'flex',
204
+ flexDirection: 'column'
205
+ };
206
+ });
207
+
208
+ /**
209
+ * Calculates the padding values for full bleed content.
210
+ * Inherits the card's theme padding (minus borderWidth to visually match card body padding)
211
+ * and allows custom overrides via the fullBleedPadding prop.
212
+ *
213
+ * @param {object} themeTokens - The card's resolved theme tokens (contains padding values and borderWidth)
214
+ * @param {object|boolean} fullBleedPadding - true to inherit card padding, or an object with overrides
215
+ * @param {object} paddingSides - Boolean flags for which sides to apply padding
216
+ * @returns {object} Padding values for each side in px
217
+ */
218
+ const calculateFullBleedPadding = (themeTokens, fullBleedPadding, paddingSides) => {
219
+ const customTokens = typeof fullBleedPadding === 'object' ? fullBleedPadding : undefined;
220
+ const borderWidth = themeTokens.borderWidth || 0;
221
+ const applyPadding = side => {
222
+ const sideKey = `applyPadding${side}`;
223
+ if (!paddingSides[sideKey]) return 0;
224
+ const tokenKey = `padding${side}`;
225
+ return customTokens?.[tokenKey] ?? themeTokens[tokenKey] - borderWidth ?? 0;
226
+ };
227
+ return {
228
+ paddingTop: applyPadding('Top'),
229
+ paddingBottom: applyPadding('Bottom'),
230
+ paddingLeft: applyPadding('Left'),
231
+ paddingRight: applyPadding('Right')
232
+ };
233
+ };
188
234
  const Card = /*#__PURE__*/_react.default.forwardRef(function () {
189
235
  let {
190
236
  children,
@@ -192,6 +238,7 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
192
238
  footerPadding,
193
239
  fullBleedImage,
194
240
  fullBleedContent = fullBleedImage,
241
+ fullBleedPadding,
195
242
  tokens = {},
196
243
  variant,
197
244
  interactiveCard,
@@ -218,7 +265,8 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
218
265
  contentStackDirection,
219
266
  fullBleedContentPosition,
220
267
  fullBleedContentProps,
221
- fullBleedContentChildrenAlign
268
+ fullBleedContentChildrenAlign,
269
+ fullBleedPaddingSides
222
270
  } = (0, _FullBleedContent.useFullBleedContentProps)(fullBleedContent);
223
271
  const {
224
272
  imgCol,
@@ -243,6 +291,12 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
243
291
  borderRadius
244
292
  } = allThemeTokens;
245
293
 
294
+ // Get viewport-aware tokens for responsive full bleed padding
295
+ // useThemeTokens (above) doesn't pass viewport state, so it resolves to base/mobile tokens.
296
+ // useAllViewportTokens resolves tokens for ALL viewports + the current one (reactive to resize).
297
+ // This ensures fullBleedPadding matches CardContent's padding at every breakpoint.
298
+ const allViewportTokens = (0, _componentsBase.useAllViewportTokens)('Card', tokens, variant);
299
+
246
300
  // Interactive cards: merge variants for CardBase (outer container)
247
301
  // The outer variant takes priority over interactiveCard.variant for the style property
248
302
  // This ensures the gradient is only applied to CardBase, not PressableCardBase and avoid duplication
@@ -318,6 +372,13 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
318
372
  const hasFooter = Boolean(footer);
319
373
  const fullBleedBorderRadius = (0, _FullBleedContent.getFullBleedBorderRadius)(borderRadius, fullBleedContentPosition, hasFooter);
320
374
 
375
+ // Calculate full bleed content padding values
376
+ // When fullBleedPadding is truthy, apply padding around full bleed content
377
+ // Uses viewport-aware tokens (allViewportTokens.current) so padding matches
378
+ // CardContent's responsive padding at every breakpoint
379
+ const hasFullBleedPadding = Boolean(fullBleedPadding);
380
+ const fullBleedPaddingValues = hasFullBleedPadding ? calculateFullBleedPadding(allViewportTokens.current, fullBleedPadding, fullBleedPaddingSides) : null;
381
+
321
382
  // takes imgCol from fullBleedContent if present, to dynamically set width of image
322
383
  // card content will adapt to the size of image to add up to 100% width of card width
323
384
  // pass as props to ConditionalWrapper
@@ -343,8 +404,8 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
343
404
  flexShrink: 1,
344
405
  justifyContent: 'space-between'
345
406
  };
346
- const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref4 => {
347
- let [key] = _ref4;
407
+ const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref5 => {
408
+ let [key] = _ref5;
348
409
  return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage ? ['backgroundColor', 'gradient', 'backgroundGradient'] : [])].includes(key);
349
410
  }));
350
411
  const cardBaseVariant = backgroundImage ? {
@@ -456,11 +517,16 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
456
517
  WrapperComponent: DynamicWidthContainer,
457
518
  wrapperProps: imageWrapperStyleProps,
458
519
  condition: isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed,
459
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FullBleedContent.default, {
460
- borderRadius: fullBleedBorderRadius,
461
- ...fullBleedContentPropsClean,
462
- position: fullBleedContentPosition,
463
- cardState: undefined
520
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ConditionalWrapper.default, {
521
+ WrapperComponent: FullBleedPaddingWrapper,
522
+ wrapperProps: fullBleedPaddingValues,
523
+ condition: hasFullBleedPadding,
524
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FullBleedContent.default, {
525
+ borderRadius: fullBleedBorderRadius,
526
+ ...fullBleedContentPropsClean,
527
+ position: fullBleedContentPosition,
528
+ cardState: undefined
529
+ })
464
530
  })
465
531
  })]
466
532
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(InteractiveOverlay, {
@@ -504,11 +570,16 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
504
570
  WrapperComponent: DynamicWidthContainer,
505
571
  wrapperProps: imageWrapperStyleProps,
506
572
  condition: isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed,
507
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FullBleedContent.default, {
508
- borderRadius: fullBleedBorderRadius,
509
- ...fullBleedContentPropsClean,
510
- position: fullBleedContentPosition,
511
- cardState: undefined
573
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ConditionalWrapper.default, {
574
+ WrapperComponent: FullBleedPaddingWrapper,
575
+ wrapperProps: fullBleedPaddingValues,
576
+ condition: hasFullBleedPadding,
577
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FullBleedContent.default, {
578
+ borderRadius: fullBleedBorderRadius,
579
+ ...fullBleedContentPropsClean,
580
+ position: fullBleedContentPosition,
581
+ cardState: undefined
582
+ })
512
583
  })
513
584
  })]
514
585
  }) : null, footer && /*#__PURE__*/(0, _jsxRuntime.jsx)(_CardFooter.default, {
@@ -563,6 +634,13 @@ Card.propTypes = {
563
634
  * Custom card footer padding.
564
635
  */
565
636
  footerPadding: _componentsBase.paddingProp.propType,
637
+ /**
638
+ * Custom full bleed content padding.
639
+ * When true, inherits the card's own padding (from variant/tokens).
640
+ * When an object, provides custom padding overrides: { paddingTop, paddingBottom, paddingLeft, paddingRight }.
641
+ * Padding is automatically adjusted based on the full bleed content's position.
642
+ */
643
+ fullBleedPadding: _propTypes.default.oneOfType([_propTypes.default.bool, _componentsBase.paddingProp.propType]),
566
644
  /**
567
645
  * Full bleed image to be placed on the card, deprecated in favor of `fullBleedContent`.
568
646
  *
@@ -44,8 +44,11 @@ const ContentOverlay = /*#__PURE__*/_styledComponents.default.div.withConfig({
44
44
  })({
45
45
  position: 'absolute',
46
46
  width: '100%',
47
+ top: 0,
48
+ left: 0,
47
49
  height: '100%',
48
- zIndex: _constants.BACKDROP_Z_INDEX
50
+ zIndex: _constants.BACKDROP_Z_INDEX,
51
+ pointerEvents: 'none'
49
52
  });
50
53
  const FullscreenOverlay = /*#__PURE__*/_styledComponents.default.div.withConfig({
51
54
  displayName: "Spinner__FullscreenOverlay",
@@ -67,8 +70,15 @@ const FullscreenOverlay = /*#__PURE__*/_styledComponents.default.div.withConfig(
67
70
  const OpaqueContainer = /*#__PURE__*/_styledComponents.default.div.withConfig({
68
71
  displayName: "Spinner__OpaqueContainer",
69
72
  componentId: "components-web__sc-116rqck-3"
70
- })({
71
- opacity: _constants.BACKDROP_OPACITY
73
+ })(_ref3 => {
74
+ let {
75
+ show = true
76
+ } = _ref3;
77
+ return {
78
+ ...(show && {
79
+ opacity: _constants.BACKDROP_OPACITY
80
+ })
81
+ };
72
82
  });
73
83
  const recursiveMap = (children, fn) => _react.default.Children.map(children, child => {
74
84
  if (! /*#__PURE__*/_react.default.isValidElement(child)) {
@@ -85,7 +95,7 @@ const recursiveMap = (children, fn) => _react.default.Children.map(children, chi
85
95
  /**
86
96
  * Loading indicator.
87
97
  */
88
- const Spinner = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
98
+ const Spinner = /*#__PURE__*/_react.default.forwardRef((_ref4, ref) => {
89
99
  let {
90
100
  children,
91
101
  fullScreen = false,
@@ -96,8 +106,9 @@ const Spinner = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
96
106
  isStatic = false,
97
107
  tokens,
98
108
  variant = {},
109
+ persistChildrenState = false,
99
110
  ...rest
100
- } = _ref3;
111
+ } = _ref4;
101
112
  const {
102
113
  fullScreenOverLayBackground,
103
114
  size,
@@ -107,6 +118,32 @@ const Spinner = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
107
118
  size: sizeVariant = 'large'
108
119
  } = variant;
109
120
  (0, _componentsBase.useScrollBlocking)([fullScreen, show]);
121
+
122
+ // Overlay spinner with persistChildrenState enabled
123
+ if (children && persistChildrenState) {
124
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(SpinnerContainer, {
125
+ inline: inline,
126
+ "aria-live": "assertive",
127
+ overlay: true,
128
+ ...selectProps(rest),
129
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(OpaqueContainer, {
130
+ show: show,
131
+ ...(show ? {
132
+ inert: 'true',
133
+ 'aria-hidden': 'true'
134
+ } : {}),
135
+ children: children
136
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(ContentOverlay, {}), show && /*#__PURE__*/(0, _jsxRuntime.jsx)(_SpinnerContent.default, {
137
+ label: label,
138
+ labelPosition: labelPosition,
139
+ overlay: true,
140
+ size: size,
141
+ thickness: thickness,
142
+ sizeVariant: sizeVariant,
143
+ isStatic: isStatic
144
+ })]
145
+ });
146
+ }
110
147
  if (!show) {
111
148
  return children ?? null;
112
149
  }
@@ -136,7 +173,7 @@ const Spinner = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
136
173
  }
137
174
 
138
175
  // Overlay spinner
139
- if (children) {
176
+ if (children && !persistChildrenState) {
140
177
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(SpinnerContainer, {
141
178
  inline: inline,
142
179
  "aria-live": "assertive",
@@ -218,6 +255,12 @@ Spinner.propTypes = {
218
255
  /**
219
256
  * Determine where the label of the spinner should be placed, left, right, bottom or top.
220
257
  */
221
- labelPosition: _propTypes.default.string
258
+ labelPosition: _propTypes.default.string,
259
+ /**
260
+ * When true, preserves the state of overlaid children when `show` toggles.
261
+ * Only applicable when the Spinner is used in overlay mode (with children).
262
+ * Use this when overlaying stateful components such as Micro Frontends (MFEs).
263
+ */
264
+ persistChildrenState: _propTypes.default.bool
222
265
  };
223
266
  var _default = exports.default = Spinner;
@@ -10,6 +10,12 @@ Object.defineProperty(exports, "getFullBleedBorderRadius", {
10
10
  return _getFullBleedBorderRadius.default;
11
11
  }
12
12
  });
13
+ Object.defineProperty(exports, "getFullBleedPaddingSides", {
14
+ enumerable: true,
15
+ get: function () {
16
+ return _useFullBleedContentProps.getFullBleedPaddingSides;
17
+ }
18
+ });
13
19
  Object.defineProperty(exports, "useFullBleedContentProps", {
14
20
  enumerable: true,
15
21
  get: function () {
@@ -18,6 +24,8 @@ Object.defineProperty(exports, "useFullBleedContentProps", {
18
24
  });
19
25
  var _FullBleedContent = _interopRequireDefault(require("./FullBleedContent"));
20
26
  var _getFullBleedBorderRadius = _interopRequireDefault(require("./getFullBleedBorderRadius"));
21
- var _useFullBleedContentProps = _interopRequireDefault(require("./useFullBleedContentProps"));
27
+ var _useFullBleedContentProps = _interopRequireWildcard(require("./useFullBleedContentProps"));
28
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
29
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
22
30
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
23
31
  var _default = exports.default = _FullBleedContent.default;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = void 0;
6
+ exports.getFullBleedPaddingSides = exports.default = void 0;
7
7
  var _componentsBase = require("@telus-uds/components-base");
8
8
  const getContentStackDirection = fullBleedContentPosition => {
9
9
  switch (fullBleedContentPosition) {
@@ -32,6 +32,21 @@ const getContentStackAlign = fullBleedContentAlign => {
32
32
  }
33
33
  };
34
34
 
35
+ /**
36
+ * Calculates which padding sides should be applied to full bleed content
37
+ * based on its position relative to card content.
38
+ * The side adjacent to card body gets no padding since the body already provides spacing.
39
+ *
40
+ * @param {string} position - 'top' | 'bottom' | 'left' | 'right' | 'none'
41
+ * @returns {object} Boolean flags for which sides to apply padding
42
+ */
43
+ const getFullBleedPaddingSides = position => ({
44
+ applyPaddingTop: position !== 'bottom',
45
+ applyPaddingBottom: position !== 'top',
46
+ applyPaddingLeft: position !== 'right',
47
+ applyPaddingRight: position !== 'left'
48
+ });
49
+
35
50
  /**
36
51
  * Resolves a set of `FullBleedContent` props into the variables a container needs to
37
52
  * correctly position a `FullBleedContent` component for the current viewport.
@@ -39,6 +54,7 @@ const getContentStackAlign = fullBleedContentAlign => {
39
54
  * @param {object} [fullBleedContent] - a set of valid proptypes for FullBleedContent
40
55
  * @returns
41
56
  */
57
+ exports.getFullBleedPaddingSides = getFullBleedPaddingSides;
42
58
  const useFullBleedContentProps = fullBleedContent => {
43
59
  const {
44
60
  align: fullBleedContentAlignProp,
@@ -53,12 +69,16 @@ const useFullBleedContentProps = fullBleedContent => {
53
69
  const fullBleedContentAlign = (0, _componentsBase.useResponsiveProp)(fullBleedContentAlignProp, 'stretch');
54
70
  const contentStackAlign = getContentStackAlign(fullBleedContentAlign);
55
71
  const fullBleedContentChildrenAlign = (0, _componentsBase.useResponsiveProp)(fullBleedContentChildrenAlignProp, fullBleedContentAlign);
72
+
73
+ // Get padding sides based on position
74
+ const fullBleedPaddingSides = getFullBleedPaddingSides(fullBleedContentPosition);
56
75
  return {
57
76
  contentStackAlign,
58
77
  contentStackDirection,
59
78
  fullBleedContentPosition,
60
79
  fullBleedContentProps,
61
- fullBleedContentChildrenAlign
80
+ fullBleedContentChildrenAlign,
81
+ fullBleedPaddingSides
62
82
  };
63
83
  };
64
84
  var _default = exports.default = useFullBleedContentProps;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Card as CardBase, getTokensPropType, paddingProp, responsiveProps, selectSystemProps, StackView, useThemeTokens, useThemeTokensCallback, variantProp, a11yProps, viewProps, PressableCardBase, useResponsiveProp, hrefAttrsProp } from '@telus-uds/components-base';
3
+ import { Card as CardBase, getTokensPropType, paddingProp, responsiveProps, selectSystemProps, StackView, useThemeTokens, useThemeTokensCallback, variantProp, a11yProps, viewProps, PressableCardBase, useResponsiveProp, hrefAttrsProp, useAllViewportTokens } from '@telus-uds/components-base';
4
4
  import styled from 'styled-components';
5
5
  import CardContent from './CardContent';
6
6
  import CardFooter from './CardFooter';
@@ -177,6 +177,52 @@ const FocusBorder = /*#__PURE__*/styled.div.withConfig({
177
177
  zIndex: 2
178
178
  };
179
179
  });
180
+ const FullBleedPaddingWrapper = /*#__PURE__*/styled.div.withConfig({
181
+ displayName: "Card__FullBleedPaddingWrapper",
182
+ componentId: "components-web__sc-1elbtwd-4"
183
+ })(_ref4 => {
184
+ let {
185
+ paddingTop,
186
+ paddingBottom,
187
+ paddingLeft,
188
+ paddingRight
189
+ } = _ref4;
190
+ return {
191
+ paddingTop,
192
+ paddingBottom,
193
+ paddingLeft,
194
+ paddingRight,
195
+ display: 'flex',
196
+ flexDirection: 'column'
197
+ };
198
+ });
199
+
200
+ /**
201
+ * Calculates the padding values for full bleed content.
202
+ * Inherits the card's theme padding (minus borderWidth to visually match card body padding)
203
+ * and allows custom overrides via the fullBleedPadding prop.
204
+ *
205
+ * @param {object} themeTokens - The card's resolved theme tokens (contains padding values and borderWidth)
206
+ * @param {object|boolean} fullBleedPadding - true to inherit card padding, or an object with overrides
207
+ * @param {object} paddingSides - Boolean flags for which sides to apply padding
208
+ * @returns {object} Padding values for each side in px
209
+ */
210
+ const calculateFullBleedPadding = (themeTokens, fullBleedPadding, paddingSides) => {
211
+ const customTokens = typeof fullBleedPadding === 'object' ? fullBleedPadding : undefined;
212
+ const borderWidth = themeTokens.borderWidth || 0;
213
+ const applyPadding = side => {
214
+ const sideKey = `applyPadding${side}`;
215
+ if (!paddingSides[sideKey]) return 0;
216
+ const tokenKey = `padding${side}`;
217
+ return customTokens?.[tokenKey] ?? themeTokens[tokenKey] - borderWidth ?? 0;
218
+ };
219
+ return {
220
+ paddingTop: applyPadding('Top'),
221
+ paddingBottom: applyPadding('Bottom'),
222
+ paddingLeft: applyPadding('Left'),
223
+ paddingRight: applyPadding('Right')
224
+ };
225
+ };
180
226
  const Card = /*#__PURE__*/React.forwardRef(function () {
181
227
  let {
182
228
  children,
@@ -184,6 +230,7 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
184
230
  footerPadding,
185
231
  fullBleedImage,
186
232
  fullBleedContent = fullBleedImage,
233
+ fullBleedPadding,
187
234
  tokens = {},
188
235
  variant,
189
236
  interactiveCard,
@@ -210,7 +257,8 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
210
257
  contentStackDirection,
211
258
  fullBleedContentPosition,
212
259
  fullBleedContentProps,
213
- fullBleedContentChildrenAlign
260
+ fullBleedContentChildrenAlign,
261
+ fullBleedPaddingSides
214
262
  } = useFullBleedContentProps(fullBleedContent);
215
263
  const {
216
264
  imgCol,
@@ -235,6 +283,12 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
235
283
  borderRadius
236
284
  } = allThemeTokens;
237
285
 
286
+ // Get viewport-aware tokens for responsive full bleed padding
287
+ // useThemeTokens (above) doesn't pass viewport state, so it resolves to base/mobile tokens.
288
+ // useAllViewportTokens resolves tokens for ALL viewports + the current one (reactive to resize).
289
+ // This ensures fullBleedPadding matches CardContent's padding at every breakpoint.
290
+ const allViewportTokens = useAllViewportTokens('Card', tokens, variant);
291
+
238
292
  // Interactive cards: merge variants for CardBase (outer container)
239
293
  // The outer variant takes priority over interactiveCard.variant for the style property
240
294
  // This ensures the gradient is only applied to CardBase, not PressableCardBase and avoid duplication
@@ -310,6 +364,13 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
310
364
  const hasFooter = Boolean(footer);
311
365
  const fullBleedBorderRadius = getFullBleedBorderRadius(borderRadius, fullBleedContentPosition, hasFooter);
312
366
 
367
+ // Calculate full bleed content padding values
368
+ // When fullBleedPadding is truthy, apply padding around full bleed content
369
+ // Uses viewport-aware tokens (allViewportTokens.current) so padding matches
370
+ // CardContent's responsive padding at every breakpoint
371
+ const hasFullBleedPadding = Boolean(fullBleedPadding);
372
+ const fullBleedPaddingValues = hasFullBleedPadding ? calculateFullBleedPadding(allViewportTokens.current, fullBleedPadding, fullBleedPaddingSides) : null;
373
+
313
374
  // takes imgCol from fullBleedContent if present, to dynamically set width of image
314
375
  // card content will adapt to the size of image to add up to 100% width of card width
315
376
  // pass as props to ConditionalWrapper
@@ -335,8 +396,8 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
335
396
  flexShrink: 1,
336
397
  justifyContent: 'space-between'
337
398
  };
338
- const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref4 => {
339
- let [key] = _ref4;
399
+ const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref5 => {
400
+ let [key] = _ref5;
340
401
  return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage ? ['backgroundColor', 'gradient', 'backgroundGradient'] : [])].includes(key);
341
402
  }));
342
403
  const cardBaseVariant = backgroundImage ? {
@@ -448,11 +509,16 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
448
509
  WrapperComponent: DynamicWidthContainer,
449
510
  wrapperProps: imageWrapperStyleProps,
450
511
  condition: isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed,
451
- children: /*#__PURE__*/_jsx(FullBleedContent, {
452
- borderRadius: fullBleedBorderRadius,
453
- ...fullBleedContentPropsClean,
454
- position: fullBleedContentPosition,
455
- cardState: undefined
512
+ children: /*#__PURE__*/_jsx(ConditionalWrapper, {
513
+ WrapperComponent: FullBleedPaddingWrapper,
514
+ wrapperProps: fullBleedPaddingValues,
515
+ condition: hasFullBleedPadding,
516
+ children: /*#__PURE__*/_jsx(FullBleedContent, {
517
+ borderRadius: fullBleedBorderRadius,
518
+ ...fullBleedContentPropsClean,
519
+ position: fullBleedContentPosition,
520
+ cardState: undefined
521
+ })
456
522
  })
457
523
  })]
458
524
  }), /*#__PURE__*/_jsx(InteractiveOverlay, {
@@ -496,11 +562,16 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
496
562
  WrapperComponent: DynamicWidthContainer,
497
563
  wrapperProps: imageWrapperStyleProps,
498
564
  condition: isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed,
499
- children: /*#__PURE__*/_jsx(FullBleedContent, {
500
- borderRadius: fullBleedBorderRadius,
501
- ...fullBleedContentPropsClean,
502
- position: fullBleedContentPosition,
503
- cardState: undefined
565
+ children: /*#__PURE__*/_jsx(ConditionalWrapper, {
566
+ WrapperComponent: FullBleedPaddingWrapper,
567
+ wrapperProps: fullBleedPaddingValues,
568
+ condition: hasFullBleedPadding,
569
+ children: /*#__PURE__*/_jsx(FullBleedContent, {
570
+ borderRadius: fullBleedBorderRadius,
571
+ ...fullBleedContentPropsClean,
572
+ position: fullBleedContentPosition,
573
+ cardState: undefined
574
+ })
504
575
  })
505
576
  })]
506
577
  }) : null, footer && /*#__PURE__*/_jsx(CardFooter, {
@@ -555,6 +626,13 @@ Card.propTypes = {
555
626
  * Custom card footer padding.
556
627
  */
557
628
  footerPadding: paddingProp.propType,
629
+ /**
630
+ * Custom full bleed content padding.
631
+ * When true, inherits the card's own padding (from variant/tokens).
632
+ * When an object, provides custom padding overrides: { paddingTop, paddingBottom, paddingLeft, paddingRight }.
633
+ * Padding is automatically adjusted based on the full bleed content's position.
634
+ */
635
+ fullBleedPadding: PropTypes.oneOfType([PropTypes.bool, paddingProp.propType]),
558
636
  /**
559
637
  * Full bleed image to be placed on the card, deprecated in favor of `fullBleedContent`.
560
638
  *
@@ -37,8 +37,11 @@ const ContentOverlay = /*#__PURE__*/styled.div.withConfig({
37
37
  })({
38
38
  position: 'absolute',
39
39
  width: '100%',
40
+ top: 0,
41
+ left: 0,
40
42
  height: '100%',
41
- zIndex: BACKDROP_Z_INDEX
43
+ zIndex: BACKDROP_Z_INDEX,
44
+ pointerEvents: 'none'
42
45
  });
43
46
  const FullscreenOverlay = /*#__PURE__*/styled.div.withConfig({
44
47
  displayName: "Spinner__FullscreenOverlay",
@@ -60,8 +63,15 @@ const FullscreenOverlay = /*#__PURE__*/styled.div.withConfig({
60
63
  const OpaqueContainer = /*#__PURE__*/styled.div.withConfig({
61
64
  displayName: "Spinner__OpaqueContainer",
62
65
  componentId: "components-web__sc-116rqck-3"
63
- })({
64
- opacity: BACKDROP_OPACITY
66
+ })(_ref3 => {
67
+ let {
68
+ show = true
69
+ } = _ref3;
70
+ return {
71
+ ...(show && {
72
+ opacity: BACKDROP_OPACITY
73
+ })
74
+ };
65
75
  });
66
76
  const recursiveMap = (children, fn) => React.Children.map(children, child => {
67
77
  if (! /*#__PURE__*/React.isValidElement(child)) {
@@ -78,7 +88,7 @@ const recursiveMap = (children, fn) => React.Children.map(children, child => {
78
88
  /**
79
89
  * Loading indicator.
80
90
  */
81
- const Spinner = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
91
+ const Spinner = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
82
92
  let {
83
93
  children,
84
94
  fullScreen = false,
@@ -89,8 +99,9 @@ const Spinner = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
89
99
  isStatic = false,
90
100
  tokens,
91
101
  variant = {},
102
+ persistChildrenState = false,
92
103
  ...rest
93
- } = _ref3;
104
+ } = _ref4;
94
105
  const {
95
106
  fullScreenOverLayBackground,
96
107
  size,
@@ -100,6 +111,32 @@ const Spinner = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
100
111
  size: sizeVariant = 'large'
101
112
  } = variant;
102
113
  useScrollBlocking([fullScreen, show]);
114
+
115
+ // Overlay spinner with persistChildrenState enabled
116
+ if (children && persistChildrenState) {
117
+ return /*#__PURE__*/_jsxs(SpinnerContainer, {
118
+ inline: inline,
119
+ "aria-live": "assertive",
120
+ overlay: true,
121
+ ...selectProps(rest),
122
+ children: [/*#__PURE__*/_jsx(OpaqueContainer, {
123
+ show: show,
124
+ ...(show ? {
125
+ inert: 'true',
126
+ 'aria-hidden': 'true'
127
+ } : {}),
128
+ children: children
129
+ }), /*#__PURE__*/_jsx(ContentOverlay, {}), show && /*#__PURE__*/_jsx(SpinnerContent, {
130
+ label: label,
131
+ labelPosition: labelPosition,
132
+ overlay: true,
133
+ size: size,
134
+ thickness: thickness,
135
+ sizeVariant: sizeVariant,
136
+ isStatic: isStatic
137
+ })]
138
+ });
139
+ }
103
140
  if (!show) {
104
141
  return children ?? null;
105
142
  }
@@ -129,7 +166,7 @@ const Spinner = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
129
166
  }
130
167
 
131
168
  // Overlay spinner
132
- if (children) {
169
+ if (children && !persistChildrenState) {
133
170
  return /*#__PURE__*/_jsxs(SpinnerContainer, {
134
171
  inline: inline,
135
172
  "aria-live": "assertive",
@@ -211,6 +248,12 @@ Spinner.propTypes = {
211
248
  /**
212
249
  * Determine where the label of the spinner should be placed, left, right, bottom or top.
213
250
  */
214
- labelPosition: PropTypes.string
251
+ labelPosition: PropTypes.string,
252
+ /**
253
+ * When true, preserves the state of overlaid children when `show` toggles.
254
+ * Only applicable when the Spinner is used in overlay mode (with children).
255
+ * Use this when overlaying stateful components such as Micro Frontends (MFEs).
256
+ */
257
+ persistChildrenState: PropTypes.bool
215
258
  };
216
259
  export default Spinner;
@@ -1,4 +1,4 @@
1
1
  import FullBleedContent from './FullBleedContent';
2
2
  export default FullBleedContent;
3
3
  export { default as getFullBleedBorderRadius } from './getFullBleedBorderRadius';
4
- export { default as useFullBleedContentProps } from './useFullBleedContentProps';
4
+ export { default as useFullBleedContentProps, getFullBleedPaddingSides } from './useFullBleedContentProps';
@@ -26,6 +26,21 @@ const getContentStackAlign = fullBleedContentAlign => {
26
26
  }
27
27
  };
28
28
 
29
+ /**
30
+ * Calculates which padding sides should be applied to full bleed content
31
+ * based on its position relative to card content.
32
+ * The side adjacent to card body gets no padding since the body already provides spacing.
33
+ *
34
+ * @param {string} position - 'top' | 'bottom' | 'left' | 'right' | 'none'
35
+ * @returns {object} Boolean flags for which sides to apply padding
36
+ */
37
+ const getFullBleedPaddingSides = position => ({
38
+ applyPaddingTop: position !== 'bottom',
39
+ applyPaddingBottom: position !== 'top',
40
+ applyPaddingLeft: position !== 'right',
41
+ applyPaddingRight: position !== 'left'
42
+ });
43
+
29
44
  /**
30
45
  * Resolves a set of `FullBleedContent` props into the variables a container needs to
31
46
  * correctly position a `FullBleedContent` component for the current viewport.
@@ -47,12 +62,17 @@ const useFullBleedContentProps = fullBleedContent => {
47
62
  const fullBleedContentAlign = useResponsiveProp(fullBleedContentAlignProp, 'stretch');
48
63
  const contentStackAlign = getContentStackAlign(fullBleedContentAlign);
49
64
  const fullBleedContentChildrenAlign = useResponsiveProp(fullBleedContentChildrenAlignProp, fullBleedContentAlign);
65
+
66
+ // Get padding sides based on position
67
+ const fullBleedPaddingSides = getFullBleedPaddingSides(fullBleedContentPosition);
50
68
  return {
51
69
  contentStackAlign,
52
70
  contentStackDirection,
53
71
  fullBleedContentPosition,
54
72
  fullBleedContentProps,
55
- fullBleedContentChildrenAlign
73
+ fullBleedContentChildrenAlign,
74
+ fullBleedPaddingSides
56
75
  };
57
76
  };
77
+ export { getFullBleedPaddingSides };
58
78
  export default useFullBleedContentProps;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  ],
6
6
  "dependencies": {
7
7
  "@gorhom/portal": "^1.0.14",
8
- "@telus-uds/components-base": "^3.28.1",
8
+ "@telus-uds/components-base": "^3.28.2",
9
9
  "@telus-uds/system-constants": "^3.0.0",
10
10
  "@telus-uds/system-theme-tokens": "^4.20.0",
11
11
  "fscreen": "^1.2.0",
@@ -82,5 +82,5 @@
82
82
  "skip": true
83
83
  },
84
84
  "types": "types/index.d.ts",
85
- "version": "4.18.2"
85
+ "version": "4.19.0"
86
86
  }
package/src/Card/Card.jsx CHANGED
@@ -14,7 +14,8 @@ import {
14
14
  viewProps,
15
15
  PressableCardBase,
16
16
  useResponsiveProp,
17
- hrefAttrsProp
17
+ hrefAttrsProp,
18
+ useAllViewportTokens
18
19
  } from '@telus-uds/components-base'
19
20
  import styled from 'styled-components'
20
21
  import CardContent from './CardContent'
@@ -170,6 +171,46 @@ const FocusBorder = styled.div(({ borderWidth, borderColor, borderRadius }) => (
170
171
  zIndex: 2
171
172
  }))
172
173
 
174
+ const FullBleedPaddingWrapper = styled.div(
175
+ ({ paddingTop, paddingBottom, paddingLeft, paddingRight }) => ({
176
+ paddingTop,
177
+ paddingBottom,
178
+ paddingLeft,
179
+ paddingRight,
180
+ display: 'flex',
181
+ flexDirection: 'column'
182
+ })
183
+ )
184
+
185
+ /**
186
+ * Calculates the padding values for full bleed content.
187
+ * Inherits the card's theme padding (minus borderWidth to visually match card body padding)
188
+ * and allows custom overrides via the fullBleedPadding prop.
189
+ *
190
+ * @param {object} themeTokens - The card's resolved theme tokens (contains padding values and borderWidth)
191
+ * @param {object|boolean} fullBleedPadding - true to inherit card padding, or an object with overrides
192
+ * @param {object} paddingSides - Boolean flags for which sides to apply padding
193
+ * @returns {object} Padding values for each side in px
194
+ */
195
+ const calculateFullBleedPadding = (themeTokens, fullBleedPadding, paddingSides) => {
196
+ const customTokens = typeof fullBleedPadding === 'object' ? fullBleedPadding : undefined
197
+ const borderWidth = themeTokens.borderWidth || 0
198
+
199
+ const applyPadding = (side) => {
200
+ const sideKey = `applyPadding${side}`
201
+ if (!paddingSides[sideKey]) return 0
202
+ const tokenKey = `padding${side}`
203
+ return customTokens?.[tokenKey] ?? themeTokens[tokenKey] - borderWidth ?? 0
204
+ }
205
+
206
+ return {
207
+ paddingTop: applyPadding('Top'),
208
+ paddingBottom: applyPadding('Bottom'),
209
+ paddingLeft: applyPadding('Left'),
210
+ paddingRight: applyPadding('Right')
211
+ }
212
+ }
213
+
173
214
  const Card = React.forwardRef(
174
215
  (
175
216
  {
@@ -178,6 +219,7 @@ const Card = React.forwardRef(
178
219
  footerPadding,
179
220
  fullBleedImage,
180
221
  fullBleedContent = fullBleedImage,
222
+ fullBleedPadding,
181
223
  tokens = {},
182
224
  variant,
183
225
  interactiveCard,
@@ -198,7 +240,8 @@ const Card = React.forwardRef(
198
240
  contentStackDirection,
199
241
  fullBleedContentPosition,
200
242
  fullBleedContentProps,
201
- fullBleedContentChildrenAlign
243
+ fullBleedContentChildrenAlign,
244
+ fullBleedPaddingSides
202
245
  } = useFullBleedContentProps(fullBleedContent)
203
246
 
204
247
  const {
@@ -220,6 +263,12 @@ const Card = React.forwardRef(
220
263
  const allThemeTokens = useThemeTokens('Card', tokens, variantForTokens)
221
264
  const { borderRadius } = allThemeTokens
222
265
 
266
+ // Get viewport-aware tokens for responsive full bleed padding
267
+ // useThemeTokens (above) doesn't pass viewport state, so it resolves to base/mobile tokens.
268
+ // useAllViewportTokens resolves tokens for ALL viewports + the current one (reactive to resize).
269
+ // This ensures fullBleedPadding matches CardContent's padding at every breakpoint.
270
+ const allViewportTokens = useAllViewportTokens('Card', tokens, variant)
271
+
223
272
  // Interactive cards: merge variants for CardBase (outer container)
224
273
  // The outer variant takes priority over interactiveCard.variant for the style property
225
274
  // This ensures the gradient is only applied to CardBase, not PressableCardBase and avoid duplication
@@ -312,6 +361,19 @@ const Card = React.forwardRef(
312
361
  hasFooter
313
362
  )
314
363
 
364
+ // Calculate full bleed content padding values
365
+ // When fullBleedPadding is truthy, apply padding around full bleed content
366
+ // Uses viewport-aware tokens (allViewportTokens.current) so padding matches
367
+ // CardContent's responsive padding at every breakpoint
368
+ const hasFullBleedPadding = Boolean(fullBleedPadding)
369
+ const fullBleedPaddingValues = hasFullBleedPadding
370
+ ? calculateFullBleedPadding(
371
+ allViewportTokens.current,
372
+ fullBleedPadding,
373
+ fullBleedPaddingSides
374
+ )
375
+ : null
376
+
315
377
  // takes imgCol from fullBleedContent if present, to dynamically set width of image
316
378
  // card content will adapt to the size of image to add up to 100% width of card width
317
379
  // pass as props to ConditionalWrapper
@@ -503,12 +565,18 @@ const Card = React.forwardRef(
503
565
  isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed
504
566
  }
505
567
  >
506
- <FullBleedContent
507
- borderRadius={fullBleedBorderRadius}
508
- {...fullBleedContentPropsClean}
509
- position={fullBleedContentPosition}
510
- cardState={undefined}
511
- />
568
+ <ConditionalWrapper
569
+ WrapperComponent={FullBleedPaddingWrapper}
570
+ wrapperProps={fullBleedPaddingValues}
571
+ condition={hasFullBleedPadding}
572
+ >
573
+ <FullBleedContent
574
+ borderRadius={fullBleedBorderRadius}
575
+ {...fullBleedContentPropsClean}
576
+ position={fullBleedContentPosition}
577
+ cardState={undefined}
578
+ />
579
+ </ConditionalWrapper>
512
580
  </ConditionalWrapper>
513
581
  )}
514
582
  </StackView>
@@ -571,12 +639,18 @@ const Card = React.forwardRef(
571
639
  wrapperProps={imageWrapperStyleProps}
572
640
  condition={isImageWidthAdjustable || isHorizontalFullBleed || isVerticalFullBleed}
573
641
  >
574
- <FullBleedContent
575
- borderRadius={fullBleedBorderRadius}
576
- {...fullBleedContentPropsClean}
577
- position={fullBleedContentPosition}
578
- cardState={undefined}
579
- />
642
+ <ConditionalWrapper
643
+ WrapperComponent={FullBleedPaddingWrapper}
644
+ wrapperProps={fullBleedPaddingValues}
645
+ condition={hasFullBleedPadding}
646
+ >
647
+ <FullBleedContent
648
+ borderRadius={fullBleedBorderRadius}
649
+ {...fullBleedContentPropsClean}
650
+ position={fullBleedContentPosition}
651
+ cardState={undefined}
652
+ />
653
+ </ConditionalWrapper>
580
654
  </ConditionalWrapper>
581
655
  </StackView>
582
656
  ) : null}
@@ -636,6 +710,13 @@ Card.propTypes = {
636
710
  * Custom card footer padding.
637
711
  */
638
712
  footerPadding: paddingProp.propType,
713
+ /**
714
+ * Custom full bleed content padding.
715
+ * When true, inherits the card's own padding (from variant/tokens).
716
+ * When an object, provides custom padding overrides: { paddingTop, paddingBottom, paddingLeft, paddingRight }.
717
+ * Padding is automatically adjusted based on the full bleed content's position.
718
+ */
719
+ fullBleedPadding: PropTypes.oneOfType([PropTypes.bool, paddingProp.propType]),
639
720
  /**
640
721
  * Full bleed image to be placed on the card, deprecated in favor of `fullBleedContent`.
641
722
  *
@@ -28,8 +28,11 @@ const SpinnerContainer = styled.div(({ inline, fullScreen, overlay }) => ({
28
28
  const ContentOverlay = styled.div({
29
29
  position: 'absolute',
30
30
  width: '100%',
31
+ top: 0,
32
+ left: 0,
31
33
  height: '100%',
32
- zIndex: BACKDROP_Z_INDEX
34
+ zIndex: BACKDROP_Z_INDEX,
35
+ pointerEvents: 'none'
33
36
  })
34
37
 
35
38
  const FullscreenOverlay = styled.div(({ fullScreenOverLayBackground }) => ({
@@ -41,9 +44,12 @@ const FullscreenOverlay = styled.div(({ fullScreenOverLayBackground }) => ({
41
44
  zIndex: BACKDROP_Z_INDEX,
42
45
  backgroundColor: fullScreenOverLayBackground
43
46
  }))
44
- const OpaqueContainer = styled.div({
45
- opacity: BACKDROP_OPACITY
46
- })
47
+
48
+ const OpaqueContainer = styled.div(({ show = true }) => ({
49
+ ...(show && {
50
+ opacity: BACKDROP_OPACITY
51
+ })
52
+ }))
47
53
 
48
54
  const recursiveMap = (children, fn) =>
49
55
  React.Children.map(children, (child) => {
@@ -76,6 +82,7 @@ const Spinner = React.forwardRef(
76
82
  isStatic = false,
77
83
  tokens,
78
84
  variant = {},
85
+ persistChildrenState = false,
79
86
  ...rest
80
87
  },
81
88
  ref
@@ -90,6 +97,30 @@ const Spinner = React.forwardRef(
90
97
 
91
98
  useScrollBlocking([fullScreen, show])
92
99
 
100
+ // Overlay spinner with persistChildrenState enabled
101
+ if (children && persistChildrenState) {
102
+ return (
103
+ <SpinnerContainer inline={inline} aria-live="assertive" overlay {...selectProps(rest)}>
104
+ {/* Children ALWAYS rendered in same position - OpaqueContainer styles toggle with show */}
105
+ <OpaqueContainer show={show} {...(show ? { inert: 'true', 'aria-hidden': 'true' } : {})}>
106
+ {children}
107
+ </OpaqueContainer>
108
+ <ContentOverlay />
109
+ {show && (
110
+ <SpinnerContent
111
+ label={label}
112
+ labelPosition={labelPosition}
113
+ overlay={true}
114
+ size={size}
115
+ thickness={thickness}
116
+ sizeVariant={sizeVariant}
117
+ isStatic={isStatic}
118
+ />
119
+ )}
120
+ </SpinnerContainer>
121
+ )
122
+ }
123
+
93
124
  if (!show) {
94
125
  return children ?? null
95
126
  }
@@ -121,7 +152,7 @@ const Spinner = React.forwardRef(
121
152
  }
122
153
 
123
154
  // Overlay spinner
124
- if (children) {
155
+ if (children && !persistChildrenState) {
125
156
  return (
126
157
  <SpinnerContainer inline={inline} aria-live="assertive" overlay {...selectProps(rest)}>
127
158
  <SpinnerContent
@@ -204,7 +235,13 @@ Spinner.propTypes = {
204
235
  /**
205
236
  * Determine where the label of the spinner should be placed, left, right, bottom or top.
206
237
  */
207
- labelPosition: PropTypes.string
238
+ labelPosition: PropTypes.string,
239
+ /**
240
+ * When true, preserves the state of overlaid children when `show` toggles.
241
+ * Only applicable when the Spinner is used in overlay mode (with children).
242
+ * Use this when overlaying stateful components such as Micro Frontends (MFEs).
243
+ */
244
+ persistChildrenState: PropTypes.bool
208
245
  }
209
246
 
210
247
  export default Spinner
@@ -3,4 +3,7 @@ import FullBleedContent from './FullBleedContent'
3
3
  export default FullBleedContent
4
4
 
5
5
  export { default as getFullBleedBorderRadius } from './getFullBleedBorderRadius'
6
- export { default as useFullBleedContentProps } from './useFullBleedContentProps'
6
+ export {
7
+ default as useFullBleedContentProps,
8
+ getFullBleedPaddingSides
9
+ } from './useFullBleedContentProps'
@@ -31,6 +31,21 @@ const getContentStackAlign = (fullBleedContentAlign) => {
31
31
  }
32
32
  }
33
33
 
34
+ /**
35
+ * Calculates which padding sides should be applied to full bleed content
36
+ * based on its position relative to card content.
37
+ * The side adjacent to card body gets no padding since the body already provides spacing.
38
+ *
39
+ * @param {string} position - 'top' | 'bottom' | 'left' | 'right' | 'none'
40
+ * @returns {object} Boolean flags for which sides to apply padding
41
+ */
42
+ const getFullBleedPaddingSides = (position) => ({
43
+ applyPaddingTop: position !== 'bottom',
44
+ applyPaddingBottom: position !== 'top',
45
+ applyPaddingLeft: position !== 'right',
46
+ applyPaddingRight: position !== 'left'
47
+ })
48
+
34
49
  /**
35
50
  * Resolves a set of `FullBleedContent` props into the variables a container needs to
36
51
  * correctly position a `FullBleedContent` component for the current viewport.
@@ -58,13 +73,18 @@ const useFullBleedContentProps = (fullBleedContent) => {
58
73
  fullBleedContentAlign
59
74
  )
60
75
 
76
+ // Get padding sides based on position
77
+ const fullBleedPaddingSides = getFullBleedPaddingSides(fullBleedContentPosition)
78
+
61
79
  return {
62
80
  contentStackAlign,
63
81
  contentStackDirection,
64
82
  fullBleedContentPosition,
65
83
  fullBleedContentProps,
66
- fullBleedContentChildrenAlign
84
+ fullBleedContentChildrenAlign,
85
+ fullBleedPaddingSides
67
86
  }
68
87
  }
69
88
 
89
+ export { getFullBleedPaddingSides }
70
90
  export default useFullBleedContentProps
@@ -22,6 +22,8 @@ export interface AutocompleteProps extends AutocompleteCommonProps {
22
22
  isLoading?: boolean
23
23
  items?: AutocompleteItem[]
24
24
  loadingLabel?: string
25
+ maxDropdownHeight?: number
26
+ showOptionsOnFocus?: boolean
25
27
  minToSuggestion?: number
26
28
  maxSuggestions?: number
27
29
  noResults?: ReactNode
@@ -9,6 +9,11 @@ export interface SpinnerProps extends HTMLAttrs {
9
9
  label: string
10
10
  show?: boolean
11
11
  isStatic?: boolean
12
+ /**
13
+ * When true, preserves the state of overlaid children when `show` toggles.
14
+ * Only applicable when the Spinner is used in overlay mode (with `children`).
15
+ */
16
+ persistChildrenState?: boolean
12
17
  }
13
18
 
14
19
  declare const Spinner: ComponentType<SpinnerProps>