@telus-uds/components-base 1.59.2 → 1.61.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 (90) hide show
  1. package/CHANGELOG.md +26 -2
  2. package/component-docs.json +526 -76
  3. package/lib/Autocomplete/Autocomplete.js +483 -0
  4. package/lib/Autocomplete/Loading.js +51 -0
  5. package/lib/Autocomplete/Suggestions.js +85 -0
  6. package/lib/Autocomplete/constants.js +14 -0
  7. package/lib/Autocomplete/dictionary.js +19 -0
  8. package/lib/Autocomplete/index.js +13 -0
  9. package/lib/Button/ButtonLink.js +7 -3
  10. package/lib/ExpandCollapse/Panel.js +7 -0
  11. package/lib/IconButton/IconButton.js +8 -0
  12. package/lib/Link/ChevronLink.js +9 -2
  13. package/lib/Link/LinkBase.js +14 -0
  14. package/lib/Link/TextButton.js +12 -1
  15. package/lib/Listbox/GroupControl.js +121 -0
  16. package/lib/Listbox/Listbox.js +198 -0
  17. package/lib/Listbox/ListboxGroup.js +142 -0
  18. package/lib/Listbox/ListboxItem.js +97 -0
  19. package/lib/Listbox/ListboxOverlay.js +106 -0
  20. package/lib/Listbox/PressableItem.js +0 -2
  21. package/lib/Listbox/index.js +5 -24
  22. package/lib/Pagination/dictionary.js +3 -3
  23. package/lib/Progress/ProgressBarBackground.js +2 -2
  24. package/lib/SideNav/Item.js +15 -5
  25. package/lib/Tags/Tags.js +6 -1
  26. package/lib/TextInput/TextInputBase.js +2 -0
  27. package/lib/Tooltip/Tooltip.js +6 -1
  28. package/lib/Tooltip/Tooltip.native.js +6 -1
  29. package/lib/Tooltip/shared.js +5 -0
  30. package/lib/index.js +17 -13
  31. package/lib/utils/useOverlaidPosition.js +6 -4
  32. package/lib-module/Autocomplete/Autocomplete.js +448 -0
  33. package/lib-module/Autocomplete/Loading.js +36 -0
  34. package/lib-module/Autocomplete/Suggestions.js +66 -0
  35. package/lib-module/Autocomplete/constants.js +4 -0
  36. package/lib-module/Autocomplete/dictionary.js +12 -0
  37. package/lib-module/Autocomplete/index.js +2 -0
  38. package/lib-module/Button/ButtonLink.js +4 -1
  39. package/lib-module/ExpandCollapse/Panel.js +7 -0
  40. package/lib-module/IconButton/IconButton.js +8 -0
  41. package/lib-module/Link/ChevronLink.js +10 -3
  42. package/lib-module/Link/LinkBase.js +14 -0
  43. package/lib-module/Link/TextButton.js +11 -1
  44. package/lib-module/Listbox/GroupControl.js +102 -0
  45. package/lib-module/Listbox/Listbox.js +172 -0
  46. package/lib-module/Listbox/ListboxGroup.js +117 -0
  47. package/lib-module/Listbox/ListboxItem.js +71 -0
  48. package/lib-module/Listbox/ListboxOverlay.js +80 -0
  49. package/lib-module/Listbox/PressableItem.js +0 -2
  50. package/lib-module/Listbox/index.js +2 -2
  51. package/lib-module/Pagination/dictionary.js +3 -3
  52. package/lib-module/Progress/ProgressBarBackground.js +2 -2
  53. package/lib-module/SideNav/Item.js +15 -5
  54. package/lib-module/Tags/Tags.js +6 -1
  55. package/lib-module/TextInput/TextInputBase.js +2 -0
  56. package/lib-module/Tooltip/Tooltip.js +6 -1
  57. package/lib-module/Tooltip/Tooltip.native.js +6 -1
  58. package/lib-module/Tooltip/shared.js +5 -0
  59. package/lib-module/index.js +2 -1
  60. package/lib-module/utils/useOverlaidPosition.js +5 -4
  61. package/package.json +5 -3
  62. package/src/Autocomplete/Autocomplete.jsx +411 -0
  63. package/src/Autocomplete/Loading.jsx +18 -0
  64. package/src/Autocomplete/Suggestions.jsx +54 -0
  65. package/src/Autocomplete/constants.js +4 -0
  66. package/src/Autocomplete/dictionary.js +12 -0
  67. package/src/Autocomplete/index.js +3 -0
  68. package/src/Button/ButtonLink.jsx +4 -1
  69. package/src/ExpandCollapse/Panel.jsx +11 -1
  70. package/src/IconButton/IconButton.jsx +7 -0
  71. package/src/Link/ChevronLink.jsx +10 -3
  72. package/src/Link/LinkBase.jsx +11 -0
  73. package/src/Link/TextButton.jsx +8 -2
  74. package/src/Listbox/GroupControl.jsx +93 -0
  75. package/src/Listbox/Listbox.jsx +165 -0
  76. package/src/Listbox/ListboxGroup.jsx +120 -0
  77. package/src/Listbox/ListboxItem.jsx +76 -0
  78. package/src/Listbox/ListboxOverlay.jsx +82 -0
  79. package/src/Listbox/PressableItem.jsx +0 -2
  80. package/src/Listbox/index.js +3 -2
  81. package/src/Pagination/dictionary.js +3 -3
  82. package/src/Progress/ProgressBarBackground.jsx +2 -2
  83. package/src/SideNav/Item.jsx +13 -5
  84. package/src/Tags/Tags.jsx +5 -1
  85. package/src/TextInput/TextInputBase.jsx +2 -0
  86. package/src/Tooltip/Tooltip.jsx +16 -2
  87. package/src/Tooltip/Tooltip.native.jsx +15 -2
  88. package/src/Tooltip/shared.js +4 -0
  89. package/src/index.js +2 -1
  90. package/src/utils/useOverlaidPosition.js +6 -5
@@ -0,0 +1,448 @@
1
+ /* eslint-disable react/require-default-props */
2
+ import React, { forwardRef, useRef, useState } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import Dimensions from "react-native-web/dist/exports/Dimensions";
5
+ import Platform from "react-native-web/dist/exports/Platform";
6
+ import View from "react-native-web/dist/exports/View";
7
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
8
+ import throttle from 'lodash.throttle';
9
+ import matchAll from 'string.prototype.matchall';
10
+ import { inputSupportsProps, selectSystemProps, textInputProps, textInputHandlerProps, useCopy, htmlAttrs, useOverlaidPosition, useSafeLayoutEffect } from '../utils';
11
+ import { useThemeTokens } from '../ThemeProvider';
12
+ import Listbox from '../Listbox';
13
+ import Typography from '../Typography';
14
+ import { TextInput } from '../TextInput';
15
+ import InputSupports from '../InputSupports';
16
+ import Loading from './Loading';
17
+ import Suggestions from './Suggestions';
18
+ import { DEFAULT_MAX_SUGGESTIONS, DEFAULT_MIN_TO_SUGGESTION, INPUT_LEFT_PADDING, MIN_LISTBOX_WIDTH } from './constants';
19
+ import dictionary from './dictionary';
20
+ import { jsx as _jsx } from "react/jsx-runtime";
21
+ import { Fragment as _Fragment } from "react/jsx-runtime";
22
+ import { jsxs as _jsxs } from "react/jsx-runtime";
23
+ const staticStyles = StyleSheet.create({
24
+ container: {
25
+ zIndex: 100,
26
+ flexDirection: 'column',
27
+ justifyContent: 'flex-start',
28
+ flexGrow: 0,
29
+ flexShrink: 0
30
+ }
31
+ });
32
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, inputSupportsProps, textInputHandlerProps, textInputProps]); // Returns JSX to display a bold string `str` with unbolded occurrences of the
33
+ // `substring` based in the array of `matchIndexes` provided
34
+
35
+ const highlightAllMatches = function (str) {
36
+ let substring = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
37
+ let matchIndexes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
38
+ let resultsTextColor = arguments.length > 3 ? arguments[3] : undefined;
39
+ return (
40
+ /*#__PURE__*/
41
+ // Wrapping all in bold
42
+ _jsx(Typography, {
43
+ variant: {
44
+ bold: false
45
+ },
46
+ tokens: {
47
+ color: resultsTextColor
48
+ },
49
+ children: matchIndexes.reduce((acc, matchIndex, index) => [...acc, // Add a piece of the string up to the first occurrence of the substring
50
+ index === 0 && (str.slice(0, matchIndex) ?? ''),
51
+ /*#__PURE__*/
52
+ // Unbold the occurrence of the substring (while keeping the original casing)
53
+ _jsx(Typography, {
54
+ variant: {
55
+ bold: true
56
+ },
57
+ tokens: {
58
+ color: resultsTextColor
59
+ },
60
+ children: str.slice(matchIndex, matchIndex + substring.length)
61
+ }, matchIndex), // Add the rest of the string until the next occurrence or the end of it
62
+ str.slice(matchIndex + substring.length, matchIndexes[index + 1] ?? str.length)], [])
63
+ })
64
+ );
65
+ };
66
+
67
+ const highlight = function () {
68
+ let items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
69
+ let text = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
70
+ let color = arguments.length > 2 ? arguments[2] : undefined;
71
+ return items.reduce((acc, item) => {
72
+ var _Array$from;
73
+
74
+ const matches = (_Array$from = Array.from(matchAll(item.label.toLowerCase(), text.toLowerCase().replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&')))) === null || _Array$from === void 0 ? void 0 : _Array$from.map(_ref => {
75
+ let {
76
+ index
77
+ } = _ref;
78
+ return index;
79
+ });
80
+
81
+ if (matches !== null && matches !== void 0 && matches.length) {
82
+ return [...acc, { ...item,
83
+ label: highlightAllMatches(item.label, text, matches, color)
84
+ }];
85
+ }
86
+
87
+ return [...acc, item];
88
+ }, []);
89
+ };
90
+
91
+ const Autocomplete = /*#__PURE__*/forwardRef((_ref2, ref) => {
92
+ var _ref3;
93
+
94
+ let {
95
+ children,
96
+ copy = 'en',
97
+ fullWidth = true,
98
+ initialItems,
99
+ initialValue,
100
+ isLoading = false,
101
+ items,
102
+ maxSuggestions = DEFAULT_MAX_SUGGESTIONS,
103
+ minToSuggestion = DEFAULT_MIN_TO_SUGGESTION,
104
+ noResults,
105
+ onChange,
106
+ onClear,
107
+ onSelect,
108
+ readOnly,
109
+ validation,
110
+ value,
111
+ helpText = '',
112
+ ...rest
113
+ } = _ref2;
114
+ const {
115
+ color: resultsTextColor
116
+ } = useThemeTokens('Search', {}, {
117
+ focus: true
118
+ }); // The wrapped input is mostly responsible for controlled vs uncontrolled handling,
119
+ // but we also need to adjust suggestions based on the mode:
120
+ // - in controlled mode we rely entirely on the suggestions passed via the `items` prop,
121
+ // - in uncontrolled mode we filter the suggestions ourselves based on the `initialItems`
122
+ // prop and the text entered
123
+
124
+ const isControlled = value !== undefined; // We need to store current items for uncontrolled usage
125
+
126
+ const [currentItems, setCurrentItems] = useState(initialItems); // We need to store the current value as well to be able to highlight it
127
+
128
+ const [currentValue, setCurrentValue] = useState(value ?? initialValue);
129
+ const inputTokens = {
130
+ paddingLeft: INPUT_LEFT_PADDING
131
+ }; // Setting up the overlay
132
+
133
+ const openOverlayRef = useRef();
134
+ const [isExpanded, setIsExpanded] = useState(((_ref3 = value ?? initialValue) === null || _ref3 === void 0 ? void 0 : _ref3.length) >= minToSuggestion);
135
+ const [isFocused, setisFocused] = useState(false);
136
+ const [sourceLayout, setSourceLayout] = useState(null);
137
+ const {
138
+ supportsProps,
139
+ ...selectedProps
140
+ } = selectProps(rest);
141
+ const {
142
+ hint,
143
+ label: inputLabel
144
+ } = supportsProps;
145
+ const hintExpansionEnabled = isFocused && helpText && !currentValue;
146
+ const {
147
+ overlaidPosition,
148
+ sourceRef: inputRef,
149
+ // targetRef,
150
+ onTargetLayout,
151
+ isReady
152
+ } = useOverlaidPosition({
153
+ isShown: isExpanded || hintExpansionEnabled,
154
+ offsets: {
155
+ vertical: Platform.OS !== 'web' && (hint || inputLabel) ? 28 : 4
156
+ }
157
+ });
158
+ const targetRef = useRef(null); // We limit the number of suggestions displayed to avoid huge lists
159
+ // TODO: add a way to make the `Listbox` occupy fixed height and be scrollable
160
+ // within that height, which will unlock similar behaviour for `AutoComplete` as well
161
+
162
+ const itemsToSuggest = function () {
163
+ let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
164
+ return maxSuggestions ? data.slice(0, maxSuggestions) : [...data];
165
+ };
166
+
167
+ const getCopy = useCopy({
168
+ dictionary,
169
+ copy
170
+ }); // Tracking input width changes to resize the listbox overlay accordingly
171
+
172
+ const [inputWidth, setInputWidth] = useState();
173
+ useSafeLayoutEffect(() => {
174
+ if (Platform.OS === 'web') {
175
+ const updateInputWidth = () => {
176
+ var _inputRef$current;
177
+
178
+ setInputWidth((inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.clientWidth) + 4); // adding back all the input borders / outlines
179
+
180
+ setIsExpanded(false); // close the suggestions while the input is changing
181
+ };
182
+
183
+ const throttledUpdateInputWidth = throttle(updateInputWidth, 100, {
184
+ leading: false
185
+ });
186
+ updateInputWidth();
187
+ Dimensions.addEventListener('change', throttledUpdateInputWidth);
188
+ return () => {
189
+ Dimensions.removeEventListener('change', throttledUpdateInputWidth);
190
+ };
191
+ }
192
+
193
+ setInputWidth(sourceLayout === null || sourceLayout === void 0 ? void 0 : sourceLayout.width);
194
+ return () => {};
195
+ }, [inputRef, sourceLayout]);
196
+
197
+ const handleMeasure = event => {
198
+ onTargetLayout(event);
199
+
200
+ if (Platform.OS !== 'web') {
201
+ var _inputRef$current2;
202
+
203
+ inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current2 = inputRef.current) === null || _inputRef$current2 === void 0 ? void 0 : _inputRef$current2.measureInWindow((x, y, width) => {
204
+ setInputWidth(width);
205
+ });
206
+ }
207
+ };
208
+
209
+ const handleChange = newValue => {
210
+ onChange === null || onChange === void 0 ? void 0 : onChange(newValue || '');
211
+ setCurrentValue(newValue);
212
+ setIsExpanded((newValue === null || newValue === void 0 ? void 0 : newValue.length) >= minToSuggestion);
213
+
214
+ if (!isControlled && initialItems !== undefined) {
215
+ setCurrentItems(initialItems.filter(_ref4 => {
216
+ var _label$toLowerCase;
217
+
218
+ let {
219
+ label
220
+ } = _ref4;
221
+ return label === null || label === void 0 ? void 0 : (_label$toLowerCase = label.toLowerCase()) === null || _label$toLowerCase === void 0 ? void 0 : _label$toLowerCase.includes(newValue === null || newValue === void 0 ? void 0 : newValue.toLowerCase());
222
+ }));
223
+ }
224
+ };
225
+
226
+ const handleSelect = selectedId => {
227
+ var _ref5;
228
+
229
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedId);
230
+ const {
231
+ label: newValue,
232
+ nested
233
+ } = (_ref5 = isControlled ? items : currentItems) === null || _ref5 === void 0 ? void 0 : _ref5.find(_ref6 => {
234
+ let {
235
+ id
236
+ } = _ref6;
237
+ return id === selectedId;
238
+ });
239
+
240
+ if (!nested) {
241
+ onChange === null || onChange === void 0 ? void 0 : onChange(newValue);
242
+ setIsExpanded(false);
243
+ }
244
+
245
+ setCurrentValue(newValue);
246
+ if (!isControlled && inputRef !== null && inputRef !== void 0 && inputRef.current) inputRef.current.value = newValue;
247
+ };
248
+
249
+ const handleClose = event => {
250
+ var _openOverlayRef$curre, _openOverlayRef$curre2;
251
+
252
+ if (event.type === 'keydown') {
253
+ if (event.key === 'Escape' || event.key === 27) {
254
+ setIsExpanded(false);
255
+ } else if (event.key === 'ArrowDown' && isExpanded && !isLoading && targetRef !== null && targetRef !== void 0 && targetRef.current) {
256
+ targetRef.current.focus();
257
+ }
258
+ } else if (event.type === 'click' && openOverlayRef !== null && openOverlayRef !== void 0 && openOverlayRef.current && event.target && !(openOverlayRef !== null && openOverlayRef !== void 0 && (_openOverlayRef$curre = openOverlayRef.current) !== null && _openOverlayRef$curre !== void 0 && _openOverlayRef$curre.contains(event.target))) {
259
+ setIsExpanded(false);
260
+ } else if (event.type === 'touchstart' && openOverlayRef !== null && openOverlayRef !== void 0 && openOverlayRef.current && event.touches[0].target && !(openOverlayRef !== null && openOverlayRef !== void 0 && (_openOverlayRef$curre2 = openOverlayRef.current) !== null && _openOverlayRef$curre2 !== void 0 && _openOverlayRef$curre2.contains(event.touches[0].target))) {
261
+ setIsExpanded(false);
262
+ } else if (Platform.OS === 'web') {
263
+ // needed for dropdown to be collapsed when clicking outside on web
264
+ setIsExpanded(false);
265
+ }
266
+ };
267
+
268
+ const itemsToShow = currentValue ? itemsToSuggest(highlight(isControlled ? items : currentItems, currentValue, resultsTextColor)) : [];
269
+ const helpTextToShow = isFocused && !currentValue ? helpText : noResults ?? getCopy('noResults');
270
+ return /*#__PURE__*/_jsxs(View, {
271
+ style: staticStyles.container,
272
+ children: [/*#__PURE__*/_jsx(InputSupports, { ...supportsProps,
273
+ accessibilityAutoComplete: "list",
274
+ accessibilityControls: "autocomplete",
275
+ accessibilityExpanded: isExpanded,
276
+ accessibilityRole: "combobox",
277
+ ...selectedProps,
278
+ validation: validation,
279
+ ref: ref,
280
+ children: _ref7 => {
281
+ let {
282
+ inputId,
283
+ ...props
284
+ } = _ref7;
285
+ if (typeof children === 'function') return children({
286
+ inputId,
287
+ inputRef,
288
+ onChange: handleChange,
289
+ onKeyPress: handleClose,
290
+ readOnly,
291
+ tokens: inputTokens,
292
+ ...selectedProps,
293
+ ...props,
294
+ ...(isControlled ? {
295
+ value
296
+ } : {
297
+ initialValue
298
+ })
299
+ });
300
+ return /*#__PURE__*/_jsx(TextInput, {
301
+ onChange: handleChange,
302
+ onFocus: () => {
303
+ setisFocused(true);
304
+ },
305
+ onBlur: () => {
306
+ setisFocused(false);
307
+ },
308
+ onClear: onClear,
309
+ onKeyPress: handleClose,
310
+ readOnly: readOnly,
311
+ ref: inputRef,
312
+ ...(Platform.OS !== 'web' ? {
313
+ onLayout: event => setSourceLayout(event.nativeEvent.layout)
314
+ } : {}),
315
+ tokens: inputTokens,
316
+ validation: validation,
317
+ ...selectedProps,
318
+ ...props,
319
+ ...(isControlled ? {
320
+ value
321
+ } : {
322
+ initialValue
323
+ })
324
+ });
325
+ }
326
+ }), (isExpanded || hintExpansionEnabled) && /*#__PURE__*/_jsxs(_Fragment, {
327
+ children: [/*#__PURE__*/_jsx(Listbox.Overlay, {
328
+ overlaidPosition: overlaidPosition,
329
+ isReady: isReady,
330
+ minWidth: fullWidth ? inputWidth : MIN_LISTBOX_WIDTH,
331
+ maxWidth: inputWidth,
332
+ onLayout: handleMeasure,
333
+ ref: openOverlayRef,
334
+ children: isLoading ? /*#__PURE__*/_jsx(Loading, {
335
+ label: getCopy('loading')
336
+ }) : /*#__PURE__*/_jsx(Suggestions, {
337
+ hasResults: getCopy('hasResults'),
338
+ id: "autocomplete",
339
+ items: itemsToShow,
340
+ noResults: helpTextToShow,
341
+ onClose: handleClose,
342
+ onSelect: handleSelect,
343
+ parentRef: inputRef,
344
+ ref: targetRef
345
+ })
346
+ }), (targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) && /*#__PURE__*/_jsx(View // This catches and shifts focus to other interactive elements.
347
+ , {
348
+ onFocus: () => {
349
+ var _targetRef$current;
350
+
351
+ return targetRef === null || targetRef === void 0 ? void 0 : (_targetRef$current = targetRef.current) === null || _targetRef$current === void 0 ? void 0 : _targetRef$current.focus();
352
+ },
353
+ tabIndex: 0
354
+ })]
355
+ })]
356
+ });
357
+ });
358
+ Autocomplete.displayName = 'Autocomplete'; // If a language dictionary entry is provided, it must contain every key
359
+
360
+ const dictionaryContentShape = PropTypes.shape({
361
+ hasResults: PropTypes.string.isRequired,
362
+ loading: PropTypes.string.isRequired,
363
+ noResults: PropTypes.string.isRequired
364
+ });
365
+ Autocomplete.propTypes = { ...selectedSystemPropTypes,
366
+
367
+ /**
368
+ * Can be used to provide a function that renders a custom input:
369
+ * <Autocomplete items={items} value={currentValue}>
370
+ * {({ inputId, inputRef, onChange, onKeyPress, readOnly, tokens, value }) => (
371
+ * <Search
372
+ * nativeID={inputId}
373
+ * ref={inputRef}
374
+ * onChange={onChange}
375
+ * onKeyPress={onKeyPress}
376
+ * readOnly={readOnly}
377
+ * tokens={tokens}
378
+ * value={value}
379
+ * />
380
+ * )}
381
+ * </Autocomplete>
382
+ */
383
+ children: PropTypes.func,
384
+
385
+ /**
386
+ * Copy language identifier
387
+ */
388
+ copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
389
+
390
+ /**
391
+ * Set to true in order to display the loading indicator instead of results
392
+ */
393
+ isLoading: PropTypes.bool,
394
+
395
+ /**
396
+ * List of items to display as suggestions
397
+ */
398
+ items: PropTypes.arrayOf(PropTypes.shape({
399
+ id: PropTypes.string,
400
+ label: PropTypes.string
401
+ })),
402
+
403
+ /**
404
+ * Label to display alongside the spinner when in a loading state
405
+ */
406
+ loadingLabel: PropTypes.string,
407
+
408
+ /**
409
+ * Minimum number of characters typed for a list of suggestions to appear
410
+ */
411
+ minToSuggestion: PropTypes.number,
412
+
413
+ /**
414
+ * Maximum number of suggestions provided at the same time
415
+ */
416
+ maxSuggestions: PropTypes.number,
417
+
418
+ /**
419
+ * Text or JSX to render when no results are available
420
+ */
421
+ noResults: PropTypes.node,
422
+
423
+ /**
424
+ * Help text to display when the input is focused and empty
425
+ */
426
+ helpText: PropTypes.string,
427
+
428
+ /**
429
+ * Handler function to be called when the input value changes
430
+ */
431
+ onChange: PropTypes.func,
432
+
433
+ /**
434
+ * Handler function to be called when the clear button (appears if the handler is passed) is pressed
435
+ */
436
+ onClear: PropTypes.func,
437
+
438
+ /**
439
+ * Callback function to be called when an item is selected from the list
440
+ */
441
+ onSelect: PropTypes.func,
442
+
443
+ /**
444
+ * Input value for controlled usage
445
+ */
446
+ value: PropTypes.string
447
+ };
448
+ export default Autocomplete;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import ActivityIndicator from '../ActivityIndicator';
4
+ import Typography from '../Typography';
5
+ import Box from '../Box';
6
+ import StackView from '../StackView';
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { jsxs as _jsxs } from "react/jsx-runtime";
9
+
10
+ const Loading = _ref => {
11
+ let {
12
+ label
13
+ } = _ref;
14
+ return /*#__PURE__*/_jsx(Box, {
15
+ space: 3,
16
+ children: /*#__PURE__*/_jsxs(StackView, {
17
+ direction: "row",
18
+ space: 2,
19
+ tokens: {
20
+ alignItems: 'center'
21
+ },
22
+ children: [/*#__PURE__*/_jsx(ActivityIndicator, {
23
+ variant: {
24
+ size: 'large'
25
+ }
26
+ }), /*#__PURE__*/_jsx(Typography, {
27
+ children: label
28
+ })]
29
+ })
30
+ });
31
+ };
32
+
33
+ Loading.propTypes = {
34
+ label: PropTypes.string
35
+ };
36
+ export default Loading;
@@ -0,0 +1,66 @@
1
+ import React, { forwardRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import A11yText from '../A11yText';
4
+ import Typography from '../Typography';
5
+ import Box from '../Box';
6
+ import Listbox from '../Listbox';
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { Fragment as _Fragment } from "react/jsx-runtime";
9
+ import { jsxs as _jsxs } from "react/jsx-runtime";
10
+ const Suggestions = /*#__PURE__*/forwardRef((_ref, ref) => {
11
+ let {
12
+ hasResults,
13
+ items = [],
14
+ noResults,
15
+ onClose,
16
+ onSelect,
17
+ parentRef
18
+ } = _ref;
19
+ const pressableItems = items.map(_ref2 => {
20
+ let {
21
+ id,
22
+ ...rest
23
+ } = _ref2;
24
+ return {
25
+ id,
26
+ onPress: () => onSelect(id),
27
+ ...rest
28
+ };
29
+ });
30
+ if (items !== null && items !== void 0 && items.length) return /*#__PURE__*/_jsxs(_Fragment, {
31
+ children: [/*#__PURE__*/_jsx(A11yText, {
32
+ accessibilityLiveRegion: "polite",
33
+ text: hasResults
34
+ }), /*#__PURE__*/_jsx(Listbox, {
35
+ items: pressableItems,
36
+ firstItemRef: ref,
37
+ parentRef: parentRef,
38
+ onClose: onClose
39
+ })]
40
+ });
41
+ return /*#__PURE__*/_jsx(Box, {
42
+ space: 3,
43
+ children: typeof noResults === 'string' ? /*#__PURE__*/_jsx(_Fragment, {
44
+ children: /*#__PURE__*/_jsx(Typography, {
45
+ accessibilityLiveRegion: "polite",
46
+ variant: {
47
+ size: 'small'
48
+ },
49
+ children: noResults
50
+ })
51
+ }) : noResults
52
+ });
53
+ });
54
+ Suggestions.displayName = 'Suggestions';
55
+ Suggestions.propTypes = {
56
+ hasResults: PropTypes.string.isRequired,
57
+ items: PropTypes.arrayOf(PropTypes.shape({
58
+ id: PropTypes.string,
59
+ label: PropTypes.node
60
+ })).isRequired,
61
+ noResults: PropTypes.node.isRequired,
62
+ onClose: PropTypes.func.isRequired,
63
+ onSelect: PropTypes.func.isRequired,
64
+ parentRef: PropTypes.object.isRequired
65
+ };
66
+ export default Suggestions;
@@ -0,0 +1,4 @@
1
+ export const DEFAULT_MIN_TO_SUGGESTION = 1;
2
+ export const DEFAULT_MAX_SUGGESTIONS = 5;
3
+ export const INPUT_LEFT_PADDING = 16;
4
+ export const MIN_LISTBOX_WIDTH = 288;
@@ -0,0 +1,12 @@
1
+ export default {
2
+ en: {
3
+ hasResults: 'Some results are available',
4
+ loading: 'Searching...',
5
+ noResults: 'No results found'
6
+ },
7
+ fr: {
8
+ hasResults: 'Quelques suggestions sont disponible',
9
+ loading: 'Recherche...',
10
+ noResults: 'Aucun résultat trouvé'
11
+ }
12
+ };
@@ -0,0 +1,2 @@
1
+ import Autocomplete from './Autocomplete';
2
+ export default Autocomplete;
@@ -1,4 +1,5 @@
1
1
  import React, { forwardRef } from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import ButtonBase from './ButtonBase';
3
4
  import buttonPropTypes, { textAndA11yText } from './propTypes';
4
5
  import { a11yProps, hrefAttrsProp, linkProps } from '../utils/props';
@@ -41,6 +42,8 @@ ButtonLink.displayName = 'ButtonLink';
41
42
  ButtonLink.propTypes = { ...a11yProps.types,
42
43
  ...buttonPropTypes,
43
44
  ...linkProps.types,
44
- children: textAndA11yText
45
+ children: textAndA11yText,
46
+ dataSet: PropTypes.object,
47
+ accessibilityRole: PropTypes.string
45
48
  };
46
49
  export default ButtonLink;
@@ -159,6 +159,13 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
159
159
  ...focusabilityProps,
160
160
  children: /*#__PURE__*/_jsx(View, {
161
161
  onLayout: onContainerLayout,
162
+ style: { ...Platform.select({
163
+ default: {
164
+ flex: 1
165
+ },
166
+ web: {}
167
+ })
168
+ },
162
169
  children: /*#__PURE__*/_jsx(View, {
163
170
  style: selectContainerStyles(themeTokens),
164
171
  children: children
@@ -106,6 +106,7 @@ const IconButton = /*#__PURE__*/forwardRef((_ref3, ref) => {
106
106
  icon: IconComponent,
107
107
  href,
108
108
  hrefAttrs,
109
+ testID,
109
110
  accessibilityRole = href ? 'link' : 'button',
110
111
  ...rawRest
111
112
  } = _ref3;
@@ -141,6 +142,7 @@ const IconButton = /*#__PURE__*/forwardRef((_ref3, ref) => {
141
142
  hrefAttrs: hrefAttrs,
142
143
  style: getOuterStyle,
143
144
  ...selectedProps,
145
+ testID: testID,
144
146
  children: pressableState => {
145
147
  const themeTokens = getTokens(resolvePressableState(pressableState));
146
148
  return /*#__PURE__*/_jsx(View, {
@@ -160,6 +162,12 @@ IconButton.propTypes = { ...selectedSystemPropTypes,
160
162
  variant: variantProp.propType,
161
163
  tokens: getTokensPropType('IconButton'),
162
164
 
165
+ /**
166
+ * A unique identifier for testing purposes.
167
+ * Will be added as a `data-testid` attribute.
168
+ */
169
+ testID: PropTypes.string,
170
+
163
171
  /**
164
172
  * Defines the icon to be rendered
165
173
  */
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useThemeTokensCallback } from '../ThemeProvider';
4
- import { selectTokens, getTokensPropType } from '../utils';
4
+ import { selectTokens, getTokensPropType, linkProps } from '../utils';
5
5
  import LinkBase from './LinkBase';
6
6
  /**
7
7
  * `ChevronLink` is a convenience wrapper around the `Link` component to enable "directional" links.
@@ -18,7 +18,7 @@ const ChevronLink = /*#__PURE__*/forwardRef((_ref, ref) => {
18
18
  tokens = {},
19
19
  variant,
20
20
  dataSet,
21
- ...linkProps
21
+ ...otherlinkProps
22
22
  } = _ref;
23
23
  const getChevronTokens = useThemeTokensCallback('ChevronLink', tokens, variant);
24
24
 
@@ -41,7 +41,7 @@ const ChevronLink = /*#__PURE__*/forwardRef((_ref, ref) => {
41
41
  };
42
42
 
43
43
  const getTokens = useThemeTokensCallback('Link', applyChevronTokens, variant);
44
- return /*#__PURE__*/_jsx(LinkBase, { ...linkProps,
44
+ return /*#__PURE__*/_jsx(LinkBase, { ...otherlinkProps,
45
45
  iconPosition: direction,
46
46
  tokens: getTokens,
47
47
  dataSet: dataSet,
@@ -51,6 +51,13 @@ const ChevronLink = /*#__PURE__*/forwardRef((_ref, ref) => {
51
51
  });
52
52
  ChevronLink.displayName = 'ChevronLink';
53
53
  ChevronLink.propTypes = { ...LinkBase.propTypes,
54
+ children: PropTypes.node,
55
+ variant: PropTypes.exact({
56
+ size: PropTypes.oneOf(['large', 'small', 'micro']),
57
+ alternative: PropTypes.bool,
58
+ inverse: PropTypes.bool
59
+ }),
60
+ ...linkProps.types,
54
61
  tokens: getTokensPropType('ChevronLink', 'Link'),
55
62
 
56
63
  /**
@@ -210,6 +210,20 @@ const LinkBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
210
210
  LinkBase.displayName = 'LinkBase';
211
211
  LinkBase.propTypes = { ...selectedSystemPropTypes,
212
212
  tokens: getTokensPropType('Link'),
213
+
214
+ /**
215
+ * href for the Link
216
+ */
217
+ href: PropTypes.string,
218
+
219
+ /**
220
+ * AccesibilityRole for the link base
221
+ */
222
+ accessibilityrole: PropTypes.string,
223
+
224
+ /**
225
+ * Children nodes that can be added
226
+ */
213
227
  variant: variantProp.propType,
214
228
 
215
229
  /**