@telus-uds/components-base 1.19.0 → 1.21.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 (97) hide show
  1. package/CHANGELOG.md +34 -2
  2. package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +3 -1
  3. package/component-docs.json +838 -125
  4. package/lib/BaseProvider/index.js +2 -1
  5. package/lib/Box/Box.js +14 -1
  6. package/lib/Button/ButtonDropdown.js +207 -0
  7. package/lib/Button/index.js +8 -0
  8. package/lib/Carousel/Carousel.js +2 -2
  9. package/lib/Carousel/CarouselItem/CarouselItem.js +7 -1
  10. package/lib/Carousel/CarouselTabs/CarouselTabsPanel.js +21 -4
  11. package/lib/FlexGrid/Col/Col.js +1 -3
  12. package/lib/FlexGrid/FlexGrid.js +3 -5
  13. package/lib/FlexGrid/Row/Row.js +3 -3
  14. package/lib/IconButton/IconButton.js +12 -4
  15. package/lib/MultiSelectFilter/MultiSelectFilter.js +276 -0
  16. package/lib/MultiSelectFilter/dictionary.js +19 -0
  17. package/lib/MultiSelectFilter/index.js +13 -0
  18. package/lib/Search/Search.js +4 -1
  19. package/lib/Select/Picker.native.js +16 -13
  20. package/lib/Select/Select.js +7 -1
  21. package/lib/Select/constants.js +15 -0
  22. package/lib/StepTracker/Step.js +2 -1
  23. package/lib/TextInput/TextInput.js +9 -2
  24. package/lib/TextInput/TextInputBase.js +52 -8
  25. package/lib/TextInput/dictionary.js +15 -0
  26. package/lib/ThemeProvider/ThemeProvider.js +24 -7
  27. package/lib/ThemeProvider/utils/styles.js +3 -1
  28. package/lib/index.js +18 -0
  29. package/lib/utils/BaseView/BaseView.js +64 -0
  30. package/lib/utils/BaseView/BaseView.native.js +16 -0
  31. package/lib/utils/BaseView/index.js +13 -0
  32. package/lib/utils/index.js +10 -1
  33. package/lib/utils/input.js +11 -3
  34. package/lib/utils/props/handlerProps.js +5 -0
  35. package/lib-module/BaseProvider/index.js +2 -1
  36. package/lib-module/Box/Box.js +14 -1
  37. package/lib-module/Button/ButtonDropdown.js +181 -0
  38. package/lib-module/Button/index.js +2 -1
  39. package/lib-module/Carousel/Carousel.js +2 -2
  40. package/lib-module/Carousel/CarouselItem/CarouselItem.js +8 -2
  41. package/lib-module/Carousel/CarouselTabs/CarouselTabsPanel.js +23 -6
  42. package/lib-module/FlexGrid/Col/Col.js +2 -3
  43. package/lib-module/FlexGrid/FlexGrid.js +2 -3
  44. package/lib-module/FlexGrid/Row/Row.js +2 -2
  45. package/lib-module/IconButton/IconButton.js +14 -4
  46. package/lib-module/MultiSelectFilter/MultiSelectFilter.js +248 -0
  47. package/lib-module/MultiSelectFilter/dictionary.js +12 -0
  48. package/lib-module/MultiSelectFilter/index.js +2 -0
  49. package/lib-module/Search/Search.js +4 -1
  50. package/lib-module/Select/Picker.native.js +15 -13
  51. package/lib-module/Select/Select.js +6 -1
  52. package/lib-module/Select/constants.js +5 -0
  53. package/lib-module/StepTracker/Step.js +2 -1
  54. package/lib-module/TextInput/TextInput.js +6 -0
  55. package/lib-module/TextInput/TextInputBase.js +52 -10
  56. package/lib-module/TextInput/dictionary.js +8 -0
  57. package/lib-module/ThemeProvider/ThemeProvider.js +24 -7
  58. package/lib-module/ThemeProvider/utils/styles.js +3 -1
  59. package/lib-module/index.js +2 -0
  60. package/lib-module/utils/BaseView/BaseView.js +43 -0
  61. package/lib-module/utils/BaseView/BaseView.native.js +6 -0
  62. package/lib-module/utils/BaseView/index.js +2 -0
  63. package/lib-module/utils/index.js +2 -1
  64. package/lib-module/utils/input.js +11 -3
  65. package/lib-module/utils/props/handlerProps.js +5 -0
  66. package/package.json +3 -3
  67. package/src/BaseProvider/index.jsx +4 -1
  68. package/src/Box/Box.jsx +14 -1
  69. package/src/Button/ButtonDropdown.jsx +179 -0
  70. package/src/Button/index.js +2 -1
  71. package/src/Carousel/Carousel.jsx +6 -3
  72. package/src/Carousel/CarouselItem/CarouselItem.jsx +9 -2
  73. package/src/Carousel/CarouselTabs/CarouselTabsPanel.jsx +19 -5
  74. package/src/FlexGrid/Col/Col.jsx +4 -4
  75. package/src/FlexGrid/FlexGrid.jsx +11 -10
  76. package/src/FlexGrid/Row/Row.jsx +4 -3
  77. package/src/IconButton/IconButton.jsx +3 -1
  78. package/src/MultiSelectFilter/MultiSelectFilter.jsx +227 -0
  79. package/src/MultiSelectFilter/dictionary.js +12 -0
  80. package/src/MultiSelectFilter/index.js +3 -0
  81. package/src/Search/Search.jsx +2 -1
  82. package/src/Select/Picker.native.jsx +29 -14
  83. package/src/Select/Select.jsx +7 -1
  84. package/src/Select/constants.js +5 -0
  85. package/src/StepTracker/Step.jsx +5 -1
  86. package/src/TextInput/TextInput.jsx +5 -0
  87. package/src/TextInput/TextInputBase.jsx +43 -8
  88. package/src/TextInput/dictionary.js +8 -0
  89. package/src/ThemeProvider/ThemeProvider.jsx +23 -6
  90. package/src/ThemeProvider/utils/styles.js +3 -1
  91. package/src/index.js +2 -0
  92. package/src/utils/BaseView/BaseView.jsx +38 -0
  93. package/src/utils/BaseView/BaseView.native.jsx +6 -0
  94. package/src/utils/BaseView/index.js +3 -0
  95. package/src/utils/index.js +1 -0
  96. package/src/utils/input.js +9 -4
  97. package/src/utils/props/handlerProps.js +4 -0
@@ -77,10 +77,20 @@ const IconButton = /*#__PURE__*/forwardRef((_ref3, ref) => {
77
77
  const selectedProps = selectProps({ ...rest,
78
78
  accessibilityRole
79
79
  });
80
- const handlePress = linkProps.handleHref({
81
- href,
82
- onPress
83
- });
80
+
81
+ const handlePress = () => {
82
+ var _ref$current;
83
+
84
+ linkProps.handleHref({
85
+ href,
86
+ onPress
87
+ })({
88
+ nativeEvent: {
89
+ target: ref === null || ref === void 0 ? void 0 : (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.id
90
+ }
91
+ });
92
+ };
93
+
84
94
  const getTokens = useThemeTokensCallback('IconButton', tokens, variant);
85
95
 
86
96
  const getOuterStyle = pressableState => selectOuterStyle(getTokens(resolvePressableState(pressableState)));
@@ -0,0 +1,248 @@
1
+ import React, { forwardRef, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useThemeTokensCallback } from '../ThemeProvider';
4
+ import { containUniqueFields, getTokensPropType, getPressHandlersWithArgs, selectTokens, useCopy, useMultipleInputValues, variantProp } from '../utils';
5
+ import dictionary from './dictionary';
6
+ import Box from '../Box';
7
+ import { Button, ButtonDropdown } from '../Button';
8
+ import { CheckboxGroup } from '../Checkbox';
9
+ import Divider from '../Divider';
10
+ import FlexGrid from '../FlexGrid';
11
+ import Modal from '../Modal';
12
+ import Spacer from '../Spacer';
13
+ import StackView from '../StackView';
14
+ import Typography from '../Typography';
15
+ import { TextButton } from '../Link';
16
+ import { jsx as _jsx } from "react/jsx-runtime";
17
+ import { jsxs as _jsxs } from "react/jsx-runtime";
18
+ import { Fragment as _Fragment } from "react/jsx-runtime";
19
+ const {
20
+ Col,
21
+ Row
22
+ } = FlexGrid;
23
+ const MultiSelectFilter = /*#__PURE__*/forwardRef((_ref, ref) => {
24
+ let {
25
+ label,
26
+ id = label,
27
+ variant,
28
+ tokens,
29
+ items = [],
30
+ values,
31
+ initialValues,
32
+ maxValues,
33
+ onChange,
34
+ copy = 'en',
35
+ readOnly = false,
36
+ inactive = false,
37
+ rowLimit = 12,
38
+ ...rest
39
+ } = _ref;
40
+ const {
41
+ currentValues,
42
+ setValues
43
+ } = useMultipleInputValues({
44
+ initialValues,
45
+ values,
46
+ maxValues,
47
+ onChange,
48
+ readOnly
49
+ });
50
+ const getItemTokens = useThemeTokensCallback('ButtonDropdown', tokens, variant);
51
+
52
+ const getButtonTokens = buttonState => selectTokens('Button', getItemTokens(buttonState));
53
+
54
+ const getCopy = useCopy({
55
+ dictionary,
56
+ copy
57
+ });
58
+ const [isOpen, setIsOpen] = useState(false);
59
+ const [checkedIds, setCheckedIds] = useState(currentValues !== null && currentValues !== void 0 ? currentValues : []);
60
+ const colSize = items.length > rowLimit ? 2 : 1;
61
+ const isSelected = currentValues.length > 0;
62
+ const uniqueFields = ['id', 'label'];
63
+
64
+ if (!containUniqueFields(items, uniqueFields)) {
65
+ throw new Error("MultiSelectFilter items must have unique ".concat(uniqueFields.join(', ')));
66
+ } // Pass an object of relevant component state as first argument for any passed-in press handlers
67
+
68
+
69
+ const pressHandlers = getPressHandlersWithArgs(rest, [{
70
+ id,
71
+ label,
72
+ currentValues
73
+ }]);
74
+
75
+ const handleChange = event => {
76
+ if (pressHandlers.onPress) pressHandlers === null || pressHandlers === void 0 ? void 0 : pressHandlers.onPress(event);
77
+ setIsOpen(true);
78
+ };
79
+
80
+ const onApply = e => {
81
+ setValues(e);
82
+ setIsOpen(false);
83
+ };
84
+
85
+ return /*#__PURE__*/_jsxs(_Fragment, {
86
+ children: [/*#__PURE__*/_jsxs(Modal, {
87
+ isOpen: isOpen,
88
+ onClose: () => setIsOpen(false),
89
+ variant: {
90
+ width: colSize > 1 ? 'size576' : 's'
91
+ },
92
+ children: [/*#__PURE__*/_jsx(Row, {
93
+ children: /*#__PURE__*/_jsx(Typography, {
94
+ variant: {
95
+ size: 'h4'
96
+ },
97
+ children: getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())
98
+ })
99
+ }), /*#__PURE__*/_jsx(Spacer, {
100
+ space: 4
101
+ }), /*#__PURE__*/_jsx(Spacer, {
102
+ space: 1
103
+ }), /*#__PURE__*/_jsx(Box, {
104
+ scroll: true,
105
+ children: /*#__PURE__*/_jsx(Row, {
106
+ distribute: "between",
107
+ children: [...Array(colSize).keys()].map(i => /*#__PURE__*/_jsxs(Col, {
108
+ xs: 12 / colSize,
109
+ children: [/*#__PURE__*/_jsx(CheckboxGroup, {
110
+ items: items.slice(i * rowLimit, (i + 1) * rowLimit),
111
+ checkedIds: checkedIds,
112
+ onChange: e => setCheckedIds(e, i)
113
+ }), /*#__PURE__*/_jsx(Spacer, {
114
+ size: 4
115
+ })]
116
+ }, i))
117
+ })
118
+ }), /*#__PURE__*/_jsx(Divider, {
119
+ variant: {
120
+ width: 'full',
121
+ color: 'E3E6E8',
122
+ decorative: true,
123
+ weight: 'thin'
124
+ },
125
+ space: 4
126
+ }), /*#__PURE__*/_jsx(Row, {
127
+ children: /*#__PURE__*/_jsxs(StackView, {
128
+ direction: "row",
129
+ space: 3,
130
+ tokens: {
131
+ alignItems: 'center'
132
+ },
133
+ children: [/*#__PURE__*/_jsx(Button, {
134
+ onPress: () => onApply(checkedIds),
135
+ variant: {
136
+ size: 'small',
137
+ priority: 'high'
138
+ },
139
+ children: getCopy('applyButtonLabel')
140
+ }), /*#__PURE__*/_jsx(Box, {
141
+ children: /*#__PURE__*/_jsx(TextButton, {
142
+ onPress: () => setCheckedIds([]),
143
+ children: getCopy('clearButtonLabel')
144
+ })
145
+ })]
146
+ })
147
+ })]
148
+ }), /*#__PURE__*/_jsx(ButtonDropdown, {
149
+ ref: ref,
150
+ ...pressHandlers,
151
+ value: isOpen,
152
+ selected: isSelected,
153
+ label: label,
154
+ onChange: handleChange,
155
+ tokens: getButtonTokens,
156
+ inactive: inactive
157
+ }, id)]
158
+ });
159
+ });
160
+ MultiSelectFilter.displayName = 'MultiSelectFilter';
161
+ MultiSelectFilter.propTypes = {
162
+ /**
163
+ * The text displayed to the user in a ButtonDropdown.
164
+ */
165
+ label: PropTypes.string.isRequired,
166
+
167
+ /**
168
+ * An optional unique string may be provided to identify the ButtonDropdown.
169
+ * If not provided, the label is used.
170
+ */
171
+ id: PropTypes.string,
172
+
173
+ /**
174
+ * Sets the variant for ButtonDropdown element.
175
+ */
176
+ variant: variantProp.propType,
177
+
178
+ /**
179
+ * Sets the tokens for ButtonDropdown element.
180
+ */
181
+ tokens: getTokensPropType('ButtonDropdown'),
182
+
183
+ /**
184
+ * The options a user may select.
185
+ */
186
+ items: PropTypes.arrayOf(PropTypes.shape({
187
+ /**
188
+ * The text displayed to the user with a checkbox, describing this option.
189
+ */
190
+ label: PropTypes.string.isRequired,
191
+
192
+ /**
193
+ * An optional unique string may be provided to identify this option.
194
+ * If not provided, the label is used.
195
+ */
196
+ id: PropTypes.string
197
+ })),
198
+
199
+ /**
200
+ * If the selected item(s) in the checkbox group(s) are to be controlled externally by
201
+ * a parent component, pass an array of strings as well as an `onChange` handler.
202
+ * Passing an array for "values" makes the MultiSelectFilter a "controlled" component that
203
+ * expects its state to be handled via `onChange` and so doesn't handle it itself.
204
+ */
205
+ values: PropTypes.arrayOf(PropTypes.string),
206
+
207
+ /**
208
+ * If `values` is not passed, making the MultiSelectFilter an "uncontrolled" component
209
+ * managing its own selected state, a default set of selections may be provided.
210
+ * Changing the `initialValues` does not change the user's selections.
211
+ */
212
+ initialValues: PropTypes.arrayOf(PropTypes.string),
213
+
214
+ /**
215
+ * If provided, sets a maximum number of items a user may select at once.
216
+ */
217
+ maxValues: PropTypes.number,
218
+
219
+ /**
220
+ * If provided, this function is called when the current selection is changed
221
+ * and is passed an array of the `id`s of all currently selected `items`.
222
+ */
223
+ onChange: PropTypes.func,
224
+
225
+ /**
226
+ * Select English or French copy for the accessible label.
227
+ */
228
+ copy: PropTypes.oneOf(['en', 'fr']),
229
+
230
+ /**
231
+ * If true, the ButtonDropdown cannot be selected by the user and simply show their current state.
232
+ */
233
+ readOnly: PropTypes.string,
234
+
235
+ /**
236
+ * If true, the MultiSelectFilter cannot be interacted with, ButtonDropdown is
237
+ * set as `disabled` and if the theme supports `inactive` appearances rules, these
238
+ * are applied.
239
+ */
240
+ inactive: PropTypes.string,
241
+
242
+ /**
243
+ * Sets the maximum number of items in one column. If number of items are more
244
+ * than the `rowLimit`, they will be rendered in 2 columns.
245
+ */
246
+ rowLimit: PropTypes.number
247
+ };
248
+ export default MultiSelectFilter;
@@ -0,0 +1,12 @@
1
+ export default {
2
+ en: {
3
+ filterByLabel: 'Filter by %{filterCategory}:',
4
+ applyButtonLabel: 'Apply',
5
+ clearButtonLabel: 'Clear'
6
+ },
7
+ fr: {
8
+ filterByLabel: 'Filtrer par %{filterCategory}:',
9
+ applyButtonLabel: 'Appliquer',
10
+ clearButtonLabel: 'Effacer'
11
+ }
12
+ };
@@ -0,0 +1,2 @@
1
+ import MultiSelectFilter from './MultiSelectFilter';
2
+ export default MultiSelectFilter;
@@ -110,8 +110,11 @@ const Search = /*#__PURE__*/forwardRef((_ref3, ref) => {
110
110
  };
111
111
 
112
112
  const handleClear = event => {
113
+ var _ref$current;
114
+
113
115
  setValue('', event);
114
- if (onClear !== undefined) onClear('', event);
116
+ onClear === null || onClear === void 0 ? void 0 : onClear('', event);
117
+ ref === null || ref === void 0 ? void 0 : (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.focus();
115
118
  };
116
119
 
117
120
  const handleFocus = event => {
@@ -4,7 +4,8 @@ import View from "react-native-web/dist/exports/View";
4
4
  import Platform from "react-native-web/dist/exports/Platform";
5
5
  import NativePicker from 'react-native-picker-select';
6
6
  import { a11yProps, componentPropType } from '../utils';
7
- import Group from './Group'; // styling of the native input is very limited, most of the styles have to be applied to an additional View
7
+ import Group from './Group';
8
+ import { ANDROID_HEIGHT_OFFSET, ANDROID_HORIZONTAL_PADDING_OFFSET, ANDROID_DEFAULT_PADDING } from './constants'; // Styling of the native input is very limited, most of the styles have to be applied to an additional View
8
9
 
9
10
  import { jsx as _jsx } from "react/jsx-runtime";
10
11
  import { Fragment as _Fragment } from "react/jsx-runtime";
@@ -12,28 +13,29 @@ import { Fragment as _Fragment } from "react/jsx-runtime";
12
13
  const selectAndroidInputStyles = _ref => {
13
14
  let {
14
15
  height = 0,
15
- paddingBottom = 0,
16
- paddingTop = 0,
17
- borderWidth = 0,
18
16
  color
19
17
  } = _ref;
20
18
  return {
21
- height: height - paddingTop - paddingBottom - 2 * borderWidth,
19
+ height,
20
+ paddingBottom: 0,
21
+ paddingTop: 0,
22
22
  color
23
23
  };
24
- }; // the native input has a side padding of 8px, which can't be adjusted, so we have to account for that in the container
24
+ }; // The native input has a side padding of 8px, which can't be adjusted, so we have to account for that in the container
25
25
 
26
26
 
27
27
  const selectAndroidContainerStyles = _ref2 => {
28
28
  let {
29
- paddingLeft = 0,
30
- paddingRight = 0,
29
+ paddingLeft = ANDROID_DEFAULT_PADDING,
30
+ paddingRight = ANDROID_DEFAULT_PADDING,
31
31
  ...rest
32
32
  } = _ref2;
33
- return {
34
- paddingLeft: paddingLeft > 8 ? paddingLeft - 8 : 0,
35
- paddingRight: paddingRight > 8 ? paddingRight - 8 : 0,
36
- ...rest
33
+ return { ...rest,
34
+ paddingLeft: paddingLeft > ANDROID_HORIZONTAL_PADDING_OFFSET ? paddingLeft - ANDROID_HORIZONTAL_PADDING_OFFSET : ANDROID_DEFAULT_PADDING,
35
+ paddingRight: paddingRight > ANDROID_HORIZONTAL_PADDING_OFFSET ? paddingRight - ANDROID_HORIZONTAL_PADDING_OFFSET : ANDROID_DEFAULT_PADDING,
36
+ paddingBottom: ANDROID_DEFAULT_PADDING,
37
+ paddingTop: ANDROID_DEFAULT_PADDING,
38
+ height: rest.height + ANDROID_HEIGHT_OFFSET
37
39
  };
38
40
  };
39
41
 
@@ -49,7 +51,7 @@ const Picker = /*#__PURE__*/forwardRef((_ref3, ref) => {
49
51
  placeholder,
50
52
  ...rest
51
53
  } = _ref3;
52
- // ungroup items, since there's no way to support groups on native
54
+ // Ungroup items, since there's no way to support groups on native
53
55
  const flatChildren = Children.toArray(children).flatMap(child => {
54
56
  if (child.type === Group) {
55
57
  return child.props.children;
@@ -7,6 +7,7 @@ import { applyTextStyles, useThemeTokens, applyOuterBorder, useTheme } from '../
7
7
  import { a11yProps, componentPropType, getTokensPropType, inputSupportsProps, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
8
8
  import Picker from './Picker';
9
9
  import InputSupports from '../InputSupports';
10
+ import { ANDROID_VALIDATION_ICON_CONTAINER_OFFSET } from './constants';
10
11
  import { jsx as _jsx } from "react/jsx-runtime";
11
12
  import { jsxs as _jsxs } from "react/jsx-runtime";
12
13
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inputSupportsProps, viewProps]);
@@ -142,7 +143,11 @@ const selectValidationIconContainerStyles = _ref6 => {
142
143
  } = _ref6;
143
144
  return {
144
145
  paddingRight: icon ? paddingRight + iconSize : paddingRight,
145
- paddingBottom
146
+ ...(Platform.OS === 'android' ? {
147
+ paddingBottom: paddingBottom + ANDROID_VALIDATION_ICON_CONTAINER_OFFSET
148
+ } : {
149
+ paddingBottom
150
+ })
146
151
  };
147
152
  };
148
153
  /**
@@ -0,0 +1,5 @@
1
+ // Because Android
2
+ export const ANDROID_VALIDATION_ICON_CONTAINER_OFFSET = 5;
3
+ export const ANDROID_HEIGHT_OFFSET = 12;
4
+ export const ANDROID_HORIZONTAL_PADDING_OFFSET = 8;
5
+ export const ANDROID_DEFAULT_PADDING = 0;
@@ -185,7 +185,8 @@ const Step = _ref7 => {
185
185
  space: 0,
186
186
  tokens: {
187
187
  alignItems: 'center',
188
- flexGrow: 0
188
+ flexGrow: 0,
189
+ justifyContent: 'center'
189
190
  },
190
191
  children: [/*#__PURE__*/_jsx(View, {
191
192
  style: [staticStyles.connector, !isFirst && selectConnectorStyles(themeTokens, isActive)]
@@ -1,4 +1,5 @@
1
1
  import React, { forwardRef } from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import { a11yProps, focusHandlerProps, getTokensPropType, inputSupportsProps, selectSystemProps, textInputHandlerProps, textInputProps, variantProp, viewProps } from '../utils';
3
4
  import InputSupports from '../InputSupports';
4
5
  import TextInputBase from './TextInputBase';
@@ -61,6 +62,11 @@ const TextInput = /*#__PURE__*/forwardRef((_ref, ref) => {
61
62
  TextInput.displayName = 'TextInput';
62
63
  TextInput.propTypes = { ...selectedSystemPropTypes,
63
64
  ...textInputPropTypes,
65
+
66
+ /**
67
+ * A callback which if provided will get a clear button rendered and will be called whenever that button gets pressed.
68
+ */
69
+ onClear: PropTypes.func,
64
70
  tokens: getTokensPropType('TextInput'),
65
71
  variant: variantProp.propType
66
72
  };
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useEffect, useState } from 'react';
1
+ import React, { forwardRef, useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import Platform from "react-native-web/dist/exports/Platform";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
@@ -6,7 +6,9 @@ import NativeTextInput from "react-native-web/dist/exports/TextInput";
6
6
  import View from "react-native-web/dist/exports/View";
7
7
  import { applyTextStyles, useTheme, useThemeTokens, applyOuterBorder } from '../ThemeProvider';
8
8
  import StackView from '../StackView';
9
- import { a11yProps, getTokensPropType, selectSystemProps, textInputHandlerProps, textInputProps, useInputValue, useSpacingScale, variantProp, viewProps } from '../utils';
9
+ import IconButton from '../IconButton';
10
+ import { a11yProps, getTokensPropType, selectSystemProps, textInputHandlerProps, textInputProps, useCopy, useInputValue, useSpacingScale, variantProp, viewProps } from '../utils';
11
+ import dictionary from './dictionary';
10
12
  import { jsx as _jsx } from "react/jsx-runtime";
11
13
  import { jsxs as _jsxs } from "react/jsx-runtime";
12
14
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, textInputHandlerProps, textInputProps, viewProps]);
@@ -136,12 +138,14 @@ const selectButtonsContainerStyle = _ref5 => {
136
138
  const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
137
139
  let {
138
140
  buttons = [],
141
+ copy = 'en',
139
142
  height,
140
143
  inactive,
141
144
  initialValue,
142
145
  onBlur,
143
146
  onChange,
144
147
  onChangeText,
148
+ onClear,
145
149
  onFocus,
146
150
  onMouseOut,
147
151
  onMouseOver,
@@ -176,17 +180,22 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
176
180
  if (typeof onMouseOut === 'function') onMouseOut(event);
177
181
  };
178
182
 
183
+ const defaultRef = useRef();
184
+ const inputRef = ref !== null && ref !== void 0 ? ref : defaultRef;
179
185
  const {
180
186
  currentValue,
187
+ resetValue,
181
188
  setValue,
182
- isControlled
189
+ isControlled,
190
+ isDirty
183
191
  } = useInputValue({
184
192
  value,
185
193
  initialValue,
194
+ inputRef,
186
195
  onChange,
187
196
  readOnly
188
197
  });
189
- const element = ref === null || ref === void 0 ? void 0 : ref.current;
198
+ const element = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current;
190
199
  useEffect(() => {
191
200
  if (Platform.OS === 'web' && pattern && element) {
192
201
  // React Native Web doesn't support `pattern`, so we have to attach it via a ref,
@@ -210,9 +219,35 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
210
219
  };
211
220
  const themeTokens = useThemeTokens('TextInput', tokens, variant, states);
212
221
  const {
213
- icon: IconComponent,
214
- buttonsGap
222
+ buttonsGap,
223
+ clearButtonIcon: ClearButtonIcon,
224
+ icon: IconComponent
215
225
  } = themeTokens;
226
+ const buttonsGapSize = useSpacingScale(buttonsGap);
227
+ const getCopy = useCopy({
228
+ dictionary,
229
+ copy
230
+ });
231
+
232
+ if (onClear && isDirty) {
233
+ const handleClear = event => {
234
+ var _inputRef$current;
235
+
236
+ onClear === null || onClear === void 0 ? void 0 : onClear(event);
237
+ resetValue(event);
238
+ inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
239
+ };
240
+
241
+ buttons === null || buttons === void 0 ? void 0 : buttons.unshift( /*#__PURE__*/_jsx(IconButton, {
242
+ accessibilityLabel: getCopy('clearButtonAccessibilityLabel'),
243
+ icon: ClearButtonIcon,
244
+ onPress: handleClear,
245
+ variant: {
246
+ compact: true
247
+ }
248
+ }, "clear"));
249
+ }
250
+
216
251
  const inputProps = { ...selectProps(rest),
217
252
  editable: !inactive,
218
253
  onFocus: handleFocus,
@@ -224,9 +259,7 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
224
259
  // currentValue is being updated even if the input is not controlled, passing it down to the
225
260
  // Input could lead to changing its state from uncontrolled to controlled
226
261
  value: isControlled ? currentValue : undefined
227
- }; // Get the actual gap value for the current viewport
228
-
229
- const buttonsGapSize = useSpacingScale(buttonsGap);
262
+ };
230
263
  const {
231
264
  themeOptions
232
265
  } = useTheme();
@@ -236,7 +269,7 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
236
269
  return /*#__PURE__*/_jsxs(View, {
237
270
  style: selectOuterBorderStyles(themeTokens),
238
271
  children: [/*#__PURE__*/_jsx(NativeTextInput, {
239
- ref: ref,
272
+ ref: inputRef,
240
273
  style: nativeInputStyle,
241
274
  ...inputProps
242
275
  }), IconComponent && /*#__PURE__*/_jsx(View, {
@@ -260,12 +293,21 @@ const TextInputBase = /*#__PURE__*/forwardRef((_ref6, ref) => {
260
293
  TextInputBase.displayName = 'TextInputBase';
261
294
  TextInputBase.propTypes = { ...selectedSystemPropTypes,
262
295
  buttons: PropTypes.arrayOf(PropTypes.node),
296
+
297
+ /**
298
+ * Select English or French copy for the accessible labels.
299
+ * You may also pass in a custom dictionary object.
300
+ */
301
+ copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), PropTypes.shape({
302
+ clearButtonAccessibilityLabel: PropTypes.string
303
+ })]),
263
304
  height: PropTypes.number,
264
305
  inactive: PropTypes.bool,
265
306
  initialValue: PropTypes.string,
266
307
  onBlur: PropTypes.func,
267
308
  onChange: PropTypes.func,
268
309
  onChangeText: PropTypes.func,
310
+ onClear: PropTypes.func,
269
311
  onFocus: PropTypes.func,
270
312
  onMouseOut: PropTypes.func,
271
313
  onMouseOver: PropTypes.func,
@@ -0,0 +1,8 @@
1
+ export default {
2
+ en: {
3
+ clearButtonAccessibilityLabel: 'Clear'
4
+ },
5
+ fr: {
6
+ clearButtonAccessibilityLabel: 'Effacer'
7
+ }
8
+ };
@@ -5,18 +5,28 @@ import responsiveProps from '../utils/props/responsiveProps';
5
5
  import { jsx as _jsx } from "react/jsx-runtime";
6
6
  export const uninitialisedError = new Error('Theme context used outside of ThemeProvider');
7
7
  export const ThemeContext = /*#__PURE__*/createContext(uninitialisedError);
8
- export const ThemeSetterContext = /*#__PURE__*/createContext(uninitialisedError);
8
+ export const ThemeSetterContext = /*#__PURE__*/createContext(uninitialisedError); // These options default to `true` in v1.x to match legacy defaults and avoid breaking changes.
9
+ // This should change in future major releases to become "opt-in" legacy support.
10
+
11
+ const defaultThemeOptions = {
12
+ // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
13
+ forceAbsoluteFontSizing: true,
14
+ // TODO: switch `forceZIndex` to be false by default in the next major version
15
+ forceZIndex: true,
16
+ // TODO: switch `enableHelmetSSR` to be false by default in the next major version
17
+ enableHelmetSSR: true
18
+ };
9
19
 
10
20
  const ThemeProvider = _ref => {
11
21
  let {
12
22
  children,
13
23
  defaultTheme,
14
- // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
15
- themeOptions = {
16
- forceAbsoluteFontSizing: true
17
- }
24
+ themeOptions = {}
18
25
  } = _ref;
19
- const [theme, setTheme] = useState(defaultTheme); // Validate the theme tokens version on every render.
26
+ const [theme, setTheme] = useState(defaultTheme);
27
+ const appliedThemeOptions = { ...defaultThemeOptions,
28
+ ...themeOptions
29
+ }; // Validate the theme tokens version on every render.
20
30
  // This will intentionally break the application when attempting to use an invalid theme.
21
31
  // This will surface an incompatibility quickly rather than allowing the potential for strange bugs due to missing or incompatible tokens.
22
32
 
@@ -25,7 +35,7 @@ const ThemeProvider = _ref => {
25
35
  value: setTheme,
26
36
  children: /*#__PURE__*/_jsx(ThemeContext.Provider, {
27
37
  value: { ...theme,
28
- themeOptions
38
+ themeOptions: appliedThemeOptions
29
39
  },
30
40
  children: children
31
41
  })
@@ -48,9 +58,16 @@ ThemeProvider.propTypes = {
48
58
  * relative sizing (in `rem`, scales depending on the browser settings)
49
59
  * - `contentMaxWidth`: allows configuration of the content max width to be used in components
50
60
  * such as Footnote and Notification to avoid content to stretch width more then the page's width
61
+ * - `forceZIndex`: available on web only, when set to false, sets zIndex on `View` to be `auto`
62
+ * and when true, sets zIndex to be `0` (the default from `react-native-web`)
63
+ * - `enableHelmetSSR`: on Web SSR, allows React Helmet to run during server-side rendering. This should be
64
+ * disabled unless a web app has been specifically configured to stop React Helmet accumulating
65
+ * instances (which may cause a memory leak). See React Helmet's docs: https://github.com/nfl/react-helmet
51
66
  */
52
67
  themeOptions: PropTypes.shape({
53
68
  forceAbsoluteFontSizing: PropTypes.bool,
69
+ forceZIndex: PropTypes.bool,
70
+ enableHelmetSSR: PropTypes.bool,
54
71
  contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number)
55
72
  })
56
73
  };
@@ -45,7 +45,9 @@ export function applyTextStyles(_ref) {
45
45
  // Don't set undefined font families. May need some validation here that the font is available.
46
46
  // Android doesn't recognise font weights natively so apply custom font weights via `fontFamily`.
47
47
  styles.fontFamily = "".concat(fontName).concat(fontWeight).concat(fontStyle);
48
- } else if (fontWeight) {
48
+ }
49
+
50
+ if (fontWeight) {
49
51
  // If using system default font, apply the font weight directly.
50
52
  // Font weight support in Android is limited to 'bold' or anything else === 'normal'.
51
53
  styles.fontWeight = Platform.OS === 'android' && Number(fontWeight) > 400 ? 'bold' : fontWeight;
@@ -21,6 +21,7 @@ export { default as InputSupports } from './InputSupports';
21
21
  export * from './Link';
22
22
  export { default as List, ListItem, ListBase } from './List';
23
23
  export { default as Modal } from './Modal';
24
+ export { default as MultiSelectFilter } from './MultiSelectFilter';
24
25
  export { default as Notification } from './Notification';
25
26
  export { default as Pagination } from './Pagination';
26
27
  export { default as Progress } from './Progress';
@@ -49,6 +50,7 @@ export { default as TooltipButton } from './TooltipButton';
49
50
  export { default as Typography } from './Typography';
50
51
  export { default as A11yInfoProvider, useA11yInfo } from './A11yInfoProvider';
51
52
  export { default as BaseProvider } from './BaseProvider';
53
+ export { useHydrationContext } from './BaseProvider/HydrationContext';
52
54
  export { default as ViewportProvider, useViewport, ViewportContext } from './ViewportProvider';
53
55
  export { default as ThemeProvider, useTheme, useSetTheme, useThemeTokens, getThemeTokens, applyOuterBorder, applyTextStyles, applyShadowToken } from './ThemeProvider';
54
56
  export * from './utils';