@telus-uds/components-web 4.9.1 → 4.10.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,12 +1,37 @@
1
1
  # Change Log - @telus-uds/components-web
2
2
 
3
- This log was last generated on Wed, 16 Jul 2025 15:16:33 GMT and should not be manually modified.
3
+ This log was last generated on Fri, 15 Aug 2025 00:45:23 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 4.10.0
8
+
9
+ Fri, 15 Aug 2025 00:45:23 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - `Card`: add content-align prop to full-bleed-content (guillermo.peitzner@telus.com)
14
+ - Bump @telus-uds/components-base to v3.13.0
15
+ - Bump @telus-uds/system-theme-tokens to v4.12.0
16
+
17
+ ### Patches
18
+
19
+ - `PriceLockup`: fix text and footnote position (sergio.ramirez@telus.com)
20
+ - `PriceLockup`: fix text and footnote alignment (sergio.ramirez@telus.com)
21
+ - `NavigationBar`: fix ui to match design intention (guillermo.peitzner@telus.com)
22
+
23
+ ## 4.9.2
24
+
25
+ Fri, 25 Jul 2025 04:13:56 GMT
26
+
27
+ ### Patches
28
+
29
+ - Bump @telus-uds/components-base to v3.12.2
30
+ - Bump @telus-uds/system-theme-tokens to v4.11.0
31
+
7
32
  ## 4.9.1
8
33
 
9
- Wed, 16 Jul 2025 15:16:33 GMT
34
+ Wed, 16 Jul 2025 15:18:32 GMT
10
35
 
11
36
  ### Patches
12
37
 
@@ -89,7 +89,8 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
89
89
  contentStackAlign,
90
90
  contentStackDirection,
91
91
  fullBleedContentPosition,
92
- fullBleedContentProps
92
+ fullBleedContentProps,
93
+ fullBleedContentChildrenAlign
93
94
  } = (0, _FullBleedContent.useFullBleedContentProps)(fullBleedContent);
94
95
  const {
95
96
  imgCol
@@ -173,7 +174,12 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
173
174
  wrapperProps: contentWrapperStyleProps,
174
175
  condition: isImageWidthAdjustable,
175
176
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CardContent.default, {
176
- tokens: tokens,
177
+ tokens: {
178
+ ...tokens,
179
+ ...(fullBleedContentChildrenAlign && {
180
+ alignSelf: fullBleedContentChildrenAlign
181
+ })
182
+ },
177
183
  variant: variant,
178
184
  withFooter: hasFooter,
179
185
  children: children
@@ -201,6 +207,7 @@ const alignValues = ['start', 'end', 'center', 'stretch'];
201
207
  const PositionedFullBleedContentPropType = _propTypes.default.shape({
202
208
  position: _componentsBase.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(positionValues)).isRequired,
203
209
  align: _componentsBase.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(alignValues)),
210
+ contentAlign: _componentsBase.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(alignValues)),
204
211
  // eslint-disable-next-line react/forbid-foreign-prop-types
205
212
  ..._FullBleedContent.default.propTypes
206
213
  });
@@ -27,7 +27,8 @@ const CardContentContainer = /*#__PURE__*/_styledComponents.default.div.withConf
27
27
  contentFlexGrow: flexGrow,
28
28
  contentFlexShrink: flexShrink,
29
29
  contentJustifyContent: justifyContent,
30
- borderWidth
30
+ borderWidth,
31
+ alignSelf
31
32
  } = _ref;
32
33
  return {
33
34
  // We need to make sure to have sharp corners on the bottom
@@ -45,7 +46,8 @@ const CardContentContainer = /*#__PURE__*/_styledComponents.default.div.withConf
45
46
  alignItems,
46
47
  flexGrow,
47
48
  flexShrink,
48
- justifyContent
49
+ justifyContent,
50
+ alignSelf
49
51
  };
50
52
  });
51
53
 
@@ -84,7 +86,10 @@ CardContent.propTypes = {
84
86
  /**
85
87
  * Card tokens.
86
88
  */
87
- tokens: (0, _componentsBase.getTokensPropType)('Card'),
89
+ tokens: _propTypes.default.shape({
90
+ ...(0, _componentsBase.getTokensPropType)('Card'),
91
+ alignSelf: _propTypes.default.string
92
+ }),
88
93
  /**
89
94
  * Card variant.
90
95
  */
@@ -18,15 +18,20 @@ const ItemContainer = /*#__PURE__*/_styledComponents.default.div.withConfig({
18
18
  componentId: "components-web__sc-ql9x7c-0"
19
19
  })(_ref => {
20
20
  let {
21
- targetWidth
21
+ targetWidth,
22
+ viewport
22
23
  } = _ref;
23
24
  return {
24
25
  display: 'flex',
25
26
  flexDirection: 'column',
26
27
  justifyContent: 'center',
27
- maxWidth: `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px`,
28
+ maxWidth: viewport !== 'xs' ? `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px` : '100%',
28
29
  flexGrow: targetWidth ? 1 : 0,
29
- flexShrink: 1
30
+ flexShrink: 1,
31
+ ...(viewport === 'xs' && {
32
+ width: '100%',
33
+ flex: 1
34
+ })
30
35
  };
31
36
  });
32
37
 
@@ -60,12 +65,25 @@ const NavigationItem = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
60
65
  });
61
66
  const viewport = (0, _componentsBase.useViewport)();
62
67
  const getTokens = (0, _componentsBase.useThemeTokensCallback)('NavigationBar', tokens, variant);
63
- const getStateTokens = state => getTokens({
64
- ...state,
65
- viewport
66
- });
68
+ const getStateTokens = state => {
69
+ if (viewport !== 'xs') {
70
+ return getTokens({
71
+ ...state,
72
+ viewport
73
+ });
74
+ }
75
+ const {
76
+ width,
77
+ ...tokensWithoutWidth
78
+ } = getTokens({
79
+ ...state,
80
+ viewport
81
+ });
82
+ return tokensWithoutWidth;
83
+ };
67
84
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(ItemContainer, {
68
85
  targetWidth: targetWidth,
86
+ viewport: viewport,
69
87
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_componentsBase.Button, {
70
88
  accessibilityRole: accessibilityRole,
71
89
  accessibilityState: accessibilityState,
@@ -73,7 +91,10 @@ const NavigationItem = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
73
91
  ref: ref,
74
92
  tokens: getStateTokens,
75
93
  variant: {
76
- selected
94
+ selected,
95
+ ...(viewport === 'xs' && {
96
+ width: 'full'
97
+ })
77
98
  },
78
99
  href: href,
79
100
  LinkRouter: LinkRouter,
@@ -128,6 +128,10 @@ const TypographyContainer = /*#__PURE__*/_styledComponents.default.div.withConfi
128
128
  } = _ref13;
129
129
  return `${paddingTop || 0}px`;
130
130
  });
131
+ const InlineTextWithFootnote = /*#__PURE__*/_styledComponents.default.div.withConfig({
132
+ displayName: "PriceLockup__InlineTextWithFootnote",
133
+ componentId: "components-web__sc-1x6duay-11"
134
+ })(["display:inline;position:relative;> *:first-child{display:inline;}> *:last-child{display:inline-block;position:relative;top:-0.5em;font-size:0.75em;margin-left:2px;line-height:0;vertical-align:bottom;}"]);
131
135
  const selectFootnoteLinkStyles = _ref14 => {
132
136
  let {
133
137
  footnoteLinkColor,
@@ -168,6 +172,7 @@ const PriceLockup = /*#__PURE__*/_react.default.forwardRef((_ref16, ref) => {
168
172
  onClickFootnote,
169
173
  strikeThrough,
170
174
  a11yText,
175
+ linkPosition = 'default',
171
176
  tokens: priceLockupTokens,
172
177
  variant = {},
173
178
  copy = 'en',
@@ -271,16 +276,31 @@ const PriceLockup = /*#__PURE__*/_react.default.forwardRef((_ref16, ref) => {
271
276
  })]
272
277
  }), !bottomText && footnoteLinks.length > 3 && renderFootnoteLinks()]
273
278
  });
274
- const renderFootnoteContent = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
275
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(FootnoteContainer, {
279
+ const renderFootnoteContent = () => {
280
+ const containerProps = {
276
281
  footnoteMarginTop: `${footnoteMarginTop}px`,
277
- footnoteGap: `${footnoteGap}px`,
278
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(BottomTextContainer, {
279
- bottomTextMarginTop: `${bottomTextMarginTop}px`,
280
- children: renderTypography(bottomText, typographyTokens.bottomText)
281
- }), footnoteLinks.length <= 3 && renderFootnoteLinks()]
282
- }), footnoteLinks.length > 3 && renderFootnoteLinks()]
283
- });
282
+ footnoteGap: `${footnoteGap}px`
283
+ };
284
+ const shouldUseInline = linkPosition === 'inline';
285
+ const bottomTextContent = shouldUseInline ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(InlineTextWithFootnote, {
286
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_componentsBase.Typography, {
287
+ tokens: typographyTokens.bottomText,
288
+ as: "span",
289
+ children: bottomText
290
+ }), renderFootnoteLinks()]
291
+ }) : renderTypography(bottomText, typographyTokens.bottomText);
292
+ const showFootnotesInContainer = !shouldUseInline && footnoteLinks.length <= 3;
293
+ const showFootnotesOutside = !shouldUseInline && footnoteLinks.length > 3;
294
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
295
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(FootnoteContainer, {
296
+ ...containerProps,
297
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(BottomTextContainer, {
298
+ bottomTextMarginTop: `${bottomTextMarginTop}px`,
299
+ children: bottomTextContent
300
+ }), showFootnotesInContainer && renderFootnoteLinks()]
301
+ }), showFootnotesOutside && renderFootnoteLinks()]
302
+ });
303
+ };
284
304
  if (strikeThrough && !a11yText) {
285
305
  (0, _utils.warn)('PriceLockup', 'a11yText must be provided with strikethrough pricing');
286
306
  }
@@ -404,6 +424,13 @@ PriceLockup.propTypes = {
404
424
  * Select English or French copy for the accessible label.
405
425
  */
406
426
  copy: _propTypes.default.oneOf(['en', 'fr']),
427
+ /**
428
+ * Controls footnote link positioning when bottomText is present
429
+ *
430
+ * - 'default': Footnote appears next to the text container (current behavior)
431
+ * - 'inline': Footnote appears inline at the end of the text
432
+ */
433
+ linkPosition: _propTypes.default.oneOf(['default', 'inline']),
407
434
  /* Custom dictionary containing the labels
408
435
  */
409
436
  dictionary: _propTypes.default.shape({
@@ -43,6 +43,7 @@ const useFullBleedContentProps = fullBleedContent => {
43
43
  const {
44
44
  align: fullBleedContentAlignProp,
45
45
  position: fullBleedContentPositionProp,
46
+ contentAlign: fullBleedContentChildrenAlignProp,
46
47
  ...fullBleedContentProps
47
48
  } = fullBleedContent ?? {
48
49
  position: 'none'
@@ -51,11 +52,13 @@ const useFullBleedContentProps = fullBleedContent => {
51
52
  const contentStackDirection = getContentStackDirection(fullBleedContentPosition);
52
53
  const fullBleedContentAlign = (0, _componentsBase.useResponsiveProp)(fullBleedContentAlignProp, 'stretch');
53
54
  const contentStackAlign = getContentStackAlign(fullBleedContentAlign);
55
+ const fullBleedContentChildrenAlign = (0, _componentsBase.useResponsiveProp)(fullBleedContentChildrenAlignProp, 'stretch');
54
56
  return {
55
57
  contentStackAlign,
56
58
  contentStackDirection,
57
59
  fullBleedContentPosition,
58
- fullBleedContentProps
60
+ fullBleedContentProps,
61
+ fullBleedContentChildrenAlign
59
62
  };
60
63
  };
61
64
  var _default = exports.default = useFullBleedContentProps;
@@ -81,7 +81,8 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
81
81
  contentStackAlign,
82
82
  contentStackDirection,
83
83
  fullBleedContentPosition,
84
- fullBleedContentProps
84
+ fullBleedContentProps,
85
+ fullBleedContentChildrenAlign
85
86
  } = useFullBleedContentProps(fullBleedContent);
86
87
  const {
87
88
  imgCol
@@ -165,7 +166,12 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
165
166
  wrapperProps: contentWrapperStyleProps,
166
167
  condition: isImageWidthAdjustable,
167
168
  children: /*#__PURE__*/_jsx(CardContent, {
168
- tokens: tokens,
169
+ tokens: {
170
+ ...tokens,
171
+ ...(fullBleedContentChildrenAlign && {
172
+ alignSelf: fullBleedContentChildrenAlign
173
+ })
174
+ },
169
175
  variant: variant,
170
176
  withFooter: hasFooter,
171
177
  children: children
@@ -193,6 +199,7 @@ const alignValues = ['start', 'end', 'center', 'stretch'];
193
199
  const PositionedFullBleedContentPropType = PropTypes.shape({
194
200
  position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(positionValues)).isRequired,
195
201
  align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
202
+ contentAlign: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
196
203
  // eslint-disable-next-line react/forbid-foreign-prop-types
197
204
  ...FullBleedContent.propTypes
198
205
  });
@@ -20,7 +20,8 @@ const CardContentContainer = /*#__PURE__*/styled.div.withConfig({
20
20
  contentFlexGrow: flexGrow,
21
21
  contentFlexShrink: flexShrink,
22
22
  contentJustifyContent: justifyContent,
23
- borderWidth
23
+ borderWidth,
24
+ alignSelf
24
25
  } = _ref;
25
26
  return {
26
27
  // We need to make sure to have sharp corners on the bottom
@@ -38,7 +39,8 @@ const CardContentContainer = /*#__PURE__*/styled.div.withConfig({
38
39
  alignItems,
39
40
  flexGrow,
40
41
  flexShrink,
41
- justifyContent
42
+ justifyContent,
43
+ alignSelf
42
44
  };
43
45
  });
44
46
 
@@ -77,7 +79,10 @@ CardContent.propTypes = {
77
79
  /**
78
80
  * Card tokens.
79
81
  */
80
- tokens: getTokensPropType('Card'),
82
+ tokens: PropTypes.shape({
83
+ ...getTokensPropType('Card'),
84
+ alignSelf: PropTypes.string
85
+ }),
81
86
  /**
82
87
  * Card variant.
83
88
  */
@@ -11,15 +11,20 @@ const ItemContainer = /*#__PURE__*/styled.div.withConfig({
11
11
  componentId: "components-web__sc-ql9x7c-0"
12
12
  })(_ref => {
13
13
  let {
14
- targetWidth
14
+ targetWidth,
15
+ viewport
15
16
  } = _ref;
16
17
  return {
17
18
  display: 'flex',
18
19
  flexDirection: 'column',
19
20
  justifyContent: 'center',
20
- maxWidth: `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px`,
21
+ maxWidth: viewport !== 'xs' ? `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px` : '100%',
21
22
  flexGrow: targetWidth ? 1 : 0,
22
- flexShrink: 1
23
+ flexShrink: 1,
24
+ ...(viewport === 'xs' && {
25
+ width: '100%',
26
+ flex: 1
27
+ })
23
28
  };
24
29
  });
25
30
 
@@ -53,12 +58,25 @@ const NavigationItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
53
58
  });
54
59
  const viewport = useViewport();
55
60
  const getTokens = useThemeTokensCallback('NavigationBar', tokens, variant);
56
- const getStateTokens = state => getTokens({
57
- ...state,
58
- viewport
59
- });
61
+ const getStateTokens = state => {
62
+ if (viewport !== 'xs') {
63
+ return getTokens({
64
+ ...state,
65
+ viewport
66
+ });
67
+ }
68
+ const {
69
+ width,
70
+ ...tokensWithoutWidth
71
+ } = getTokens({
72
+ ...state,
73
+ viewport
74
+ });
75
+ return tokensWithoutWidth;
76
+ };
60
77
  return /*#__PURE__*/_jsx(ItemContainer, {
61
78
  targetWidth: targetWidth,
79
+ viewport: viewport,
62
80
  children: /*#__PURE__*/_jsx(Button, {
63
81
  accessibilityRole: accessibilityRole,
64
82
  accessibilityState: accessibilityState,
@@ -66,7 +84,10 @@ const NavigationItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
66
84
  ref: ref,
67
85
  tokens: getStateTokens,
68
86
  variant: {
69
- selected
87
+ selected,
88
+ ...(viewport === 'xs' && {
89
+ width: 'full'
90
+ })
70
91
  },
71
92
  href: href,
72
93
  LinkRouter: LinkRouter,
@@ -121,6 +121,10 @@ const TypographyContainer = /*#__PURE__*/styled.div.withConfig({
121
121
  } = _ref13;
122
122
  return `${paddingTop || 0}px`;
123
123
  });
124
+ const InlineTextWithFootnote = /*#__PURE__*/styled.div.withConfig({
125
+ displayName: "PriceLockup__InlineTextWithFootnote",
126
+ componentId: "components-web__sc-1x6duay-11"
127
+ })(["display:inline;position:relative;> *:first-child{display:inline;}> *:last-child{display:inline-block;position:relative;top:-0.5em;font-size:0.75em;margin-left:2px;line-height:0;vertical-align:bottom;}"]);
124
128
  const selectFootnoteLinkStyles = _ref14 => {
125
129
  let {
126
130
  footnoteLinkColor,
@@ -161,6 +165,7 @@ const PriceLockup = /*#__PURE__*/React.forwardRef((_ref16, ref) => {
161
165
  onClickFootnote,
162
166
  strikeThrough,
163
167
  a11yText,
168
+ linkPosition = 'default',
164
169
  tokens: priceLockupTokens,
165
170
  variant = {},
166
171
  copy = 'en',
@@ -264,16 +269,31 @@ const PriceLockup = /*#__PURE__*/React.forwardRef((_ref16, ref) => {
264
269
  })]
265
270
  }), !bottomText && footnoteLinks.length > 3 && renderFootnoteLinks()]
266
271
  });
267
- const renderFootnoteContent = () => /*#__PURE__*/_jsxs(_Fragment, {
268
- children: [/*#__PURE__*/_jsxs(FootnoteContainer, {
272
+ const renderFootnoteContent = () => {
273
+ const containerProps = {
269
274
  footnoteMarginTop: `${footnoteMarginTop}px`,
270
- footnoteGap: `${footnoteGap}px`,
271
- children: [/*#__PURE__*/_jsx(BottomTextContainer, {
272
- bottomTextMarginTop: `${bottomTextMarginTop}px`,
273
- children: renderTypography(bottomText, typographyTokens.bottomText)
274
- }), footnoteLinks.length <= 3 && renderFootnoteLinks()]
275
- }), footnoteLinks.length > 3 && renderFootnoteLinks()]
276
- });
275
+ footnoteGap: `${footnoteGap}px`
276
+ };
277
+ const shouldUseInline = linkPosition === 'inline';
278
+ const bottomTextContent = shouldUseInline ? /*#__PURE__*/_jsxs(InlineTextWithFootnote, {
279
+ children: [/*#__PURE__*/_jsx(Typography, {
280
+ tokens: typographyTokens.bottomText,
281
+ as: "span",
282
+ children: bottomText
283
+ }), renderFootnoteLinks()]
284
+ }) : renderTypography(bottomText, typographyTokens.bottomText);
285
+ const showFootnotesInContainer = !shouldUseInline && footnoteLinks.length <= 3;
286
+ const showFootnotesOutside = !shouldUseInline && footnoteLinks.length > 3;
287
+ return /*#__PURE__*/_jsxs(_Fragment, {
288
+ children: [/*#__PURE__*/_jsxs(FootnoteContainer, {
289
+ ...containerProps,
290
+ children: [/*#__PURE__*/_jsx(BottomTextContainer, {
291
+ bottomTextMarginTop: `${bottomTextMarginTop}px`,
292
+ children: bottomTextContent
293
+ }), showFootnotesInContainer && renderFootnoteLinks()]
294
+ }), showFootnotesOutside && renderFootnoteLinks()]
295
+ });
296
+ };
277
297
  if (strikeThrough && !a11yText) {
278
298
  warn('PriceLockup', 'a11yText must be provided with strikethrough pricing');
279
299
  }
@@ -397,6 +417,13 @@ PriceLockup.propTypes = {
397
417
  * Select English or French copy for the accessible label.
398
418
  */
399
419
  copy: PropTypes.oneOf(['en', 'fr']),
420
+ /**
421
+ * Controls footnote link positioning when bottomText is present
422
+ *
423
+ * - 'default': Footnote appears next to the text container (current behavior)
424
+ * - 'inline': Footnote appears inline at the end of the text
425
+ */
426
+ linkPosition: PropTypes.oneOf(['default', 'inline']),
400
427
  /* Custom dictionary containing the labels
401
428
  */
402
429
  dictionary: PropTypes.shape({
@@ -37,6 +37,7 @@ const useFullBleedContentProps = fullBleedContent => {
37
37
  const {
38
38
  align: fullBleedContentAlignProp,
39
39
  position: fullBleedContentPositionProp,
40
+ contentAlign: fullBleedContentChildrenAlignProp,
40
41
  ...fullBleedContentProps
41
42
  } = fullBleedContent ?? {
42
43
  position: 'none'
@@ -45,11 +46,13 @@ const useFullBleedContentProps = fullBleedContent => {
45
46
  const contentStackDirection = getContentStackDirection(fullBleedContentPosition);
46
47
  const fullBleedContentAlign = useResponsiveProp(fullBleedContentAlignProp, 'stretch');
47
48
  const contentStackAlign = getContentStackAlign(fullBleedContentAlign);
49
+ const fullBleedContentChildrenAlign = useResponsiveProp(fullBleedContentChildrenAlignProp, 'stretch');
48
50
  return {
49
51
  contentStackAlign,
50
52
  contentStackDirection,
51
53
  fullBleedContentPosition,
52
- fullBleedContentProps
54
+ fullBleedContentProps,
55
+ fullBleedContentChildrenAlign
53
56
  };
54
57
  };
55
58
  export default useFullBleedContentProps;
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.12.1",
8
+ "@telus-uds/components-base": "^3.13.0",
9
9
  "@telus-uds/system-constants": "^3.0.0",
10
- "@telus-uds/system-theme-tokens": "^4.10.0",
10
+ "@telus-uds/system-theme-tokens": "^4.12.0",
11
11
  "fscreen": "^1.2.0",
12
12
  "lodash.omit": "^4.5.0",
13
13
  "lodash.throttle": "^4.1.1",
@@ -83,5 +83,5 @@
83
83
  "skip": true
84
84
  },
85
85
  "types": "types/index.d.ts",
86
- "version": "4.9.1"
86
+ "version": "4.10.0"
87
87
  }
package/src/Card/Card.jsx CHANGED
@@ -88,7 +88,8 @@ const Card = React.forwardRef(
88
88
  contentStackAlign,
89
89
  contentStackDirection,
90
90
  fullBleedContentPosition,
91
- fullBleedContentProps
91
+ fullBleedContentProps,
92
+ fullBleedContentChildrenAlign
92
93
  } = useFullBleedContentProps(fullBleedContent)
93
94
 
94
95
  const { imgCol } = fullBleedContentProps
@@ -179,7 +180,16 @@ const Card = React.forwardRef(
179
180
  wrapperProps={contentWrapperStyleProps}
180
181
  condition={isImageWidthAdjustable}
181
182
  >
182
- <CardContent tokens={tokens} variant={variant} withFooter={hasFooter}>
183
+ <CardContent
184
+ tokens={{
185
+ ...tokens,
186
+ ...(fullBleedContentChildrenAlign && {
187
+ alignSelf: fullBleedContentChildrenAlign
188
+ })
189
+ }}
190
+ variant={variant}
191
+ withFooter={hasFooter}
192
+ >
183
193
  {children}
184
194
  </CardContent>
185
195
  </ConditionalWrapper>
@@ -214,6 +224,7 @@ const alignValues = ['start', 'end', 'center', 'stretch']
214
224
  const PositionedFullBleedContentPropType = PropTypes.shape({
215
225
  position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(positionValues)).isRequired,
216
226
  align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
227
+ contentAlign: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(alignValues)),
217
228
  // eslint-disable-next-line react/forbid-foreign-prop-types
218
229
  ...FullBleedContent.propTypes
219
230
  })
@@ -24,7 +24,8 @@ const CardContentContainer = styled.div(
24
24
  contentFlexGrow: flexGrow,
25
25
  contentFlexShrink: flexShrink,
26
26
  contentJustifyContent: justifyContent,
27
- borderWidth
27
+ borderWidth,
28
+ alignSelf
28
29
  }) => ({
29
30
  // We need to make sure to have sharp corners on the bottom
30
31
  // if the card has a footer
@@ -41,7 +42,8 @@ const CardContentContainer = styled.div(
41
42
  alignItems,
42
43
  flexGrow,
43
44
  flexShrink,
44
- justifyContent
45
+ justifyContent,
46
+ alignSelf
45
47
  })
46
48
  )
47
49
 
@@ -78,7 +80,11 @@ CardContent.propTypes = {
78
80
  /**
79
81
  * Card tokens.
80
82
  */
81
- tokens: getTokensPropType('Card'),
83
+ tokens: PropTypes.shape({
84
+ ...getTokensPropType('Card'),
85
+ alignSelf: PropTypes.string
86
+ }),
87
+
82
88
  /**
83
89
  * Card variant.
84
90
  */
@@ -15,13 +15,17 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
15
15
 
16
16
  const defaultMaxWidth = 192
17
17
 
18
- const ItemContainer = styled.div(({ targetWidth }) => ({
18
+ const ItemContainer = styled.div(({ targetWidth, viewport }) => ({
19
19
  display: 'flex',
20
20
  flexDirection: 'column',
21
21
  justifyContent: 'center',
22
- maxWidth: `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px`,
22
+ maxWidth: viewport !== 'xs' ? `${Math.max(defaultMaxWidth, targetWidth ?? 0)}px` : '100%',
23
23
  flexGrow: targetWidth ? 1 : 0,
24
- flexShrink: 1
24
+ flexShrink: 1,
25
+ ...(viewport === 'xs' && {
26
+ width: '100%',
27
+ flex: 1
28
+ })
25
29
  }))
26
30
 
27
31
  /**
@@ -51,17 +55,25 @@ const NavigationItem = React.forwardRef(
51
55
  const targetWidth = useResponsiveProp({ xs: 288, lg: null })
52
56
  const viewport = useViewport()
53
57
  const getTokens = useThemeTokensCallback('NavigationBar', tokens, variant)
54
- const getStateTokens = (state) => getTokens({ ...state, viewport })
58
+ const getStateTokens = (state) => {
59
+ if (viewport !== 'xs') {
60
+ return getTokens({ ...state, viewport })
61
+ }
62
+
63
+ const { width, ...tokensWithoutWidth } = getTokens({ ...state, viewport })
64
+
65
+ return tokensWithoutWidth
66
+ }
55
67
 
56
68
  return (
57
- <ItemContainer targetWidth={targetWidth}>
69
+ <ItemContainer targetWidth={targetWidth} viewport={viewport}>
58
70
  <Button
59
71
  accessibilityRole={accessibilityRole}
60
72
  accessibilityState={accessibilityState}
61
73
  onPress={handleClick}
62
74
  ref={ref}
63
75
  tokens={getStateTokens}
64
- variant={{ selected }}
76
+ variant={{ selected, ...(viewport === 'xs' && { width: 'full' }) }}
65
77
  href={href}
66
78
  LinkRouter={LinkRouter}
67
79
  linkRouterProps={linkRouterProps}
@@ -81,6 +81,24 @@ const TypographyContainer = styled.div`
81
81
  display: flex;
82
82
  padding-top: ${({ paddingTop }) => `${paddingTop || 0}px`};
83
83
  `
84
+ const InlineTextWithFootnote = styled.div`
85
+ display: inline;
86
+ position: relative;
87
+
88
+ > *:first-child {
89
+ display: inline;
90
+ }
91
+
92
+ > *:last-child {
93
+ display: inline-block;
94
+ position: relative;
95
+ top: -0.5em;
96
+ font-size: 0.75em;
97
+ margin-left: 2px;
98
+ line-height: 0;
99
+ vertical-align: bottom;
100
+ }
101
+ `
84
102
 
85
103
  const selectFootnoteLinkStyles = ({
86
104
  footnoteLinkColor,
@@ -121,6 +139,7 @@ const PriceLockup = React.forwardRef(
121
139
  onClickFootnote,
122
140
  strikeThrough,
123
141
  a11yText,
142
+ linkPosition = 'default',
124
143
  tokens: priceLockupTokens,
125
144
  variant = {},
126
145
  copy = 'en',
@@ -232,20 +251,40 @@ const PriceLockup = React.forwardRef(
232
251
  </>
233
252
  )
234
253
 
235
- const renderFootnoteContent = () => (
236
- <>
237
- <FootnoteContainer
238
- footnoteMarginTop={`${footnoteMarginTop}px`}
239
- footnoteGap={`${footnoteGap}px`}
240
- >
241
- <BottomTextContainer bottomTextMarginTop={`${bottomTextMarginTop}px`}>
242
- {renderTypography(bottomText, typographyTokens.bottomText)}
243
- </BottomTextContainer>
244
- {footnoteLinks.length <= 3 && renderFootnoteLinks()}
245
- </FootnoteContainer>
246
- {footnoteLinks.length > 3 && renderFootnoteLinks()}
247
- </>
248
- )
254
+ const renderFootnoteContent = () => {
255
+ const containerProps = {
256
+ footnoteMarginTop: `${footnoteMarginTop}px`,
257
+ footnoteGap: `${footnoteGap}px`
258
+ }
259
+
260
+ const shouldUseInline = linkPosition === 'inline'
261
+
262
+ const bottomTextContent = shouldUseInline ? (
263
+ <InlineTextWithFootnote>
264
+ <Typography tokens={typographyTokens.bottomText} as="span">
265
+ {bottomText}
266
+ </Typography>
267
+ {renderFootnoteLinks()}
268
+ </InlineTextWithFootnote>
269
+ ) : (
270
+ renderTypography(bottomText, typographyTokens.bottomText)
271
+ )
272
+
273
+ const showFootnotesInContainer = !shouldUseInline && footnoteLinks.length <= 3
274
+ const showFootnotesOutside = !shouldUseInline && footnoteLinks.length > 3
275
+
276
+ return (
277
+ <>
278
+ <FootnoteContainer {...containerProps}>
279
+ <BottomTextContainer bottomTextMarginTop={`${bottomTextMarginTop}px`}>
280
+ {bottomTextContent}
281
+ </BottomTextContainer>
282
+ {showFootnotesInContainer && renderFootnoteLinks()}
283
+ </FootnoteContainer>
284
+ {showFootnotesOutside && renderFootnoteLinks()}
285
+ </>
286
+ )
287
+ }
249
288
 
250
289
  if (strikeThrough && !a11yText) {
251
290
  warn('PriceLockup', 'a11yText must be provided with strikethrough pricing')
@@ -390,6 +429,13 @@ PriceLockup.propTypes = {
390
429
  * Select English or French copy for the accessible label.
391
430
  */
392
431
  copy: PropTypes.oneOf(['en', 'fr']),
432
+ /**
433
+ * Controls footnote link positioning when bottomText is present
434
+ *
435
+ * - 'default': Footnote appears next to the text container (current behavior)
436
+ * - 'inline': Footnote appears inline at the end of the text
437
+ */
438
+ linkPosition: PropTypes.oneOf(['default', 'inline']),
393
439
  /* Custom dictionary containing the labels
394
440
  */
395
441
  dictionary: PropTypes.shape({
@@ -42,6 +42,7 @@ const useFullBleedContentProps = (fullBleedContent) => {
42
42
  const {
43
43
  align: fullBleedContentAlignProp,
44
44
  position: fullBleedContentPositionProp,
45
+ contentAlign: fullBleedContentChildrenAlignProp,
45
46
  ...fullBleedContentProps
46
47
  } = fullBleedContent ?? {
47
48
  position: 'none'
@@ -52,11 +53,17 @@ const useFullBleedContentProps = (fullBleedContent) => {
52
53
  const fullBleedContentAlign = useResponsiveProp(fullBleedContentAlignProp, 'stretch')
53
54
  const contentStackAlign = getContentStackAlign(fullBleedContentAlign)
54
55
 
56
+ const fullBleedContentChildrenAlign = useResponsiveProp(
57
+ fullBleedContentChildrenAlignProp,
58
+ 'stretch'
59
+ )
60
+
55
61
  return {
56
62
  contentStackAlign,
57
63
  contentStackDirection,
58
64
  fullBleedContentPosition,
59
- fullBleedContentProps
65
+ fullBleedContentProps,
66
+ fullBleedContentChildrenAlign
60
67
  }
61
68
  }
62
69