@telus-uds/components-web 4.18.0 → 4.18.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.
package/CHANGELOG.md CHANGED
@@ -1,28 +1,40 @@
1
1
  # Change Log - @telus-uds/components-web
2
2
 
3
- This log was last generated on Thu, 29 Jan 2026 16:42:47 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 18 Feb 2026 02:50:19 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 4.18.1
8
+
9
+ Wed, 18 Feb 2026 02:50:19 GMT
10
+
11
+ ### Patches
12
+
13
+ - refactor: simplify cardBaseTokens object creation by removing redundant destructuring (josue.higueroscalderon@telus.com)
14
+ - `IconButton`: Update stories (david.melara1@telus.com)
15
+ - `Card`: background image hidden behind the default color issue fixed (josue.higueroscalderon@telus.com)
16
+ - Bump @telus-uds/components-base to v3.28.0
17
+ - Bump @telus-uds/system-theme-tokens to v4.20.0
18
+
7
19
  ## 4.18.0
8
20
 
9
- Thu, 29 Jan 2026 16:42:47 GMT
21
+ Thu, 29 Jan 2026 15:50:17 GMT
10
22
 
11
23
  ### Minor changes
12
24
 
13
- - `NavigationBar`: add contentMaxWidth prop (guillermo.peitzner@telus.com)
14
25
  - `List`: new feature alignment for the icons added (josue.higueroscalderon@telus.com)
15
- - `QuantitySelector`: add inactive prop (guillermo.peitzner@telus.com)
16
26
  - `Card`: Add full-bleed content support to Card component allowing content to extend to card edges (josue.higueroscalderon@telus.com)
27
+ - `QuantitySelector`: add inactive prop (guillermo.peitzner@telus.com)
17
28
  - `Card`: Unnecessary comments removed. (josue.higueroscalderon@telus.com)
29
+ - `NavigationBar`: add contentMaxWidth prop (guillermo.peitzner@telus.com)
18
30
  - Bump @telus-uds/components-base to v3.27.0
19
31
  - Bump @telus-uds/system-theme-tokens to v4.19.0
20
32
 
21
33
  ### Patches
22
34
 
23
- - `Card`: component enhaced to manage the hover, focus and pressed states of interactiveCard properly (josue.higueroscalderon@telus.com)
24
- - `NavigationBar`: fix console warning (guillermo.peitzner@telus.com)
25
35
  - `QuantitySelector`: spacer alignment issue fixed (josue.higueroscalderon@telus.com)
36
+ - `NavigationBar`: fix console warning (guillermo.peitzner@telus.com)
37
+ - `Card`: component enhaced to manage the hover, focus and pressed states of interactiveCard properly (josue.higueroscalderon@telus.com)
26
38
 
27
39
  ## 4.17.0
28
40
 
@@ -58,11 +58,9 @@ const getCardContentTokens = function (baseTokens) {
58
58
  // Determine background color based on conditions
59
59
  let backgroundColorOverride = {};
60
60
  if (useTransparentBackground) {
61
- if (!backgroundImage) {
62
- backgroundColorOverride = {
63
- backgroundColor: 'transparent'
64
- };
65
- }
61
+ backgroundColorOverride = {
62
+ backgroundColor: 'transparent'
63
+ };
66
64
  } else if (backgroundImage) {
67
65
  backgroundColorOverride = {
68
66
  backgroundColor: 'transparent'
@@ -236,7 +234,11 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
236
234
 
237
235
  // If the card has rounded corners and a full bleed image, we need to apply
238
236
  // those corners on the image as well, but partially
239
- const allThemeTokens = (0, _componentsBase.useThemeTokens)('Card', tokens, variant);
237
+ const variantForTokens = backgroundImage ? {
238
+ ...variant,
239
+ style: undefined
240
+ } : variant;
241
+ const allThemeTokens = (0, _componentsBase.useThemeTokens)('Card', tokens, variantForTokens);
240
242
  const {
241
243
  borderRadius
242
244
  } = allThemeTokens;
@@ -289,16 +291,17 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
289
291
  interactive: true
290
292
  });
291
293
 
292
- // Remove backgroundColor from tokens for full bleed interactive content
293
- // to prevent background from covering the full bleed image
294
- const {
295
- backgroundColor: _,
296
- ...tokensWithoutBg
297
- } = tokens;
298
- const getFullBleedInteractiveTokens = (0, _componentsBase.useThemeTokensCallback)('Card', tokensWithoutBg, {
294
+ // Keep backgroundColor for CardContent, it won't affect FullBleedContent image
295
+ const tokensWithoutBg = tokens;
296
+ const fullBleedInteractiveVariant = backgroundImage ? {
297
+ ...variant,
298
+ interactive: true,
299
+ style: undefined
300
+ } : {
299
301
  ...variant,
300
302
  interactive: true
301
- });
303
+ };
304
+ const getFullBleedInteractiveTokens = (0, _componentsBase.useThemeTokensCallback)('Card', tokensWithoutBg, fullBleedInteractiveVariant);
302
305
  const getFullBleedInteractiveCardTokens = cardState => {
303
306
  return {
304
307
  ...getFullBleedInteractiveTokens(cardState),
@@ -342,8 +345,16 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
342
345
  };
343
346
  const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref4 => {
344
347
  let [key] = _ref4;
345
- return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage && interactiveCard?.body ? ['backgroundColor'] : [])].includes(key);
348
+ return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage ? ['backgroundColor', 'gradient', 'backgroundGradient'] : [])].includes(key);
346
349
  }));
350
+ const cardBaseVariant = backgroundImage ? {
351
+ ...(interactiveCard?.body ? mergedVariant : variant),
352
+ padding: 'custom',
353
+ style: undefined
354
+ } : {
355
+ ...(interactiveCard?.body ? mergedVariant : variant),
356
+ padding: 'custom'
357
+ };
347
358
  const isHorizontalFullBleed = fullBleedContentPosition === POSITION.LEFT || fullBleedContentPosition === POSITION.RIGHT;
348
359
  const isVerticalFullBleed = fullBleedContentPosition === POSITION.TOP || fullBleedContentPosition === POSITION.BOTTOM;
349
360
  const imageWrapperStyleProps = {
@@ -360,12 +371,9 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
360
371
  };
361
372
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_componentsBase.Card, {
362
373
  ref: ref,
363
- variant: {
364
- ...(interactiveCard?.body ? mergedVariant : variant),
365
- padding: 'custom'
366
- },
374
+ variant: cardBaseVariant,
367
375
  tokens: cardBaseTokens,
368
- backgroundImage: !interactiveCard?.body && backgroundImage,
376
+ backgroundImage: backgroundImage,
369
377
  onPress: fullBleedInteractive ? undefined : onPress,
370
378
  ...(interactiveCard?.selectionType && {
371
379
  interactiveCard,
@@ -373,16 +381,18 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
373
381
  }),
374
382
  ...selectProps(restProps),
375
383
  children: [interactiveCard?.selectionType && children ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_CardContent.default, {
376
- tokens: tokens,
384
+ tokens: getCardContentTokens(tokens, {
385
+ backgroundImage
386
+ }),
377
387
  variant: variant,
378
388
  withFooter: hasFooter,
389
+ backgroundImage: backgroundImage,
379
390
  children: children
380
391
  }) : null, interactiveCard?.body && !interactiveCard.selectionType ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
381
392
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_componentsBase.PressableCardBase, {
382
393
  ref: ref,
383
394
  tokens: getThemeTokens,
384
395
  dataSet: dataSet,
385
- backgroundImage: backgroundImage,
386
396
  onPress: onPress,
387
397
  href: interactiveCard?.href,
388
398
  hrefAttrs: interactiveCard?.hrefAttrs,
@@ -391,9 +401,12 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
391
401
  children: typeof interactiveCard?.body === 'function' ? interactiveCard.body(cardState) : interactiveCard.body
392
402
  })
393
403
  }), children && fullBleedContentPosition === POSITION.NONE && !fullBleedInteractive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_CardContent.default, {
394
- tokens: tokens,
404
+ tokens: getCardContentTokens(tokens, {
405
+ backgroundImage
406
+ }),
395
407
  variant: variant,
396
408
  withFooter: hasFooter,
409
+ backgroundImage: backgroundImage,
397
410
  children: children
398
411
  }) : null]
399
412
  }) : null, fullBleedInteractive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_componentsBase.PressableCardBase, {
@@ -436,6 +449,7 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
436
449
  },
437
450
  variant: variant,
438
451
  withFooter: hasFooter,
452
+ backgroundImage: backgroundImage,
439
453
  children: children
440
454
  })
441
455
  }) : null, fullBleedContentPosition !== POSITION.NONE && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ConditionalWrapper.default, {
@@ -466,6 +480,7 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
466
480
  }),
467
481
  variant: variant,
468
482
  withFooter: hasFooter,
483
+ backgroundImage: backgroundImage,
469
484
  children: children
470
485
  }) : null, !fullBleedInteractive && fullBleedContentPosition !== POSITION.NONE ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_componentsBase.StackView, {
471
486
  direction: contentStackDirection,
@@ -477,10 +492,12 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
477
492
  condition: isImageWidthAdjustable,
478
493
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CardContent.default, {
479
494
  tokens: getCardContentTokens(tokens, {
495
+ backgroundImage,
480
496
  fullBleedContentChildrenAlign
481
497
  }),
482
498
  variant: variant,
483
499
  withFooter: hasFooter,
500
+ backgroundImage: backgroundImage,
484
501
  children: children
485
502
  })
486
503
  }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_ConditionalWrapper.default, {
@@ -63,6 +63,7 @@ const CardContent = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
63
63
  tokens,
64
64
  variant,
65
65
  withFooter = false,
66
+ backgroundImage,
66
67
  ...rest
67
68
  } = _ref2;
68
69
  const {
@@ -71,10 +72,32 @@ const CardContent = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
71
72
  }
72
73
  } = (0, _componentsBase.useTheme)();
73
74
  const allTokens = (0, _componentsBase.useAllViewportTokens)('Card', tokens, variant);
75
+
76
+ // Override backgroundColor if explicitly set in tokens to ensure it takes priority over theme tokens
77
+ // This is crucial for scenarios with backgroundImage where we need transparency
78
+ if (tokens.backgroundColor !== undefined) {
79
+ allTokens.current.backgroundColor = tokens.backgroundColor;
80
+ allTokens.xs.backgroundColor = tokens.backgroundColor;
81
+ allTokens.sm.backgroundColor = tokens.backgroundColor;
82
+ allTokens.md.backgroundColor = tokens.backgroundColor;
83
+ allTokens.lg.backgroundColor = tokens.backgroundColor;
84
+ allTokens.xl.backgroundColor = tokens.backgroundColor;
85
+ }
86
+
87
+ // When backgroundImage is present on the Card, ensure CardContent background is transparent
88
+ // so the background image from the parent CardBase can show through
89
+ if (backgroundImage) {
90
+ allTokens.current.backgroundColor = 'transparent';
91
+ allTokens.xs.backgroundColor = 'transparent';
92
+ allTokens.sm.backgroundColor = 'transparent';
93
+ allTokens.md.backgroundColor = 'transparent';
94
+ allTokens.lg.backgroundColor = 'transparent';
95
+ allTokens.xl.backgroundColor = 'transparent';
96
+ }
74
97
  let themeTokens;
75
98
  let mediaIds;
76
99
  if (enableMediaQueryStyleSheet) {
77
- const stylesByViewport = {
100
+ const paddingAdjustments = {
78
101
  xs: {
79
102
  paddingBottom: allTokens.xs.paddingBottom - allTokens.xs.borderWidth,
80
103
  paddingLeft: allTokens.xs.paddingLeft - allTokens.xs.borderWidth,
@@ -106,15 +129,16 @@ const CardContent = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
106
129
  paddingTop: allTokens.xl.paddingTop - allTokens.xl.borderWidth
107
130
  }
108
131
  };
109
- const mediaQueryStyles = (0, _componentsBase.createMediaQueryStyles)(stylesByViewport);
132
+ const mediaQueryStyles = (0, _componentsBase.createMediaQueryStyles)(paddingAdjustments);
133
+ const baseStyle = {
134
+ ...allTokens.current,
135
+ ...mediaQueryStyles
136
+ };
110
137
  const {
111
138
  ids,
112
139
  styles
113
140
  } = _componentsBase.StyleSheet.create({
114
- cardContent: {
115
- ...allTokens.current,
116
- ...mediaQueryStyles
117
- }
141
+ cardContent: baseStyle
118
142
  });
119
143
  themeTokens = styles.cardContent;
120
144
  mediaIds = ids.cardContent;
@@ -152,6 +176,11 @@ CardContent.propTypes = {
152
176
  /**
153
177
  * Whether the card has a footer.
154
178
  */
155
- withFooter: _propTypes.default.bool
179
+ withFooter: _propTypes.default.bool,
180
+ /**
181
+ * Background image object from parent Card component.
182
+ * When present, makes the CardContent background transparent.
183
+ */
184
+ backgroundImage: _propTypes.default.object
156
185
  };
157
186
  var _default = exports.default = CardContent;
@@ -226,7 +226,8 @@ Testimonial.propTypes = {
226
226
  testimonialStyle: _propTypes.default.oneOf(['large', 'heading']),
227
227
  /**
228
228
  * Whether to show or not dividers at the top and the bottom of the testimonial
229
- showDivider: PropTypes.bool,
229
+ */
230
+ showDivider: _propTypes.default.bool,
230
231
  /**
231
232
  * The src attribute for the Image component or custom JSX content to render instead
232
233
  */
@@ -50,11 +50,9 @@ const getCardContentTokens = function (baseTokens) {
50
50
  // Determine background color based on conditions
51
51
  let backgroundColorOverride = {};
52
52
  if (useTransparentBackground) {
53
- if (!backgroundImage) {
54
- backgroundColorOverride = {
55
- backgroundColor: 'transparent'
56
- };
57
- }
53
+ backgroundColorOverride = {
54
+ backgroundColor: 'transparent'
55
+ };
58
56
  } else if (backgroundImage) {
59
57
  backgroundColorOverride = {
60
58
  backgroundColor: 'transparent'
@@ -228,7 +226,11 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
228
226
 
229
227
  // If the card has rounded corners and a full bleed image, we need to apply
230
228
  // those corners on the image as well, but partially
231
- const allThemeTokens = useThemeTokens('Card', tokens, variant);
229
+ const variantForTokens = backgroundImage ? {
230
+ ...variant,
231
+ style: undefined
232
+ } : variant;
233
+ const allThemeTokens = useThemeTokens('Card', tokens, variantForTokens);
232
234
  const {
233
235
  borderRadius
234
236
  } = allThemeTokens;
@@ -281,16 +283,17 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
281
283
  interactive: true
282
284
  });
283
285
 
284
- // Remove backgroundColor from tokens for full bleed interactive content
285
- // to prevent background from covering the full bleed image
286
- const {
287
- backgroundColor: _,
288
- ...tokensWithoutBg
289
- } = tokens;
290
- const getFullBleedInteractiveTokens = useThemeTokensCallback('Card', tokensWithoutBg, {
286
+ // Keep backgroundColor for CardContent, it won't affect FullBleedContent image
287
+ const tokensWithoutBg = tokens;
288
+ const fullBleedInteractiveVariant = backgroundImage ? {
289
+ ...variant,
290
+ interactive: true,
291
+ style: undefined
292
+ } : {
291
293
  ...variant,
292
294
  interactive: true
293
- });
295
+ };
296
+ const getFullBleedInteractiveTokens = useThemeTokensCallback('Card', tokensWithoutBg, fullBleedInteractiveVariant);
294
297
  const getFullBleedInteractiveCardTokens = cardState => {
295
298
  return {
296
299
  ...getFullBleedInteractiveTokens(cardState),
@@ -334,8 +337,16 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
334
337
  };
335
338
  const cardBaseTokens = Object.fromEntries(Object.entries(tokens).filter(_ref4 => {
336
339
  let [key] = _ref4;
337
- return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage && interactiveCard?.body ? ['backgroundColor'] : [])].includes(key);
340
+ return !['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', ...(backgroundImage ? ['backgroundColor', 'gradient', 'backgroundGradient'] : [])].includes(key);
338
341
  }));
342
+ const cardBaseVariant = backgroundImage ? {
343
+ ...(interactiveCard?.body ? mergedVariant : variant),
344
+ padding: 'custom',
345
+ style: undefined
346
+ } : {
347
+ ...(interactiveCard?.body ? mergedVariant : variant),
348
+ padding: 'custom'
349
+ };
339
350
  const isHorizontalFullBleed = fullBleedContentPosition === POSITION.LEFT || fullBleedContentPosition === POSITION.RIGHT;
340
351
  const isVerticalFullBleed = fullBleedContentPosition === POSITION.TOP || fullBleedContentPosition === POSITION.BOTTOM;
341
352
  const imageWrapperStyleProps = {
@@ -352,12 +363,9 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
352
363
  };
353
364
  return /*#__PURE__*/_jsxs(CardBase, {
354
365
  ref: ref,
355
- variant: {
356
- ...(interactiveCard?.body ? mergedVariant : variant),
357
- padding: 'custom'
358
- },
366
+ variant: cardBaseVariant,
359
367
  tokens: cardBaseTokens,
360
- backgroundImage: !interactiveCard?.body && backgroundImage,
368
+ backgroundImage: backgroundImage,
361
369
  onPress: fullBleedInteractive ? undefined : onPress,
362
370
  ...(interactiveCard?.selectionType && {
363
371
  interactiveCard,
@@ -365,16 +373,18 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
365
373
  }),
366
374
  ...selectProps(restProps),
367
375
  children: [interactiveCard?.selectionType && children ? /*#__PURE__*/_jsx(CardContent, {
368
- tokens: tokens,
376
+ tokens: getCardContentTokens(tokens, {
377
+ backgroundImage
378
+ }),
369
379
  variant: variant,
370
380
  withFooter: hasFooter,
381
+ backgroundImage: backgroundImage,
371
382
  children: children
372
383
  }) : null, interactiveCard?.body && !interactiveCard.selectionType ? /*#__PURE__*/_jsxs(_Fragment, {
373
384
  children: [/*#__PURE__*/_jsx(PressableCardBase, {
374
385
  ref: ref,
375
386
  tokens: getThemeTokens,
376
387
  dataSet: dataSet,
377
- backgroundImage: backgroundImage,
378
388
  onPress: onPress,
379
389
  href: interactiveCard?.href,
380
390
  hrefAttrs: interactiveCard?.hrefAttrs,
@@ -383,9 +393,12 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
383
393
  children: typeof interactiveCard?.body === 'function' ? interactiveCard.body(cardState) : interactiveCard.body
384
394
  })
385
395
  }), children && fullBleedContentPosition === POSITION.NONE && !fullBleedInteractive ? /*#__PURE__*/_jsx(CardContent, {
386
- tokens: tokens,
396
+ tokens: getCardContentTokens(tokens, {
397
+ backgroundImage
398
+ }),
387
399
  variant: variant,
388
400
  withFooter: hasFooter,
401
+ backgroundImage: backgroundImage,
389
402
  children: children
390
403
  }) : null]
391
404
  }) : null, fullBleedInteractive ? /*#__PURE__*/_jsx(PressableCardBase, {
@@ -428,6 +441,7 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
428
441
  },
429
442
  variant: variant,
430
443
  withFooter: hasFooter,
444
+ backgroundImage: backgroundImage,
431
445
  children: children
432
446
  })
433
447
  }) : null, fullBleedContentPosition !== POSITION.NONE && /*#__PURE__*/_jsx(ConditionalWrapper, {
@@ -458,6 +472,7 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
458
472
  }),
459
473
  variant: variant,
460
474
  withFooter: hasFooter,
475
+ backgroundImage: backgroundImage,
461
476
  children: children
462
477
  }) : null, !fullBleedInteractive && fullBleedContentPosition !== POSITION.NONE ? /*#__PURE__*/_jsxs(StackView, {
463
478
  direction: contentStackDirection,
@@ -469,10 +484,12 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
469
484
  condition: isImageWidthAdjustable,
470
485
  children: /*#__PURE__*/_jsx(CardContent, {
471
486
  tokens: getCardContentTokens(tokens, {
487
+ backgroundImage,
472
488
  fullBleedContentChildrenAlign
473
489
  }),
474
490
  variant: variant,
475
491
  withFooter: hasFooter,
492
+ backgroundImage: backgroundImage,
476
493
  children: children
477
494
  })
478
495
  }) : null, /*#__PURE__*/_jsx(ConditionalWrapper, {
@@ -56,6 +56,7 @@ const CardContent = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
56
56
  tokens,
57
57
  variant,
58
58
  withFooter = false,
59
+ backgroundImage,
59
60
  ...rest
60
61
  } = _ref2;
61
62
  const {
@@ -64,10 +65,32 @@ const CardContent = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
64
65
  }
65
66
  } = useTheme();
66
67
  const allTokens = useAllViewportTokens('Card', tokens, variant);
68
+
69
+ // Override backgroundColor if explicitly set in tokens to ensure it takes priority over theme tokens
70
+ // This is crucial for scenarios with backgroundImage where we need transparency
71
+ if (tokens.backgroundColor !== undefined) {
72
+ allTokens.current.backgroundColor = tokens.backgroundColor;
73
+ allTokens.xs.backgroundColor = tokens.backgroundColor;
74
+ allTokens.sm.backgroundColor = tokens.backgroundColor;
75
+ allTokens.md.backgroundColor = tokens.backgroundColor;
76
+ allTokens.lg.backgroundColor = tokens.backgroundColor;
77
+ allTokens.xl.backgroundColor = tokens.backgroundColor;
78
+ }
79
+
80
+ // When backgroundImage is present on the Card, ensure CardContent background is transparent
81
+ // so the background image from the parent CardBase can show through
82
+ if (backgroundImage) {
83
+ allTokens.current.backgroundColor = 'transparent';
84
+ allTokens.xs.backgroundColor = 'transparent';
85
+ allTokens.sm.backgroundColor = 'transparent';
86
+ allTokens.md.backgroundColor = 'transparent';
87
+ allTokens.lg.backgroundColor = 'transparent';
88
+ allTokens.xl.backgroundColor = 'transparent';
89
+ }
67
90
  let themeTokens;
68
91
  let mediaIds;
69
92
  if (enableMediaQueryStyleSheet) {
70
- const stylesByViewport = {
93
+ const paddingAdjustments = {
71
94
  xs: {
72
95
  paddingBottom: allTokens.xs.paddingBottom - allTokens.xs.borderWidth,
73
96
  paddingLeft: allTokens.xs.paddingLeft - allTokens.xs.borderWidth,
@@ -99,15 +122,16 @@ const CardContent = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
99
122
  paddingTop: allTokens.xl.paddingTop - allTokens.xl.borderWidth
100
123
  }
101
124
  };
102
- const mediaQueryStyles = createMediaQueryStyles(stylesByViewport);
125
+ const mediaQueryStyles = createMediaQueryStyles(paddingAdjustments);
126
+ const baseStyle = {
127
+ ...allTokens.current,
128
+ ...mediaQueryStyles
129
+ };
103
130
  const {
104
131
  ids,
105
132
  styles
106
133
  } = StyleSheet.create({
107
- cardContent: {
108
- ...allTokens.current,
109
- ...mediaQueryStyles
110
- }
134
+ cardContent: baseStyle
111
135
  });
112
136
  themeTokens = styles.cardContent;
113
137
  mediaIds = ids.cardContent;
@@ -145,6 +169,11 @@ CardContent.propTypes = {
145
169
  /**
146
170
  * Whether the card has a footer.
147
171
  */
148
- withFooter: PropTypes.bool
172
+ withFooter: PropTypes.bool,
173
+ /**
174
+ * Background image object from parent Card component.
175
+ * When present, makes the CardContent background transparent.
176
+ */
177
+ backgroundImage: PropTypes.object
149
178
  };
150
179
  export default CardContent;
@@ -219,6 +219,7 @@ Testimonial.propTypes = {
219
219
  testimonialStyle: PropTypes.oneOf(['large', 'heading']),
220
220
  /**
221
221
  * Whether to show or not dividers at the top and the bottom of the testimonial
222
+ */
222
223
  showDivider: PropTypes.bool,
223
224
  /**
224
225
  * The src attribute for the Image component or custom JSX content to render instead
package/package.json CHANGED
@@ -5,9 +5,9 @@
5
5
  ],
6
6
  "dependencies": {
7
7
  "@gorhom/portal": "^1.0.14",
8
- "@telus-uds/components-base": "^3.27.0",
8
+ "@telus-uds/components-base": "^3.28.0",
9
9
  "@telus-uds/system-constants": "^3.0.0",
10
- "@telus-uds/system-theme-tokens": "^4.19.0",
10
+ "@telus-uds/system-theme-tokens": "^4.20.0",
11
11
  "fscreen": "^1.2.0",
12
12
  "lodash.omit": "^4.5.0",
13
13
  "lodash.throttle": "^4.1.1",
@@ -82,5 +82,5 @@
82
82
  "skip": true
83
83
  },
84
84
  "types": "types/index.d.ts",
85
- "version": "4.18.0"
85
+ "version": "4.18.1"
86
86
  }
package/src/Card/Card.jsx CHANGED
@@ -64,9 +64,7 @@ const getCardContentTokens = (baseTokens, options = {}) => {
64
64
  // Determine background color based on conditions
65
65
  let backgroundColorOverride = {}
66
66
  if (useTransparentBackground) {
67
- if (!backgroundImage) {
68
- backgroundColorOverride = { backgroundColor: 'transparent' }
69
- }
67
+ backgroundColorOverride = { backgroundColor: 'transparent' }
70
68
  } else if (backgroundImage) {
71
69
  backgroundColorOverride = { backgroundColor: 'transparent' }
72
70
  }
@@ -218,7 +216,8 @@ const Card = React.forwardRef(
218
216
 
219
217
  // If the card has rounded corners and a full bleed image, we need to apply
220
218
  // those corners on the image as well, but partially
221
- const allThemeTokens = useThemeTokens('Card', tokens, variant)
219
+ const variantForTokens = backgroundImage ? { ...variant, style: undefined } : variant
220
+ const allThemeTokens = useThemeTokens('Card', tokens, variantForTokens)
222
221
  const { borderRadius } = allThemeTokens
223
222
 
224
223
  // Interactive cards: merge variants for CardBase (outer container)
@@ -274,14 +273,25 @@ const Card = React.forwardRef(
274
273
  }
275
274
  )
276
275
 
277
- // Remove backgroundColor from tokens for full bleed interactive content
278
- // to prevent background from covering the full bleed image
279
- const { backgroundColor: _, ...tokensWithoutBg } = tokens
280
-
281
- const getFullBleedInteractiveTokens = useThemeTokensCallback('Card', tokensWithoutBg, {
282
- ...variant,
283
- interactive: true
284
- })
276
+ // Keep backgroundColor for CardContent, it won't affect FullBleedContent image
277
+ const tokensWithoutBg = tokens
278
+
279
+ const fullBleedInteractiveVariant = backgroundImage
280
+ ? {
281
+ ...variant,
282
+ interactive: true,
283
+ style: undefined
284
+ }
285
+ : {
286
+ ...variant,
287
+ interactive: true
288
+ }
289
+
290
+ const getFullBleedInteractiveTokens = useThemeTokensCallback(
291
+ 'Card',
292
+ tokensWithoutBg,
293
+ fullBleedInteractiveVariant
294
+ )
285
295
 
286
296
  const getFullBleedInteractiveCardTokens = (cardState) => {
287
297
  return {
@@ -343,11 +353,19 @@ const Card = React.forwardRef(
343
353
  'paddingBottom',
344
354
  'paddingLeft',
345
355
  'paddingRight',
346
- ...(backgroundImage && interactiveCard?.body ? ['backgroundColor'] : [])
356
+ ...(backgroundImage ? ['backgroundColor', 'gradient', 'backgroundGradient'] : [])
347
357
  ].includes(key)
348
358
  )
349
359
  )
350
360
 
361
+ const cardBaseVariant = backgroundImage
362
+ ? {
363
+ ...(interactiveCard?.body ? mergedVariant : variant),
364
+ padding: 'custom',
365
+ style: undefined
366
+ }
367
+ : { ...(interactiveCard?.body ? mergedVariant : variant), padding: 'custom' }
368
+
351
369
  const isHorizontalFullBleed =
352
370
  fullBleedContentPosition === POSITION.LEFT || fullBleedContentPosition === POSITION.RIGHT
353
371
  const isVerticalFullBleed =
@@ -363,15 +381,20 @@ const Card = React.forwardRef(
363
381
  return (
364
382
  <CardBase
365
383
  ref={ref}
366
- variant={{ ...(interactiveCard?.body ? mergedVariant : variant), padding: 'custom' }}
384
+ variant={cardBaseVariant}
367
385
  tokens={cardBaseTokens}
368
- backgroundImage={!interactiveCard?.body && backgroundImage}
386
+ backgroundImage={backgroundImage}
369
387
  onPress={fullBleedInteractive ? undefined : onPress}
370
388
  {...(interactiveCard?.selectionType && { interactiveCard, id: rest.id })}
371
389
  {...selectProps(restProps)}
372
390
  >
373
391
  {interactiveCard?.selectionType && children ? (
374
- <CardContent tokens={tokens} variant={variant} withFooter={hasFooter}>
392
+ <CardContent
393
+ tokens={getCardContentTokens(tokens, { backgroundImage })}
394
+ variant={variant}
395
+ withFooter={hasFooter}
396
+ backgroundImage={backgroundImage}
397
+ >
375
398
  {children}
376
399
  </CardContent>
377
400
  ) : null}
@@ -381,7 +404,6 @@ const Card = React.forwardRef(
381
404
  ref={ref}
382
405
  tokens={getThemeTokens}
383
406
  dataSet={dataSet}
384
- backgroundImage={backgroundImage}
385
407
  onPress={onPress}
386
408
  href={interactiveCard?.href}
387
409
  hrefAttrs={interactiveCard?.hrefAttrs}
@@ -396,7 +418,12 @@ const Card = React.forwardRef(
396
418
  )}
397
419
  </PressableCardBase>
398
420
  {children && fullBleedContentPosition === POSITION.NONE && !fullBleedInteractive ? (
399
- <CardContent tokens={tokens} variant={variant} withFooter={hasFooter}>
421
+ <CardContent
422
+ tokens={getCardContentTokens(tokens, { backgroundImage })}
423
+ variant={variant}
424
+ withFooter={hasFooter}
425
+ backgroundImage={backgroundImage}
426
+ >
400
427
  {children}
401
428
  </CardContent>
402
429
  ) : null}
@@ -462,6 +489,7 @@ const Card = React.forwardRef(
462
489
  }}
463
490
  variant={variant}
464
491
  withFooter={hasFooter}
492
+ backgroundImage={backgroundImage}
465
493
  >
466
494
  {children}
467
495
  </CardContent>
@@ -508,6 +536,7 @@ const Card = React.forwardRef(
508
536
  })}
509
537
  variant={variant}
510
538
  withFooter={hasFooter}
539
+ backgroundImage={backgroundImage}
511
540
  >
512
541
  {children}
513
542
  </CardContent>
@@ -526,10 +555,12 @@ const Card = React.forwardRef(
526
555
  >
527
556
  <CardContent
528
557
  tokens={getCardContentTokens(tokens, {
558
+ backgroundImage,
529
559
  fullBleedContentChildrenAlign
530
560
  })}
531
561
  variant={variant}
532
562
  withFooter={hasFooter}
563
+ backgroundImage={backgroundImage}
533
564
  >
534
565
  {children}
535
566
  </CardContent>
@@ -55,18 +55,43 @@ const CardContentContainer = styled.div(
55
55
  * Card content, applying the card tokens as per the theme used.
56
56
  */
57
57
  const CardContent = React.forwardRef(
58
- ({ children, flexContent, tokens, variant, withFooter = false, ...rest }, ref) => {
58
+ (
59
+ { children, flexContent, tokens, variant, withFooter = false, backgroundImage, ...rest },
60
+ ref
61
+ ) => {
59
62
  const {
60
63
  themeOptions: { enableMediaQueryStyleSheet }
61
64
  } = useTheme()
62
65
 
63
66
  const allTokens = useAllViewportTokens('Card', tokens, variant)
64
67
 
68
+ // Override backgroundColor if explicitly set in tokens to ensure it takes priority over theme tokens
69
+ // This is crucial for scenarios with backgroundImage where we need transparency
70
+ if (tokens.backgroundColor !== undefined) {
71
+ allTokens.current.backgroundColor = tokens.backgroundColor
72
+ allTokens.xs.backgroundColor = tokens.backgroundColor
73
+ allTokens.sm.backgroundColor = tokens.backgroundColor
74
+ allTokens.md.backgroundColor = tokens.backgroundColor
75
+ allTokens.lg.backgroundColor = tokens.backgroundColor
76
+ allTokens.xl.backgroundColor = tokens.backgroundColor
77
+ }
78
+
79
+ // When backgroundImage is present on the Card, ensure CardContent background is transparent
80
+ // so the background image from the parent CardBase can show through
81
+ if (backgroundImage) {
82
+ allTokens.current.backgroundColor = 'transparent'
83
+ allTokens.xs.backgroundColor = 'transparent'
84
+ allTokens.sm.backgroundColor = 'transparent'
85
+ allTokens.md.backgroundColor = 'transparent'
86
+ allTokens.lg.backgroundColor = 'transparent'
87
+ allTokens.xl.backgroundColor = 'transparent'
88
+ }
89
+
65
90
  let themeTokens
66
91
  let mediaIds
67
92
 
68
93
  if (enableMediaQueryStyleSheet) {
69
- const stylesByViewport = {
94
+ const paddingAdjustments = {
70
95
  xs: {
71
96
  paddingBottom: allTokens.xs.paddingBottom - allTokens.xs.borderWidth,
72
97
  paddingLeft: allTokens.xs.paddingLeft - allTokens.xs.borderWidth,
@@ -99,12 +124,14 @@ const CardContent = React.forwardRef(
99
124
  }
100
125
  }
101
126
 
102
- const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
127
+ const mediaQueryStyles = createMediaQueryStyles(paddingAdjustments)
128
+ const baseStyle = {
129
+ ...allTokens.current,
130
+ ...mediaQueryStyles
131
+ }
132
+
103
133
  const { ids, styles } = StyleSheet.create({
104
- cardContent: {
105
- ...allTokens.current,
106
- ...mediaQueryStyles
107
- }
134
+ cardContent: baseStyle
108
135
  })
109
136
 
110
137
  themeTokens = styles.cardContent
@@ -151,7 +178,12 @@ CardContent.propTypes = {
151
178
  /**
152
179
  * Whether the card has a footer.
153
180
  */
154
- withFooter: PropTypes.bool
181
+ withFooter: PropTypes.bool,
182
+ /**
183
+ * Background image object from parent Card component.
184
+ * When present, makes the CardContent background transparent.
185
+ */
186
+ backgroundImage: PropTypes.object
155
187
  }
156
188
 
157
189
  export default CardContent
@@ -229,6 +229,7 @@ Testimonial.propTypes = {
229
229
  testimonialStyle: PropTypes.oneOf(['large', 'heading']),
230
230
  /**
231
231
  * Whether to show or not dividers at the top and the bottom of the testimonial
232
+ */
232
233
  showDivider: PropTypes.bool,
233
234
  /**
234
235
  * The src attribute for the Image component or custom JSX content to render instead