@telus-uds/components-base 3.12.1 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +29 -2
  2. package/lib/cjs/Button/ButtonDropdown.js +105 -12
  3. package/lib/cjs/Carousel/Carousel.js +26 -0
  4. package/lib/cjs/Carousel/CarouselContext.js +7 -4
  5. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +13 -2
  6. package/lib/cjs/Carousel/Constants.js +2 -1
  7. package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
  8. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +1 -1
  9. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  10. package/lib/cjs/FlexGrid/FlexGrid.js +54 -5
  11. package/lib/cjs/Icon/Icon.js +3 -1
  12. package/lib/cjs/InputLabel/InputLabel.js +1 -1
  13. package/lib/cjs/InputSupports/InputSupports.js +1 -1
  14. package/lib/cjs/Notification/Notification.js +27 -8
  15. package/lib/cjs/utils/props/inputSupportsProps.js +1 -1
  16. package/lib/esm/Button/ButtonDropdown.js +107 -14
  17. package/lib/esm/Carousel/Carousel.js +27 -1
  18. package/lib/esm/Carousel/CarouselContext.js +7 -4
  19. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +13 -2
  20. package/lib/esm/Carousel/Constants.js +1 -0
  21. package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
  22. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +2 -2
  23. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  24. package/lib/esm/FlexGrid/FlexGrid.js +55 -6
  25. package/lib/esm/Icon/Icon.js +3 -1
  26. package/lib/esm/InputLabel/InputLabel.js +1 -1
  27. package/lib/esm/InputSupports/InputSupports.js +1 -1
  28. package/lib/esm/Notification/Notification.js +27 -8
  29. package/lib/esm/utils/props/inputSupportsProps.js +1 -1
  30. package/lib/package.json +2 -2
  31. package/package.json +2 -2
  32. package/src/Button/ButtonDropdown.jsx +109 -16
  33. package/src/Carousel/Carousel.jsx +30 -0
  34. package/src/Carousel/CarouselContext.jsx +17 -4
  35. package/src/Carousel/CarouselItem/CarouselItem.jsx +17 -2
  36. package/src/Carousel/Constants.js +1 -0
  37. package/src/ExpandCollapse/ExpandCollapse.jsx +5 -2
  38. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +2 -2
  39. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +39 -9
  40. package/src/FlexGrid/FlexGrid.jsx +62 -6
  41. package/src/Icon/Icon.jsx +3 -1
  42. package/src/InputLabel/InputLabel.jsx +1 -1
  43. package/src/InputSupports/InputSupports.jsx +1 -1
  44. package/src/Notification/Notification.jsx +58 -9
  45. package/src/utils/props/inputSupportsProps.js +1 -1
@@ -10,7 +10,8 @@ import {
10
10
  selectSystemProps,
11
11
  BaseView,
12
12
  StyleSheet,
13
- createMediaQueryStyles
13
+ createMediaQueryStyles,
14
+ useResponsiveProp
14
15
  } from '../utils'
15
16
  import Row from './Row'
16
17
  import Col from './Col'
@@ -21,6 +22,37 @@ import { useViewport } from '../ViewportProvider'
21
22
 
22
23
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
23
24
 
25
+ const CONTENT_MAX_WIDTH = 'max'
26
+ const CONTENT_FULL_WIDTH = 'full'
27
+
28
+ /**
29
+ * Resolves the maximum width for content based on the provided value and responsive width.
30
+ *
31
+ * @param {number|string|null|undefined} contentMinWidthValue - The minimum width value for the content.
32
+ * Can be a number (pixels), a string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
33
+ * @param {number|string} responsiveWidth - The responsive width to use when contentMinWidthValue is CONTENT_MAX_WIDTH.
34
+ * @returns {number|string|null} The resolved maximum width value, which can be a number, a string (e.g., '100%'), or null.
35
+ */
36
+ const resolveContentMaxWidth = (contentMinWidthValue, responsiveWidth) => {
37
+ if (!contentMinWidthValue) {
38
+ return null
39
+ }
40
+
41
+ if (contentMinWidthValue === CONTENT_FULL_WIDTH) {
42
+ return '100%'
43
+ }
44
+
45
+ if (Number.isFinite(contentMinWidthValue)) {
46
+ return contentMinWidthValue
47
+ }
48
+
49
+ if (contentMinWidthValue === CONTENT_MAX_WIDTH) {
50
+ return responsiveWidth
51
+ }
52
+
53
+ return contentMinWidthValue
54
+ }
55
+
24
56
  /**
25
57
  * A mobile-first flexbox grid.
26
58
  */
@@ -40,6 +72,7 @@ const FlexGrid = React.forwardRef(
40
72
  accessibilityRole,
41
73
  children,
42
74
  dataSet,
75
+ contentMinWidth,
43
76
  ...rest
44
77
  },
45
78
  ref
@@ -47,30 +80,36 @@ const FlexGrid = React.forwardRef(
47
80
  const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
48
81
  const viewport = useViewport()
49
82
  const {
83
+ themeOptions,
50
84
  themeOptions: { enableMediaQueryStyleSheet }
51
85
  } = useTheme()
52
86
 
53
87
  let flexgridStyles
54
88
  let mediaIds
55
89
 
90
+ const contentMinWidthValue = useResponsiveProp(contentMinWidth)
91
+ const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
92
+ const maxWidth = resolveContentMaxWidth(contentMinWidthValue, responsiveWidth)
93
+
56
94
  const stylesByViewport = {
57
95
  xs: {
96
+ maxWidth: limitWidth ? viewports.map.get('sm') : maxWidth,
58
97
  flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
59
98
  },
60
99
  sm: {
61
- maxWidth: limitWidth && viewports.map.get('sm'),
100
+ maxWidth: limitWidth ? viewports.map.get('sm') : maxWidth,
62
101
  flexDirection: reverseLevel[1] ? 'column-reverse' : 'column'
63
102
  },
64
103
  md: {
65
- maxWidth: limitWidth && viewports.map.get('md'),
104
+ maxWidth: limitWidth ? viewports.map.get('md') : maxWidth,
66
105
  flexDirection: reverseLevel[2] ? 'column-reverse' : 'column'
67
106
  },
68
107
  lg: {
69
- maxWidth: limitWidth && viewports.map.get('lg'),
108
+ maxWidth: limitWidth ? viewports.map.get('lg') : maxWidth,
70
109
  flexDirection: reverseLevel[3] ? 'column-reverse' : 'column'
71
110
  },
72
111
  xl: {
73
- maxWidth: limitWidth && viewports.map.get('xl'),
112
+ maxWidth: limitWidth ? viewports.map.get('xl') : maxWidth,
74
113
  flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
75
114
  }
76
115
  }
@@ -162,7 +201,24 @@ FlexGrid.propTypes = {
162
201
  /**
163
202
  * The rows and columns of the Grid. Will typically be `FlexGrid.Row` and `FlexGrid.Col` components.
164
203
  */
165
- children: PropTypes.node.isRequired
204
+ children: PropTypes.node.isRequired,
205
+ /**
206
+ * The minimum width of the content in the FlexGrid.
207
+ * This prop accepts responsive values for different viewports. If a number is provided,
208
+ * it will be the max content width for the desired viewport.
209
+ * - `xs`: 'max' | 'full' | <number>
210
+ * - `sm`: 'max' | 'full' | <number>
211
+ * - `md`: 'max' | 'full' | <number>
212
+ * - `lg`: 'max' | 'full' | <number>
213
+ * - `xl`: 'max' | 'full' | <number>
214
+ */
215
+ contentMinWidth: PropTypes.shape({
216
+ xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
217
+ lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
218
+ md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
219
+ sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
220
+ xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
221
+ })
166
222
  }
167
223
 
168
224
  FlexGrid.Row = Row
package/src/Icon/Icon.jsx CHANGED
@@ -38,7 +38,9 @@ const Icon = React.forwardRef(
38
38
  width: themeTokens.size + themeTokens.width * 2, // sets the diameter of the circle which is the size of the icon plus twice the general padding established to obtain a perfect circle
39
39
  height: themeTokens.size + themeTokens.width * 2
40
40
  }
41
- : {}
41
+ : {
42
+ padding: themeTokens.padding
43
+ }
42
44
 
43
45
  const getIconContentForMobile = () => {
44
46
  if (Object.keys(paddingStyles).length) {
@@ -161,7 +161,7 @@ InputLabel.propTypes = {
161
161
  /**
162
162
  * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
163
163
  */
164
- tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
164
+ tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
165
165
  /**
166
166
  * Current number of characterts of an input text.
167
167
  */
@@ -121,7 +121,7 @@ InputSupports.propTypes = {
121
121
  * 1. `tooltip` as a string - The content of the tooltip.
122
122
  * 2. `tooltip` as an object - Tooltip component props to be passed.
123
123
  */
124
- tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
124
+ tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
125
125
  /**
126
126
  * Use to visually mark an input as valid or invalid.
127
127
  */
@@ -26,6 +26,8 @@ import useCopy from '../utils/useCopy'
26
26
  import dictionary from './dictionary'
27
27
  import { useViewport } from '../ViewportProvider'
28
28
 
29
+ const CONTENT_MAX_WIDTH = 'max'
30
+
29
31
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
30
32
 
31
33
  const selectContainerStyles = (tokens) => ({ ...tokens })
@@ -65,8 +67,14 @@ const selectDismissButtonContainerStyles = ({ dismissButtonGap }) => ({
65
67
  placeContent: 'start'
66
68
  })
67
69
 
68
- const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport) => ({
69
- maxWidth: system && viewport === 'xl' ? maxWidth : '100%',
70
+ const selectContentContainerStyle = (
71
+ themeTokens,
72
+ maxWidth,
73
+ system,
74
+ viewport,
75
+ useContentMaxWidth
76
+ ) => ({
77
+ maxWidth: system && (useContentMaxWidth || viewport === 'xl') ? maxWidth : '100%',
70
78
  width: '100%',
71
79
  paddingRight: themeTokens?.containerPaddingRight,
72
80
  paddingLeft: themeTokens?.containerPaddingLeft
@@ -79,7 +87,8 @@ const getMediaQueryStyles = (
79
87
  mediaIdsRef,
80
88
  dismissible,
81
89
  viewport,
82
- system
90
+ system,
91
+ useContentMaxWidth
83
92
  ) => {
84
93
  const transformedSelectContainerStyles = Object.entries(themeTokens).reduce(
85
94
  (acc, [vp, viewportTokens]) => {
@@ -100,7 +109,7 @@ const getMediaQueryStyles = (
100
109
  const transformedSelectContentContainerStyles = Object.entries(themeTokens).reduce(
101
110
  (acc, [vp, viewportTokens]) => {
102
111
  acc[vp] = {
103
- ...selectContentContainerStyle(viewportTokens, maxWidth, system, vp),
112
+ ...selectContentContainerStyle(viewportTokens, maxWidth, system, vp, useContentMaxWidth),
104
113
  flexDirection: 'row',
105
114
  flexShrink: 1,
106
115
  justifyContent: 'space-between',
@@ -169,7 +178,15 @@ const getMediaQueryStyles = (
169
178
  }
170
179
  }
171
180
 
172
- const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system) => ({
181
+ const getDefaultStyles = (
182
+ themeTokens,
183
+ themeOptions,
184
+ maxWidth,
185
+ dismissible,
186
+ viewport,
187
+ system,
188
+ useContentMaxWidth
189
+ ) => ({
173
190
  containerStyles: {
174
191
  container: {
175
192
  flexDirection: 'column',
@@ -181,7 +198,7 @@ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, view
181
198
  flexDirection: 'row',
182
199
  flexShrink: 1,
183
200
  justifyContent: 'space-between',
184
- ...selectContentContainerStyle(themeTokens, maxWidth, system, viewport),
201
+ ...selectContentContainerStyle(themeTokens, maxWidth, system, viewport, useContentMaxWidth),
185
202
  ...(system && { alignSelf: 'center' })
186
203
  }
187
204
  },
@@ -251,7 +268,20 @@ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, view
251
268
  * Show system notifications at the top of the page, below the navigation, and expands the full-width of the viewport
252
269
  */
253
270
  const Notification = React.forwardRef(
254
- ({ children, system, dismissible, copy = 'en', tokens, variant, onDismiss, ...rest }, ref) => {
271
+ (
272
+ {
273
+ children,
274
+ system,
275
+ dismissible,
276
+ copy = 'en',
277
+ tokens,
278
+ variant,
279
+ onDismiss,
280
+ contentMinWidth,
281
+ ...rest
282
+ },
283
+ ref
284
+ ) => {
255
285
  const [isDismissed, setIsDismissed] = React.useState(false)
256
286
  const viewport = useViewport()
257
287
  const getCopy = useCopy({ dictionary, copy })
@@ -266,6 +296,7 @@ const Notification = React.forwardRef(
266
296
  system: isSystemEnabled,
267
297
  viewport
268
298
  })
299
+ const useContentMaxWidth = useResponsiveProp(contentMinWidth) === CONTENT_MAX_WIDTH
269
300
  const maxWidth = useResponsiveProp(
270
301
  themeOptions?.contentMaxWidth,
271
302
  viewports.map.get(viewports.xl)
@@ -300,7 +331,8 @@ const Notification = React.forwardRef(
300
331
  mediaIdsRef,
301
332
  dismissible,
302
333
  viewport,
303
- isSystemEnabled
334
+ isSystemEnabled,
335
+ useContentMaxWidth
304
336
  )
305
337
  } else {
306
338
  notificationComponentRef.current = getDefaultStyles(
@@ -309,7 +341,8 @@ const Notification = React.forwardRef(
309
341
  maxWidth,
310
342
  dismissible,
311
343
  viewport,
312
- isSystemEnabled
344
+ isSystemEnabled,
345
+ useContentMaxWidth
313
346
  )
314
347
  }
315
348
 
@@ -434,6 +467,22 @@ Notification.propTypes = {
434
467
  * Callback function called when the dismiss button is clicked
435
468
  */
436
469
  onDismiss: PropTypes.func,
470
+ /**
471
+ * The minimum width of the content in the Notification when using the system variant.
472
+ * This prop accepts responsive values for different viewports.
473
+ * - `xs`: 'max' | 'full'
474
+ * - `sm`: 'max' | 'full'
475
+ * - `md`: 'max' | 'full'
476
+ * - `lg`: 'max' | 'full'
477
+ * - `xl`: 'max' | 'full'
478
+ */
479
+ contentMinWidth: PropTypes.shape({
480
+ xl: PropTypes.oneOf(['max', 'full']),
481
+ lg: PropTypes.oneOf(['max', 'full']),
482
+ md: PropTypes.oneOf(['max', 'full']),
483
+ sm: PropTypes.oneOf(['max', 'full']),
484
+ xs: PropTypes.oneOf(['max', 'full'])
485
+ }),
437
486
  tokens: getTokensPropType('Notification'),
438
487
  variant: variantProp.propType
439
488
  }
@@ -38,7 +38,7 @@ export default {
38
38
  * 1. `tooltip` as a string - The content of the tooltip.
39
39
  * 2. `tooltip` as an object - Tooltip component props to be passed.
40
40
  */
41
- tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
41
+ tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
42
42
  /**
43
43
  * Use to visually mark an input as valid or invalid.
44
44
  */