@telus-uds/components-base 1.5.0 → 1.6.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 (96) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +13 -0
  3. package/CHANGELOG.json +98 -1
  4. package/CHANGELOG.md +24 -2
  5. package/__tests__/FlexGrid/Row.test.jsx +100 -25
  6. package/__tests__/utils/containUniqueFields.test.js +25 -0
  7. package/component-docs.json +18 -2
  8. package/lib/Button/ButtonBase.js +1 -1
  9. package/lib/Button/ButtonGroup.js +20 -12
  10. package/lib/Card/PressableCardBase.js +1 -1
  11. package/lib/Checkbox/Checkbox.js +27 -16
  12. package/lib/Checkbox/CheckboxGroup.js +19 -5
  13. package/lib/ExpandCollapse/Panel.js +10 -10
  14. package/lib/FlexGrid/Col/Col.js +13 -3
  15. package/lib/FlexGrid/Row/Row.js +8 -2
  16. package/lib/InputLabel/InputLabel.js +27 -25
  17. package/lib/Link/LinkBase.js +19 -6
  18. package/lib/Modal/Modal.js +18 -18
  19. package/lib/Radio/Radio.js +23 -12
  20. package/lib/Radio/RadioGroup.js +12 -3
  21. package/lib/RadioCard/RadioCard.js +1 -1
  22. package/lib/RadioCard/RadioCardGroup.js +11 -2
  23. package/lib/Select/Select.js +2 -3
  24. package/lib/Tags/Tags.js +23 -17
  25. package/lib/TextInput/TextArea.js +2 -2
  26. package/lib/TextInput/TextInput.js +12 -2
  27. package/lib/TextInput/TextInputBase.js +1 -1
  28. package/lib/TextInput/propTypes.js +8 -1
  29. package/lib/ToggleSwitch/ToggleSwitch.js +5 -2
  30. package/lib/ToggleSwitch/ToggleSwitchGroup.js +20 -12
  31. package/lib/utils/containUniqueFields.js +34 -0
  32. package/lib/utils/index.js +10 -1
  33. package/lib/utils/props/handlerProps.js +72 -0
  34. package/lib/utils/props/index.js +14 -0
  35. package/lib/utils/props/inputSupportsProps.js +3 -5
  36. package/lib-module/Button/ButtonBase.js +2 -2
  37. package/lib-module/Button/ButtonGroup.js +15 -6
  38. package/lib-module/Card/PressableCardBase.js +2 -2
  39. package/lib-module/Checkbox/Checkbox.js +28 -17
  40. package/lib-module/Checkbox/CheckboxGroup.js +20 -7
  41. package/lib-module/ExpandCollapse/Panel.js +10 -10
  42. package/lib-module/FlexGrid/Col/Col.js +13 -3
  43. package/lib-module/FlexGrid/Row/Row.js +8 -2
  44. package/lib-module/InputLabel/InputLabel.js +28 -25
  45. package/lib-module/Link/LinkBase.js +19 -6
  46. package/lib-module/Modal/Modal.js +19 -19
  47. package/lib-module/Radio/Radio.js +24 -13
  48. package/lib-module/Radio/RadioGroup.js +13 -4
  49. package/lib-module/RadioCard/RadioCard.js +2 -2
  50. package/lib-module/RadioCard/RadioCardGroup.js +12 -3
  51. package/lib-module/Select/Select.js +2 -3
  52. package/lib-module/Tags/Tags.js +18 -11
  53. package/lib-module/TextInput/TextArea.js +3 -3
  54. package/lib-module/TextInput/TextInput.js +11 -3
  55. package/lib-module/TextInput/TextInputBase.js +2 -2
  56. package/lib-module/TextInput/propTypes.js +7 -1
  57. package/lib-module/ToggleSwitch/ToggleSwitch.js +6 -3
  58. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +15 -6
  59. package/lib-module/utils/containUniqueFields.js +26 -0
  60. package/lib-module/utils/index.js +2 -1
  61. package/lib-module/utils/props/handlerProps.js +59 -0
  62. package/lib-module/utils/props/index.js +1 -0
  63. package/lib-module/utils/props/inputSupportsProps.js +3 -5
  64. package/package.json +5 -5
  65. package/src/Button/ButtonBase.jsx +8 -2
  66. package/src/Button/ButtonGroup.jsx +51 -34
  67. package/src/Card/PressableCardBase.jsx +6 -1
  68. package/src/Checkbox/Checkbox.jsx +35 -23
  69. package/src/Checkbox/CheckboxGroup.jsx +52 -22
  70. package/src/ExpandCollapse/Panel.jsx +9 -9
  71. package/src/FlexGrid/Col/Col.jsx +11 -2
  72. package/src/FlexGrid/Row/Row.jsx +8 -2
  73. package/src/InputLabel/InputLabel.jsx +36 -27
  74. package/src/Link/LinkBase.jsx +20 -4
  75. package/src/Modal/Modal.jsx +30 -26
  76. package/src/Radio/Radio.jsx +26 -14
  77. package/src/Radio/RadioGroup.jsx +39 -21
  78. package/src/RadioCard/RadioCard.jsx +6 -1
  79. package/src/RadioCard/RadioCardGroup.jsx +17 -1
  80. package/src/Select/Select.jsx +2 -2
  81. package/src/Tags/Tags.jsx +23 -9
  82. package/src/TextInput/TextArea.jsx +5 -1
  83. package/src/TextInput/TextInput.jsx +13 -3
  84. package/src/TextInput/TextInputBase.jsx +6 -1
  85. package/src/TextInput/propTypes.js +7 -1
  86. package/src/ToggleSwitch/ToggleSwitch.jsx +11 -2
  87. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +19 -6
  88. package/src/utils/containUniqueFields.js +32 -0
  89. package/src/utils/index.js +1 -0
  90. package/src/utils/props/handlerProps.js +47 -0
  91. package/src/utils/props/index.js +1 -0
  92. package/src/utils/props/inputSupportsProps.js +3 -4
  93. package/stories/InputLabel/InputLabel.stories.jsx +25 -28
  94. package/stories/Modal/Modal.stories.jsx +25 -0
  95. package/stories/Search/Search.stories.jsx +4 -1
  96. package/stories/TextInput/TextInput.stories.jsx +40 -2
@@ -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,
@@ -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, 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, 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, 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, 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, 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, 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;
@@ -7,11 +7,11 @@ import InputLabel from '../InputLabel';
7
7
  import ButtonBase from '../Button/ButtonBase';
8
8
  import StackView from '../StackView';
9
9
  import { useThemeTokensCallback, applyShadowToken } from '../ThemeProvider';
10
- import { a11yProps, getTokensPropType, selectTokens, pressProps, selectSystemProps, useUniqueId, variantProp, viewProps } from '../utils';
10
+ import { a11yProps, focusHandlerProps, getTokensPropType, selectTokens, pressProps, selectSystemProps, useUniqueId, variantProp, viewProps } from '../utils';
11
11
  import { useInputValue } from '../utils/input';
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, pressProps, viewProps]);
14
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, pressProps, viewProps]);
15
15
 
16
16
  const selectButtonTokens = tokens => selectTokens('Button', { ...tokens,
17
17
  // Width tokens are applied to our inner track. Disable Button width token so it wraps our track width.
@@ -117,7 +117,7 @@ const ToggleSwitch = /*#__PURE__*/forwardRef(({
117
117
  space: 2,
118
118
  direction: "row",
119
119
  children: [Boolean(label) && /*#__PURE__*/_jsx(View, {
120
- style: selectLabelStyles(themeTokens),
120
+ style: [selectLabelStyles(themeTokens), staticStyles.containText],
121
121
  children: /*#__PURE__*/_jsx(InputLabel, {
122
122
  forId: inputId,
123
123
  label: label,
@@ -219,6 +219,9 @@ const staticStyles = StyleSheet.create({
219
219
  switch: {
220
220
  alignItems: 'center',
221
221
  justifyContent: 'center'
222
+ },
223
+ containText: {
224
+ flexShrink: 1
222
225
  }
223
226
  });
224
227
  export default ToggleSwitch;
@@ -7,10 +7,10 @@ import Fieldset from '../Fieldset';
7
7
  import { getStackedContent } from '../StackView';
8
8
  import { useViewport } from '../ViewportProvider';
9
9
  import { useThemeTokens } from '../ThemeProvider';
10
- import { a11yProps, getTokensPropType, pressProps, selectSystemProps, variantProp, viewProps } from '../utils/props';
11
- import { useMultipleInputValues } from '../utils/input';
10
+ import { a11yProps, containUniqueFields, focusHandlerProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps } from '../utils';
12
11
  import { jsx as _jsx } from "react/jsx-runtime";
13
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps]);
12
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
14
14
  const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
15
15
  variant,
16
16
  tokens,
@@ -57,13 +57,20 @@ const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
57
57
  ...rest
58
58
  });
59
59
  const itemA11yRole = selectedProps.accessibilityRole === 'radiogroup' ? 'radio' : 'switch';
60
+ const uniqueFields = ['id', 'label'];
61
+
62
+ if (!containUniqueFields(items, uniqueFields)) {
63
+ throw new Error(`ToggleSwitchGroup items must have unique ${uniqueFields.join(', ')}`);
64
+ }
65
+
60
66
  const toggleSwitches = items.map(({
61
67
  label,
62
68
  id = label,
63
69
  accessibilityLabel = label,
64
70
  onChange: itemOnChange,
65
71
  ref: itemRef,
66
- tooltip: itemTooltip
72
+ tooltip: itemTooltip,
73
+ ...itemRest
67
74
  }, index) => {
68
75
  const isSelected = currentValues.includes(id);
69
76
 
@@ -89,7 +96,8 @@ const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
89
96
  inactive: inactive,
90
97
  label: label,
91
98
  tooltip: itemTooltip,
92
- ...itemA11y
99
+ ...itemA11y,
100
+ ...selectItemProps(itemRest)
93
101
  }, id);
94
102
  });
95
103
  return /*#__PURE__*/_jsx(Fieldset, {
@@ -123,7 +131,8 @@ ToggleSwitchGroup.propTypes = { ...selectedSystemPropTypes,
123
131
  /**
124
132
  * The options a user may select
125
133
  */
126
- items: PropTypes.arrayOf(PropTypes.shape({
134
+ items: PropTypes.arrayOf(PropTypes.shape({ ...selectedItemPropTypes,
135
+
127
136
  /**
128
137
  * The text displayed to the user on the label.
129
138
  */
@@ -0,0 +1,26 @@
1
+ // Returns true if there are no duplicate values of the fields listed
2
+ // in the `fields` array across the objects in the `items` array, false
3
+ // otherwise.
4
+ // Note that if a value of a field in an item is not set, it will be
5
+ // excluded from comparison.
6
+ const containUniqueFields = (items, fields) => {
7
+ const map = [];
8
+ const itemsHaveDuplicateFields = items.some(item => fields.some(field => {
9
+ if (!map[field]) {
10
+ map[field] = [];
11
+ }
12
+
13
+ if (!item[field]) {
14
+ // We exclude empty values from comparison
15
+ return false;
16
+ } // Duplicate found!
17
+
18
+
19
+ if (map[field][item[field]]) return true;
20
+ map[field][item[field]] = true;
21
+ return false;
22
+ }));
23
+ return !itemsHaveDuplicateFields;
24
+ };
25
+
26
+ export default containUniqueFields;
@@ -12,4 +12,5 @@ export { default as useResponsiveProp } from './useResponsiveProp';
12
12
  export * from './useResponsiveProp';
13
13
  export { default as useUniqueId } from './useUniqueId';
14
14
  export { default as withLinkRouter } from './withLinkRouter';
15
- export * from './ssr';
15
+ export * from './ssr';
16
+ export { default as containUniqueFields } from './containUniqueFields';
@@ -0,0 +1,59 @@
1
+ import PropTypes from 'prop-types';
2
+ export const focusHandlerProps = {
3
+ types: {
4
+ /**
5
+ * onBlur handler
6
+ */
7
+ onBlur: PropTypes.func,
8
+
9
+ /**
10
+ * onFocus handler
11
+ */
12
+ onFocus: PropTypes.func
13
+ },
14
+ select: ({
15
+ onBlur,
16
+ onFocus
17
+ }) => ({
18
+ onBlur,
19
+ onFocus
20
+ })
21
+ };
22
+ export const textInputHandlerProps = {
23
+ types: {
24
+ /**
25
+ * onChange handler
26
+ */
27
+ onChange: PropTypes.func,
28
+
29
+ /**
30
+ * onChangeText handler
31
+ */
32
+ onChangeText: PropTypes.func,
33
+
34
+ /**
35
+ * onSubmit handler
36
+ */
37
+ onSubmit: PropTypes.func,
38
+
39
+ /**
40
+ * onSubmitEditing handler
41
+ */
42
+ onSubmitEditing: PropTypes.func
43
+ },
44
+ select: ({
45
+ onChange,
46
+ onChangeText,
47
+ onSubmit,
48
+ onSubmitEditing
49
+ }) => ({
50
+ onChange,
51
+ onChangeText,
52
+ onSubmit,
53
+ onSubmitEditing
54
+ })
55
+ };
56
+ export default {
57
+ focusHandlerProps,
58
+ textInputHandlerProps
59
+ };
@@ -1,4 +1,5 @@
1
1
  export * from './tokens';
2
+ export * from './handlerProps';
2
3
  export { default as a11yProps } from './a11yProps';
3
4
  export { default as clickProps } from './clickProps';
4
5
  export { default as copyPropTypes } from './copyPropTypes';
@@ -38,17 +38,15 @@ export default {
38
38
  hintPosition,
39
39
  feedback,
40
40
  tooltip,
41
- validation,
42
- ...rest
41
+ validation
43
42
  }) => ({
44
- props: {
43
+ supportsProps: {
45
44
  label,
46
45
  hint,
47
46
  hintPosition,
48
47
  feedback,
49
48
  tooltip,
50
49
  validation
51
- },
52
- ...rest
50
+ }
53
51
  })
54
52
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telus-uds/components-base",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Base components",
5
5
  "keywords": [
6
6
  "base"
@@ -22,14 +22,14 @@
22
22
  },
23
23
  "scripts": {
24
24
  "test": "jest",
25
- "lint": "telus-standard",
26
- "lint:fix": "telus-standard --fix",
25
+ "lint": "yarn --cwd ../.. lint:path --color packages/components-base",
26
+ "lint:fix": "yarn --cwd ../.. lint:path --fix packages/components-base",
27
27
  "format": "prettier --write .",
28
28
  "build": "yarn build:code && yarn build:docs",
29
29
  "build:main": "babel src -d lib",
30
30
  "build:module": "babel src -d lib-module --env-name module",
31
31
  "build:code": "yarn build:main && yarn build:module",
32
- "build:docs": "babel-node --plugins=react-docgen-alpha generate-component-docs.js",
32
+ "build:docs": "babel-node --plugins=@nearform/babel-plugin-react-docgen generate-component-docs.js",
33
33
  "storybook": "start-storybook",
34
34
  "dev": "yarn build:code --watch",
35
35
  "release": "standard-version"
@@ -59,7 +59,7 @@
59
59
  "dependencies": {
60
60
  "airbnb-prop-types": "^2.16.0",
61
61
  "@telus-uds/system-constants": "^1.0.2",
62
- "@telus-uds/system-theme-tokens": "^1.4.0",
62
+ "@telus-uds/system-theme-tokens": "^1.5.0",
63
63
  "lodash.debounce": "^4.0.8",
64
64
  "lodash.merge": "^4.6.2",
65
65
  "prop-types": "^15.7.2",
@@ -6,8 +6,9 @@ import { applyTextStyles, applyShadowToken, applyOuterBorder } from '../ThemePro
6
6
  import buttonPropTypes from './propTypes'
7
7
  import {
8
8
  a11yProps,
9
- getCursorStyle,
10
9
  clickProps,
10
+ focusHandlerProps,
11
+ getCursorStyle,
11
12
  linkProps,
12
13
  resolvePressableState,
13
14
  resolvePressableTokens,
@@ -17,7 +18,12 @@ import {
17
18
  withLinkRouter
18
19
  } from '../utils'
19
20
 
20
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, linkProps, viewProps])
21
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
22
+ a11yProps,
23
+ focusHandlerProps,
24
+ linkProps,
25
+ viewProps
26
+ ])
21
27
 
22
28
  const getOuterBorderOffset = ({ outerBorderGap = 0, outerBorderWidth = 0 }) =>
23
29
  outerBorderGap + outerBorderWidth
@@ -9,17 +9,25 @@ import { useViewport } from '../ViewportProvider'
9
9
  import { useThemeTokens, useThemeTokensCallback } from '../ThemeProvider'
10
10
  import {
11
11
  a11yProps,
12
+ containUniqueFields,
13
+ focusHandlerProps,
12
14
  pressProps,
13
15
  getTokensPropType,
14
16
  selectSystemProps,
15
17
  selectTokens,
18
+ useMultipleInputValues,
16
19
  variantProp,
17
20
  viewProps
18
- } from '../utils/props'
19
- import { useMultipleInputValues } from '../utils/input'
21
+ } from '../utils'
20
22
  import { getPressHandlersWithArgs } from '../utils/pressability'
21
23
 
22
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps])
24
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
25
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
26
+ a11yProps,
27
+ focusHandlerProps,
28
+ pressProps,
29
+ viewProps
30
+ ])
23
31
 
24
32
  const ButtonGroup = forwardRef(
25
33
  (
@@ -61,6 +69,11 @@ const ButtonGroup = forwardRef(
61
69
  })
62
70
  const itemA11yRole = systemProps.accessibilityRole === 'radiogroup' ? 'radio' : 'checkbox'
63
71
 
72
+ const uniqueFields = ['id', 'label']
73
+ if (!containUniqueFields(items, uniqueFields)) {
74
+ throw new Error(`ButtonGroup items must have unique ${uniqueFields.join(', ')}`)
75
+ }
76
+
64
77
  return (
65
78
  <StackWrap
66
79
  {...systemProps}
@@ -69,41 +82,44 @@ const ButtonGroup = forwardRef(
69
82
  tokens={stackTokens}
70
83
  ref={ref}
71
84
  >
72
- {items.map(({ label, id = label, accessibilityLabel, ref: itemRef }, index) => {
73
- const isSelected = currentValues.includes(id)
85
+ {items.map(
86
+ ({ label, id = label, accessibilityLabel, ref: itemRef, ...itemRest }, index) => {
87
+ const isSelected = currentValues.includes(id)
74
88
 
75
- // Pass an object of relevant component state as first argument for any passed-in press handlers
76
- const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
89
+ // Pass an object of relevant component state as first argument for any passed-in press handlers
90
+ const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
77
91
 
78
- const handlePress = (event) => {
79
- if (pressHandlers.onPress) pressHandlers.onPress()
80
- toggleOneValue(id, event)
81
- }
92
+ const handlePress = (event) => {
93
+ if (pressHandlers.onPress) pressHandlers.onPress(event)
94
+ toggleOneValue(id, event)
95
+ }
82
96
 
83
- const itemA11y = {
84
- accessibilityState: { checked: isSelected },
85
- accessibilityRole: itemA11yRole,
86
- accessibilityLabel,
87
- ...a11yProps.getPositionInSet(items.length, index)
88
- }
97
+ const itemA11y = {
98
+ accessibilityState: { checked: isSelected },
99
+ accessibilityRole: itemA11yRole,
100
+ accessibilityLabel,
101
+ ...a11yProps.getPositionInSet(items.length, index)
102
+ }
89
103
 
90
- // Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
91
- // "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
92
- return (
93
- <ButtonBase
94
- ref={itemRef}
95
- key={id}
96
- {...pressHandlers}
97
- onPress={handlePress}
98
- tokens={getButtonTokens}
99
- selected={isSelected}
100
- inactive={inactive}
101
- {...itemA11y}
102
- >
103
- {label}
104
- </ButtonBase>
105
- )
106
- })}
104
+ // Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
105
+ // "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
106
+ return (
107
+ <ButtonBase
108
+ ref={itemRef}
109
+ key={id}
110
+ {...pressHandlers}
111
+ onPress={handlePress}
112
+ tokens={getButtonTokens}
113
+ selected={isSelected}
114
+ inactive={inactive}
115
+ {...itemA11y}
116
+ {...selectItemProps(itemRest)}
117
+ >
118
+ {label}
119
+ </ButtonBase>
120
+ )
121
+ }
122
+ )}
107
123
  </StackWrap>
108
124
  )
109
125
  }
@@ -124,6 +140,7 @@ ButtonGroup.propTypes = {
124
140
  */
125
141
  items: PropTypes.arrayOf(
126
142
  PropTypes.shape({
143
+ ...selectedItemPropTypes,
127
144
  /**
128
145
  * The text displayed to the user in the button, describing this option,
129
146
  * passed to the button as its child.
@@ -7,6 +7,7 @@ import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider'
7
7
  import {
8
8
  a11yProps,
9
9
  clickProps,
10
+ focusHandlerProps,
10
11
  getTokenNames,
11
12
  getTokensSetPropType,
12
13
  linkProps,
@@ -20,7 +21,11 @@ import {
20
21
  } from '../utils'
21
22
  import CardBase from './CardBase'
22
23
 
23
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
24
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
25
+ a11yProps,
26
+ focusHandlerProps,
27
+ viewProps
28
+ ])
24
29
 
25
30
  const tokenKeys = [
26
31
  ...getTokenNames('Card'),