@telus-uds/components-base 1.50.0 → 1.51.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/component-docs.json +160 -46
  3. package/lib/A11yText/index.js +8 -0
  4. package/lib/Divider/Divider.js +4 -0
  5. package/lib/IconButton/IconButton.js +16 -0
  6. package/lib/Link/ChevronLink.js +4 -0
  7. package/lib/Link/LinkBase.js +1 -1
  8. package/lib/Modal/Modal.js +16 -0
  9. package/lib/MultiSelectFilter/ModalOverlay.js +24 -8
  10. package/lib/MultiSelectFilter/MultiSelectFilter.js +154 -21
  11. package/lib/Pagination/PageButton.js +12 -0
  12. package/lib/Pagination/Pagination.js +12 -0
  13. package/lib/QuickLinks/QuickLinks.js +12 -0
  14. package/lib/QuickLinksFeature/QuickLinksFeatureItem.js +62 -41
  15. package/lib/Responsive/Responsive.js +7 -0
  16. package/lib/Select/Group.js +4 -0
  17. package/lib/Select/Group.native.js +4 -0
  18. package/lib/Select/Item.js +4 -0
  19. package/lib/SideNav/ItemsGroup.js +4 -0
  20. package/lib/SideNav/SideNav.js +8 -0
  21. package/lib/StepTracker/Step.js +35 -14
  22. package/lib/StepTracker/StepTracker.js +21 -2
  23. package/lib/StepTracker/dictionary.js +24 -4
  24. package/lib/Tabs/Tabs.js +20 -0
  25. package/lib-module/A11yText/index.js +8 -0
  26. package/lib-module/Divider/Divider.js +4 -0
  27. package/lib-module/IconButton/IconButton.js +16 -0
  28. package/lib-module/Link/ChevronLink.js +4 -0
  29. package/lib-module/Link/LinkBase.js +1 -1
  30. package/lib-module/Modal/Modal.js +16 -0
  31. package/lib-module/MultiSelectFilter/ModalOverlay.js +25 -9
  32. package/lib-module/MultiSelectFilter/MultiSelectFilter.js +154 -23
  33. package/lib-module/Pagination/PageButton.js +12 -0
  34. package/lib-module/Pagination/Pagination.js +12 -0
  35. package/lib-module/QuickLinks/QuickLinks.js +12 -0
  36. package/lib-module/QuickLinksFeature/QuickLinksFeatureItem.js +64 -43
  37. package/lib-module/Responsive/Responsive.js +7 -0
  38. package/lib-module/Select/Group.js +4 -0
  39. package/lib-module/Select/Group.native.js +4 -0
  40. package/lib-module/Select/Item.js +4 -0
  41. package/lib-module/SideNav/ItemsGroup.js +4 -0
  42. package/lib-module/SideNav/SideNav.js +8 -0
  43. package/lib-module/StepTracker/Step.js +35 -14
  44. package/lib-module/StepTracker/StepTracker.js +21 -2
  45. package/lib-module/StepTracker/dictionary.js +24 -4
  46. package/lib-module/Tabs/Tabs.js +20 -0
  47. package/package.json +2 -2
  48. package/src/A11yText/index.jsx +6 -0
  49. package/src/Divider/Divider.jsx +3 -0
  50. package/src/IconButton/IconButton.jsx +12 -0
  51. package/src/Link/ChevronLink.jsx +3 -0
  52. package/src/Link/LinkBase.jsx +3 -1
  53. package/src/Modal/Modal.jsx +12 -0
  54. package/src/MultiSelectFilter/ModalOverlay.jsx +30 -6
  55. package/src/MultiSelectFilter/MultiSelectFilter.jsx +142 -20
  56. package/src/Pagination/PageButton.jsx +9 -0
  57. package/src/Pagination/Pagination.jsx +9 -0
  58. package/src/QuickLinks/QuickLinks.jsx +9 -0
  59. package/src/QuickLinksFeature/QuickLinksFeatureItem.jsx +42 -35
  60. package/src/Responsive/Responsive.jsx +6 -0
  61. package/src/Select/Group.jsx +3 -0
  62. package/src/Select/Group.native.jsx +3 -0
  63. package/src/Select/Item.jsx +3 -0
  64. package/src/SideNav/ItemsGroup.jsx +3 -0
  65. package/src/SideNav/SideNav.jsx +6 -0
  66. package/src/StepTracker/Step.jsx +34 -10
  67. package/src/StepTracker/StepTracker.jsx +23 -2
  68. package/src/StepTracker/dictionary.js +24 -4
  69. package/src/Tabs/Tabs.jsx +15 -0
  70. package/src/Typography/Typography.jsx +1 -0
@@ -1,7 +1,7 @@
1
- import React, { useState } from 'react'
1
+ import React, { useState, useEffect } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
- import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider'
4
+ import { useThemeTokens, useThemeTokensCallback, applyTextStyles } from '../ThemeProvider'
5
5
  import {
6
6
  containUniqueFields,
7
7
  getTokensPropType,
@@ -14,7 +14,7 @@ import {
14
14
  variantProp
15
15
  } from '../utils'
16
16
  import dictionary from './dictionary'
17
-
17
+ import { useViewport } from '../ViewportProvider'
18
18
  import Box from '../Box'
19
19
  import { Button, ButtonDropdown } from '../Button'
20
20
  import { CheckboxGroup } from '../Checkbox'
@@ -25,6 +25,7 @@ import StackView from '../StackView'
25
25
  import Typography from '../Typography'
26
26
  import { TextButton } from '../Link'
27
27
  import ModalOverlay from './ModalOverlay'
28
+ import Modal from '../Modal'
28
29
 
29
30
  const { Col, Row } = FlexGrid
30
31
 
@@ -47,6 +48,7 @@ const MultiSelectFilter = ({
47
48
  tokens,
48
49
  items = [],
49
50
  values,
51
+ maxHeight = false,
50
52
  initialValues,
51
53
  maxValues,
52
54
  onChange,
@@ -56,6 +58,7 @@ const MultiSelectFilter = ({
56
58
  rowLimit = 12,
57
59
  ...rest
58
60
  }) => {
61
+ const viewport = useViewport()
59
62
  const { currentValues, setValues } = useMultipleInputValues({
60
63
  initialValues,
61
64
  values,
@@ -63,17 +66,39 @@ const MultiSelectFilter = ({
63
66
  onChange,
64
67
  readOnly
65
68
  })
69
+ const [isOpen, setIsOpen] = useState(false)
70
+ const [checkedIds, setCheckedIds] = useState(currentValues ?? [])
71
+ const [maxWidth, setMaxWidth] = useState(false)
66
72
 
67
73
  const themeTokens = useThemeTokens('ButtonDropdown', tokens, variant)
68
74
  const getItemTokens = useThemeTokensCallback('ButtonDropdown', tokens, variant)
69
75
  const getButtonTokens = (buttonState) => selectTokens('Button', getItemTokens(buttonState))
70
76
  const getCopy = useCopy({ dictionary, copy })
77
+ const colSizeNotMobile = items.length > rowLimit ? 2 : 1
78
+ const colSize = viewport !== 'xs' ? colSizeNotMobile : 1
79
+ const itemsLengthNotMobile = items.length > 24 ? items.length / 2 : rowLimit
80
+ const isSelected = currentValues.length > 0
81
+ const rowLength = viewport !== 'xs' ? itemsLengthNotMobile : items.length
71
82
 
72
- const [isOpen, setIsOpen] = useState(false)
73
- const [checkedIds, setCheckedIds] = useState(currentValues ?? [])
83
+ useEffect(() => {
84
+ if (colSize === 1) return setMaxWidth(false)
85
+ return colSize === 2 && setMaxWidth(true)
86
+ }, [colSize])
74
87
 
75
- const colSize = items.length > rowLimit ? 2 : 1
76
- const isSelected = currentValues.length > 0
88
+ const {
89
+ headerFontColor,
90
+ headerFontSize,
91
+ buttonDirection,
92
+ headerFontWeight,
93
+ headerLineHeight,
94
+ subHeaderFontWeight,
95
+ subHeaderFontSize,
96
+ maxHeightSize,
97
+ maxWidthSize,
98
+ subHeaderLineHeight,
99
+ minHeight,
100
+ minWidth
101
+ } = useThemeTokens('MultiSelectFilter', tokens, { ...variant, maxHeight, maxWidth }, { viewport })
77
102
 
78
103
  const uniqueFields = ['id', 'label']
79
104
  if (!containUniqueFields(items, uniqueFields)) {
@@ -93,14 +118,31 @@ const MultiSelectFilter = ({
93
118
  setIsOpen(false)
94
119
  }
95
120
 
121
+ const onClear = () => {
122
+ setCheckedIds(() => [])
123
+ onApply([])
124
+ }
125
+
96
126
  const { align, offsets } = useResponsiveProp({
97
- xs: { align: { top: 'top', left: 'left', bottom: 'bottom', right: 'right' } },
127
+ xs: { align: { top: 'top', left: 'left' } },
98
128
  sm: {
99
129
  align: { top: 'bottom', left: 'left' },
100
130
  offsets: { vertical: 4 }
101
131
  }
102
132
  })
103
133
 
134
+ const headerStyles = applyTextStyles({
135
+ fontSize: headerFontSize,
136
+ fontWeight: headerFontWeight,
137
+ fontColor: headerFontColor
138
+ })
139
+
140
+ const subeHeaderStyles = applyTextStyles({
141
+ fontSize: subHeaderFontSize,
142
+ fontWeight: subHeaderFontWeight,
143
+ fontColor: selectSubTitleTokens(themeTokens)
144
+ })
145
+
104
146
  const { overlaidPosition, onTargetLayout, isReady, sourceRef } = useOverlaidPosition({
105
147
  isShown: isOpen,
106
148
  offsets,
@@ -120,18 +162,84 @@ const MultiSelectFilter = ({
120
162
  tokens={getButtonTokens}
121
163
  inactive={inactive}
122
164
  />
123
- {isOpen && (
165
+ {isOpen && viewport === 'xs' && (
166
+ <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
167
+ <Row>
168
+ <Typography tokens={{ ...headerStyles, lineHeight: headerLineHeight }}>
169
+ {getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
170
+ </Typography>
171
+ </Row>
172
+ {subtitle && (
173
+ <>
174
+ <Spacer space={5} />
175
+ <Row>
176
+ <Typography tokens={{ ...subeHeaderStyles, lineHeight: subHeaderLineHeight }}>
177
+ {subtitle}
178
+ </Typography>
179
+ </Row>
180
+ </>
181
+ )}
182
+ <Spacer space={4} />
183
+ <Box scroll={true}>
184
+ <Row distribute="between">
185
+ {[...Array(colSize).keys()].map((i) => (
186
+ <Col xs={12 / colSize} key={i}>
187
+ <CheckboxGroup
188
+ items={items.slice(i * rowLength, (i + 1) * rowLength)}
189
+ checkedIds={checkedIds}
190
+ onChange={(e) => setCheckedIds(e, i)}
191
+ />
192
+ <Spacer size={4} />
193
+ </Col>
194
+ ))}
195
+ </Row>
196
+ </Box>
197
+ <Divider variant={selectDividerToknes({ ...themeTokens, width: 'full' })} space={4} />
198
+ <Row horizontalAlign={buttonDirection === 'column' ? 'center' : 'start'}>
199
+ <StackView
200
+ direction={buttonDirection}
201
+ space={3}
202
+ tokens={{
203
+ alignItems: 'center',
204
+ ...(viewport === 'xs' && { flexGrow: 1 })
205
+ }}
206
+ >
207
+ <Button
208
+ onPress={() => onApply(checkedIds)}
209
+ variant={{
210
+ size: 'small',
211
+ priority: 'high',
212
+ ...(viewport === 'xs' && { width: 'full' })
213
+ }}
214
+ >
215
+ {getCopy('applyButtonLabel')}
216
+ </Button>
217
+ <Box>
218
+ <TextButton onPress={() => onClear()}>{getCopy('clearButtonLabel')}</TextButton>
219
+ </Box>
220
+ </StackView>
221
+ </Row>
222
+ </Modal>
223
+ )}
224
+ {isOpen && viewport !== 'xs' && (
124
225
  <ModalOverlay
125
226
  overlaidPosition={overlaidPosition}
126
- variant={{ width: colSize > 1 ? 'size576' : 's' }}
127
227
  onClose={() => setIsOpen(false)}
128
- tokens={tokens}
228
+ maxHeight={maxHeight}
229
+ maxHeightSize={maxHeightSize}
230
+ maxWidthSize={maxWidthSize}
231
+ minHeight={minHeight}
232
+ minWidth={minWidth}
233
+ tokens={{
234
+ ...tokens,
235
+ maxWidth
236
+ }}
129
237
  copy={copy}
130
238
  isReady={isReady}
131
239
  onLayout={onTargetLayout}
132
240
  >
133
241
  <Row>
134
- <Typography variant={{ size: 'h4' }}>
242
+ <Typography tokens={{ ...headerStyles, lineHeight: headerLineHeight }}>
135
243
  {getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
136
244
  </Typography>
137
245
  </Row>
@@ -139,7 +247,7 @@ const MultiSelectFilter = ({
139
247
  <>
140
248
  <Spacer space={5} />
141
249
  <Row>
142
- <Typography variant={{ size: 'h5' }} tokens={selectSubTitleTokens(themeTokens)}>
250
+ <Typography tokens={{ ...subeHeaderStyles, lineHeight: subHeaderLineHeight }}>
143
251
  {subtitle}
144
252
  </Typography>
145
253
  </Row>
@@ -151,7 +259,7 @@ const MultiSelectFilter = ({
151
259
  {[...Array(colSize).keys()].map((i) => (
152
260
  <Col xs={12 / colSize} key={i}>
153
261
  <CheckboxGroup
154
- items={items.slice(i * rowLimit, (i + 1) * rowLimit)}
262
+ items={items.slice(i * rowLength, (i + 1) * rowLength)}
155
263
  checkedIds={checkedIds}
156
264
  onChange={(e) => setCheckedIds(e, i)}
157
265
  />
@@ -161,18 +269,27 @@ const MultiSelectFilter = ({
161
269
  </Row>
162
270
  </Box>
163
271
  <Divider variant={selectDividerToknes({ ...themeTokens, width: 'full' })} space={4} />
164
- <Row>
165
- <StackView direction="row" space={3} tokens={{ alignItems: 'center' }}>
272
+ <Row horizontalAlign={buttonDirection === 'column' ? 'center' : 'start'}>
273
+ <StackView
274
+ direction={buttonDirection}
275
+ space={3}
276
+ tokens={{
277
+ alignItems: 'center',
278
+ ...(viewport === 'xs' && { flexGrow: 1 })
279
+ }}
280
+ >
166
281
  <Button
167
282
  onPress={() => onApply(checkedIds)}
168
- variant={{ size: 'small', priority: 'high' }}
283
+ variant={{
284
+ size: 'small',
285
+ priority: 'high',
286
+ ...(viewport === 'xs' && { width: 'full' })
287
+ }}
169
288
  >
170
289
  {getCopy('applyButtonLabel')}
171
290
  </Button>
172
291
  <Box>
173
- <TextButton onPress={() => setCheckedIds([])}>
174
- {getCopy('clearButtonLabel')}
175
- </TextButton>
292
+ <TextButton onPress={() => onClear()}>{getCopy('clearButtonLabel')}</TextButton>
176
293
  </Box>
177
294
  </StackView>
178
295
  </Row>
@@ -239,10 +356,15 @@ MultiSelectFilter.propTypes = {
239
356
  * If provided, sets a maximum number of items a user may select at once.
240
357
  */
241
358
  maxValues: PropTypes.number,
359
+ /**
360
+ * If provided sets maxHeight to be active
361
+ */
362
+ maxHeight: PropTypes.bool,
242
363
  /**
243
364
  * If provided, this function is called when the current selection is changed
244
365
  * and is passed an array of the `id`s of all currently selected `items`.
245
366
  */
367
+
246
368
  onChange: PropTypes.func,
247
369
  /**
248
370
  * Select English or French copy for the accessible label.
@@ -60,8 +60,17 @@ PageButton.displayName = 'PageButton'
60
60
 
61
61
  PageButton.propTypes = {
62
62
  ...linkProps.types,
63
+ /**
64
+ * To set custom label for the button
65
+ */
63
66
  label: PropTypes.string,
67
+ /**
68
+ * To set `PageButton` to active state
69
+ */
64
70
  isActive: PropTypes.bool,
71
+ /**
72
+ * To change the language for labels
73
+ */
65
74
  copy: copyPropTypes,
66
75
  variant: variantProp.propType,
67
76
  tokens: getTokensPropType('PaginationPageButton')
@@ -207,10 +207,19 @@ Pagination.propTypes = {
207
207
  ...selectedSystemPropTypes,
208
208
  ...withLinkRouter.propTypes,
209
209
  children: componentPropType('PageButton'),
210
+ /**
211
+ * To change the language for labels
212
+ */
210
213
  copy: copyPropTypes,
211
214
  variant: variantProp.propType,
212
215
  tokens: getTokensPropType('Pagination'),
216
+ /**
217
+ * When passed as `{{ compact: true }}`, `Pagination` does not render labels along side buttons
218
+ */
213
219
  sideButtonVariant: variantProp.propType,
220
+ /**
221
+ * Custom tokens for `PaginationSideButton`
222
+ */
214
223
  sideButtonTokens: getTokensPropType('PaginationSideButton')
215
224
  }
216
225
 
@@ -51,8 +51,17 @@ QuickLinks.displayName = 'QuickLinks'
51
51
 
52
52
  QuickLinks.propTypes = {
53
53
  tokens: getTokensPropType('QuickLinks'),
54
+ /**
55
+ * Custom tokens override for `Card`
56
+ */
54
57
  cardTokens: getTokensPropType('Card'),
58
+ /**
59
+ * Custom tokens override for `QuickLinksList`
60
+ */
55
61
  listTokens: getTokensPropType('QuickLinksList'),
62
+ /**
63
+ * The HTML tag to render the list as
64
+ */
56
65
  tag: PropTypes.string,
57
66
  variant: variantProp.propType,
58
67
  children: PropTypes.node
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useState } from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
4
  import { Image, Platform, Text, View } from 'react-native'
@@ -11,7 +11,7 @@ import {
11
11
  selectSystemProps
12
12
  } from '../utils'
13
13
  import { useViewport } from '../ViewportProvider'
14
- import { useThemeTokensCallback } from '../ThemeProvider'
14
+ import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
15
15
  import { Link } from '../Link'
16
16
  import { StackWrap } from '../StackView'
17
17
 
@@ -29,6 +29,7 @@ const selectImageStyle = (imageDimension) => ({
29
29
 
30
30
  const selectContainerStyle = ({ contentMaxDimension, textAlign }) => ({
31
31
  textAlign,
32
+ outline: 'red',
32
33
  width: contentMaxDimension,
33
34
  overflow: 'hidden'
34
35
  })
@@ -40,6 +41,8 @@ const selectImageContainerStyle = (contentMaxDimension) => ({
40
41
  alignItems: 'center'
41
42
  })
42
43
 
44
+ const selectLinkToken = ({ outerBorderColor }) => ({ outerBorderColor })
45
+
43
46
  /**
44
47
  * Component export along with QuickLinksFeature to be used as children
45
48
  *
@@ -49,42 +52,46 @@ const QuickLinksFeatureItem = forwardRef(
49
52
  ({ tokens, variant, children, imageAccessibilityLabel, imageSource, ...rest }, ref) => {
50
53
  const viewport = useViewport()
51
54
  const getTokens = useThemeTokensCallback('QuickLinksFeatureItem', tokens, variant)
52
- const [hover, setHover] = useState(false)
53
- const {
54
- contentDirection,
55
- contentSpace,
56
- contentAlignItems,
57
- contentMaxDimension,
58
- imageDimension,
59
- textAlign
60
- } = getTokens({ viewport, hover })
61
55
 
62
56
  return (
63
- <Link
64
- ref={ref}
65
- tokens={(state) => {
66
- setHover(state.hover)
67
- return getTokens(state)
68
- }}
69
- {...selectProps(rest)}
70
- >
71
- <View style={selectContainerStyle({ contentMaxDimension, textAlign })}>
72
- <StackWrap
73
- direction={contentDirection}
74
- space={contentSpace}
75
- tokens={{ alignItems: contentAlignItems }}
76
- >
77
- <View style={selectImageContainerStyle(contentMaxDimension)}>
78
- <Image
79
- accessibilityIgnoresInvertColors
80
- imageAccessibilityLabel={imageAccessibilityLabel}
81
- source={imageSource}
82
- style={selectImageStyle(imageDimension)}
83
- />
57
+ <Link ref={ref} {...selectProps(rest)} tokens={(state) => selectLinkToken(getTokens(state))}>
58
+ {({ hovered: hover, pressed, focused: focus }) => {
59
+ const {
60
+ contentDirection,
61
+ contentSpace,
62
+ contentAlignItems,
63
+ contentMaxDimension,
64
+ imageDimension,
65
+ textLine,
66
+ gap,
67
+ ...themeTokens
68
+ } = getTokens({ viewport, hover, pressed, focus })
69
+
70
+ const textStyle = {
71
+ ...applyTextStyles(themeTokens),
72
+ textDecorationLine: textLine
73
+ }
74
+ return (
75
+ <View style={selectContainerStyle({ ...themeTokens, contentMaxDimension })}>
76
+ <StackWrap
77
+ direction={contentDirection}
78
+ space={contentSpace}
79
+ gap={gap}
80
+ tokens={{ alignItems: contentAlignItems }}
81
+ >
82
+ <View style={selectImageContainerStyle(contentMaxDimension)}>
83
+ <Image
84
+ accessibilityIgnoresInvertColors
85
+ imageAccessibilityLabel={imageAccessibilityLabel}
86
+ source={imageSource}
87
+ style={selectImageStyle(imageDimension)}
88
+ />
89
+ </View>
90
+ <Text style={textStyle}>{children}</Text>
91
+ </StackWrap>
84
92
  </View>
85
- <Text>{children}</Text>
86
- </StackWrap>
87
- </View>
93
+ )
94
+ }}
88
95
  </Link>
89
96
  )
90
97
  }
@@ -25,7 +25,13 @@ const Responsive = ({ min = 'xs', max, children }) => {
25
25
  }
26
26
 
27
27
  Responsive.propTypes = {
28
+ /**
29
+ * To hide children of `Responsive` if the current viewport is smaller than `min`
30
+ */
28
31
  min: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
32
+ /**
33
+ * To hide children of `Responsive` if the current viewport is larger than `max`
34
+ */
29
35
  max: PropTypes.oneOf(['sm', 'md', 'lg', 'xl']),
30
36
  children: PropTypes.node.isRequired
31
37
  }
@@ -11,5 +11,8 @@ export default Group
11
11
 
12
12
  Group.propTypes = {
13
13
  children: componentPropType('Item'),
14
+ /**
15
+ * The label for the group.
16
+ */
14
17
  label: PropTypes.string.isRequired
15
18
  }
@@ -10,5 +10,8 @@ export default Group
10
10
 
11
11
  Group.propTypes = {
12
12
  children: componentPropType('Item'),
13
+ /**
14
+ * The label for the group.
15
+ */
13
16
  label: PropTypes.string.isRequired
14
17
  }
@@ -7,5 +7,8 @@ export default Item
7
7
 
8
8
  Item.propTypes = {
9
9
  children: PropTypes.string.isRequired,
10
+ /**
11
+ * The value of the option
12
+ */
10
13
  value: PropTypes.string.isRequired
11
14
  }
@@ -109,6 +109,9 @@ ItemsGroup.propTypes = {
109
109
  isActive: PropTypes.bool,
110
110
  variant: variantProp.propType,
111
111
  tokens: getTokensPropType('SideNavItemsGroup'),
112
+ /**
113
+ * Custom tokens for `SideNav.Item`
114
+ */
112
115
  itemTokens: getTokensPropType('SideNavItem')
113
116
  }
114
117
 
@@ -120,7 +120,13 @@ SideNav.propTypes = {
120
120
  accordion: PropTypes.bool,
121
121
  variant: variantProp.propType,
122
122
  tokens: getTokensPropType('SideNav'),
123
+ /**
124
+ * Custom tokens for `SideNav.Item`
125
+ */
123
126
  itemTokens: getTokensPropType('SideNavItem'),
127
+ /**
128
+ * Custom tokens for `SideNavItemsGroup`
129
+ */
124
130
  groupTokens: getTokensPropType('SideNavItemsGroup')
125
131
  }
126
132
 
@@ -59,17 +59,18 @@ const selectKnobStyles = (
59
59
  height: knobSize,
60
60
  width: knobSize,
61
61
  ...(isCompleted && {
62
- backgroundColor: knobCompletedBackgroundColor,
63
- borderColor: knobCompletedBorderColor,
64
- paddingLeft: knobCompletedPaddingLeft,
65
- paddingTop: knobCompletedPaddingTop
66
- }),
67
- ...(isCurrent && {
68
62
  backgroundColor: knobCurrentBackgroundColor,
69
63
  borderColor: knobCurrentBorderColor,
70
64
  borderWidth: knobCurrentBorderWidth,
71
65
  paddingLeft: knobCurrentPaddingLeft,
72
66
  paddingTop: knobCurrentPaddingTop
67
+ }),
68
+ ...(isCurrent && {
69
+ backgroundColor: knobCompletedBackgroundColor,
70
+ borderColor: knobCompletedBorderColor,
71
+ borderWidth: knobCurrentBorderWidth,
72
+ paddingLeft: knobCompletedPaddingLeft,
73
+ paddingTop: knobCompletedPaddingTop
73
74
  })
74
75
  })
75
76
  const selectLabelContainerStyles = ({
@@ -85,6 +86,26 @@ const selectLabelContainerStyles = ({
85
86
  flexDirection: labelDirection,
86
87
  gap: labelGap
87
88
  })
89
+ const selectStepLabelStyles = (
90
+ {
91
+ stepLabelColor,
92
+ labelCurrentColor,
93
+ stepLabelFontWeight,
94
+ stepLabelFontSize,
95
+ stepLabelFontName,
96
+ stepLabelLineHeight
97
+ },
98
+ themeOptions,
99
+ isCurrent
100
+ ) =>
101
+ applyTextStyles({
102
+ color: isCurrent ? labelCurrentColor : stepLabelColor,
103
+ fontSize: stepLabelFontSize,
104
+ lineHeight: stepLabelLineHeight,
105
+ fontWeight: stepLabelFontWeight,
106
+ fontName: stepLabelFontName,
107
+ themeOptions
108
+ })
88
109
  const selectLabelStyles = (
89
110
  {
90
111
  labelColor,
@@ -121,7 +142,8 @@ const getStepTestID = (isCompleted, isCurrent) => {
121
142
  * A single step of a StepTracker.
122
143
  */
123
144
  const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, ...rest }) => {
124
- const { completedIcon, showStepLabel, showStepName, ...themeTokens } = tokens
145
+ const { completedIcon, showStepLabel, showStepName, textStepTrackerLabel, ...themeTokens } =
146
+ tokens
125
147
  const isFirst = stepIndex === 0
126
148
  const isLast = stepIndex === stepCount - 1
127
149
  const isCompleted = status > stepIndex
@@ -150,10 +172,12 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
150
172
  style={[staticStyles.knob, selectKnobStyles(themeTokens, isCompleted, isCurrent)]}
151
173
  testID={getStepTestID(isCompleted, isCurrent)}
152
174
  >
153
- {isCompleted && completedIcon && (
175
+ {((isCompleted && completedIcon) || (isCurrent && !completedIcon)) && (
176
+ <View style={selectCurrentInnerStyles(themeTokens)} />
177
+ )}
178
+ {isCurrent && completedIcon && (
154
179
  <Icon icon={completedIcon} tokens={selectCompletedIconTokens(themeTokens)} />
155
180
  )}
156
- {isCurrent && <View style={selectCurrentInnerStyles(themeTokens)} />}
157
181
  </View>
158
182
  <View
159
183
  style={[
@@ -168,7 +192,7 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
168
192
  <Text
169
193
  style={[
170
194
  staticStyles.centeredText,
171
- selectLabelStyles(tokens, themeOptions, isCurrent)
195
+ selectStepLabelStyles(tokens, themeOptions, isCurrent)
172
196
  ]}
173
197
  >
174
198
  {name}
@@ -92,9 +92,13 @@ const StepTracker = forwardRef(
92
92
  viewport
93
93
  }
94
94
  )
95
+ const { textStepTrackerLabel } = themeTokens
95
96
  const getCopy = useCopy({ dictionary, copy })
96
97
  const stepTrackerLabel = showStepTrackerLabel
97
- ? getCopy('stepTrackerLabel')
98
+ ? (typeof copy === 'string'
99
+ ? getCopy(textStepTrackerLabel ?? 1).stepTrackerLabel
100
+ : getCopy('stepTrackerLabel')
101
+ )
98
102
  .replace('%{stepNumber}', current < steps.length ? current + 1 : steps.length)
99
103
  .replace('%{stepCount}', steps.length)
100
104
  .replace(
@@ -103,7 +107,12 @@ const StepTracker = forwardRef(
103
107
  )
104
108
  : ''
105
109
  const getStepLabel = (index) =>
106
- themeTokens.showStepLabel ? getCopy('stepLabel').replace('%{stepNumber}', index + 1) : ''
110
+ themeTokens.showStepLabel
111
+ ? (typeof copy === 'string'
112
+ ? getCopy(textStepTrackerLabel ?? 1).stepLabel
113
+ : getCopy('stepLabel')
114
+ ).replace('%{stepNumber}', index + 1)
115
+ : ''
107
116
  const { themeOptions } = useTheme()
108
117
  if (!steps.length) return null
109
118
  const selectedProps = selectProps({
@@ -164,12 +173,24 @@ const dictionaryContentShape = PropTypes.shape({
164
173
 
165
174
  StepTracker.propTypes = {
166
175
  ...selectedSystemPropTypes,
176
+ /**
177
+ * The current step, 0-based number
178
+ */
167
179
  current: PropTypes.number,
180
+ /**
181
+ * The language to use for the labels
182
+ */
168
183
  copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
184
+ /**
185
+ * Custom dictionary containing the labels to use for the steps
186
+ */
169
187
  dictionary: PropTypes.shape({
170
188
  en: dictionaryContentShape,
171
189
  fr: dictionaryContentShape
172
190
  }),
191
+ /**
192
+ * An array of strings defining the step titles
193
+ */
173
194
  steps: PropTypes.arrayOf(PropTypes.string),
174
195
  tokens: getTokensPropType('StepTracker'),
175
196
  variant: variantProp.propType