@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.
- package/CHANGELOG.md +26 -2
- package/component-docs.json +526 -76
- package/lib/Autocomplete/Autocomplete.js +483 -0
- package/lib/Autocomplete/Loading.js +51 -0
- package/lib/Autocomplete/Suggestions.js +85 -0
- package/lib/Autocomplete/constants.js +14 -0
- package/lib/Autocomplete/dictionary.js +19 -0
- package/lib/Autocomplete/index.js +13 -0
- package/lib/Button/ButtonLink.js +7 -3
- package/lib/ExpandCollapse/Panel.js +7 -0
- package/lib/IconButton/IconButton.js +8 -0
- package/lib/Link/ChevronLink.js +9 -2
- package/lib/Link/LinkBase.js +14 -0
- package/lib/Link/TextButton.js +12 -1
- package/lib/Listbox/GroupControl.js +121 -0
- package/lib/Listbox/Listbox.js +198 -0
- package/lib/Listbox/ListboxGroup.js +142 -0
- package/lib/Listbox/ListboxItem.js +97 -0
- package/lib/Listbox/ListboxOverlay.js +106 -0
- package/lib/Listbox/PressableItem.js +0 -2
- package/lib/Listbox/index.js +5 -24
- package/lib/Pagination/dictionary.js +3 -3
- package/lib/Progress/ProgressBarBackground.js +2 -2
- package/lib/SideNav/Item.js +15 -5
- package/lib/Tags/Tags.js +6 -1
- package/lib/TextInput/TextInputBase.js +2 -0
- package/lib/Tooltip/Tooltip.js +6 -1
- package/lib/Tooltip/Tooltip.native.js +6 -1
- package/lib/Tooltip/shared.js +5 -0
- package/lib/index.js +17 -13
- package/lib/utils/useOverlaidPosition.js +6 -4
- package/lib-module/Autocomplete/Autocomplete.js +448 -0
- package/lib-module/Autocomplete/Loading.js +36 -0
- package/lib-module/Autocomplete/Suggestions.js +66 -0
- package/lib-module/Autocomplete/constants.js +4 -0
- package/lib-module/Autocomplete/dictionary.js +12 -0
- package/lib-module/Autocomplete/index.js +2 -0
- package/lib-module/Button/ButtonLink.js +4 -1
- package/lib-module/ExpandCollapse/Panel.js +7 -0
- package/lib-module/IconButton/IconButton.js +8 -0
- package/lib-module/Link/ChevronLink.js +10 -3
- package/lib-module/Link/LinkBase.js +14 -0
- package/lib-module/Link/TextButton.js +11 -1
- package/lib-module/Listbox/GroupControl.js +102 -0
- package/lib-module/Listbox/Listbox.js +172 -0
- package/lib-module/Listbox/ListboxGroup.js +117 -0
- package/lib-module/Listbox/ListboxItem.js +71 -0
- package/lib-module/Listbox/ListboxOverlay.js +80 -0
- package/lib-module/Listbox/PressableItem.js +0 -2
- package/lib-module/Listbox/index.js +2 -2
- package/lib-module/Pagination/dictionary.js +3 -3
- package/lib-module/Progress/ProgressBarBackground.js +2 -2
- package/lib-module/SideNav/Item.js +15 -5
- package/lib-module/Tags/Tags.js +6 -1
- package/lib-module/TextInput/TextInputBase.js +2 -0
- package/lib-module/Tooltip/Tooltip.js +6 -1
- package/lib-module/Tooltip/Tooltip.native.js +6 -1
- package/lib-module/Tooltip/shared.js +5 -0
- package/lib-module/index.js +2 -1
- package/lib-module/utils/useOverlaidPosition.js +5 -4
- package/package.json +5 -3
- package/src/Autocomplete/Autocomplete.jsx +411 -0
- package/src/Autocomplete/Loading.jsx +18 -0
- package/src/Autocomplete/Suggestions.jsx +54 -0
- package/src/Autocomplete/constants.js +4 -0
- package/src/Autocomplete/dictionary.js +12 -0
- package/src/Autocomplete/index.js +3 -0
- package/src/Button/ButtonLink.jsx +4 -1
- package/src/ExpandCollapse/Panel.jsx +11 -1
- package/src/IconButton/IconButton.jsx +7 -0
- package/src/Link/ChevronLink.jsx +10 -3
- package/src/Link/LinkBase.jsx +11 -0
- package/src/Link/TextButton.jsx +8 -2
- package/src/Listbox/GroupControl.jsx +93 -0
- package/src/Listbox/Listbox.jsx +165 -0
- package/src/Listbox/ListboxGroup.jsx +120 -0
- package/src/Listbox/ListboxItem.jsx +76 -0
- package/src/Listbox/ListboxOverlay.jsx +82 -0
- package/src/Listbox/PressableItem.jsx +0 -2
- package/src/Listbox/index.js +3 -2
- package/src/Pagination/dictionary.js +3 -3
- package/src/Progress/ProgressBarBackground.jsx +2 -2
- package/src/SideNav/Item.jsx +13 -5
- package/src/Tags/Tags.jsx +5 -1
- package/src/TextInput/TextInputBase.jsx +2 -0
- package/src/Tooltip/Tooltip.jsx +16 -2
- package/src/Tooltip/Tooltip.native.jsx +15 -2
- package/src/Tooltip/shared.js +4 -0
- package/src/index.js +2 -1
- 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,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
|
+
};
|
|
@@ -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
|
-
...
|
|
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, { ...
|
|
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
|
/**
|