@telus-uds/components-base 1.76.0 → 1.77.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,12 +1,36 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Wed, 07 Feb 2024 19:09:40 GMT and should not be manually modified.
3
+ This log was last generated on Tue, 20 Feb 2024 23:07:37 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 1.77.1
8
+
9
+ Tue, 20 Feb 2024 23:07:37 GMT
10
+
11
+ ### Patches
12
+
13
+ - refactor getsteplabel function so step tracker displays multiple labels (evander.owusu@telus.com)
14
+ - fix pagination button-base props warning (guillermo.peitzner@telus.com)
15
+ - fix unexpected scroll when pressing the chevron-link component using the space key (guillermo.peitzner@telus.com)
16
+
17
+ ## 1.77.0
18
+
19
+ Wed, 14 Feb 2024 02:29:37 GMT
20
+
21
+ ### Minor changes
22
+
23
+ - add background-image to box component (guillermo.peitzner@telus.com)
24
+ - skeleton border radius for line shape (tim.hysniu@telus.com)
25
+ - Bump @telus-uds/system-theme-tokens to v2.51.0
26
+
27
+ ### Patches
28
+
29
+ - fix expand-collapse visual states for koodo theme (guillermo.peitzner@telus.com)
30
+
7
31
  ## 1.76.0
8
32
 
9
- Wed, 07 Feb 2024 19:09:40 GMT
33
+ Wed, 07 Feb 2024 19:14:07 GMT
10
34
 
11
35
  ### Minor changes
12
36
 
package/lib/Box/Box.js CHANGED
@@ -9,8 +9,12 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
10
10
  var _ScrollView = _interopRequireDefault(require("react-native-web/dist/cjs/exports/ScrollView"));
11
11
  var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
12
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
13
+ var _ImageBackground = _interopRequireDefault(require("react-native-web/dist/cjs/exports/ImageBackground"));
14
+ var _Image = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Image"));
12
15
  var _ThemeProvider = require("../ThemeProvider");
13
16
  var _utils = require("../utils");
17
+ var _backgroundImageStylesMap = _interopRequireDefault(require("./backgroundImageStylesMap"));
14
18
  var _jsxRuntime = require("react/jsx-runtime");
15
19
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
20
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -65,6 +69,45 @@ const selectBoxStyles = (_ref, customGradient) => {
65
69
  });
66
70
  return styles;
67
71
  };
72
+ const setBackgroundImage = _ref2 => {
73
+ let {
74
+ src,
75
+ alt,
76
+ backgroundImageResizeMode,
77
+ backgroundImagePosition,
78
+ backgroundImageAlign,
79
+ backgroundImageWidth,
80
+ backgroundImageHeight,
81
+ content
82
+ } = _ref2;
83
+ if (backgroundImageResizeMode === 'contain') {
84
+ const containedViewStyle = {
85
+ ...staticStyles.containedView,
86
+ width: backgroundImageWidth,
87
+ height: backgroundImageHeight,
88
+ ..._backgroundImageStylesMap.default[`${backgroundImagePosition}-${backgroundImageAlign}`]
89
+ };
90
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
91
+ style: staticStyles.containedContainer,
92
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
93
+ style: containedViewStyle,
94
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
95
+ source: src,
96
+ alt: alt,
97
+ style: staticStyles.containedImage,
98
+ accessibilityIgnoresInvertColors: true
99
+ })
100
+ }), content]
101
+ });
102
+ }
103
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ImageBackground.default, {
104
+ source: src,
105
+ alt: alt,
106
+ style: staticStyles.backgroundImageContainer,
107
+ resizeMode: backgroundImageResizeMode,
108
+ children: content
109
+ });
110
+ };
68
111
 
69
112
  /**
70
113
  * A layout utility component. Use Box to create space (padding) around content.
@@ -131,7 +174,7 @@ const selectBoxStyles = (_ref, customGradient) => {
131
174
  * text content is inside a scrollable box, as screens are not scrollable by default and even very
132
175
  * short text will require scrolling on small devices at the highest accessibility text scaling settings.
133
176
  */
134
- const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
177
+ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
135
178
  let {
136
179
  space,
137
180
  horizontal = space,
@@ -150,8 +193,9 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
150
193
  testID,
151
194
  dataSet,
152
195
  customGradient,
196
+ backgroundImage,
153
197
  ...rest
154
- } = _ref2;
198
+ } = _ref3;
155
199
  const props = {
156
200
  accessibilityRole,
157
201
  ...(0, _utils.getA11yPropsFromHtmlTag)(tag, accessibilityRole),
@@ -166,7 +210,41 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
166
210
  paddingBottom: (0, _utils.useSpacingScale)(bottom),
167
211
  ...selectBoxStyles(themeTokens, customGradient)
168
212
  };
169
- const childrenToRender = typeof customGradient === 'function' ? customGradient(styles.colors, styles)(children) : children;
213
+ let content = children;
214
+ if (typeof customGradient === 'function') content = customGradient(styles.colors, styles)(children);
215
+ const {
216
+ src = '',
217
+ alt = '',
218
+ resizeMode = '',
219
+ position = '',
220
+ align = ''
221
+ } = backgroundImage || {};
222
+ const backgroundImageResizeMode = (0, _utils.useResponsiveProp)(resizeMode, 'cover');
223
+ const backgroundImagePosition = (0, _utils.useResponsiveProp)(position, 'none');
224
+ const backgroundImageAlign = (0, _utils.useResponsiveProp)(align, 'stretch');
225
+ const [backgroundImageWidth, setBackgroundImageWidth] = (0, _react.useState)(0);
226
+ const [backgroundImageHeight, setBackgroundImageHeight] = (0, _react.useState)(0);
227
+ if (backgroundImage) content = setBackgroundImage({
228
+ src,
229
+ alt,
230
+ backgroundImageResizeMode,
231
+ backgroundImagePosition,
232
+ backgroundImageAlign,
233
+ backgroundImageWidth,
234
+ backgroundImageHeight,
235
+ content
236
+ });
237
+ (0, _react.useEffect)(() => {
238
+ if (backgroundImage && backgroundImageWidth === 0 && backgroundImageHeight === 0) {
239
+ _Image.default.getSize(src, (width, height) => {
240
+ // Only update the state if the size has changed
241
+ if (width !== backgroundImageWidth || height !== backgroundImageHeight) {
242
+ setBackgroundImageWidth(width);
243
+ setBackgroundImageHeight(height);
244
+ }
245
+ });
246
+ }
247
+ }, [backgroundImage, backgroundImageWidth, backgroundImageHeight, src]);
170
248
  if (scroll) {
171
249
  const scrollProps = typeof scroll === 'object' ? scroll : {};
172
250
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle];
@@ -176,7 +254,7 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
176
254
  testID: testID,
177
255
  dataSet: dataSet,
178
256
  ref: ref,
179
- children: childrenToRender
257
+ children: content
180
258
  });
181
259
  }
182
260
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
@@ -185,7 +263,7 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
185
263
  testID: testID,
186
264
  dataSet: dataSet,
187
265
  ref: ref,
188
- children: childrenToRender
266
+ children: content
189
267
  });
190
268
  });
191
269
  Box.displayName = 'Box';
@@ -273,7 +351,34 @@ Box.propTypes = {
273
351
  /**
274
352
  Use this prop if need to add a custom gradient for mobile
275
353
  */
276
- customGradient: _propTypes.default.func
354
+ customGradient: _propTypes.default.func,
355
+ /**
356
+ * Use this prop to add a background image to the box.
357
+ */
358
+ backgroundImage: _propTypes.default.shape({
359
+ src: _propTypes.default.string.isRequired,
360
+ alt: _propTypes.default.string,
361
+ resizeMode: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])),
362
+ position: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['none', 'bottom', 'left', 'right', 'top'])),
363
+ align: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['start', 'end', 'center', 'stretch']))
364
+ })
277
365
  };
278
366
  var _default = Box;
279
- exports.default = _default;
367
+ exports.default = _default;
368
+ const staticStyles = _StyleSheet.default.create({
369
+ backgroundImageContainer: {
370
+ flex: 1
371
+ },
372
+ containedContainer: {
373
+ flex: 1,
374
+ overflow: 'hidden'
375
+ },
376
+ containedView: {
377
+ zIndex: -1,
378
+ position: 'absolute'
379
+ },
380
+ containedImage: {
381
+ width: '100%',
382
+ height: '100%'
383
+ }
384
+ });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _default = {
8
+ 'top-start': {
9
+ top: 0
10
+ },
11
+ 'top-center': {
12
+ left: 0,
13
+ right: 0,
14
+ marginHorizontal: 'auto'
15
+ },
16
+ 'top-end': {
17
+ top: 0,
18
+ right: 0
19
+ },
20
+ 'right-start': {
21
+ top: 0,
22
+ right: 0
23
+ },
24
+ 'left-start': {
25
+ top: 0
26
+ },
27
+ 'left-center': {
28
+ top: 0,
29
+ bottom: 0,
30
+ marginVertical: 'auto'
31
+ },
32
+ 'none-start': {
33
+ top: 0,
34
+ bottom: 0,
35
+ marginVertical: 'auto'
36
+ },
37
+ 'none-center': {
38
+ top: 0,
39
+ bottom: 0,
40
+ left: 0,
41
+ right: 0,
42
+ margin: 'auto'
43
+ },
44
+ 'right-center': {
45
+ top: 0,
46
+ bottom: 0,
47
+ right: 0,
48
+ marginVertical: 'auto'
49
+ },
50
+ 'none-end': {
51
+ top: 0,
52
+ bottom: 0,
53
+ right: 0,
54
+ marginVertical: 'auto'
55
+ },
56
+ 'bottom-start': {
57
+ bottom: 0,
58
+ left: 0
59
+ },
60
+ 'left-end': {
61
+ bottom: 0,
62
+ left: 0
63
+ },
64
+ 'bottom-center': {
65
+ left: 0,
66
+ right: 0,
67
+ bottom: 0,
68
+ marginHorizontal: 'auto'
69
+ },
70
+ 'bottom-end': {
71
+ right: 0,
72
+ bottom: 0
73
+ },
74
+ 'right-end': {
75
+ right: 0,
76
+ bottom: 0
77
+ },
78
+ 'top-stretch': {
79
+ left: 0,
80
+ right: 0,
81
+ width: '100%'
82
+ },
83
+ 'left-stretch': {
84
+ top: 0,
85
+ bottom: 0,
86
+ height: '100%'
87
+ },
88
+ 'right-stretch': {
89
+ top: 0,
90
+ bottom: 0,
91
+ right: 0,
92
+ height: '100%'
93
+ },
94
+ 'bottom-stretch': {
95
+ left: 0,
96
+ right: 0,
97
+ bottom: 0,
98
+ width: '100%'
99
+ }
100
+ };
101
+ exports.default = _default;
@@ -76,6 +76,14 @@ const selectContentPanelStyles = _ref3 => {
76
76
  marginBottom
77
77
  };
78
78
  };
79
+ const selectControlPanelStyles = _ref4 => {
80
+ let {
81
+ contentPanelBackgroundColor
82
+ } = _ref4;
83
+ return {
84
+ backgroundColor: contentPanelBackgroundColor
85
+ };
86
+ };
79
87
 
80
88
  /**
81
89
  * An item in an `ExpandCollapse` which contains collapsible `children` and a `control` that opens
@@ -87,7 +95,7 @@ const selectContentPanelStyles = _ref3 => {
87
95
  * The panel does not need to be a direct child of the `<ExpandCollapse>` (unless this is required
88
96
  * by the chosen accessibility props for a particular accessibility tools).
89
97
  */
90
- const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) => {
98
+ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref5, ref) => {
91
99
  let {
92
100
  openIds = [],
93
101
  panelId,
@@ -101,7 +109,7 @@ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) =>
101
109
  controlRef,
102
110
  content,
103
111
  ...rest
104
- } = _ref4;
112
+ } = _ref5;
105
113
  const [containerHeight, setContainerHeight] = (0, _react.useState)(null);
106
114
  const isExpanded = openIds.includes(panelId);
107
115
  const selectedProps = selectProps({
@@ -143,14 +151,17 @@ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) =>
143
151
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
144
152
  ref: ref,
145
153
  style: themeTokens,
146
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Control.default, {
147
- ...selectedProps,
148
- isExpanded: isExpanded,
149
- tokens: controlTokens,
150
- variant: variant,
151
- onPress: handleControlPress,
152
- ref: controlRef,
153
- children: control
154
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
155
+ style: selectControlPanelStyles(themeTokens),
156
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Control.default, {
157
+ ...selectedProps,
158
+ isExpanded: isExpanded,
159
+ tokens: controlTokens,
160
+ variant: variant,
161
+ onPress: handleControlPress,
162
+ ref: controlRef,
163
+ children: control
164
+ })
154
165
  }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
155
166
  style: {
156
167
  borderTopColor: themeTokens.expandDividerColor,
@@ -39,6 +39,7 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
39
39
  }
40
40
  }, [onPress]);
41
41
  const handleKeyPress = (0, _react.useCallback)(e => {
42
+ e.preventDefault();
42
43
  if (e.key === 'Enter' || e.key === ' ') {
43
44
  handlePress();
44
45
  }
@@ -11,7 +11,6 @@ var _ThemeProvider = require("../ThemeProvider");
11
11
  var _utils = require("../utils");
12
12
  var _useCopy = _interopRequireDefault(require("../utils/useCopy"));
13
13
  var _dictionary = _interopRequireDefault(require("./dictionary"));
14
- var _jsxRuntime = require("react/jsx-runtime");
15
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
15
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
17
16
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -34,11 +33,11 @@ const PageButton = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
34
33
  const getButtonTokens = buttonState => (0, _utils.selectTokens)('Button', getTokens(buttonState));
35
34
  const activeProps = isActive ? {
36
35
  selected: true,
37
- ..._utils.a11yProps.nonFocusableProps,
36
+ ..._utils.a11yProps.nonFocusableProps
38
37
  // a brute fix for the focus state being stuck on an active item since it becomes non-focusable
39
38
  // (see https://github.com/telus/universal-design-system/pull/577#issuecomment-931344107)
40
- key: 'active-item'
41
39
  } : {};
40
+ const key = isActive ? 'active-item' : undefined;
42
41
  const accessibilityRole = href !== undefined ? 'link' : 'button';
43
42
  const activeLabel = isActive ? ` ${getCopy('currentLabel')}` : '';
44
43
  const accessibilityLabel = `${getCopy('goToLabel')} ${label}${activeLabel}`;
@@ -54,13 +53,13 @@ const PageButton = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
54
53
  hrefAttrs,
55
54
  ...rest
56
55
  };
57
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ButtonBase.default, {
56
+ return /*#__PURE__*/(0, _react.createElement)(_ButtonBase.default, {
58
57
  ref: ref,
59
58
  ...buttonProps,
60
59
  tokens: getButtonTokens,
61
- ...activeProps,
62
- children: label
63
- });
60
+ key: key,
61
+ ...activeProps
62
+ }, label);
64
63
  });
65
64
  PageButton.displayName = 'PageButton';
66
65
  PageButton.propTypes = {
@@ -34,11 +34,13 @@ const selectSkeletonStyles = _ref => {
34
34
  const selectLineStyles = _ref2 => {
35
35
  let {
36
36
  skeletonHeight,
37
- lineWidth
37
+ lineWidth,
38
+ radius
38
39
  } = _ref2;
39
40
  return {
40
41
  width: lineWidth,
41
- height: skeletonHeight
42
+ height: skeletonHeight,
43
+ borderRadius: radius
42
44
  };
43
45
  };
44
46
  const selectShapeStyles = _ref3 => {
@@ -121,7 +123,8 @@ const Skeleton = /*#__PURE__*/(0, _react.forwardRef)((_ref5, ref) => {
121
123
  }
122
124
  return selectLineStyles({
123
125
  skeletonHeight,
124
- lineWidth: getLineWidth()
126
+ lineWidth: getLineWidth(),
127
+ radius: themeTokens.lineRadius
125
128
  });
126
129
  };
127
130
  const renderSkeleton = function () {
@@ -68,17 +68,16 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
68
68
  const testID = `Stack-${divider ? 'Divider' : 'Spacer'}-${index}`;
69
69
  const commonProps = {
70
70
  testID,
71
- key: testID,
72
71
  space
73
72
  };
74
73
  const separator = divider ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Divider.default, {
75
74
  vertical: direction.startsWith('row'),
76
75
  ...dividerProps,
77
76
  ...commonProps
78
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Spacer.default, {
77
+ }, testID) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Spacer.default, {
79
78
  direction: direction.startsWith('row') ? 'row' : 'column',
80
79
  ...commonProps
81
- });
80
+ }, testID);
82
81
  return [...newChildren, separator, item];
83
82
  }, []);
84
83
  return content;
@@ -74,7 +74,7 @@ const selectStepTrackerLabelStyles = (_ref3, themeOptions) => {
74
74
  * ## Usability and A11y guidelines
75
75
  *
76
76
  * Keep in mind that in its current implementation this is not an interactive
77
- * component and cant be used to navigate between steps. The application
77
+ * component and can't be used to navigate between steps. The application
78
78
  * must provide its own navigation mechanism and state control. That is the
79
79
  * main reason the component assumes the `progressbar` role in terms of
80
80
  * accessibility. This also makes it extremely important to make sure you
@@ -118,7 +118,13 @@ const StepTracker = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) => {
118
118
  copy
119
119
  });
120
120
  const stepTrackerLabel = showStepTrackerLabel ? (typeof copy === 'string' ? getCopy(textStepTrackerLabel ?? 1).stepTrackerLabel : getCopy('stepTrackerLabel')).replace('%{stepNumber}', current < steps.length ? current + 1 : steps.length).replace('%{stepCount}', steps.length).replace('%{stepLabel}', current < steps.length ? steps[current] : steps[steps.length - 1]) : '';
121
- const getStepLabel = index => themeTokens.showStepLabel ? (typeof copy === 'string' ? getCopy(textStepTrackerLabel ?? 1).stepLabel : getCopy('stepLabel')).replace('%{stepNumber}', index + 1) : '';
121
+ const getStepLabel = index => {
122
+ if (themeTokens.showStepLabel) {
123
+ var _getCopy;
124
+ return (_getCopy = getCopy(index + 1)) === null || _getCopy === void 0 ? void 0 : _getCopy.stepLabel.replace('%{stepNumber}', index + 1);
125
+ }
126
+ return '';
127
+ };
122
128
  const {
123
129
  themeOptions
124
130
  } = (0, _ThemeProvider.useTheme)();
@@ -11,7 +11,7 @@ var _default = {
11
11
  stepTrackerLabel: 'Step %{stepNumber} of %{stepCount}: %{stepLabel}'
12
12
  },
13
13
  2: {
14
- stepLabel: '%{stepNumber}.',
14
+ stepLabel: 'Step %{stepNumber}',
15
15
  stepTrackerLabel: 'Step %{stepNumber} of %{stepCount}: %{stepLabel}'
16
16
  },
17
17
  3: {
@@ -25,7 +25,7 @@ var _default = {
25
25
  stepTrackerLabel: 'Étape %{stepNumber} sur %{stepCount}: %{stepLabel}'
26
26
  },
27
27
  2: {
28
- stepLabel: '%{stepNumber}.',
28
+ stepLabel: 'Étape %{stepNumber}',
29
29
  stepTrackerLabel: 'Étape %{stepNumber} sur %{stepCount}: %{stepLabel}'
30
30
  },
31
31
  3: {