@telus-uds/components-base 1.12.1 → 1.14.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 (84) hide show
  1. package/CHANGELOG.md +41 -2
  2. package/component-docs.json +888 -66
  3. package/lib/Button/ButtonBase.js +36 -7
  4. package/lib/Button/ButtonGroup.js +7 -0
  5. package/lib/Button/propTypes.js +18 -0
  6. package/lib/Carousel/Carousel.js +69 -12
  7. package/lib/Carousel/CarouselContext.js +17 -11
  8. package/lib/Carousel/CarouselFirstFocus/CarouselFirstFocus.js +73 -0
  9. package/lib/Carousel/CarouselTabs/CarouselTabs.js +70 -0
  10. package/lib/Carousel/CarouselTabs/CarouselTabsPanel.js +95 -0
  11. package/lib/Carousel/CarouselTabs/CarouselTabsPanelItem.js +148 -0
  12. package/lib/Carousel/CarouselTabs/index.js +13 -0
  13. package/lib/Carousel/CarouselThumbnail.js +99 -0
  14. package/lib/Carousel/CarouselThumbnailNavigation.js +87 -0
  15. package/lib/Carousel/dictionary.js +4 -2
  16. package/lib/Carousel/index.js +10 -1
  17. package/lib/Checkbox/CheckboxGroup.js +7 -0
  18. package/lib/Icon/IconText.js +1 -1
  19. package/lib/Link/InlinePressable.js +1 -8
  20. package/lib/Link/LinkBase.js +6 -7
  21. package/lib/List/ListItem.js +1 -1
  22. package/lib/Notification/Notification.js +37 -22
  23. package/lib/Radio/RadioGroup.js +8 -0
  24. package/lib/RadioCard/RadioCardGroup.js +7 -0
  25. package/lib/SkipLink/SkipLink.js +216 -0
  26. package/lib/SkipLink/index.js +13 -0
  27. package/lib/ThemeProvider/ThemeProvider.js +6 -1
  28. package/lib/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  29. package/lib/index.js +9 -0
  30. package/lib-module/Button/ButtonBase.js +35 -7
  31. package/lib-module/Button/ButtonGroup.js +7 -0
  32. package/lib-module/Button/propTypes.js +17 -0
  33. package/lib-module/Carousel/Carousel.js +66 -11
  34. package/lib-module/Carousel/CarouselContext.js +17 -11
  35. package/lib-module/Carousel/CarouselFirstFocus/CarouselFirstFocus.js +51 -0
  36. package/lib-module/Carousel/CarouselTabs/CarouselTabs.js +50 -0
  37. package/lib-module/Carousel/CarouselTabs/CarouselTabsPanel.js +76 -0
  38. package/lib-module/Carousel/CarouselTabs/CarouselTabsPanelItem.js +126 -0
  39. package/lib-module/Carousel/CarouselTabs/index.js +2 -0
  40. package/lib-module/Carousel/CarouselThumbnail.js +85 -0
  41. package/lib-module/Carousel/CarouselThumbnailNavigation.js +66 -0
  42. package/lib-module/Carousel/dictionary.js +4 -2
  43. package/lib-module/Carousel/index.js +2 -1
  44. package/lib-module/Checkbox/CheckboxGroup.js +7 -0
  45. package/lib-module/Icon/IconText.js +1 -1
  46. package/lib-module/Link/InlinePressable.js +1 -8
  47. package/lib-module/Link/LinkBase.js +6 -7
  48. package/lib-module/List/ListItem.js +1 -1
  49. package/lib-module/Notification/Notification.js +38 -23
  50. package/lib-module/Radio/RadioGroup.js +8 -0
  51. package/lib-module/RadioCard/RadioCardGroup.js +7 -0
  52. package/lib-module/SkipLink/SkipLink.js +188 -0
  53. package/lib-module/SkipLink/index.js +2 -0
  54. package/lib-module/ThemeProvider/ThemeProvider.js +5 -1
  55. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  56. package/lib-module/index.js +1 -0
  57. package/package.json +46 -47
  58. package/src/Button/ButtonBase.jsx +28 -9
  59. package/src/Button/ButtonGroup.jsx +6 -0
  60. package/src/Button/propTypes.js +14 -0
  61. package/src/Carousel/Carousel.jsx +68 -10
  62. package/src/Carousel/CarouselContext.jsx +22 -9
  63. package/src/Carousel/CarouselFirstFocus/CarouselFirstFocus.jsx +49 -0
  64. package/src/Carousel/CarouselTabs/CarouselTabs.jsx +37 -0
  65. package/src/Carousel/CarouselTabs/CarouselTabsPanel.jsx +69 -0
  66. package/src/Carousel/CarouselTabs/CarouselTabsPanelItem.jsx +119 -0
  67. package/src/Carousel/CarouselTabs/index.js +3 -0
  68. package/src/Carousel/CarouselThumbnail.jsx +77 -0
  69. package/src/Carousel/CarouselThumbnailNavigation.jsx +53 -0
  70. package/src/Carousel/dictionary.js +4 -2
  71. package/src/Carousel/index.js +1 -0
  72. package/src/Checkbox/CheckboxGroup.jsx +7 -0
  73. package/src/Icon/IconText.jsx +1 -1
  74. package/src/Link/InlinePressable.jsx +2 -8
  75. package/src/Link/LinkBase.jsx +8 -17
  76. package/src/List/ListItem.jsx +1 -1
  77. package/src/Notification/Notification.jsx +35 -20
  78. package/src/Radio/RadioGroup.jsx +7 -0
  79. package/src/RadioCard/RadioCardGroup.jsx +6 -0
  80. package/src/SkipLink/SkipLink.jsx +179 -0
  81. package/src/SkipLink/index.js +3 -0
  82. package/src/ThemeProvider/ThemeProvider.jsx +7 -1
  83. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +6 -0
  84. package/src/index.js +1 -0
@@ -92,6 +92,7 @@ const RadioCardGroup = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
92
92
  legend,
93
93
  tooltip,
94
94
  hint,
95
+ hintPosition = 'inline',
95
96
  validation,
96
97
  feedback,
97
98
  initialCheckedId,
@@ -136,6 +137,7 @@ const RadioCardGroup = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
136
137
  legend: legend,
137
138
  tooltip: tooltip,
138
139
  hint: hint,
140
+ hintPosition: hintPosition,
139
141
  space: fieldSpace,
140
142
  feedback: feedback,
141
143
  inactive: inactive || readOnly,
@@ -221,6 +223,11 @@ RadioCardGroup.propTypes = { ...selectedSystemPropTypes,
221
223
  */
222
224
  hint: _propTypes.default.string,
223
225
 
226
+ /**
227
+ * Position of the hint relative to label. Use `below` to display a larger hint below the label.
228
+ */
229
+ hintPosition: _propTypes.default.oneOf(['inline', 'below']),
230
+
224
231
  /**
225
232
  * Optional tooltip text content to include alongside the legend and hint.
226
233
  */
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = _interopRequireWildcard(require("react"));
9
+
10
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
11
+
12
+ var _Pressable = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Pressable"));
13
+
14
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
15
+
16
+ var _Text = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Text"));
17
+
18
+ var _propTypes = _interopRequireDefault(require("prop-types"));
19
+
20
+ var _ThemeProvider = require("../ThemeProvider");
21
+
22
+ var _utils = require("../utils");
23
+
24
+ var _jsxRuntime = require("react/jsx-runtime");
25
+
26
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
+
28
+ 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); }
29
+
30
+ 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; }
31
+
32
+ const [selectProps, selectedSystemPropTypes] = (0, _utils.selectSystemProps)([_utils.a11yProps, _utils.linkProps]); // ensure explicit selection of tokens
33
+
34
+ const selectStyles = _ref => {
35
+ let {
36
+ backgroundColor,
37
+ outlineColor,
38
+ outlineOffset,
39
+ outlineStyle,
40
+ outlineWidth,
41
+ paddingHorizontal,
42
+ paddingVertical,
43
+ borderRadius
44
+ } = _ref;
45
+ return {
46
+ backgroundColor,
47
+ outlineColor,
48
+ outlineOffset,
49
+ outlineStyle,
50
+ outlineWidth,
51
+ paddingHorizontal,
52
+ paddingVertical,
53
+ borderRadius
54
+ };
55
+ };
56
+
57
+ const selectTextStyles = _ref2 => {
58
+ let {
59
+ color
60
+ } = _ref2;
61
+ return {
62
+ color
63
+ };
64
+ };
65
+ /**
66
+ * A generic Skip link component, unstyled by default.
67
+ * A Skip link component help keyboard-only users, screen reader users to skip
68
+ * sections and navigate to the content they want.
69
+ *
70
+ * ## Component API
71
+ *
72
+ * For common uses, pass a `href` that is a # link to a DOM id that can be skipped to (web only).
73
+ *
74
+ * The element with this ID should be focusable, e.g. `<Box nativeID="skip-target" focusable>`.
75
+ *
76
+ * Other custom behaviour may be set by passing an `onPress` function, and routers may be integrated
77
+ * in the same way as other navigation-related components by passing a `LinkRouter`; but a # anchor
78
+ * href on web and/or a `targetRef` for cross-platform applications is the recommended approach.
79
+ *
80
+ * ## Visible styling
81
+ *
82
+ * When focused, the skip link shows as a visible element similar to a simplified ButtonLink using
83
+ * UDS theming. The `tokens` prop may be used to override these styles.
84
+ *
85
+ * To control the background of a skip link, the following tokens can be used:
86
+ *
87
+ * - `backgroundColor`
88
+ * *
89
+ * In order to control the color of the skip link text, the following tokens can be used:
90
+ *
91
+ * - `color`
92
+ *
93
+ * ### Padding
94
+ *
95
+ * The following padding tokens can be used:
96
+ *
97
+ * - `paddingHorizontal`
98
+ * - `paddingVertical`
99
+ *
100
+ * ### Outline
101
+ *
102
+ * The following tokens to control the outline:
103
+ *
104
+ * - `outlineColor`
105
+ * - `outlineOffset`
106
+ * - `outlineStyle`
107
+ * - `outlineWidth`
108
+ *
109
+ * ## Usability and A11y guidelines
110
+ *
111
+ * - The skip link component is visually hidden until a keyboard press activates it.
112
+ * - Usually, you should place the skip link immediately after the opening <body> tag.
113
+ * - This lets users bypass top-level navigation links and jump to the main content on a page.
114
+ * - Also consider using SkipLink before a complex feature containing many focusable elements.
115
+ *
116
+ * ## Accessibility
117
+ *
118
+ * Skip link supports all the common a11y and link props.
119
+ */
120
+
121
+
122
+ const SkipLink = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
123
+ let {
124
+ tokens,
125
+ variant,
126
+ href,
127
+ children,
128
+ ...rawRest
129
+ } = _ref3;
130
+
131
+ const {
132
+ onPress,
133
+ ...rest
134
+ } = _utils.clickProps.toPressProps(rawRest);
135
+
136
+ const getTokens = (0, _ThemeProvider.useThemeTokensCallback)('SkipLink', tokens, variant);
137
+ const defaultTokens = getTokens();
138
+
139
+ const resolveLinkTokens = pressState => (0, _utils.resolvePressableTokens)(defaultTokens, pressState);
140
+
141
+ const handlePress = event => {
142
+ if (typeof onPress === 'function') onPress(event); // TODO - support native apps with something based on refs and/or setAccessibilityFocus
143
+ };
144
+
145
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pressable.default, {
146
+ ref: ref,
147
+ accessibilityRole: "link",
148
+ onPress: handlePress,
149
+ href: href,
150
+ style: _ref4 => {
151
+ let {
152
+ focused: focus
153
+ } = _ref4;
154
+ const themeTokens = getTokens({
155
+ focus
156
+ });
157
+ const skipLinkStyle = selectStyles(themeTokens);
158
+ return [staticStyles.absolute, skipLinkStyle, !focus && staticStyles.hidden];
159
+ },
160
+ ...selectProps(rest),
161
+ children: pressState => {
162
+ const themeTokens = resolveLinkTokens(pressState);
163
+ const textStyles = selectTextStyles(themeTokens);
164
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Text.default, {
165
+ style: [textStyles, staticStyles.baseline],
166
+ children: children
167
+ });
168
+ }
169
+ });
170
+ });
171
+ SkipLink.displayName = 'SkipLink';
172
+ SkipLink.propTypes = { ...selectedSystemPropTypes,
173
+
174
+ /**
175
+ * The text content shown or read out when the SkipLink is focused, usually a string.
176
+ */
177
+ children: _propTypes.default.node,
178
+
179
+ /**
180
+ * The target to skip to. Usually an anchor link to a section id (e.g. href="#main-section").
181
+ */
182
+ href: _propTypes.default.string,
183
+ tokens: (0, _utils.getTokensPropType)('SkipLink'),
184
+ variant: _utils.variantProp.propType
185
+ };
186
+
187
+ const staticStyles = _StyleSheet.default.create({
188
+ baseline: {
189
+ alignSelf: 'baseline'
190
+ },
191
+ absolute: {
192
+ margin: 0,
193
+ position: 'absolute',
194
+ top: 0,
195
+ left: 0
196
+ },
197
+ hidden: {
198
+ overflow: 'hidden',
199
+ ..._Platform.default.select({
200
+ web: {
201
+ clip: 'rect(0 0 0 0)',
202
+ clipPath: 'inset(50%)'
203
+ },
204
+ default: {
205
+ // width / height of 0 would make it non-focusable
206
+ height: 1,
207
+ width: 1,
208
+ opacity: 0
209
+ }
210
+ })
211
+ }
212
+ });
213
+
214
+ var _default = (0, _utils.withLinkRouter)(SkipLink);
215
+
216
+ exports.default = _default;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _SkipLink = _interopRequireDefault(require("./SkipLink"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ var _default = _SkipLink.default;
13
+ exports.default = _default;
@@ -11,6 +11,8 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
11
11
 
12
12
  var _utils = require("./utils");
13
13
 
14
+ var _responsiveProps = _interopRequireDefault(require("../utils/props/responsiveProps"));
15
+
14
16
  var _jsxRuntime = require("react/jsx-runtime");
15
17
 
16
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -65,9 +67,12 @@ ThemeProvider.propTypes = {
65
67
  * - `forceAbsoluteFontSizing`: available on web only; when set to true, allows
66
68
  * using absolute font sizing (in pixels, doesn't scale) instead of the
67
69
  * relative sizing (in `rem`, scales depending on the browser settings)
70
+ * - `contentMaxWidth`: allows configuration of the content max width to be used in components
71
+ * such as Footnote and Notification to avoid content to stretch width more then the page's width
68
72
  */
69
73
  themeOptions: _propTypes.default.shape({
70
- forceAbsoluteFontSizing: _propTypes.default.bool
74
+ forceAbsoluteFontSizing: _propTypes.default.bool,
75
+ contentMaxWidth: _responsiveProps.default.getTypeOptionallyByViewport(_propTypes.default.number)
71
76
  })
72
77
  };
73
78
  var _default = ThemeProvider;
@@ -49,6 +49,7 @@ const ToggleSwitchGroup = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
49
49
  inactive = false,
50
50
  feedback,
51
51
  hint,
52
+ hintPosition = 'inline',
52
53
  tooltip,
53
54
  legend,
54
55
  name: inputGroupName,
@@ -134,6 +135,7 @@ const ToggleSwitchGroup = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
134
135
  legend: legend,
135
136
  tooltip: tooltip,
136
137
  hint: hint,
138
+ hintPosition: hintPosition,
137
139
  space: fieldSpace,
138
140
  feedback: feedback,
139
141
  inactive: inactive,
@@ -216,6 +218,11 @@ ToggleSwitchGroup.propTypes = { ...selectedSystemPropTypes,
216
218
  */
217
219
  hint: _propTypes.default.string,
218
220
 
221
+ /**
222
+ * Position of the hint relative to label. Use `below` to display a larger hint below the label.
223
+ */
224
+ hintPosition: _propTypes.default.oneOf(['inline', 'below']),
225
+
219
226
  /**
220
227
  * Optional tooltip text content to include alongside the legend and hint.
221
228
  */
package/lib/index.js CHANGED
@@ -34,6 +34,7 @@ var _exportNames = {
34
34
  Select: true,
35
35
  SideNav: true,
36
36
  Skeleton: true,
37
+ SkipLink: true,
37
38
  Spacer: true,
38
39
  StackView: true,
39
40
  StepTracker: true,
@@ -249,6 +250,12 @@ Object.defineProperty(exports, "Skeleton", {
249
250
  return _Skeleton.default;
250
251
  }
251
252
  });
253
+ Object.defineProperty(exports, "SkipLink", {
254
+ enumerable: true,
255
+ get: function () {
256
+ return _SkipLink.default;
257
+ }
258
+ });
252
259
  Object.defineProperty(exports, "Spacer", {
253
260
  enumerable: true,
254
261
  get: function () {
@@ -524,6 +531,8 @@ var _SideNav = _interopRequireDefault(require("./SideNav"));
524
531
 
525
532
  var _Skeleton = _interopRequireDefault(require("./Skeleton"));
526
533
 
534
+ var _SkipLink = _interopRequireDefault(require("./SkipLink"));
535
+
527
536
  var _Spacer = _interopRequireDefault(require("./Spacer"));
528
537
 
529
538
  var _StackView = _interopRequireWildcard(require("./StackView"));
@@ -7,6 +7,7 @@ import Platform from "react-native-web/dist/exports/Platform";
7
7
  import { applyTextStyles, applyShadowToken, applyOuterBorder, useTheme } from '../ThemeProvider';
8
8
  import buttonPropTypes from './propTypes';
9
9
  import { a11yProps, clickProps, focusHandlerProps, getCursorStyle, linkProps, resolvePressableState, resolvePressableTokens, selectSystemProps, viewProps, wrapStringsInText, withLinkRouter } from '../utils';
10
+ import { IconText } from '../Icon';
10
11
  import { jsx as _jsx } from "react/jsx-runtime";
11
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, linkProps, viewProps]);
12
13
 
@@ -167,7 +168,18 @@ const selectWebOnlyStyles = (inactive, themeTokens, _ref7) => {
167
168
  });
168
169
  };
169
170
 
170
- const ButtonBase = /*#__PURE__*/forwardRef((_ref8, ref) => {
171
+ const selectItemIconTokens = _ref8 => {
172
+ let {
173
+ color,
174
+ iconSize
175
+ } = _ref8;
176
+ return {
177
+ size: iconSize,
178
+ color
179
+ };
180
+ };
181
+
182
+ const ButtonBase = /*#__PURE__*/forwardRef((_ref9, ref) => {
171
183
  let {
172
184
  id,
173
185
  href,
@@ -178,8 +190,11 @@ const ButtonBase = /*#__PURE__*/forwardRef((_ref8, ref) => {
178
190
  // alias for inactive
179
191
  inactive = disabled,
180
192
  selected = false,
193
+ icon,
194
+ iconPosition = icon ? 'left' : undefined,
195
+ iconProps,
181
196
  ...rawRest
182
- } = _ref8;
197
+ } = _ref9;
183
198
  const {
184
199
  onPress,
185
200
  ...rest
@@ -216,10 +231,15 @@ const ButtonBase = /*#__PURE__*/forwardRef((_ref8, ref) => {
216
231
  const themeTokens = resolveButtonTokens(pressableState);
217
232
  const containerStyles = selectInnerContainerStyles(themeTokens);
218
233
  const borderStyles = selectBorderStyles(themeTokens);
219
- const textStyles = [selectTextStyles(themeTokens, themeOptions), staticStyles.text]; // If the container has a width set, fill it instead of sizing from content.
234
+ const textStyles = [selectTextStyles(themeTokens, themeOptions), staticStyles.text];
235
+ const iconTokens = selectItemIconTokens(themeTokens);
236
+ const {
237
+ iconSpace
238
+ } = themeTokens; // If the container has a width set, fill it instead of sizing from content.
220
239
  // If in future we support text alignments other than center, add here.
221
240
 
222
241
  const stretchStyles = themeTokens.width ? staticStyles.stretch : staticStyles.align;
242
+ const IconComponent = icon || themeTokens.icon;
223
243
  return /*#__PURE__*/_jsx(View, {
224
244
  id: id,
225
245
  style: [containerStyles, borderStyles, stretchStyles, staticStyles.row, Platform.select({
@@ -230,10 +250,18 @@ const ButtonBase = /*#__PURE__*/forwardRef((_ref8, ref) => {
230
250
  transition: 'background-color 200ms, border-color 200ms'
231
251
  }
232
252
  })],
233
- children: wrapStringsInText(typeof children === 'function' ? children({ ...resolvePressableState(pressableState, extraButtonState),
234
- textStyles
235
- }) : children, {
236
- style: textStyles
253
+ children: /*#__PURE__*/_jsx(IconText, {
254
+ icon: IconComponent,
255
+ iconPosition: iconPosition,
256
+ space: iconSpace,
257
+ iconProps: { ...iconProps,
258
+ tokens: iconTokens
259
+ },
260
+ children: wrapStringsInText(typeof children === 'function' ? children({ ...resolvePressableState(pressableState, extraButtonState),
261
+ textStyles
262
+ }) : children, {
263
+ style: textStyles
264
+ })
237
265
  })
238
266
  });
239
267
  }
@@ -26,6 +26,7 @@ const ButtonGroup = /*#__PURE__*/forwardRef((_ref, ref) => {
26
26
  legend,
27
27
  tooltip,
28
28
  hint,
29
+ hintPosition = 'inline',
29
30
  validation,
30
31
  feedback,
31
32
  name: inputGroupName,
@@ -77,6 +78,7 @@ const ButtonGroup = /*#__PURE__*/forwardRef((_ref, ref) => {
77
78
  legend: legend,
78
79
  tooltip: tooltip,
79
80
  hint: hint,
81
+ hintPosition: hintPosition,
80
82
  space: fieldSpace,
81
83
  feedback: feedback,
82
84
  readOnly: readOnly,
@@ -208,6 +210,11 @@ ButtonGroup.propTypes = { ...selectedSystemPropTypes,
208
210
  */
209
211
  hint: PropTypes.string,
210
212
 
213
+ /**
214
+ * Position of the hint relative to label. Use `below` to display a larger hint below the label.
215
+ */
216
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
217
+
211
218
  /**
212
219
  * Optional tooltip text content to include alongside the legend and hint.
213
220
  */
@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import ABBPropTypes from 'airbnb-prop-types';
3
3
  import { variantProp, getTokensPropType } from '../utils/props';
4
4
  import A11yText from '../A11yText';
5
+ import { iconComponentPropTypes } from '../Icon';
5
6
  export const textAndA11yText = ABBPropTypes.childrenOf(PropTypes.oneOfType([ABBPropTypes.elementType(A11yText), PropTypes.string]));
6
7
  const buttonPropTypes = {
7
8
  tokens: getTokensPropType('Button'),
@@ -31,6 +32,22 @@ const buttonPropTypes = {
31
32
  * Function called when the button is pressed. Required unless the button has a href.
32
33
  */
33
34
  onPress: PropTypes.func,
35
+
36
+ /**
37
+ * Optional variant that may be passed down to the link's icon if there is one
38
+ */
39
+ iconProps: PropTypes.exact(iconComponentPropTypes),
40
+
41
+ /**
42
+ * When `icon` is provided, use `iconPosition` to place the Icon to the left or right side of the button.
43
+ */
44
+ iconPosition: PropTypes.oneOf(['left', 'right']),
45
+
46
+ /**
47
+ * A function component for an SVG icon to render inside the link. Inherits size and color from
48
+ * the link and any Typography the link is nested inside.
49
+ */
50
+ icon: PropTypes.func,
34
51
  variant: variantProp.propType
35
52
  };
36
53
  export default buttonPropTypes;
@@ -7,12 +7,15 @@ import Platform from "react-native-web/dist/exports/Platform";
7
7
  import PropTypes from 'prop-types';
8
8
  import { useThemeTokens } from '../ThemeProvider';
9
9
  import { useViewport } from '../ViewportProvider';
10
- import { getTokensPropType, getA11yPropsFromHtmlTag, layoutTags, variantProp, selectSystemProps, a11yProps, viewProps, useCopy } from '../utils';
10
+ import { getTokensPropType, getA11yPropsFromHtmlTag, layoutTags, variantProp, selectSystemProps, a11yProps, viewProps, useCopy, unpackFragment } from '../utils';
11
11
  import { useA11yInfo } from '../A11yInfoProvider';
12
12
  import { CarouselProvider } from './CarouselContext';
13
13
  import CarouselItem from './CarouselItem';
14
14
  import IconButton from '../IconButton';
15
- import CarouselStepTracker from './CarouselStepTracker/CarouselStepTracker';
15
+ import SkipLink from '../SkipLink';
16
+ import A11yText from '../A11yText';
17
+ import CarouselStepTracker from './CarouselStepTracker';
18
+ import CarouselThumbnailNavigation from './CarouselThumbnailNavigation';
16
19
  import dictionary from './dictionary';
17
20
  import { jsx as _jsx } from "react/jsx-runtime";
18
21
  import { jsxs as _jsxs } from "react/jsx-runtime";
@@ -134,11 +137,18 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
134
137
  onAnimationStart,
135
138
  onAnimationEnd,
136
139
  onIndexChanged,
140
+ skipLinkHref,
141
+ refocus,
142
+ title = 'carousel',
137
143
  springConfig = undefined,
138
- panelNavigation = /*#__PURE__*/_jsx(CarouselStepTracker, {}),
144
+ thumbnails = undefined,
145
+ panelNavigation = thumbnails ? /*#__PURE__*/_jsx(CarouselThumbnailNavigation, {
146
+ thumbnails: thumbnails
147
+ }) : /*#__PURE__*/_jsx(CarouselStepTracker, {}),
139
148
  tag = 'ul',
140
- accessibilityRole = 'adjustable',
141
- accessibilityLabel = 'carousel',
149
+ accessibilityRole,
150
+ accessibilityLabel = title,
151
+ accessibilityLiveRegion = 'polite',
142
152
  copy,
143
153
  ...rest
144
154
  } = _ref;
@@ -167,7 +177,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
167
177
  dictionary,
168
178
  copy
169
179
  });
170
- const childrenArray = React.Children.toArray(children);
180
+ const childrenArray = unpackFragment(children);
171
181
  const systemProps = selectProps({ ...rest,
172
182
  accessibilityRole,
173
183
  accessibilityLabel,
@@ -186,6 +196,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
186
196
  width: 0
187
197
  });
188
198
  const [previousNextNavigationButtonWidth, setPreviousNextNavigationButtonWidth] = React.useState(0);
199
+ const firstFocusRef = React.useRef(null);
189
200
  const pan = React.useRef(new Animated.ValueXY()).current;
190
201
  const animatedX = React.useRef(0);
191
202
  const animatedY = React.useRef(0);
@@ -285,10 +296,13 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
285
296
  return calcDelta;
286
297
  }, [containerLayout.width, activeIndex, animate, children.length, onIndexChanged]);
287
298
  const fixOffsetAndGo = React.useCallback(delta => {
299
+ var _firstFocusRef$curren;
300
+
288
301
  updateOffset();
289
302
  handleAnimationStart(activeIndex);
290
303
  updateIndex(delta);
291
- }, [updateIndex, updateOffset, activeIndex, handleAnimationStart]);
304
+ if (refocus) (_firstFocusRef$curren = firstFocusRef.current) === null || _firstFocusRef$curren === void 0 ? void 0 : _firstFocusRef$curren.focus();
305
+ }, [updateIndex, updateOffset, activeIndex, handleAnimationStart, refocus]);
292
306
  const goToNeighboring = React.useCallback(function () {
293
307
  let toPrev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
294
308
  fixOffsetAndGo(toPrev ? -1 : 1);
@@ -371,17 +385,20 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
371
385
  raised: true
372
386
  };
373
387
  const getCopyWithPlaceholders = React.useCallback(copyKey => {
374
- const copyText = getCopy(copyKey).replace(/%\{itemLabel\}/g, itemLabel).replace(/%\{stepNumber\}/g, activeIndex + 1).replace(/%\{stepCount\}/g, childrenArray.length); // First word might be a lowercase placeholder: capitalize the first letter
388
+ const copyText = getCopy(copyKey).replace(/%\{title\}/g, title).replace(/%\{itemLabel\}/g, itemLabel).replace(/%\{stepNumber\}/g, activeIndex + 1).replace(/%\{stepCount\}/g, childrenArray.length); // First word might be a lowercase placeholder: capitalize the first letter
375
389
 
376
390
  return "".concat(copyText[0].toUpperCase()).concat(copyText.slice(1));
377
- }, [activeIndex, childrenArray.length, itemLabel, getCopy]);
391
+ }, [activeIndex, childrenArray.length, itemLabel, getCopy, title]);
378
392
  return /*#__PURE__*/_jsxs(CarouselProvider, {
379
393
  activeIndex: activeIndex,
380
- totalItems: childrenArray.length,
381
- width: containerLayout.width,
382
394
  goTo: goTo,
383
395
  getCopyWithPlaceholders: getCopyWithPlaceholders,
396
+ itemLabel: itemLabel,
397
+ totalItems: childrenArray.length,
384
398
  themeTokens: themeTokens,
399
+ firstFocusRef: firstFocusRef,
400
+ refocus: refocus,
401
+ width: containerLayout.width,
385
402
  children: [/*#__PURE__*/_jsxs(View, {
386
403
  style: staticStyles.root,
387
404
  onLayout: onContainerLayout,
@@ -397,6 +414,17 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
397
414
  variant: previousNextIconButtonVariants,
398
415
  accessibilityLabel: getCopyWithPlaceholders('iconButtonLabel').replace('%{targetStep}', activeIndex)
399
416
  })
417
+ }), Boolean(skipLinkHref) && /*#__PURE__*/_jsx(SkipLink, {
418
+ ref: firstFocusRef,
419
+ href: skipLinkHref,
420
+ children: getCopyWithPlaceholders('skipLink')
421
+ }), /*#__PURE__*/_jsx(A11yText // Read the current slide position to screen readers on slide.
422
+ // If it's set to refocus and doesn't have a SkipLink to focus to, focus this.
423
+ , {
424
+ ref: !skipLinkHref && refocus ? firstFocusRef : null,
425
+ accessibilityLiveRegion: !skipLinkHref && refocus ? undefined : 'polite',
426
+ focusable: !skipLinkHref && refocus,
427
+ text: getCopyWithPlaceholders('stepTrackerLabel')
400
428
  }), /*#__PURE__*/_jsx(View, {
401
429
  style: selectContainerStyles(containerLayout.width),
402
430
  children: /*#__PURE__*/_jsx(Animated.View, {
@@ -409,6 +437,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref, ref) => {
409
437
  }]),
410
438
  ...panResponder.panHandlers,
411
439
  ...getA11yPropsFromHtmlTag(tag),
440
+ // In iframes on Mac (e.g. in Storybook), this content may be misread or read twice.
441
+ // This is a known Voiceover bug: https://github.com/phetsims/a11y-research/issues/132
442
+ accessibilityLiveRegion: accessibilityLiveRegion,
412
443
  children: childrenArray.map((element, index) => {
413
444
  const hidden = !isAnimating && index !== activeIndex;
414
445
  const clonedElement = /*#__PURE__*/React.cloneElement(element, {
@@ -468,6 +499,15 @@ Carousel.propTypes = { ...selectedSystemPropTypes,
468
499
  */
469
500
  springConfig: PropTypes.object,
470
501
 
502
+ /**
503
+ * An array of objects containing information on the thumbnails to be rendered as navigation panel
504
+ */
505
+ thumbnails: PropTypes.arrayOf(PropTypes.shape({
506
+ accessibilityLabel: PropTypes.string,
507
+ alt: PropTypes.string,
508
+ src: PropTypes.string
509
+ })),
510
+
471
511
  /**
472
512
  * Minimal part of slide width must be swiped for changing index.
473
513
  * Otherwise animation restore current slide. Default value 0.2 means that 20% must be swiped for change index
@@ -497,6 +537,21 @@ Carousel.propTypes = { ...selectedSystemPropTypes,
497
537
  */
498
538
  onIndexChanged: PropTypes.func,
499
539
 
540
+ /**
541
+ * If this is a complex carousel with a lot of focusable content, pass a href for a skip link. Typically, this will be an anchor link
542
+ * with the ID of a focusable element immediately after the Carousel, e.g. `'#section-2-heading'`.
543
+ */
544
+ skipLinkHref: PropTypes.string,
545
+
546
+ /**
547
+ * If true, whenever a new slide comes into view, the focus of the Carousel switches to the start.
548
+ *
549
+ * Pass this as true when using carousel items that contain interactive content, so a user can easily tab into that content.
550
+ *
551
+ * If skipLinkHref is passed, the focus target will be the SkipLink; if not, it'll be an empty element before the slide content.
552
+ */
553
+ refocus: PropTypes.bool,
554
+
500
555
  /**
501
556
  * Use this to render a custom panel navigation element instead of the default StepTracker's based navigation
502
557
  * You can make use of `useCarousel` within your custom panel navigation component to hook into various Carousel states such as: