@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
@@ -10,11 +10,11 @@ import CheckboxLabel from '../InputLabel/LabelContent';
10
10
  import Feedback from '../Feedback';
11
11
  import StackView from '../StackView';
12
12
  import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
13
- import { a11yProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
13
+ import { a11yProps, focusHandlerProps, getTokensPropType, selectSystemProps, useInputValue, variantProp, viewProps } from '../utils';
14
14
  import useUniqueId from '../utils/useUniqueId';
15
15
  import { jsx as _jsx } from "react/jsx-runtime";
16
16
  import { jsxs as _jsxs } from "react/jsx-runtime";
17
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
17
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
18
18
 
19
19
  const selectInputStyles = ({
20
20
  iconBackgroundColor,
@@ -204,24 +204,31 @@ const Checkbox = /*#__PURE__*/forwardRef(({
204
204
  pressed
205
205
  });
206
206
  const iconTokens = selectIconTokens(stateTokens);
207
+ const labelStyles = selectLabelStyles(stateTokens);
208
+ const alignWithLabel = label ? [staticStyles.alignWithLabel, {
209
+ height: labelStyles.lineHeight
210
+ }] : null;
207
211
  return /*#__PURE__*/_jsxs(View, {
208
212
  style: staticStyles.container,
209
- children: [/*#__PURE__*/_jsxs(View, {
210
- style: StyleSheet.flatten([staticStyles.defaultInputStyles, selectInputStyles(stateTokens, isChecked)]),
211
- testID: "Checkbox-Input",
212
- children: [/*#__PURE__*/_jsx(CheckboxInput, {
213
- checked: isChecked,
214
- defaultChecked: defaultChecked,
215
- disabled: inactive,
216
- id: inputId,
217
- isControlled: isControlled,
218
- name: name,
219
- value: value
220
- }), isChecked && IconComponent && /*#__PURE__*/_jsx(IconComponent, { ...iconTokens,
221
- testID: "Checkbox-Icon"
222
- })]
213
+ children: [/*#__PURE__*/_jsx(View, {
214
+ style: alignWithLabel,
215
+ children: /*#__PURE__*/_jsxs(View, {
216
+ style: [staticStyles.defaultInputStyles, selectInputStyles(stateTokens, isChecked)],
217
+ testID: "Checkbox-Input",
218
+ children: [/*#__PURE__*/_jsx(CheckboxInput, {
219
+ checked: isChecked,
220
+ defaultChecked: defaultChecked,
221
+ disabled: inactive,
222
+ id: inputId,
223
+ isControlled: isControlled,
224
+ name: name,
225
+ value: value
226
+ }), isChecked && IconComponent && /*#__PURE__*/_jsx(IconComponent, { ...iconTokens,
227
+ testID: "Checkbox-Icon"
228
+ })]
229
+ })
223
230
  }), Boolean(label) && /*#__PURE__*/_jsx(Text, {
224
- style: selectLabelStyles(stateTokens),
231
+ style: labelStyles,
225
232
  children: /*#__PURE__*/_jsx(CheckboxLabel, {
226
233
  forId: inputId,
227
234
  children: label
@@ -317,5 +324,9 @@ const staticStyles = StyleSheet.create({
317
324
  defaultInputStyles: {
318
325
  alignItems: 'center',
319
326
  justifyContent: 'center'
327
+ },
328
+ alignWithLabel: {
329
+ alignSelf: 'flex-start',
330
+ justifyContent: 'center'
320
331
  }
321
332
  });
@@ -3,10 +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 { getTokensPropType, useMultipleInputValues, variantProp } from '../utils';
6
+ import { a11yProps, containUniqueFields, focusHandlerProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps } from '../utils';
7
7
  import { getStackedContent } from '../StackView';
8
8
  import Checkbox from './Checkbox';
9
9
  import Fieldset from '../Fieldset';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
12
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
10
13
  /**
11
14
  * A group of Checkboxs that behave as a fieldset. Use when users select any number of choices from options.
12
15
  *
@@ -55,7 +58,6 @@ import Fieldset from '../Fieldset';
55
58
  * ```
56
59
  */
57
60
 
58
- import { jsx as _jsx } from "react/jsx-runtime";
59
61
  const CheckboxGroup = /*#__PURE__*/forwardRef(({
60
62
  tokens,
61
63
  radioTokens,
@@ -71,7 +73,8 @@ const CheckboxGroup = /*#__PURE__*/forwardRef(({
71
73
  onChange,
72
74
  readOnly,
73
75
  name: inputGroupName,
74
- inactive
76
+ inactive,
77
+ ...rest
75
78
  }, ref) => {
76
79
  const viewport = useViewport();
77
80
  const {
@@ -89,11 +92,18 @@ const CheckboxGroup = /*#__PURE__*/forwardRef(({
89
92
  onChange,
90
93
  readOnly: readOnly || inactive
91
94
  });
95
+ const uniqueFields = ['id', 'label'];
96
+
97
+ if (!containUniqueFields(items, uniqueFields)) {
98
+ throw new Error(`CheckboxGroup items must have unique ${uniqueFields.join(', ')}`);
99
+ }
100
+
92
101
  const checkboxes = items.map(({
93
102
  label,
94
103
  id,
95
104
  onChange: itemOnChange,
96
- ref: itemRef
105
+ ref: itemRef,
106
+ ...itemRest
97
107
  }, index) => {
98
108
  const checkboxId = id || `Checkbox[${index}]`;
99
109
 
@@ -111,7 +121,8 @@ const CheckboxGroup = /*#__PURE__*/forwardRef(({
111
121
  label: label,
112
122
  name: inputGroupName,
113
123
  tokens: radioTokens,
114
- variant: variant
124
+ variant: variant,
125
+ ...selectItemProps(itemRest)
115
126
  }, checkboxId);
116
127
  });
117
128
  return /*#__PURE__*/_jsx(Fieldset, {
@@ -124,6 +135,7 @@ const CheckboxGroup = /*#__PURE__*/forwardRef(({
124
135
  feedback: feedback,
125
136
  inactive: inactive,
126
137
  validation: validation,
138
+ ...selectProps(rest),
127
139
  children: getStackedContent(checkboxes, {
128
140
  space,
129
141
  direction: 'column'
@@ -131,7 +143,8 @@ const CheckboxGroup = /*#__PURE__*/forwardRef(({
131
143
  });
132
144
  });
133
145
  CheckboxGroup.displayName = 'CheckboxGroup';
134
- CheckboxGroup.propTypes = {
146
+ CheckboxGroup.propTypes = { ...selectedSystemPropTypes,
147
+
135
148
  /**
136
149
  * Optional theme token overrides for the outer CheckboxGroup component
137
150
  */
@@ -150,7 +163,7 @@ CheckboxGroup.propTypes = {
150
163
  /**
151
164
  * Array of objects containing specifics for each Checkbox to be rendered in the group.
152
165
  */
153
- items: PropTypes.arrayOf(PropTypes.exact({
166
+ items: PropTypes.arrayOf(PropTypes.exact({ ...selectedItemPropTypes,
154
167
  label: PropTypes.string,
155
168
  id: PropTypes.string,
156
169
  onChange: PropTypes.func,
@@ -64,12 +64,13 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef(({
64
64
  };
65
65
 
66
66
  const onContainerLayout = event => {
67
- if (containerHeight === null) {
68
- const {
69
- layout: {
70
- height = 0
71
- } = {}
72
- } = event.nativeEvent;
67
+ const {
68
+ layout: {
69
+ height = 0
70
+ } = {}
71
+ } = event.nativeEvent;
72
+
73
+ if (Platform.OS === 'web' || Platform.OS !== 'web' && containerHeight === null) {
73
74
  setContainerHeight(height);
74
75
  }
75
76
  };
@@ -94,12 +95,11 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef(({
94
95
  tokens: controlTokens,
95
96
  onPress: handleControlPress,
96
97
  children: control
97
- }), /*#__PURE__*/_jsx(View, {
98
- style: overflowContainerStyles,
98
+ }), /*#__PURE__*/_jsx(Animated.View, {
99
+ style: [overflowContainerStyles, animatedStyles, staticStyles.itemsContainer],
99
100
  ...focusabilityProps,
100
- children: /*#__PURE__*/_jsx(Animated.View, {
101
+ children: /*#__PURE__*/_jsx(View, {
101
102
  onLayout: onContainerLayout,
102
- style: [staticStyles.itemsContainer, animatedStyles],
103
103
  children: /*#__PURE__*/_jsx(View, {
104
104
  style: selectContainerStyles(themeTokens),
105
105
  children: children
@@ -22,6 +22,7 @@ const Col = /*#__PURE__*/forwardRef(({
22
22
  mdOffset,
23
23
  lgOffset,
24
24
  xlOffset,
25
+ flex,
25
26
  ...viewProps
26
27
  }, ref) => {
27
28
  const gutter = useContext(GutterContext);
@@ -95,8 +96,11 @@ const Col = /*#__PURE__*/forwardRef(({
95
96
  paddingLeft: gutter ? 16 : 0,
96
97
  paddingRight: gutter ? 16 : 0
97
98
  };
98
- let hidingStyles = {};
99
- const shown = Platform.OS === 'web' ? 'block' : 'flex';
99
+ let hidingStyles = {}; // TODO: consider setting this to always 'flex' in a major release.
100
+ // `display: block` is invalid in native apps.
101
+ // See https://telusdigital.atlassian.net/browse/UDS1-92
102
+
103
+ const shown = !flex && Platform.OS === 'web' ? 'block' : 'flex';
100
104
 
101
105
  if (viewPort === viewports.xs) {
102
106
  hidingStyles = {
@@ -260,6 +264,12 @@ Col.propTypes = {
260
264
  *
261
265
  * Accepts a `PropType.string` following the [responsive prop](#/Layout?id=responsive) structure.
262
266
  */
263
- horizontalAlign: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['left', 'center', 'right']))
267
+ horizontalAlign: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['left', 'center', 'right'])),
268
+
269
+ /**
270
+ * (web only) Stretches the column to fill vertical space using `display: flex`.
271
+ * In native apps, FlexGrid.Col behaves as if this is always true (as do all `View`s).
272
+ */
273
+ flex: PropTypes.bool
264
274
  };
265
275
  export default Col;
@@ -86,25 +86,31 @@ const Row = /*#__PURE__*/forwardRef(({
86
86
  const viewPort = useViewport();
87
87
  const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse]);
88
88
  let flexDirection = '';
89
+ let flexWrap = '';
89
90
 
90
91
  if (viewPort === viewports.xs) {
91
92
  flexDirection = reverseLevel[0] ? 'row-reverse' : 'row';
93
+ flexWrap = reverseLevel[0] ? 'wrap-reverse' : 'wrap';
92
94
  }
93
95
 
94
96
  if (viewPort === viewports.sm) {
95
97
  flexDirection = reverseLevel[1] ? 'row-reverse' : 'row';
98
+ flexWrap = reverseLevel[1] ? 'wrap-reverse' : 'wrap';
96
99
  }
97
100
 
98
101
  if (viewPort === viewports.md) {
99
102
  flexDirection = reverseLevel[2] ? 'row-reverse' : 'row';
103
+ flexWrap = reverseLevel[2] ? 'wrap-reverse' : 'wrap';
100
104
  }
101
105
 
102
106
  if (viewPort === viewports.lg) {
103
107
  flexDirection = reverseLevel[3] ? 'row-reverse' : 'row';
108
+ flexWrap = reverseLevel[3] ? 'wrap-reverse' : 'wrap';
104
109
  }
105
110
 
106
111
  if (viewPort === viewports.xl) {
107
112
  flexDirection = reverseLevel[4] ? 'row-reverse' : 'row';
113
+ flexWrap = reverseLevel[4] ? 'wrap-reverse' : 'wrap';
108
114
  }
109
115
 
110
116
  return /*#__PURE__*/_jsx(View, {
@@ -112,6 +118,7 @@ const Row = /*#__PURE__*/forwardRef(({
112
118
  ...rest,
113
119
  style: [styles.row, {
114
120
  flexDirection,
121
+ flexWrap,
115
122
  ...horizontalAlignStyles(horizontalAlign),
116
123
  ...verticalAlignStyles(verticalAlign),
117
124
  ...distributeStyles(distribute)
@@ -128,8 +135,7 @@ const styles = StyleSheet.create({
128
135
  flexGrow: 0,
129
136
  flexShrink: 1,
130
137
  flexBasis: 'auto',
131
- flexDirection: 'row',
132
- flexWrap: 'wrap'
138
+ flexDirection: 'row'
133
139
  }
134
140
  });
135
141
  Row.propTypes = {
@@ -9,6 +9,7 @@ import LabelContent from './LabelContent';
9
9
  import Tooltip from '../Tooltip';
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
11
  import { jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { Fragment as _Fragment } from "react/jsx-runtime";
12
13
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
14
 
14
15
  const selectLabelStyles = tokens => applyTextStyles(selectTokens('Typography', tokens));
@@ -47,25 +48,29 @@ const InputLabel = /*#__PURE__*/forwardRef(({
47
48
  const themeTokens = useThemeTokens('InputLabel', tokens, variant);
48
49
  const hasTooltip = tooltip !== undefined;
49
50
  const isHintInline = hintPosition === 'inline';
50
- return /*#__PURE__*/_jsxs(View, {
51
- ref: ref,
52
- style: [staticStyles.container, !isHintInline && staticStyles.containerWithHintBelow],
53
- ...selectProps(rest),
54
- children: [/*#__PURE__*/_jsx(Text, {
55
- style: [selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label],
56
- children: /*#__PURE__*/_jsx(LabelContent, {
57
- forId: forId,
58
- children: label
59
- })
60
- }), hint && isHintInline && /*#__PURE__*/_jsx(Text, {
61
- style: [selectHintStyles(themeTokens), hasTooltip && selectGapStyles(themeTokens)],
62
- nativeID: hintId,
63
- children: hint
64
- }), hasTooltip && /*#__PURE__*/_jsx(View, {
65
- style: staticStyles.tooltipAlign,
66
- children: /*#__PURE__*/_jsx(Tooltip, {
67
- content: tooltip
68
- })
51
+ return /*#__PURE__*/_jsxs(_Fragment, {
52
+ children: [/*#__PURE__*/_jsxs(View, {
53
+ ref: ref,
54
+ style: staticStyles.container,
55
+ ...selectProps(rest),
56
+ children: [/*#__PURE__*/_jsx(Text, {
57
+ style: [selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label],
58
+ children: /*#__PURE__*/_jsx(LabelContent, {
59
+ forId: forId,
60
+ children: label
61
+ })
62
+ }), hint && isHintInline && /*#__PURE__*/_jsx(Text, {
63
+ style: [selectHintStyles(themeTokens), hasTooltip && selectGapStyles(themeTokens), staticStyles.label],
64
+ nativeID: hintId,
65
+ children: hint
66
+ }), hasTooltip && /*#__PURE__*/_jsx(View, {
67
+ style: [staticStyles.tooltipAlign, {
68
+ height: themeTokens.fontSize * themeTokens.lineHeight
69
+ }],
70
+ children: /*#__PURE__*/_jsx(Tooltip, {
71
+ content: tooltip
72
+ })
73
+ })]
69
74
  }), hint && !isHintInline && /*#__PURE__*/_jsx(Text, {
70
75
  style: [selectHintStyles(themeTokens), staticStyles.hintBelow],
71
76
  nativeID: hintId,
@@ -111,21 +116,19 @@ InputLabel.propTypes = { ...selectedSystemPropTypes,
111
116
  export default InputLabel;
112
117
  const staticStyles = StyleSheet.create({
113
118
  container: {
114
- display: 'flex',
119
+ flexShrink: 1,
115
120
  flexDirection: 'row',
116
121
  alignItems: 'baseline'
117
122
  },
118
- containerWithHintBelow: {
119
- flexWrap: 'wrap'
120
- },
121
123
  label: {
122
- flexShrink: 0
124
+ flexShrink: 1
123
125
  },
124
126
  hintBelow: {
125
127
  flexBasis: '100%',
126
128
  flexShrink: 0
127
129
  },
128
130
  tooltipAlign: {
129
- alignSelf: 'center'
131
+ alignSelf: 'flex-start',
132
+ justifyContent: 'center'
130
133
  }
131
134
  });
@@ -35,13 +35,9 @@ Platform.OS === 'web' ? {
35
35
  } : {};
36
36
 
37
37
  const selectTextStyles = ({
38
- color,
39
- textLine,
40
- textLineStyle
38
+ color
41
39
  }) => ({
42
40
  color,
43
- textDecorationLine: textLine,
44
- textDecorationStyle: textLineStyle,
45
41
  ...Platform.select({
46
42
  web: {
47
43
  // TODO: https://github.com/telus/universal-design-system/issues/487
@@ -62,6 +58,22 @@ const selectBlockStyles = ({
62
58
  fontName: blockFontName
63
59
  });
64
60
 
61
+ const selectDecorationStyles = ({
62
+ color,
63
+ textLine,
64
+ textLineStyle
65
+ }) => ({
66
+ color,
67
+ textDecorationLine: textLine,
68
+ textDecorationStyle: textLineStyle,
69
+ ...Platform.select({
70
+ web: {
71
+ // TODO: https://github.com/telus/universal-design-system/issues/487
72
+ transition: 'color 200ms'
73
+ }
74
+ })
75
+ });
76
+
65
77
  const selectIconTokens = ({
66
78
  color,
67
79
  iconSize,
@@ -138,8 +150,9 @@ const LinkBase = /*#__PURE__*/forwardRef(({
138
150
  style: linkState => {
139
151
  const themeTokens = resolveLinkTokens(linkState);
140
152
  const outerBorderStyles = selectOuterBorderStyles(themeTokens);
153
+ const decorationStyles = selectDecorationStyles(themeTokens);
141
154
  const hasIcon = Boolean(icon || themeTokens.icon);
142
- return [outerBorderStyles, blockLeftStyle, hasIcon && staticStyles.rowContainer];
155
+ return [outerBorderStyles, blockLeftStyle, decorationStyles, hasIcon && staticStyles.rowContainer];
143
156
  },
144
157
  children: linkState => {
145
158
  const themeTokens = resolveLinkTokens(linkState);
@@ -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.