@telus-uds/components-base 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.storybook/main.js +7 -0
  2. package/.turbo/turbo-build.log +3 -3
  3. package/.turbo/turbo-lint.log +3 -13
  4. package/CHANGELOG.json +112 -1
  5. package/CHANGELOG.md +41 -2
  6. package/__fixtures__/Accessible.js +1 -2
  7. package/__fixtures__/Accessible.native.js +1 -2
  8. package/__tests__/FlexGrid/Col.test.jsx +5 -0
  9. package/__tests__/InputLabel/InputLabel.test.jsx +28 -0
  10. package/__tests__/InputLabel/__snapshots__/InputLabel.test.jsx.snap +3 -0
  11. package/__tests__/InputSupports/InputSupports.test.jsx +10 -0
  12. package/component-docs.json +278 -40
  13. package/lib/Button/ButtonGroup.js +118 -45
  14. package/lib/Checkbox/CheckboxGroup.js +3 -3
  15. package/lib/ExpandCollapse/Panel.js +2 -1
  16. package/lib/Fieldset/Fieldset.js +7 -0
  17. package/lib/InputLabel/InputLabel.js +8 -1
  18. package/lib/InputSupports/InputSupports.js +7 -0
  19. package/lib/List/ListItem.js +22 -12
  20. package/lib/Notification/Notification.js +1 -1
  21. package/lib/Radio/RadioGroup.js +12 -5
  22. package/lib/RadioCard/RadioCardGroup.js +7 -0
  23. package/lib/Search/Search.js +28 -20
  24. package/lib/Skeleton/Skeleton.js +48 -2
  25. package/lib/TextInput/TextArea.js +1 -1
  26. package/lib/TextInput/TextInput.js +1 -1
  27. package/lib/TextInput/TextInputBase.js +1 -1
  28. package/lib/ToggleSwitch/ToggleSwitch.js +7 -0
  29. package/lib/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  30. package/lib/Tooltip/Tooltip.js +1 -1
  31. package/lib/Typography/Typography.js +12 -10
  32. package/lib/index.js +22 -1
  33. package/lib/utils/animation/useVerticalExpandAnimation.js +26 -13
  34. package/lib/utils/input.js +5 -6
  35. package/lib/utils/props/index.js +18 -0
  36. package/lib/utils/props/inputSupportsProps.js +7 -0
  37. package/lib/utils/props/textInputProps.js +207 -0
  38. package/lib/utils/props/textProps.js +72 -0
  39. package/lib-module/Button/ButtonGroup.js +117 -45
  40. package/lib-module/Checkbox/CheckboxGroup.js +3 -3
  41. package/lib-module/ExpandCollapse/Panel.js +2 -1
  42. package/lib-module/Fieldset/Fieldset.js +7 -0
  43. package/lib-module/InputLabel/InputLabel.js +8 -1
  44. package/lib-module/InputSupports/InputSupports.js +7 -0
  45. package/lib-module/List/ListItem.js +22 -12
  46. package/lib-module/Notification/Notification.js +1 -1
  47. package/lib-module/Radio/RadioGroup.js +12 -5
  48. package/lib-module/RadioCard/RadioCardGroup.js +7 -0
  49. package/lib-module/Search/Search.js +30 -22
  50. package/lib-module/Skeleton/Skeleton.js +49 -3
  51. package/lib-module/TextInput/TextArea.js +2 -2
  52. package/lib-module/TextInput/TextInput.js +2 -2
  53. package/lib-module/TextInput/TextInputBase.js +2 -2
  54. package/lib-module/ToggleSwitch/ToggleSwitch.js +7 -0
  55. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  56. package/lib-module/Tooltip/Tooltip.js +1 -1
  57. package/lib-module/Typography/Typography.js +13 -11
  58. package/lib-module/index.js +1 -1
  59. package/lib-module/utils/animation/useVerticalExpandAnimation.js +26 -14
  60. package/lib-module/utils/input.js +6 -6
  61. package/lib-module/utils/props/index.js +2 -0
  62. package/lib-module/utils/props/inputSupportsProps.js +7 -0
  63. package/lib-module/utils/props/textInputProps.js +194 -0
  64. package/lib-module/utils/props/textProps.js +59 -0
  65. package/package.json +9 -4
  66. package/src/Button/ButtonGroup.jsx +106 -41
  67. package/src/Checkbox/Checkbox.jsx +7 -4
  68. package/src/Checkbox/CheckboxGroup.jsx +3 -3
  69. package/src/ExpandCollapse/Panel.jsx +3 -1
  70. package/src/Fieldset/Fieldset.jsx +6 -0
  71. package/src/InputLabel/InputLabel.jsx +17 -2
  72. package/src/InputSupports/InputSupports.jsx +9 -1
  73. package/src/List/ListItem.jsx +17 -9
  74. package/src/Notification/Notification.jsx +1 -1
  75. package/src/Radio/Radio.jsx +5 -1
  76. package/src/Radio/RadioGroup.jsx +11 -5
  77. package/src/RadioCard/RadioCard.jsx +5 -1
  78. package/src/RadioCard/RadioCardGroup.jsx +6 -0
  79. package/src/Search/Search.jsx +34 -22
  80. package/src/Skeleton/Skeleton.jsx +56 -3
  81. package/src/TextInput/TextArea.jsx +2 -0
  82. package/src/TextInput/TextInput.jsx +2 -0
  83. package/src/TextInput/TextInputBase.jsx +2 -0
  84. package/src/ToggleSwitch/ToggleSwitch.jsx +6 -0
  85. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +6 -0
  86. package/src/Tooltip/Tooltip.jsx +1 -1
  87. package/src/Typography/Typography.jsx +13 -9
  88. package/src/index.js +4 -1
  89. package/src/utils/animation/useVerticalExpandAnimation.js +25 -12
  90. package/src/utils/input.js +5 -7
  91. package/src/utils/props/index.js +2 -0
  92. package/src/utils/props/inputSupportsProps.js +6 -1
  93. package/src/utils/props/textInputProps.js +178 -0
  94. package/src/utils/props/textProps.js +58 -0
  95. package/src/utils/props/tokens.js +21 -19
  96. package/stories/Search/Search.stories.jsx +49 -2
  97. package/stories/Tabs/Tabs.stories.jsx +4 -3
@@ -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.
@@ -208,7 +216,7 @@ Search.propTypes = { ...selectedSystemPropTypes,
208
216
  accessibilityLabel: PropTypes.string,
209
217
 
210
218
  /**
211
- * Select english or french copy for the accessible labels.
219
+ * Select English or French copy for the accessible labels.
212
220
  * You may also pass in a custom dictionary object.
213
221
  */
214
222
  copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), PropTypes.shape({
@@ -4,7 +4,7 @@ import Platform from "react-native-web/dist/exports/Platform";
4
4
  import propTypes from 'prop-types';
5
5
  import StackView from '../StackView';
6
6
  import { useThemeTokens } from '../ThemeProvider';
7
- import { a11yProps, getTokensPropType, selectSystemProps, useSpacingScale, variantProp, viewProps } from '../utils';
7
+ import { a11yProps, getTokensPropType, selectSystemProps, responsiveProps, useResponsiveProp, useSpacingScale, spacingProps, variantProp, viewProps } from '../utils';
8
8
  import useSkeletonNativeAnimation from './useSkeletonNativeAnimation';
9
9
  import skeletonWebAnimation from './skeletonWebAnimation';
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -45,13 +45,23 @@ const Skeleton = /*#__PURE__*/forwardRef(({
45
45
  tokens,
46
46
  variant,
47
47
  size,
48
+ sizeIndex = size,
49
+ sizePixels,
48
50
  characters,
49
51
  lines,
50
52
  shape = 'line',
51
53
  ...rest
52
54
  }, ref) => {
53
55
  const themeTokens = useThemeTokens('Skeleton', tokens, variant);
54
- const skeletonHeight = useSpacingScale(size || themeTokens.size);
56
+ const pixels = useResponsiveProp(sizePixels);
57
+ const spacingScaleValue = typeof pixels === 'number' ? // Size by an exact number of pixels
58
+ {
59
+ options: {
60
+ size: pixels
61
+ }
62
+ } : // Size by an index on the spacing scale (getting default index from theme if none provided)
63
+ sizeIndex || themeTokens.size;
64
+ const skeletonHeight = useSpacingScale(spacingScaleValue);
55
65
  const nativeAnimation = useSkeletonNativeAnimation();
56
66
 
57
67
  const getAnimationBaseOnPlatform = () => {
@@ -109,9 +119,45 @@ Skeleton.displayName = 'Skeleton';
109
119
  Skeleton.propTypes = { ...selectedSystemPropTypes,
110
120
  tokens: getTokensPropType('Skeleton'),
111
121
  variant: variantProp.propType,
112
- size: propTypes.number,
122
+
123
+ /**
124
+ * Sets the size of Skeleton lines or shape according to the theme's spacing scale. For example, size={1} gives the smallest non-zero theme-defined spacing size.
125
+ *
126
+ * May also accept an object with responsive viewport keys or spacing scale options - see `useSpacingScale` for details.
127
+ */
128
+ sizeIndex: spacingProps.types.spacingValue,
129
+
130
+ /**
131
+ * @deprecated alias for `sizeIndex`
132
+ */
133
+ size: spacingProps.types.spacingValue,
134
+
135
+ /**
136
+ * Sets the size of Skeleton lines or shape to an exact number of pixels. Use when it's necessary to exactly match sizes of images or other boxes.
137
+ *
138
+ * Accepts a number or an object with responsive viewport keys, e.g. { xs: 32, lg: 64 } would be 32px at xs, sm and md and 64 at lg and xl viewports.
139
+ */
140
+ sizePixels: responsiveProps.getTypeOptionallyByViewport(propTypes.number),
141
+
142
+ /**
143
+ * Determines the width of simulated lines of text if the Skeleton's shape is 'line' (the default shape).
144
+ *
145
+ * Only has any affect if shape is line (the default). If unset, takes a default value from the theme.
146
+ */
113
147
  characters: propTypes.number,
148
+
149
+ /**
150
+ * Determines how many Skeleton items are rendered (default 1).
151
+ *
152
+ * Recommended usage is to simulate paragraphs of text when Skeleton's shape is 'line' (the default shape).
153
+ *
154
+ * The amount of spacing between multiple lines is controlled by theme tokens.
155
+ */
114
156
  lines: propTypes.number,
157
+
158
+ /**
159
+ * Determines if the skeleton should resemble lines of text (default), a circle, or a square box with themed rounded corners.
160
+ */
115
161
  shape: propTypes.oneOf(['line', 'circle', 'box'])
116
162
  };
117
163
  export default Skeleton;
@@ -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, focusHandlerProps, getTokensPropType, inputSupportsProps, selectSystemProps, textInputHandlerProps, 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, focusHandlerProps, inputSupportsProps, textInputHandlerProps, 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.
@@ -1,11 +1,11 @@
1
1
  import React, { forwardRef } from 'react';
2
2
  import Platform from "react-native-web/dist/exports/Platform";
3
- import { a11yProps, focusHandlerProps, getTokensPropType, inputSupportsProps, selectSystemProps, textInputHandlerProps, 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 textInputPropTypes from './propTypes';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, inputSupportsProps, textInputHandlerProps, viewProps]);
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, inputSupportsProps, textInputHandlerProps, textInputProps, viewProps]);
9
9
  /**
10
10
  * A basic text input component. Use in forms or individually to receive user's input.
11
11
  * Due to React Native's implementation of `TextInput` it's not possible to access the current value by passing a ref.
@@ -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, textInputHandlerProps, 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, textInputHandlerProps, viewProps]);
11
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, textInputHandlerProps, textInputProps, viewProps]);
12
12
 
13
13
  const selectInputStyles = ({
14
14
  backgroundColor,
@@ -83,6 +83,7 @@ const selectLabelTokens = ({
83
83
  });
84
84
 
85
85
  const ToggleSwitch = /*#__PURE__*/forwardRef(({
86
+ copy = 'en',
86
87
  value,
87
88
  initialValue,
88
89
  onChange,
@@ -119,6 +120,7 @@ const ToggleSwitch = /*#__PURE__*/forwardRef(({
119
120
  children: [Boolean(label) && /*#__PURE__*/_jsx(View, {
120
121
  style: [selectLabelStyles(themeTokens), staticStyles.containText],
121
122
  children: /*#__PURE__*/_jsx(InputLabel, {
123
+ copy: copy,
122
124
  forId: inputId,
123
125
  label: label,
124
126
  tokens: selectLabelTokens(themeTokens),
@@ -167,6 +169,11 @@ const ToggleSwitch = /*#__PURE__*/forwardRef(({
167
169
  });
168
170
  ToggleSwitch.displayName = 'ToggleSwitch';
169
171
  ToggleSwitch.propTypes = { ...selectedSystemPropTypes,
172
+
173
+ /**
174
+ * Whether the English or French copy will be used (e.g. for accessibility labels).
175
+ */
176
+ copy: PropTypes.oneOf(['en', 'fr']),
170
177
  tokens: getTokensPropType('ToggleSwitch'),
171
178
  variant: variantProp.propType,
172
179
 
@@ -12,6 +12,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
13
  const [selectItemProps, selectedItemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
14
14
  const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
15
+ copy = 'en',
15
16
  variant,
16
17
  tokens,
17
18
  items = [],
@@ -88,6 +89,7 @@ const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
88
89
  ...a11yProps.getPositionInSet(items.length, index)
89
90
  };
90
91
  return /*#__PURE__*/_jsx(ToggleSwitch, {
92
+ copy: copy,
91
93
  id: id,
92
94
  ref: itemRef,
93
95
  onChange: handleChange,
@@ -119,6 +121,11 @@ const ToggleSwitchGroup = /*#__PURE__*/forwardRef(({
119
121
  });
120
122
  ToggleSwitchGroup.displayName = 'ToggleSwitchGroup';
121
123
  ToggleSwitchGroup.propTypes = { ...selectedSystemPropTypes,
124
+
125
+ /**
126
+ * Whether the English or French copy will be used (e.g. for accessibility labels).
127
+ */
128
+ copy: PropTypes.oneOf(['en', 'fr']),
122
129
  tokens: getTokensPropType('ToggleSwitchGroup'),
123
130
  variant: variantProp.propType,
124
131
 
@@ -301,7 +301,7 @@ Tooltip.propTypes = { ...selectedSystemPropTypes,
301
301
  content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
302
302
 
303
303
  /**
304
- * Select english or french copy for the accessible label.
304
+ * Select English or French copy for the accessible label.
305
305
  */
306
306
  copy: PropTypes.oneOf(['en', 'fr']),
307
307
 
@@ -5,13 +5,14 @@ import View from "react-native-web/dist/exports/View";
5
5
  import { useThemeTokens } from '../ThemeProvider';
6
6
  import { useViewport } from '../ViewportProvider';
7
7
  import { applyTextStyles } from '../ThemeProvider/utils';
8
- import { a11yProps, variantProp, getTokensPropType, getMaxFontMultiplier, headingTags, selectSystemProps, textTags, viewProps, getA11yPropsFromHtmlTag } from '../utils';
8
+ import { a11yProps, variantProp, getTokensPropType, getMaxFontMultiplier, headingTags, selectSystemProps, textTags, textProps, viewProps, getA11yPropsFromHtmlTag } from '../utils';
9
9
  /**
10
10
  * @typedef {import('../utils/a11y/semantics').TextTag} TextTag
11
11
  */
12
12
 
13
13
  import { jsx as _jsx } from "react/jsx-runtime";
14
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
14
+ const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps]);
15
+ const [selectTextProps, selectedTextPropTypes] = selectSystemProps([textProps]);
15
16
 
16
17
  const selectTextStyles = ({
17
18
  fontWeight,
@@ -50,31 +51,32 @@ const Typography = /*#__PURE__*/forwardRef(({
50
51
  const themeTokens = useThemeTokens('Typography', tokens, variant, {
51
52
  viewport
52
53
  });
53
- const textProps = {
54
+ const resolvedTextProps = { ...selectTextProps(rest),
54
55
  style: selectTextStyles(align ? { ...themeTokens,
55
56
  textAlign: align
56
57
  } : themeTokens),
57
58
  dataSet,
58
59
  maxFontSizeMultiplier: getMaxFontMultiplier(themeTokens)
59
60
  };
60
- const selectedProps = selectProps({ ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
61
- ...rest
62
- });
61
+ const containerProps = { ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
62
+ ...selectContainerProps(rest)
63
+ };
63
64
  return block ? /*#__PURE__*/_jsx(View, {
64
65
  ref: ref,
65
- ...selectedProps,
66
- children: /*#__PURE__*/_jsx(Text, { ...textProps,
66
+ ...containerProps,
67
+ children: /*#__PURE__*/_jsx(Text, { ...resolvedTextProps,
67
68
  children: children
68
69
  })
69
70
  }) : /*#__PURE__*/_jsx(Text, {
70
71
  ref: ref,
71
- ...textProps,
72
- ...selectedProps,
72
+ ...containerProps,
73
+ ...resolvedTextProps,
73
74
  children: children
74
75
  });
75
76
  });
76
77
  Typography.displayName = 'Typography';
77
- Typography.propTypes = { ...selectedSystemPropTypes,
78
+ Typography.propTypes = { ...selectedContainerPropTypes,
79
+ ...selectedTextPropTypes,
78
80
  tokens: getTokensPropType('Typography'),
79
81
  variant: variantProp.propType,
80
82
 
@@ -44,5 +44,5 @@ export { default as Typography } from './Typography';
44
44
  export { default as A11yInfoProvider, useA11yInfo } from './A11yInfoProvider';
45
45
  export { default as BaseProvider } from './BaseProvider';
46
46
  export { default as ViewportProvider, useViewport, ViewportContext } from './ViewportProvider';
47
- export { default as ThemeProvider, useTheme, useSetTheme, useThemeTokens, getThemeTokens } from './ThemeProvider';
47
+ export { default as ThemeProvider, useTheme, useSetTheme, useThemeTokens, getThemeTokens, applyOuterBorder, applyTextStyles, applyShadowToken } from './ThemeProvider';
48
48
  export * from './utils';
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
  import Animated from "react-native-web/dist/exports/Animated";
3
3
  import Easing from "react-native-web/dist/exports/Easing";
4
4
  import Platform from "react-native-web/dist/exports/Platform"; // TODO: systematise animations
@@ -9,14 +9,25 @@ function useVerticalExpandAnimation({
9
9
  isExpanded,
10
10
  tokens
11
11
  }) {
12
+ const [isAnimating, setIsAnimating] = useState(false);
12
13
  const expandAnimatedValue = useRef(new Animated.Value(0)).current;
14
+ const elementRef = useRef(null);
13
15
  const {
14
16
  expandDuration,
15
17
  collapseDuration
16
- } = tokens;
18
+ } = tokens; // Treat as animating from when expanded state changes, until animation completes
19
+
20
+ useEffect(() => setIsAnimating(true), [isExpanded]);
17
21
  useEffect(() => {
22
+ const onComplete = () => !isExpanded && setIsAnimating(false);
23
+
18
24
  if (Platform.OS === 'web') {
19
- return;
25
+ if (!elementRef.current) return () => {}; // React Native Web does not pass `onTransitionEnd` through, must attach manually.
26
+ // https://github.com/necolas/react-native-web/pull/1713
27
+
28
+ const element = elementRef.current;
29
+ element.addEventListener('transitionend', onComplete);
30
+ return () => element.removeEventListener('transitionend', onComplete);
20
31
  }
21
32
 
22
33
  const animationConfig = {
@@ -25,25 +36,26 @@ function useVerticalExpandAnimation({
25
36
  toValue: isExpanded ? containerHeight : 0,
26
37
  useNativeDriver: false
27
38
  };
28
- Animated.timing(expandAnimatedValue, animationConfig).start();
29
- }, [isExpanded, expandAnimatedValue, containerHeight, expandDuration, collapseDuration]);
30
- let containerStyles; // don't visually collapse the container until we have it measured
39
+ const animation = Animated.timing(expandAnimatedValue, animationConfig);
40
+ animation.start(onComplete);
41
+ return () => animation.stop();
42
+ }, [isExpanded, expandAnimatedValue, containerHeight, expandDuration, collapseDuration]); // Without `visibility: 'hidden', descendents are focusable on web even when collapsed
43
+
44
+ const containerStyles = !isExpanded && !isAnimating ? {
45
+ visibility: 'hidden'
46
+ } : {}; // don't visually collapse the container until we have it measured
31
47
 
32
48
  if (containerHeight !== null) {
33
49
  if (Platform.OS === 'web') {
34
50
  const transitionDuration = isExpanded ? expandDuration : collapseDuration;
35
- containerStyles = {
36
- transition: `height ${transitionDuration}ms ease-in-out`,
37
- height: isExpanded ? containerHeight : 0
38
- };
51
+ containerStyles.transition = `height ${transitionDuration}ms ease-in-out`;
52
+ containerStyles.height = isExpanded ? containerHeight : 0;
39
53
  } else {
40
- containerStyles = {
41
- height: expandAnimatedValue
42
- };
54
+ containerStyles.height = expandAnimatedValue;
43
55
  }
44
56
  }
45
57
 
46
- return containerStyles;
58
+ return [containerStyles, elementRef];
47
59
  }
48
60
 
49
61
  export default useVerticalExpandAnimation;
@@ -1,4 +1,8 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
+ /**
3
+ * @typedef {import('react').SyntheticEvent} Event
4
+ */
5
+
2
6
  const pluralHooks = ['useMultipleInputValues'];
3
7
 
4
8
  const validateProps = ({
@@ -56,11 +60,9 @@ Consumers of this hook must be one of:
56
60
  *
57
61
  * @param {string} hookName - optional, used for tailoring error messages
58
62
  *
59
- * @typedef {(oldValue: string|number|null) => string|number|null} UpdaterFunction - `setValue` takes a value or
60
- * a function returning a new value from the old value
61
63
  * @returns {{
62
64
  * currentValue: string|number|null
63
- * setValue: (newValue: string|number|null|UpdaterFunction) => void
65
+ * setValue: (newValue: string|number|null|(oldValue: string|number) => string|number, event: Event) => void
64
66
  * resetValue: () => void
65
67
  * isControlled: bool
66
68
  * }}
@@ -122,12 +124,10 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
122
124
  *
123
125
  * @param {string} componentName - optional, used in error messages
124
126
  *
125
- * @typedef {(oldValues: string[]|number[]) => string[]|number[]} UpdaterFunction - `setValues` takes values or
126
- * a function returning new values from old values
127
127
  * @returns {{
128
128
  * currentValues: any
129
129
  * resetValues: () => void
130
- * setValues: (newValues: string[]|number[]|UpdaterFunction) => void
130
+ * setValues: (newValues: string[]|number[]|(oldValues: string[]|number[]) => string[]|number[], event: Event) => void
131
131
  * toggleOneValue: (value: string|number) => void
132
132
  * unsetValues: () => void
133
133
  * }}
@@ -13,5 +13,7 @@ export { default as rectProp } from './rectProp';
13
13
  export { default as responsiveProps } from './responsiveProps';
14
14
  export { default as spacingProps } from './spacingProps';
15
15
  export { default as selectSystemProps } from './selectSystemProps';
16
+ export { default as textInputProps } from './textInputProps';
17
+ export { default as textProps } from './textProps';
16
18
  export { default as variantProp } from './variantProp';
17
19
  export { default as viewProps } from './viewProps';
@@ -1,6 +1,11 @@
1
1
  import PropTypes from 'prop-types';
2
2
  export default {
3
3
  types: {
4
+ /**
5
+ * Whether the English or French copy will be used (e.g. for accessibility labels).
6
+ */
7
+ copy: PropTypes.oneOf(['en', 'fr']),
8
+
4
9
  /**
5
10
  * The input label.
6
11
  */
@@ -33,6 +38,7 @@ export default {
33
38
  validation: PropTypes.oneOf(['error', 'success'])
34
39
  },
35
40
  select: ({
41
+ copy,
36
42
  label,
37
43
  hint,
38
44
  hintPosition,
@@ -41,6 +47,7 @@ export default {
41
47
  validation
42
48
  }) => ({
43
49
  supportsProps: {
50
+ copy,
44
51
  label,
45
52
  hint,
46
53
  hintPosition,