@telus-uds/components-base 1.5.0 → 1.7.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 (125) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +13 -0
  3. package/CHANGELOG.json +154 -1
  4. package/CHANGELOG.md +47 -2
  5. package/__tests__/FlexGrid/Row.test.jsx +100 -25
  6. package/__tests__/utils/containUniqueFields.test.js +25 -0
  7. package/component-docs.json +78 -26
  8. package/generate-component-docs.js +20 -7
  9. package/lib/Button/ButtonBase.js +1 -1
  10. package/lib/Button/ButtonGroup.js +20 -12
  11. package/lib/Card/PressableCardBase.js +1 -1
  12. package/lib/Checkbox/Checkbox.js +27 -16
  13. package/lib/Checkbox/CheckboxGroup.js +19 -5
  14. package/lib/ExpandCollapse/Panel.js +10 -10
  15. package/lib/FlexGrid/Col/Col.js +13 -3
  16. package/lib/FlexGrid/Row/Row.js +8 -2
  17. package/lib/InputLabel/InputLabel.js +27 -25
  18. package/lib/Link/LinkBase.js +19 -6
  19. package/lib/Link/TextButton.js +1 -10
  20. package/lib/List/ListItem.js +22 -12
  21. package/lib/Modal/Modal.js +18 -18
  22. package/lib/Radio/Radio.js +23 -12
  23. package/lib/Radio/RadioGroup.js +12 -3
  24. package/lib/RadioCard/RadioCard.js +1 -1
  25. package/lib/RadioCard/RadioCardGroup.js +11 -2
  26. package/lib/Search/Search.js +27 -19
  27. package/lib/Select/Select.js +2 -3
  28. package/lib/Tags/Tags.js +23 -17
  29. package/lib/TextInput/TextArea.js +2 -2
  30. package/lib/TextInput/TextInput.js +12 -2
  31. package/lib/TextInput/TextInputBase.js +1 -1
  32. package/lib/TextInput/propTypes.js +8 -1
  33. package/lib/ToggleSwitch/ToggleSwitch.js +5 -2
  34. package/lib/ToggleSwitch/ToggleSwitchGroup.js +20 -12
  35. package/lib/Typography/Typography.js +12 -10
  36. package/lib/index.js +22 -1
  37. package/lib/utils/containUniqueFields.js +34 -0
  38. package/lib/utils/index.js +10 -1
  39. package/lib/utils/input.js +5 -6
  40. package/lib/utils/props/handlerProps.js +72 -0
  41. package/lib/utils/props/index.js +32 -0
  42. package/lib/utils/props/inputSupportsProps.js +3 -5
  43. package/lib/utils/props/linkProps.js +3 -7
  44. package/lib/utils/props/textInputProps.js +206 -0
  45. package/lib/utils/props/textProps.js +72 -0
  46. package/lib-module/Button/ButtonBase.js +2 -2
  47. package/lib-module/Button/ButtonGroup.js +15 -6
  48. package/lib-module/Card/PressableCardBase.js +2 -2
  49. package/lib-module/Checkbox/Checkbox.js +28 -17
  50. package/lib-module/Checkbox/CheckboxGroup.js +20 -7
  51. package/lib-module/ExpandCollapse/Panel.js +10 -10
  52. package/lib-module/FlexGrid/Col/Col.js +13 -3
  53. package/lib-module/FlexGrid/Row/Row.js +8 -2
  54. package/lib-module/InputLabel/InputLabel.js +28 -25
  55. package/lib-module/Link/LinkBase.js +19 -6
  56. package/lib-module/Link/TextButton.js +1 -10
  57. package/lib-module/List/ListItem.js +22 -12
  58. package/lib-module/Modal/Modal.js +19 -19
  59. package/lib-module/Radio/Radio.js +24 -13
  60. package/lib-module/Radio/RadioGroup.js +13 -4
  61. package/lib-module/RadioCard/RadioCard.js +2 -2
  62. package/lib-module/RadioCard/RadioCardGroup.js +12 -3
  63. package/lib-module/Search/Search.js +29 -21
  64. package/lib-module/Select/Select.js +2 -3
  65. package/lib-module/Tags/Tags.js +18 -11
  66. package/lib-module/TextInput/TextArea.js +3 -3
  67. package/lib-module/TextInput/TextInput.js +11 -3
  68. package/lib-module/TextInput/TextInputBase.js +2 -2
  69. package/lib-module/TextInput/propTypes.js +7 -1
  70. package/lib-module/ToggleSwitch/ToggleSwitch.js +6 -3
  71. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +15 -6
  72. package/lib-module/Typography/Typography.js +13 -11
  73. package/lib-module/index.js +1 -1
  74. package/lib-module/utils/containUniqueFields.js +26 -0
  75. package/lib-module/utils/index.js +2 -1
  76. package/lib-module/utils/input.js +6 -6
  77. package/lib-module/utils/props/handlerProps.js +59 -0
  78. package/lib-module/utils/props/index.js +3 -0
  79. package/lib-module/utils/props/inputSupportsProps.js +3 -5
  80. package/lib-module/utils/props/linkProps.js +3 -7
  81. package/lib-module/utils/props/textInputProps.js +193 -0
  82. package/lib-module/utils/props/textProps.js +59 -0
  83. package/package.json +5 -5
  84. package/src/Button/ButtonBase.jsx +8 -2
  85. package/src/Button/ButtonGroup.jsx +51 -34
  86. package/src/Card/PressableCardBase.jsx +6 -1
  87. package/src/Checkbox/Checkbox.jsx +35 -23
  88. package/src/Checkbox/CheckboxGroup.jsx +52 -22
  89. package/src/ExpandCollapse/Panel.jsx +9 -9
  90. package/src/FlexGrid/Col/Col.jsx +11 -2
  91. package/src/FlexGrid/Row/Row.jsx +8 -2
  92. package/src/InputLabel/InputLabel.jsx +36 -27
  93. package/src/Link/LinkBase.jsx +20 -4
  94. package/src/Link/TextButton.jsx +1 -19
  95. package/src/List/ListItem.jsx +17 -9
  96. package/src/Modal/Modal.jsx +30 -26
  97. package/src/Radio/Radio.jsx +26 -14
  98. package/src/Radio/RadioGroup.jsx +39 -21
  99. package/src/RadioCard/RadioCard.jsx +6 -1
  100. package/src/RadioCard/RadioCardGroup.jsx +17 -1
  101. package/src/Search/Search.jsx +33 -21
  102. package/src/Select/Select.jsx +2 -2
  103. package/src/Tags/Tags.jsx +23 -9
  104. package/src/TextInput/TextArea.jsx +7 -1
  105. package/src/TextInput/TextInput.jsx +15 -3
  106. package/src/TextInput/TextInputBase.jsx +8 -1
  107. package/src/TextInput/propTypes.js +7 -1
  108. package/src/ToggleSwitch/ToggleSwitch.jsx +11 -2
  109. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +19 -6
  110. package/src/Typography/Typography.jsx +13 -9
  111. package/src/index.js +4 -1
  112. package/src/utils/containUniqueFields.js +32 -0
  113. package/src/utils/index.js +1 -0
  114. package/src/utils/input.js +5 -7
  115. package/src/utils/props/handlerProps.js +47 -0
  116. package/src/utils/props/index.js +3 -0
  117. package/src/utils/props/inputSupportsProps.js +3 -4
  118. package/src/utils/props/linkProps.js +3 -6
  119. package/src/utils/props/textInputProps.js +177 -0
  120. package/src/utils/props/textProps.js +58 -0
  121. package/stories/InputLabel/InputLabel.stories.jsx +25 -28
  122. package/stories/Modal/Modal.stories.jsx +25 -0
  123. package/stories/Search/Search.stories.jsx +53 -3
  124. package/stories/TextInput/TextInput.stories.jsx +40 -2
  125. package/__tests__/Link/LinkBase.test.jsx +0 -22
@@ -26,8 +26,7 @@ const selectBulletContainerStyles = ({
26
26
  itemBulletContainerAlign
27
27
  }) => ({
28
28
  width: itemBulletContainerWidth,
29
- alignItems: itemBulletContainerAlign,
30
- justifyContent: itemBulletContainerAlign
29
+ alignItems: itemBulletContainerAlign
31
30
  });
32
31
 
33
32
  const selectItemIconTokens = ({
@@ -38,16 +37,20 @@ const selectItemIconTokens = ({
38
37
  color: itemIconColor
39
38
  });
40
39
 
41
- const selectCommonIconStyles = ({
40
+ const selectSideItemContainerStyles = ({
41
+ listGutter,
42
42
  iconMarginTop
43
43
  }) => ({
44
- marginTop: iconMarginTop
45
- });
44
+ marginTop: iconMarginTop,
45
+ marginRight: listGutter
46
+ }); // Align bullets with the top line of text the same way icons are aligned
46
47
 
47
- const selectSideItemContainerStyles = ({
48
- listGutter
48
+
49
+ const selectBulletPositioningStyles = ({
50
+ itemIconSize
49
51
  }) => ({
50
- marginRight: listGutter
52
+ width: itemIconSize,
53
+ height: itemIconSize
51
54
  });
52
55
 
53
56
  const selectItemStyles = ({
@@ -100,8 +103,8 @@ const ListItem = /*#__PURE__*/forwardRef(({
100
103
  const dividerStyles = selectDividerStyles(themeTokens);
101
104
  const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens);
102
105
  const itemBulletStyles = selectBulletStyles(themeTokens);
106
+ const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens);
103
107
  const iconTokens = selectItemIconTokens(themeTokens);
104
- const commonIconStyles = selectCommonIconStyles(themeTokens);
105
108
  const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens);
106
109
  const accessibilityRole = Platform.select({
107
110
  web: 'listitem',
@@ -144,7 +147,7 @@ const ListItem = /*#__PURE__*/forwardRef(({
144
147
 
145
148
  if (icon) {
146
149
  return /*#__PURE__*/_jsx(View, {
147
- style: [sideItemContainerStyles, commonIconStyles],
150
+ style: sideItemContainerStyles,
148
151
  children: /*#__PURE__*/_jsx(IconComponent, {
149
152
  size: iconSize || iconTokens.size,
150
153
  color: iconColor || iconTokens.color
@@ -155,8 +158,11 @@ const ListItem = /*#__PURE__*/forwardRef(({
155
158
  return /*#__PURE__*/_jsx(View, {
156
159
  style: [sideItemContainerStyles, itemBulletContainerStyles],
157
160
  children: /*#__PURE__*/_jsx(View, {
158
- style: itemBulletStyles,
159
- testID: "unordered-item-bullet"
161
+ style: [staticStyles.bulletPositioning, itemBulletPositioningStyles],
162
+ children: /*#__PURE__*/_jsx(View, {
163
+ style: itemBulletStyles,
164
+ testID: "unordered-item-bullet"
165
+ })
160
166
  })
161
167
  });
162
168
  };
@@ -176,6 +182,10 @@ const staticStyles = StyleSheet.create({
176
182
  },
177
183
  wrap: {
178
184
  flex: 1
185
+ },
186
+ bulletPositioning: {
187
+ alignItems: 'center',
188
+ justifyContent: 'center'
179
189
  }
180
190
  });
181
191
  ListItem.propTypes = { ...selectedSystemPropTypes,
@@ -6,9 +6,9 @@ import NativeModal from "react-native-web/dist/exports/Modal";
6
6
  import Platform from "react-native-web/dist/exports/Platform";
7
7
  import PropTypes from 'prop-types';
8
8
  import { applyShadowToken, useThemeTokens } from '../ThemeProvider';
9
- import { a11yProps, copyPropTypes, getTokensPropType, selectSystemProps, useCopy, variantProp, viewProps } from '../utils';
9
+ import { a11yProps, copyPropTypes, getTokensPropType, selectSystemProps, useCopy, variantProp, viewProps, selectTokens } from '../utils';
10
10
  import { useViewport } from '../ViewportProvider';
11
- import ButtonBase from '../Button/ButtonBase';
11
+ import IconButton from '../IconButton';
12
12
  import dictionary from './dictionary';
13
13
  import { jsx as _jsx } from "react/jsx-runtime";
14
14
  import { jsxs as _jsxs } from "react/jsx-runtime";
@@ -65,14 +65,6 @@ const selectCloseButtonContainerStyles = ({
65
65
  paddingRight,
66
66
  paddingTop
67
67
  });
68
-
69
- const selectCloseIconProps = ({
70
- closeIconSize,
71
- closeIconColor
72
- }) => ({
73
- size: closeIconSize,
74
- color: closeIconColor
75
- });
76
68
  /**
77
69
  * A modal window is a secondary window that opens on top of the main one.
78
70
  * Users have to interact with it before they can carry out their task and return to the main window.
@@ -97,6 +89,7 @@ const Modal = /*#__PURE__*/forwardRef(({
97
89
  tokens,
98
90
  variant,
99
91
  copy,
92
+ closeButton,
100
93
  ...rest
101
94
  }, ref) => {
102
95
  const viewport = useViewport();
@@ -119,12 +112,15 @@ const Modal = /*#__PURE__*/forwardRef(({
119
112
 
120
113
  const handleKeyUp = event => {
121
114
  if (event.key === 'Escape') onClose();
122
- };
115
+ }; // Show the custom react node passed to `closedButton` or the default close button if `closeButton` is `undefined`.
116
+ // Hide the close button if `closeButton` is `null`.
117
+
118
+
119
+ const showCloseButton = closeButton !== null;
123
120
 
124
121
  if (!isOpen) {
125
122
  return null;
126
- } // TODO: replace the close button with IconButton when implemented (https://github.com/telus/universal-design-system/issues/281)
127
-
123
+ }
128
124
 
129
125
  return /*#__PURE__*/_jsx(NativeModal, {
130
126
  transparent: true,
@@ -139,15 +135,14 @@ const Modal = /*#__PURE__*/forwardRef(({
139
135
  ref: ref,
140
136
  style: [staticStyles.modal, selectModalStyles(themeTokens)],
141
137
  onKeyUp: handleKeyUp,
142
- children: [/*#__PURE__*/_jsx(View, {
138
+ children: [showCloseButton && /*#__PURE__*/_jsx(View, {
143
139
  style: [staticStyles.closeButtonContainer, selectCloseButtonContainerStyles(themeTokens)],
144
- children: /*#__PURE__*/_jsx(ButtonBase, {
140
+ children: closeButton || /*#__PURE__*/_jsx(IconButton, {
145
141
  onPress: handleClose,
142
+ icon: CloseIconComponent,
146
143
  accessibilityRole: "button",
147
144
  accessibilityLabel: closeLabel,
148
- children: // TODO: add close button interactive states after IconButton is done
149
- () => /*#__PURE__*/_jsx(CloseIconComponent, { ...selectCloseIconProps(themeTokens)
150
- })
145
+ tokens: selectTokens('IconButton', themeTokens, 'close')
151
146
  })
152
147
  }), children]
153
148
  })
@@ -168,7 +163,12 @@ Modal.propTypes = { ...selectedSystemPropTypes,
168
163
  onClose: PropTypes.func,
169
164
  maxWidth: PropTypes.bool,
170
165
  tokens: getTokensPropType('Modal'),
171
- variant: variantProp.propType
166
+ variant: variantProp.propType,
167
+
168
+ /**
169
+ * Pass a react node to override the default close button or pass `null` to hide the close button.
170
+ */
171
+ closeButton: PropTypes.node
172
172
  };
173
173
  export default Modal;
174
174
  const staticStyles = StyleSheet.create({
@@ -7,11 +7,11 @@ import View from "react-native-web/dist/exports/View";
7
7
  import RadioLabel from '../InputLabel/LabelContent';
8
8
  import RadioButton, { selectRadioButtonTokens } from './RadioButton';
9
9
  import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
10
- import { a11yProps, getTokensPropType, selectSystemProps, useInputValue, useUniqueId, variantProp, viewProps } from '../utils';
10
+ import { a11yProps, focusHandlerProps, getTokensPropType, selectSystemProps, useInputValue, useUniqueId, variantProp, viewProps } from '../utils';
11
11
  import StackView from '../StackView';
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
13
  import { jsxs as _jsxs } from "react/jsx-runtime";
14
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
14
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
15
15
 
16
16
  const selectContainerStyles = ({
17
17
  containerBackgroundColor,
@@ -162,22 +162,29 @@ const Radio = /*#__PURE__*/forwardRef(({
162
162
  hover,
163
163
  pressed
164
164
  });
165
+ const labelStyles = selectLabelStyles(stateTokens);
166
+ const alignWithLabel = label ? [staticStyles.alignWithLabel, {
167
+ height: labelStyles.lineHeight
168
+ }] : null;
165
169
  return /*#__PURE__*/_jsxs(StackView, {
166
170
  space: 0,
167
171
  children: [/*#__PURE__*/_jsxs(View, {
168
172
  style: [staticStyles.container, selectContainerStyles(stateTokens)],
169
- children: [/*#__PURE__*/_jsx(RadioButton, {
170
- tokens: selectRadioButtonTokens(stateTokens),
171
- isControlled: isControlled,
172
- isChecked: isChecked,
173
- inactive: inactive,
174
- defaultChecked: defaultChecked,
175
- inputId: inputId,
176
- handleChange: handleChange,
177
- name: inputName,
178
- value: value
173
+ children: [/*#__PURE__*/_jsx(View, {
174
+ style: alignWithLabel,
175
+ children: /*#__PURE__*/_jsx(RadioButton, {
176
+ tokens: selectRadioButtonTokens(stateTokens),
177
+ isControlled: isControlled,
178
+ isChecked: isChecked,
179
+ inactive: inactive,
180
+ defaultChecked: defaultChecked,
181
+ inputId: inputId,
182
+ handleChange: handleChange,
183
+ name: inputName,
184
+ value: value
185
+ })
179
186
  }), Boolean(label) && /*#__PURE__*/_jsx(Text, {
180
- style: selectLabelStyles(stateTokens),
187
+ style: labelStyles,
181
188
  children: /*#__PURE__*/_jsx(RadioLabel, {
182
189
  forId: inputId,
183
190
  children: label
@@ -259,5 +266,9 @@ const staticStyles = StyleSheet.create({
259
266
  container: {
260
267
  flexDirection: 'row',
261
268
  alignItems: 'center'
269
+ },
270
+ alignWithLabel: {
271
+ alignSelf: 'flex-start',
272
+ justifyContent: 'center'
262
273
  }
263
274
  });
@@ -3,12 +3,13 @@ import PropTypes from 'prop-types';
3
3
  import ABBPropTypes from 'airbnb-prop-types';
4
4
  import { useViewport } from '../ViewportProvider';
5
5
  import { useThemeTokens } from '../ThemeProvider';
6
- import { a11yProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
6
+ import { a11yProps, containUniqueFields, focusHandlerProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
7
7
  import { getStackedContent } from '../StackView';
8
8
  import Radio from './Radio';
9
9
  import Fieldset from '../Fieldset';
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
11
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
12
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
12
13
  /**
13
14
  * A group of Radios that behave as a radio group. Use when users select a single choice from mutually
14
15
  * exclusive options.
@@ -92,11 +93,18 @@ const RadioGroup = /*#__PURE__*/forwardRef(({
92
93
  onChange,
93
94
  readOnly: readOnly || inactive
94
95
  });
96
+ const uniqueFields = ['id', 'label'];
97
+
98
+ if (!containUniqueFields(items, uniqueFields)) {
99
+ throw new Error(`RadioGroup items must have unique ${uniqueFields.join(', ')}`);
100
+ }
101
+
95
102
  const radios = items.map(({
96
103
  label,
97
104
  id,
98
105
  onChange: itemOnChange,
99
- ref: itemRef
106
+ ref: itemRef,
107
+ ...itemRest
100
108
  }, index) => {
101
109
  const radioId = id || `Radio[${index}]`;
102
110
  const isChecked = currentValue === radioId;
@@ -115,7 +123,8 @@ const RadioGroup = /*#__PURE__*/forwardRef(({
115
123
  label: label,
116
124
  name: inputGroupName,
117
125
  tokens: radioTokens,
118
- variant: variant
126
+ variant: variant,
127
+ ...selectItemProps(itemRest)
119
128
  }, radioId);
120
129
  });
121
130
  return /*#__PURE__*/_jsx(Fieldset, {
@@ -157,7 +166,7 @@ RadioGroup.propTypes = { ...selectedSystemPropTypes,
157
166
  /**
158
167
  * Array of objects containing specifics for each Radio to be rendered in the group.
159
168
  */
160
- items: PropTypes.arrayOf(PropTypes.exact({
169
+ items: PropTypes.arrayOf(PropTypes.exact({ ...selectedItemPropTypes,
161
170
  label: PropTypes.string,
162
171
  id: PropTypes.string,
163
172
  onChange: PropTypes.func,
@@ -4,13 +4,13 @@ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
4
  import Text from "react-native-web/dist/exports/Text";
5
5
  import View from "react-native-web/dist/exports/View";
6
6
  import { useThemeTokensCallback, applyTextStyles } from '../ThemeProvider';
7
- import { a11yProps, getTokensPropType, selectSystemProps, selectTokens, useInputValue, useUniqueId, variantProp, viewProps } from '../utils';
7
+ import { a11yProps, focusHandlerProps, getTokensPropType, selectSystemProps, selectTokens, useInputValue, useUniqueId, variantProp, viewProps } from '../utils';
8
8
  import { PressableCardBase, selectPressableCardTokens } from '../Card';
9
9
  import StackView from '../StackView';
10
10
  import RadioButton, { selectRadioButtonTokens } from '../Radio/RadioButton';
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  import { jsxs as _jsxs } from "react/jsx-runtime";
13
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
14
14
  /**
15
15
  * A Card that behaves like a radio button. Use when users select a single choice from mutually exclusive options
16
16
  * with need to show additional information for each option. The whole card is interactive as one item.
@@ -2,12 +2,13 @@ import React, { forwardRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useViewport } from '../ViewportProvider';
4
4
  import { useThemeTokens } from '../ThemeProvider';
5
- import { a11yProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
5
+ import { a11yProps, containUniqueFields, focusHandlerProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
6
6
  import StackView, { StackWrap } from '../StackView';
7
7
  import RadioCard from './RadioCard';
8
8
  import Fieldset from '../Fieldset';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
11
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
11
12
  /**
12
13
  * A group of Cards that behave as a radio button group. Use when users select a single choice from mutually
13
14
  * exclusive options with need to show additional information for each option. The whole cards are each
@@ -98,6 +99,12 @@ const RadioCardGroup = /*#__PURE__*/forwardRef(({
98
99
  // and also needs 'radiogroup' role on fieldset for correct description on focusing the set.
99
100
  // TODO: test this on more web screen readers.
100
101
 
102
+ const uniqueFields = ['id'];
103
+
104
+ if (!containUniqueFields(items, uniqueFields)) {
105
+ throw new Error(`RadioCardGroup items must have unique ${uniqueFields.join(', ')}`);
106
+ }
107
+
101
108
  return /*#__PURE__*/_jsx(Fieldset, {
102
109
  ref: ref,
103
110
  name: inputGroupName,
@@ -117,7 +124,8 @@ const RadioCardGroup = /*#__PURE__*/forwardRef(({
117
124
  title,
118
125
  content,
119
126
  id,
120
- onChange: itemOnChange
127
+ onChange: itemOnChange,
128
+ ...itemRest
121
129
  }, index) => {
122
130
  const cardId = id || `RadioCard[${index}]`;
123
131
 
@@ -136,6 +144,7 @@ const RadioCardGroup = /*#__PURE__*/forwardRef(({
136
144
  tokens: radioCardTokens,
137
145
  variant: variant,
138
146
  readOnly: readOnly,
147
+ ...selectItemProps(itemRest),
139
148
  ...props,
140
149
  children: content
141
150
  }, cardId);
@@ -164,7 +173,7 @@ RadioCardGroup.propTypes = { ...selectedSystemPropTypes,
164
173
  /**
165
174
  * Array of objects containing specifics for each RadioCard to be rendered in the group.
166
175
  */
167
- items: PropTypes.arrayOf(PropTypes.exact({
176
+ items: PropTypes.arrayOf(PropTypes.exact({ ...selectedItemPropTypes,
168
177
  title: PropTypes.string,
169
178
  content: PropTypes.node,
170
179
  id: PropTypes.string,
@@ -1,9 +1,9 @@
1
- import React, { forwardRef, useState } from 'react';
1
+ import React, { forwardRef } from 'react';
2
2
  import View from "react-native-web/dist/exports/View";
3
3
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
4
  import PropTypes from 'prop-types';
5
5
  import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider';
6
- import { a11yProps, getTokensPropType, selectSystemProps, selectTokens, useSpacingScale, variantProp, viewProps } from '../utils';
6
+ import { a11yProps, getTokensPropType, selectSystemProps, selectTokens, useInputValue, useSpacingScale, textInputHandlerProps, textInputProps, variantProp, viewProps } from '../utils';
7
7
  import TextInputBase from '../TextInput/TextInputBase';
8
8
  import ButtonBase from '../Button/ButtonBase';
9
9
  import StackView from '../StackView';
@@ -11,7 +11,8 @@ import useCopy from '../utils/useCopy';
11
11
  import dictionary from './dictionary';
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
13
  import { jsxs as _jsxs } from "react/jsx-runtime";
14
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
14
+ const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps]);
15
+ const [selectInputProps, selectedInputPropTypes] = selectSystemProps([textInputHandlerProps, textInputProps]);
15
16
 
16
17
  const selectInputTokens = ({
17
18
  searchTokens,
@@ -66,8 +67,9 @@ const selectIconTokens = ({
66
67
 
67
68
 
68
69
  const Search = /*#__PURE__*/forwardRef(({
69
- initialValue = '',
70
- placeholder = 'Search',
70
+ initialValue,
71
+ value,
72
+ placeholder,
71
73
  inactive,
72
74
  onChange,
73
75
  onSubmit,
@@ -78,7 +80,14 @@ const Search = /*#__PURE__*/forwardRef(({
78
80
  variant,
79
81
  ...rest
80
82
  }, ref) => {
81
- const [value, setValue] = useState(initialValue);
83
+ const {
84
+ currentValue = '',
85
+ setValue
86
+ } = useInputValue({
87
+ value,
88
+ initialValue,
89
+ onChange
90
+ });
82
91
  const themeTokens = useThemeTokens('Search', tokens, variant);
83
92
  const buttonTokens = useThemeTokens('SearchButton', tokens, variant);
84
93
  const getThemeTokens = useThemeTokensCallback('Search', tokens, variant);
@@ -99,26 +108,24 @@ const Search = /*#__PURE__*/forwardRef(({
99
108
 
100
109
  const handleSubmit = event => {
101
110
  if (onSubmit !== undefined) {
102
- onSubmit(value, event);
111
+ onSubmit(currentValue, event);
103
112
  }
104
113
  };
105
114
 
106
- const handleChange = (currentValue, event) => {
107
- setValue(currentValue, event);
108
- if (onChange !== undefined) onChange(currentValue, event);
109
- };
110
-
111
115
  const handleClear = event => {
112
116
  setValue('', event);
113
117
  if (onClear !== undefined) onClear('', event);
114
- if (onChange !== undefined) onChange('', event);
115
118
  };
116
119
 
117
- const isEmpty = value === '';
120
+ const isEmpty = currentValue === ''; // Accessibility label should always be present and correctly localised
121
+
122
+ const a11yLabelText = accessibilityLabel || getCopy('accessibilityLabel'); // Placeholder is optional and may be unset by passing an empty string
123
+
124
+ const placeholderText = placeholder ?? a11yLabelText;
118
125
  return /*#__PURE__*/_jsxs(View, {
119
126
  style: staticStyles.container,
120
- ...selectProps(rest),
121
- children: [/*#__PURE__*/_jsx(TextInputBase, {
127
+ ...selectContainerProps(rest),
128
+ children: [/*#__PURE__*/_jsx(TextInputBase, { ...selectInputProps(rest),
122
129
  ref: ref,
123
130
  tokens: appearances => selectInputTokens({
124
131
  searchTokens: getThemeTokens(appearances),
@@ -126,15 +133,15 @@ const Search = /*#__PURE__*/forwardRef(({
126
133
  buttonsGapSize,
127
134
  isEmpty
128
135
  }),
129
- placeholder: placeholder,
136
+ placeholder: placeholderText,
130
137
  placeholderTextColor: placeholderColor,
131
138
  inactive: inactive,
132
139
  enablesReturnKeyAutomatically: true,
133
140
  returnKeyType: "search",
134
- value: value,
135
- onChange: handleChange,
141
+ value: currentValue,
142
+ onChange: setValue,
136
143
  onSubmitEditing: handleSubmit,
137
- accessibilityLabel: accessibilityLabel || getCopy('accessibilityLabel')
144
+ accessibilityLabel: a11yLabelText
138
145
  }), /*#__PURE__*/_jsx(View, {
139
146
  style: [staticStyles.iconsContainer, selectIconsContainerStyle(themeTokens)],
140
147
  children: /*#__PURE__*/_jsxs(StackView, {
@@ -166,7 +173,8 @@ const Search = /*#__PURE__*/forwardRef(({
166
173
  });
167
174
  });
168
175
  Search.displayName = 'Search';
169
- Search.propTypes = { ...selectedSystemPropTypes,
176
+ Search.propTypes = { ...selectedContainerPropTypes,
177
+ ...selectedInputPropTypes,
170
178
 
171
179
  /**
172
180
  * Use this to set the initial value of the search input.
@@ -197,7 +197,7 @@ const Select = /*#__PURE__*/forwardRef(({
197
197
  const handleMouseOut = () => setIsHovered(false);
198
198
 
199
199
  const {
200
- props: supportsProps,
200
+ supportsProps,
201
201
  ...selectedProps
202
202
  } = selectProps(rest);
203
203
  const themeTokens = useThemeTokens('Select', tokens, variant, {
@@ -238,8 +238,7 @@ const Select = /*#__PURE__*/forwardRef(({
238
238
  }), ValidationIconComponent && /*#__PURE__*/_jsx(View, {
239
239
  pointerEvents: "none",
240
240
  style: [staticStyles.iconContainer, selectValidationIconContainerStyles(themeTokens)],
241
- children: /*#__PURE__*/_jsx(ValidationIconComponent, {
242
- tokens: selectValidationIconTokens(themeTokens)
241
+ children: /*#__PURE__*/_jsx(ValidationIconComponent, { ...selectValidationIconTokens(themeTokens)
243
242
  })
244
243
  }), IconComponent && Platform.OS !== 'android' &&
245
244
  /*#__PURE__*/
@@ -9,11 +9,11 @@ import Icon from '../Icon';
9
9
  import { StackWrap, getStackedContent } from '../StackView';
10
10
  import { useViewport } from '../ViewportProvider';
11
11
  import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider';
12
- import { a11yProps, getTokensPropType, pressProps, selectSystemProps, selectTokens, variantProp, viewProps } from '../utils/props';
13
- import { useMultipleInputValues } from '../utils/input';
12
+ import { a11yProps, containUniqueFields, focusHandlerProps, getTokensPropType, pressProps, selectSystemProps, selectTokens, useMultipleInputValues, variantProp, viewProps } from '../utils';
14
13
  import { getPressHandlersWithArgs } from '../utils/pressability';
15
14
  import { jsx as _jsx } from "react/jsx-runtime";
16
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps]);
15
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
16
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]);
17
17
 
18
18
  const selectIconTextTokens = ({
19
19
  icon,
@@ -95,6 +95,12 @@ const Tags = /*#__PURE__*/forwardRef(({
95
95
  ...rest
96
96
  });
97
97
  const itemA11yRole = 'checkbox';
98
+ const uniqueFields = ['id', 'label'];
99
+
100
+ if (!containUniqueFields(items, uniqueFields)) {
101
+ throw new Error(`Tags items must have unique ${uniqueFields.join(', ')}`);
102
+ }
103
+
98
104
  return /*#__PURE__*/_jsx(StackWrap, {
99
105
  ref: ref,
100
106
  ...selectedProps,
@@ -104,8 +110,8 @@ const Tags = /*#__PURE__*/forwardRef(({
104
110
  children: items.map(({
105
111
  label,
106
112
  id = label,
107
- accessibilityLabel,
108
- ref: itemRef
113
+ ref: itemRef,
114
+ ...itemRest
109
115
  }, index) => {
110
116
  const isSelected = currentValues.includes(id); // Pass an object of relevant component state as first argument for any passed-in press handlers
111
117
 
@@ -116,17 +122,17 @@ const Tags = /*#__PURE__*/forwardRef(({
116
122
  }]);
117
123
 
118
124
  const handlePress = event => {
119
- if (pressHandlers.onPress) pressHandlers.onPress();
125
+ if (pressHandlers.onPress) pressHandlers.onPress(event);
120
126
  toggleOneValue(id, event);
121
127
  };
122
128
 
123
- const itemA11y = {
129
+ const itemProps = {
124
130
  accessibilityState: {
125
131
  checked: isSelected
126
132
  },
127
133
  accessibilityRole: itemA11yRole,
128
- accessibilityLabel,
129
- ...a11yProps.getPositionInSet(items.length, index)
134
+ ...a11yProps.getPositionInSet(items.length, index),
135
+ ...selectItemProps(itemRest)
130
136
  };
131
137
  return /*#__PURE__*/_jsx(ButtonBase, {
132
138
  ref: itemRef,
@@ -135,7 +141,7 @@ const Tags = /*#__PURE__*/forwardRef(({
135
141
  tokens: getButtonTokens,
136
142
  selected: isSelected,
137
143
  inactive: inactive,
138
- ...itemA11y,
144
+ ...itemProps,
139
145
  children: ({
140
146
  textStyles,
141
147
  ...buttonState
@@ -188,7 +194,8 @@ Tags.propTypes = { ...selectedSystemPropTypes,
188
194
  /**
189
195
  * The options a user may select
190
196
  */
191
- items: PropTypes.arrayOf(PropTypes.shape({
197
+ items: PropTypes.arrayOf(PropTypes.shape({ ...selectedItemPropTypes,
198
+
192
199
  /**
193
200
  * The text displayed to the user in the button, describing this option,
194
201
  * passed to the button as its child.
@@ -1,12 +1,12 @@
1
1
  import React, { forwardRef, useState } from 'react';
2
2
  import Platform from "react-native-web/dist/exports/Platform";
3
- import { a11yProps, getTokensPropType, inputSupportsProps, selectSystemProps, variantProp, viewProps } from '../utils';
3
+ import { a11yProps, focusHandlerProps, getTokensPropType, inputSupportsProps, selectSystemProps, textInputHandlerProps, textInputProps, variantProp, viewProps } from '../utils';
4
4
  import InputSupports from '../InputSupports';
5
5
  import TextInputBase from './TextInputBase';
6
6
  import { useThemeTokens } from '../ThemeProvider';
7
7
  import textInputPropTypes from './propTypes';
8
8
  import { jsx as _jsx } from "react/jsx-runtime";
9
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inputSupportsProps, viewProps]);
9
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, inputSupportsProps, textInputHandlerProps, textInputProps, viewProps]);
10
10
  /**
11
11
  * Use to collect long-form information such as product feedback or support queries.
12
12
  * Due to React Native's implementation of `TextInput` it's not possible to access the current value by passing a ref.
@@ -54,7 +54,7 @@ const TextArea = /*#__PURE__*/forwardRef(({
54
54
  };
55
55
 
56
56
  const {
57
- props: supportsProps,
57
+ supportsProps,
58
58
  ...selectedProps
59
59
  } = selectProps(rest);
60
60
  const inputProps = { ...selectedProps,
@@ -1,10 +1,11 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { a11yProps, getTokensPropType, inputSupportsProps, selectSystemProps, variantProp, viewProps } from '../utils';
2
+ import Platform from "react-native-web/dist/exports/Platform";
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';
5
6
  import textInputPropTypes from './propTypes';
6
7
  import { jsx as _jsx } from "react/jsx-runtime";
7
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inputSupportsProps, viewProps]);
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, inputSupportsProps, textInputHandlerProps, textInputProps, viewProps]);
8
9
  /**
9
10
  * A basic text input component. Use in forms or individually to receive user's input.
10
11
  * Due to React Native's implementation of `TextInput` it's not possible to access the current value by passing a ref.
@@ -27,10 +28,17 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, inp
27
28
  const TextInput = /*#__PURE__*/forwardRef(({
28
29
  tokens,
29
30
  variant = {},
31
+ pattern,
30
32
  ...rest
31
33
  }, ref) => {
34
+ React.useEffect(() => {
35
+ if (Platform.OS === 'web' && pattern && ref.current) {
36
+ // eslint-disable-next-line no-param-reassign
37
+ ref.current.pattern = pattern;
38
+ }
39
+ }, [ref, pattern]);
32
40
  const {
33
- props: supportsProps,
41
+ supportsProps,
34
42
  ...selectedProps
35
43
  } = selectProps(rest);
36
44
  const inputProps = { ...selectedProps,
@@ -5,10 +5,10 @@ import NativeTextInput from "react-native-web/dist/exports/TextInput";
5
5
  import View from "react-native-web/dist/exports/View";
6
6
  import PropTypes from 'prop-types';
7
7
  import { applyTextStyles, useThemeTokens, applyOuterBorder } from '../ThemeProvider';
8
- import { a11yProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
8
+ import { a11yProps, getTokensPropType, selectSystemProps, textInputHandlerProps, textInputProps, useInputValue, variantProp, viewProps } from '../utils';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  import { jsxs as _jsxs } from "react/jsx-runtime";
11
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
11
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, textInputHandlerProps, textInputProps, viewProps]);
12
12
 
13
13
  const selectInputStyles = ({
14
14
  backgroundColor,
@@ -1,4 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
+ import Platform from "react-native-web/dist/exports/Platform";
2
3
  const textInputPropTypes = {
3
4
  /**
4
5
  * If the input's state is to be controlled by a parent component, use this prop
@@ -26,6 +27,11 @@ const textInputPropTypes = {
26
27
  * Use to react upon input's value changes. Required when the `value` prop is set.
27
28
  * Will receive the input's value as an argument.
28
29
  */
29
- onChange: PropTypes.func
30
+ onChange: PropTypes.func,
31
+ ...Platform.select({
32
+ web: {
33
+ pattern: PropTypes.string
34
+ }
35
+ })
30
36
  };
31
37
  export default textInputPropTypes;