@telus-uds/components-base 0.0.2-prerelease.9 → 1.0.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.
- package/.eslintrc.js +9 -0
- package/.ultra.cache.json +1 -1
- package/CHANGELOG.md +32 -0
- package/README.md +4 -2
- package/__fixtures__/test-utils.js +25 -0
- package/__fixtures__/testTheme.js +4 -2
- package/__tests__/Button/ButtonGroup.test.jsx +4 -5
- package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
- package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
- package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
- package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
- package/__tests__/Link/LinkBase.test.jsx +0 -14
- package/__tests__/Radio/Radio.test.jsx +2 -2
- package/__tests__/Radio/RadioGroup.test.jsx +4 -5
- package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
- package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
- package/__tests__/Search/Search.test.jsx +9 -8
- package/__tests__/Select/Select.test.jsx +3 -2
- package/__tests__/Tabs/Tabs.test.jsx +1 -161
- package/__tests__/Tags/Tags.test.jsx +4 -5
- package/__tests__/TextInput/TextArea.test.jsx +3 -2
- package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
- package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
- package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
- package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
- package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
- package/__tests__/utils/children.test.jsx +128 -0
- package/__tests__/utils/input.test.js +1 -1
- package/__tests__/utils/semantics.test.jsx +43 -0
- package/lib/A11yText/index.js +10 -5
- package/lib/ActivityIndicator/Spinner.js +16 -13
- package/lib/ActivityIndicator/Spinner.native.js +12 -8
- package/lib/Box/Box.js +102 -8
- package/lib/Button/Button.js +9 -8
- package/lib/Button/ButtonBase.js +14 -7
- package/lib/Button/ButtonGroup.js +25 -10
- package/lib/Button/ButtonLink.js +10 -7
- package/lib/Card/Card.js +2 -0
- package/lib/Card/CardBase.js +12 -5
- package/lib/Card/PressableCardBase.js +12 -8
- package/lib/Checkbox/Checkbox.js +25 -14
- package/lib/Checkbox/CheckboxGroup.js +22 -12
- package/lib/Divider/Divider.js +12 -7
- package/lib/ExpandCollapse/Accordion.js +10 -4
- package/lib/ExpandCollapse/Control.js +12 -6
- package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
- package/lib/ExpandCollapse/Panel.js +8 -7
- package/lib/Feedback/Feedback.js +10 -5
- package/lib/Fieldset/Fieldset.js +10 -5
- package/lib/Fieldset/FieldsetContainer.js +10 -5
- package/lib/Fieldset/FieldsetContainer.native.js +10 -5
- package/lib/Fieldset/Legend.js +10 -5
- package/lib/Fieldset/Legend.native.js +10 -5
- package/lib/FlexGrid/Col/Col.js +8 -5
- package/lib/FlexGrid/FlexGrid.js +31 -6
- package/lib/FlexGrid/Row/Row.js +12 -5
- package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
- package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
- package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
- package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
- package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
- package/lib/HorizontalScroll/index.js +35 -0
- package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
- package/lib/Icon/Icon.js +16 -9
- package/lib/Icon/IconText.js +8 -7
- package/lib/IconButton/IconButton.js +10 -5
- package/lib/InputLabel/InputLabel.js +33 -5
- package/lib/InputLabel/LabelContent.js +22 -12
- package/lib/InputLabel/LabelContent.native.js +23 -5
- package/lib/InputSupports/InputSupports.js +10 -5
- package/lib/Link/ChevronLink.js +12 -5
- package/lib/Link/InlinePressable.js +10 -4
- package/lib/Link/InlinePressable.native.js +5 -4
- package/lib/Link/Link.js +12 -5
- package/lib/Link/LinkBase.js +12 -5
- package/lib/Link/TextButton.js +10 -5
- package/lib/List/List.js +5 -4
- package/lib/List/ListItem.js +16 -8
- package/lib/Modal/Modal.js +10 -5
- package/lib/Notification/Notification.js +21 -5
- package/lib/Pagination/PageButton.js +21 -10
- package/lib/Pagination/Pagination.js +12 -7
- package/lib/Pagination/SideButton.js +12 -7
- package/lib/Pagination/usePagination.js +2 -2
- package/lib/Progress/Progress.js +10 -5
- package/lib/Progress/ProgressBar.js +21 -10
- package/lib/Progress/ProgressBarBackground.js +12 -8
- package/lib/Radio/Radio.js +14 -13
- package/lib/Radio/RadioButton.js +20 -9
- package/lib/Radio/RadioGroup.js +24 -13
- package/lib/RadioCard/RadioCard.js +14 -10
- package/lib/RadioCard/RadioCardGroup.js +13 -12
- package/lib/Search/Search.js +29 -18
- package/lib/Select/Picker.js +11 -6
- package/lib/Select/Picker.native.js +21 -6
- package/lib/Select/Select.js +46 -4
- package/lib/SideNav/Item.js +10 -5
- package/lib/SideNav/ItemsGroup.js +10 -5
- package/lib/SideNav/SideNav.js +11 -7
- package/lib/Skeleton/Skeleton.js +15 -15
- package/lib/Skeleton/skeletonWebAnimation.js +1 -1
- package/lib/Spacer/Spacer.js +19 -7
- package/lib/StackView/StackView.js +25 -7
- package/lib/StackView/StackWrap.js +16 -9
- package/lib/StackView/StackWrapBox.js +33 -8
- package/lib/StackView/StackWrapGap.js +16 -7
- package/lib/StackView/common.js +4 -2
- package/lib/StackView/getStackedContent.js +2 -2
- package/lib/StepTracker/StepTracker.js +10 -5
- package/lib/Tabs/Tabs.js +26 -19
- package/lib/Tabs/TabsItem.js +16 -12
- package/lib/Tags/Tags.js +27 -11
- package/lib/TextInput/TextArea.js +7 -5
- package/lib/TextInput/TextInput.js +12 -6
- package/lib/TextInput/TextInputBase.js +12 -8
- package/lib/ThemeProvider/ThemeProvider.js +14 -10
- package/lib/ThemeProvider/useSetTheme.js +6 -1
- package/lib/ThemeProvider/utils/styles.js +2 -2
- package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
- package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
- package/lib/Tooltip/Backdrop.js +10 -2
- package/lib/Tooltip/Tooltip.js +5 -4
- package/lib/Typography/Typography.js +40 -24
- package/lib/index.js +21 -0
- package/lib/utils/a11y/index.js +13 -0
- package/lib/utils/a11y/semantics.js +173 -0
- package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
- package/lib/utils/children.js +55 -8
- package/lib/utils/input.js +27 -17
- package/lib/utils/propTypes.js +82 -29
- package/lib/utils/useCopy.js +1 -1
- package/lib/utils/useHash.js +8 -4
- package/lib/utils/useSpacingScale.js +1 -3
- package/lib/utils/useUniqueId.js +1 -1
- package/package.json +9 -5
- package/release-context.json +4 -4
- package/src/A11yText/index.jsx +6 -4
- package/src/ActivityIndicator/Spinner.jsx +5 -3
- package/src/ActivityIndicator/Spinner.native.jsx +5 -3
- package/src/Box/Box.jsx +124 -39
- package/src/Button/Button.jsx +7 -4
- package/src/Button/ButtonBase.jsx +86 -77
- package/src/Button/ButtonGroup.jsx +81 -69
- package/src/Button/ButtonLink.jsx +18 -13
- package/src/Card/Card.jsx +2 -2
- package/src/Card/CardBase.jsx +5 -4
- package/src/Card/PressableCardBase.jsx +71 -64
- package/src/Checkbox/Checkbox.jsx +118 -108
- package/src/Checkbox/CheckboxGroup.jsx +72 -62
- package/src/Divider/Divider.jsx +7 -4
- package/src/ExpandCollapse/Accordion.jsx +3 -2
- package/src/ExpandCollapse/Control.jsx +40 -43
- package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
- package/src/ExpandCollapse/Panel.jsx +69 -63
- package/src/Feedback/Feedback.jsx +36 -33
- package/src/Fieldset/Fieldset.jsx +63 -56
- package/src/Fieldset/FieldsetContainer.jsx +14 -5
- package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
- package/src/Fieldset/Legend.jsx +7 -2
- package/src/Fieldset/Legend.native.jsx +7 -2
- package/src/FlexGrid/Col/Col.jsx +139 -132
- package/src/FlexGrid/FlexGrid.jsx +79 -51
- package/src/FlexGrid/Row/Row.jsx +55 -48
- package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
- package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
- package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
- package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
- package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
- package/src/HorizontalScroll/index.js +17 -0
- package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
- package/src/Icon/Icon.jsx +37 -35
- package/src/Icon/IconText.jsx +22 -17
- package/src/IconButton/IconButton.jsx +49 -42
- package/src/InputLabel/InputLabel.jsx +53 -38
- package/src/InputLabel/LabelContent.jsx +14 -6
- package/src/InputLabel/LabelContent.native.jsx +11 -2
- package/src/InputSupports/InputSupports.jsx +29 -34
- package/src/Link/ChevronLink.jsx +26 -16
- package/src/Link/InlinePressable.jsx +5 -3
- package/src/Link/InlinePressable.native.jsx +5 -3
- package/src/Link/Link.jsx +22 -16
- package/src/Link/LinkBase.jsx +67 -58
- package/src/Link/TextButton.jsx +30 -23
- package/src/List/List.jsx +5 -4
- package/src/List/ListItem.jsx +77 -82
- package/src/Modal/Modal.jsx +9 -4
- package/src/Notification/Notification.jsx +58 -43
- package/src/Pagination/PageButton.jsx +42 -35
- package/src/Pagination/Pagination.jsx +88 -92
- package/src/Pagination/SideButton.jsx +44 -41
- package/src/Progress/Progress.jsx +5 -4
- package/src/Progress/ProgressBar.jsx +42 -29
- package/src/Progress/ProgressBarBackground.jsx +5 -3
- package/src/Radio/Radio.jsx +85 -78
- package/src/Radio/RadioButton.jsx +54 -43
- package/src/Radio/RadioGroup.jsx +74 -63
- package/src/RadioCard/RadioCard.jsx +75 -68
- package/src/RadioCard/RadioCardGroup.jsx +82 -75
- package/src/Search/Search.jsx +127 -106
- package/src/Select/Picker.jsx +49 -42
- package/src/Select/Picker.native.jsx +56 -49
- package/src/Select/Select.jsx +115 -72
- package/src/SideNav/Item.jsx +53 -46
- package/src/SideNav/ItemsGroup.jsx +50 -43
- package/src/SideNav/SideNav.jsx +68 -60
- package/src/Skeleton/Skeleton.jsx +9 -13
- package/src/Spacer/Spacer.jsx +11 -4
- package/src/StackView/StackView.jsx +46 -23
- package/src/StackView/StackWrap.jsx +7 -6
- package/src/StackView/StackWrapBox.jsx +61 -28
- package/src/StackView/StackWrapGap.jsx +46 -24
- package/src/StackView/common.jsx +3 -2
- package/src/StepTracker/StepTracker.jsx +73 -62
- package/src/Tabs/Tabs.jsx +70 -62
- package/src/Tabs/TabsItem.jsx +111 -103
- package/src/Tags/Tags.jsx +114 -102
- package/src/TextInput/TextArea.jsx +5 -4
- package/src/TextInput/TextInput.jsx +5 -4
- package/src/TextInput/TextInputBase.jsx +84 -77
- package/src/ThemeProvider/ThemeProvider.jsx +11 -7
- package/src/ThemeProvider/useSetTheme.js +4 -0
- package/src/ThemeProvider/utils/theme-tokens.js +28 -0
- package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
- package/src/Tooltip/Tooltip.jsx +134 -130
- package/src/Typography/Typography.jsx +67 -44
- package/src/index.js +2 -0
- package/src/utils/a11y/index.js +1 -0
- package/src/utils/a11y/semantics.js +162 -0
- package/src/utils/children.jsx +60 -7
- package/src/utils/input.js +20 -17
- package/src/utils/propTypes.js +101 -39
- package/src/utils/useCopy.js +1 -1
- package/src/utils/useHash.js +8 -3
- package/stories/A11yText/A11yText.stories.jsx +2 -2
- package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +1 -1
- package/stories/Box/Box.stories.jsx +1 -1
- package/stories/Button/Button.stories.jsx +2 -2
- package/stories/Button/ButtonGroup.stories.jsx +1 -1
- package/stories/Button/ButtonLink.stories.jsx +1 -1
- package/stories/Card/Card.stories.jsx +1 -1
- package/stories/Checkbox/Checkbox.stories.jsx +1 -1
- package/stories/Divider/Divider.stories.jsx +1 -1
- package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +2 -2
- package/stories/Feedback/Feedback.stories.jsx +1 -1
- package/stories/FlexGrid/01 FlexGrid.stories.jsx +1 -1
- package/stories/FlexGrid/02 Row.stories.jsx +1 -1
- package/stories/FlexGrid/03 Col.stories.jsx +1 -1
- package/stories/Icon/Icon.stories.jsx +1 -1
- package/stories/IconButton/IconButton.stories.jsx +1 -1
- package/stories/InputLabel/InputLabel.stories.jsx +1 -1
- package/stories/Link/ChevronLink.stories.jsx +1 -1
- package/stories/Link/Link.stories.jsx +1 -1
- package/stories/Link/TextButton.stories.jsx +1 -1
- package/stories/List/List.stories.jsx +1 -1
- package/stories/Modal/Modal.stories.jsx +1 -1
- package/stories/Notification/Notification.stories.jsx +1 -1
- package/stories/Pagination/Pagination.stories.jsx +1 -1
- package/stories/Progress/Progress.stories.jsx +1 -1
- package/stories/Radio/Radio.stories.jsx +1 -1
- package/stories/RadioCard/RadioCard.stories.jsx +1 -1
- package/stories/Search/Search.stories.jsx +1 -1
- package/stories/Select/Select.stories.jsx +1 -1
- package/stories/SideNav/SideNav.stories.jsx +1 -1
- package/stories/SideNav/SideNavItem.stories.jsx +1 -1
- package/stories/SideNav/SideNavItemsGroup.stories.jsx +1 -1
- package/stories/Skeleton/Skeleton.stories.jsx +2 -2
- package/stories/Spacer/Spacer.stories.jsx +1 -1
- package/stories/StackView/StackView.stories.jsx +1 -1
- package/stories/StackView/StackWrap.stories.jsx +1 -1
- package/stories/StepTracker/StepTracker.stories.jsx +1 -1
- package/stories/Tabs/Tabs.stories.jsx +1 -1
- package/stories/Tags/Tags.stories.jsx +1 -1
- package/stories/TextInput/TextArea.stories.jsx +1 -1
- package/stories/TextInput/TextInput.stories.jsx +1 -1
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +1 -1
- package/stories/Tooltip/Tooltip.stories.jsx +1 -1
- package/stories/TooltipButton/TooltipButton.stories.jsx +1 -1
- package/stories/Typography/Typography.stories.jsx +1 -1
- package/stories/platform-supports.jsx +1 -1
- package/stories/supports.jsx +2 -2
- package/src/Tabs/HorizontalScroll.jsx +0 -165
- package/src/Tabs/TabsScrollButton.jsx +0 -100
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getA11yPropsFromHtmlTag = exports.getHeadingLevel = exports.textTags = exports.layoutTags = exports.supportedTags = exports.headingTags = exports.tagsToRoles = void 0;
|
|
7
|
+
|
|
8
|
+
var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {import('react-native').AccessibilityRole} AccessibilityRole
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This is based on the role-to-tag mapping that React Native Web uses to set HTML tags.
|
|
18
|
+
* It's not exported in any way from RNW, so we need to keep this up-to-date manually.
|
|
19
|
+
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
|
|
20
|
+
*
|
|
21
|
+
* Note: every role in this list is a web-only aria-role. There is no overlap between _these_ web tags
|
|
22
|
+
* or roles and native accessibilityRoles. Only h1, h2, h3 etc map to an RN equivalent ("heading").
|
|
23
|
+
*
|
|
24
|
+
* - RN "summary" native role maps to "region" aria-role, but setting `<section>`/"region" does not
|
|
25
|
+
* set React Native's "summary" role, which has a much narrower use case.
|
|
26
|
+
* - `<Header>`/"Banner" also do not map to RN's "heading". Only h1 / h2 etc map to RN "heading".
|
|
27
|
+
*
|
|
28
|
+
* Therefore, all of these tags / roles default to no accessibilityRole in native apps. This is not wrong:
|
|
29
|
+
* in general, RN accessibilityRoles tend to be more about interaction and less about semantics than web roles.
|
|
30
|
+
*
|
|
31
|
+
* RNW's one-way mapping of React Native accessibilityRoles to web aria-roles:
|
|
32
|
+
* https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/modules/AccessibilityUtil/propsToAriaRole.js
|
|
33
|
+
*/
|
|
34
|
+
// Adding `/** @type {const} */ ({...})` denotes object content as `readonly` in many IDEs
|
|
35
|
+
// eslint-disable-next-line prettier/prettier
|
|
36
|
+
const rolesToTags =
|
|
37
|
+
/** @type {const} */
|
|
38
|
+
{
|
|
39
|
+
article: 'article',
|
|
40
|
+
banner: 'header',
|
|
41
|
+
blockquote: 'blockquote',
|
|
42
|
+
code: 'code',
|
|
43
|
+
complementary: 'aside',
|
|
44
|
+
contentinfo: 'footer',
|
|
45
|
+
deletion: 'del',
|
|
46
|
+
emphasis: 'em',
|
|
47
|
+
figure: 'figure',
|
|
48
|
+
insertion: 'ins',
|
|
49
|
+
form: 'form',
|
|
50
|
+
list: 'ul',
|
|
51
|
+
listitem: 'li',
|
|
52
|
+
main: 'main',
|
|
53
|
+
navigation: 'nav',
|
|
54
|
+
region: 'section',
|
|
55
|
+
strong: 'strong',
|
|
56
|
+
// Add special cases that are in RNW's function logic but not in its mapping object
|
|
57
|
+
label: 'label' // eslint-disable-next-line prettier/prettier
|
|
58
|
+
|
|
59
|
+
}; // Invert React Native Web's mapping, so a tag gets the role that gets that tag
|
|
60
|
+
|
|
61
|
+
const tagsToRoles = Object.fromEntries(Object.entries(rolesToTags).map(([key, value]) => [value, key]));
|
|
62
|
+
/**
|
|
63
|
+
* Heading HTML tags map to the "heading" accessibilityRole in native apps, which is similar
|
|
64
|
+
* to headings on web but without the expectation of a hierarchy of headings within one screen.
|
|
65
|
+
*/
|
|
66
|
+
// eslint-disable-next-line prettier/prettier
|
|
67
|
+
|
|
68
|
+
exports.tagsToRoles = tagsToRoles;
|
|
69
|
+
const headingTags =
|
|
70
|
+
/** @type {const} */
|
|
71
|
+
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
|
|
72
|
+
/**
|
|
73
|
+
* @typedef {typeof headingTags[number]} HeadingTag
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* All HTML tags that may be set via RNW accesibility props alone and therefore may be set using
|
|
78
|
+
* the getA11yPropsFromHtmlTag function without changing other behaviour.
|
|
79
|
+
*
|
|
80
|
+
* Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
exports.headingTags = headingTags;
|
|
84
|
+
const supportedTags = [...Object.keys(tagsToRoles), ...headingTags];
|
|
85
|
+
/**
|
|
86
|
+
* Uses readonly mapping keys/values to generate static types for IDEs that support TS in JSDoc.
|
|
87
|
+
* @typedef {keyof rolesToTags} RoleWithTag
|
|
88
|
+
* @typedef {typeof rolesToTags[RoleWithTag] | typeof headingTags[number]} SupportedTag
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Subset of supported HTML tags that may be used with layout containers like Box.
|
|
93
|
+
*
|
|
94
|
+
* Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
|
|
95
|
+
*/
|
|
96
|
+
// eslint-disable-next-line prettier/prettier
|
|
97
|
+
|
|
98
|
+
exports.supportedTags = supportedTags;
|
|
99
|
+
const layoutTags =
|
|
100
|
+
/** @type {const} */
|
|
101
|
+
[...headingTags, 'article', 'aside', 'blockquote', 'footer', 'figure', 'form', 'header', 'ul', 'li', 'main', 'nav', 'section', 'label' // eslint-disable-next-line prettier/prettier
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* @typedef {typeof layoutTags[number]} LayoutTag
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Subset of supported HTML tags that may be used with text elements like Typography.
|
|
109
|
+
*
|
|
110
|
+
* Of these, only headers (h1, h2, ...h5, h6) set a corresponding accessibilityRole in native apps ("heading").
|
|
111
|
+
*/
|
|
112
|
+
// eslint-disable-next-line prettier/prettier
|
|
113
|
+
|
|
114
|
+
exports.layoutTags = layoutTags;
|
|
115
|
+
const textTags =
|
|
116
|
+
/** @type {const} */
|
|
117
|
+
[...headingTags, 'blockquote', 'code', 'del', 'em', 'ins', 'li', 'strong', 'label' // eslint-disable-next-line prettier/prettier
|
|
118
|
+
];
|
|
119
|
+
/**
|
|
120
|
+
* @typedef {typeof layoutTags[number]} TextTag
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* If passed a heading tag string like 'h1', 'h2' etc, returns the heading number as a number
|
|
125
|
+
* ready for use in `accessibilityLevel` props and similar.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} [tag] - HTML tag string; returns undefined if not a {@link HeadingTag}
|
|
128
|
+
* @returns {'1' | '2' | '3' | '4' | '5' | '6' | undefined}
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
exports.textTags = textTags;
|
|
132
|
+
|
|
133
|
+
const getHeadingLevel = tag => headingTags.includes(tag) ? Number(tag[1]) : undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Takes a supported HTML tag, and returns the accessibility props that, on web, make React Native Web
|
|
136
|
+
* render that tag.
|
|
137
|
+
*
|
|
138
|
+
* For cross-platform apps, a second argument may be passed with an [accessibilityRole](https://reactnative.dev/docs/accessibility#accessibilityrole)
|
|
139
|
+
* to use in native apps. Heading tags (h1, h2, ...h5, h6) map to "heading" role by default;
|
|
140
|
+
* no other supported semantic HTML tags have an equivalent native accessibilityRole.
|
|
141
|
+
*
|
|
142
|
+
* @param {SupportedTag} tag - HTML tag to use on web
|
|
143
|
+
* @param {AccessibilityRole | null} [nativeRole] - optional accessibilityRole for native apps
|
|
144
|
+
* @returns {{ accessibilityRole: string, accessibilityLevel?: string } | undefined}
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
exports.getHeadingLevel = getHeadingLevel;
|
|
149
|
+
|
|
150
|
+
const getA11yPropsFromHtmlTag = (tag, nativeRole) => {
|
|
151
|
+
// Allow cross-platform apps to set accessibilityRoles alongside a web tag without conflict
|
|
152
|
+
if (nativeRole !== undefined && _Platform.default.OS !== 'web') return {
|
|
153
|
+
accessibilityRole: nativeRole
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (tag) {
|
|
157
|
+
const accessibilityRole = tagsToRoles[tag];
|
|
158
|
+
if (accessibilityRole) return {
|
|
159
|
+
accessibilityRole
|
|
160
|
+
};
|
|
161
|
+
const accessibilityLevel = getHeadingLevel(tag);
|
|
162
|
+
if (accessibilityLevel) return {
|
|
163
|
+
accessibilityRole: 'header',
|
|
164
|
+
accessibilityLevel
|
|
165
|
+
};
|
|
166
|
+
} // If nothing matches or no tag supplied, return undefined and let component decide how to fall back.
|
|
167
|
+
// Note that return value may always be spread in objects (it is safe to spread undefined like { ...undefined })
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
return undefined;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
exports.getA11yPropsFromHtmlTag = getA11yPropsFromHtmlTag;
|
|
@@ -47,7 +47,7 @@ function useVerticalExpandAnimation({
|
|
|
47
47
|
if (_Platform.default.OS === 'web') {
|
|
48
48
|
const transitionDuration = isExpanded ? expandDuration : collapseDuration;
|
|
49
49
|
containerStyles = {
|
|
50
|
-
transition:
|
|
50
|
+
transition: `height ${transitionDuration}ms ease-in-out`,
|
|
51
51
|
height: isExpanded ? containerHeight : 0
|
|
52
52
|
};
|
|
53
53
|
} else {
|
package/lib/utils/children.js
CHANGED
|
@@ -9,6 +9,8 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
9
9
|
|
|
10
10
|
var _Text = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Text"));
|
|
11
11
|
|
|
12
|
+
var _A11yText = _interopRequireDefault(require("../A11yText"));
|
|
13
|
+
|
|
12
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
15
|
|
|
14
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -59,16 +61,38 @@ const unpackFragment = child => {
|
|
|
59
61
|
|
|
60
62
|
return child;
|
|
61
63
|
};
|
|
64
|
+
|
|
65
|
+
exports.unpackFragment = unpackFragment;
|
|
66
|
+
|
|
67
|
+
const isStringOrNumber = child => typeof child === 'string' || typeof child === 'number'; // Wrap an A11yText with neighouring text strings so it doesn't split them into multiple <Text>s
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const isWrapable = child => isStringOrNumber(child) || child.type === _A11yText.default;
|
|
71
|
+
|
|
72
|
+
const combineKeys = childrenArray => childrenArray.reduce((newKey, child) => `${newKey}${child.key || ''}`, ''); // Group wrappable children for one `<Text>` parent, merging adjacent text nodes
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
const wrapChild = (child, wrappedText) => {
|
|
76
|
+
const lastIndex = wrappedText.length - 1; // If possible, simplify content by combining text nodes into one string.
|
|
77
|
+
// jest-native's `.toHaveTextContent` is buggy about array of text nodes in <Text> elements.
|
|
78
|
+
|
|
79
|
+
if (lastIndex >= 0 && isStringOrNumber(child) && isStringOrNumber(wrappedText[lastIndex])) {
|
|
80
|
+
/* eslint-disable-next-line no-param-reassign */
|
|
81
|
+
wrappedText[lastIndex] = `${wrappedText[lastIndex]}${child}`;
|
|
82
|
+
} else {
|
|
83
|
+
wrappedText.push(child);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
62
86
|
/**
|
|
63
87
|
* React Native on Native crashes if text content is rendered outside `<Text>`, and on web,
|
|
64
|
-
* text style inheritance
|
|
88
|
+
* text style inheritance behaves differently with text compared to regular HTML.
|
|
65
89
|
*
|
|
66
90
|
* Call this function on children that may contain text (strings or numbers) at the top level,
|
|
67
91
|
* and any that are found will be wrapped in a React Native `<Text>` element with supplied props.
|
|
68
92
|
*
|
|
69
|
-
* Note that this does not wrap strings that are nested children of
|
|
93
|
+
* Note that this does not wrap strings that are nested children of rendered top level children:
|
|
70
94
|
* `wrapStringsInText(<View>Some text</View>)` will not wrap the inner text and will still crash,
|
|
71
|
-
* but `wrapStringsInText(<>{someString}{anotherString}</>)`
|
|
95
|
+
* but `wrapStringsInText(<>{someString}{anotherString}</>)` wraps the strings inside the fragment.
|
|
72
96
|
*
|
|
73
97
|
* @param {ReactChildren} children
|
|
74
98
|
* @param {TextProps} props
|
|
@@ -76,12 +100,35 @@ const unpackFragment = child => {
|
|
|
76
100
|
*/
|
|
77
101
|
|
|
78
102
|
|
|
79
|
-
|
|
103
|
+
const wrapStringsInText = (children, textProps = {}) => {
|
|
104
|
+
const childrenArray = unpackFragment(_react.Children.toArray(children)); // Group adjacent wrapable children together in one Text element to create as few Text elements
|
|
105
|
+
// as possible, e.g. give <X>Text {someString}</X> one Text, same as <X>{`Text ${someString}`}</X>
|
|
80
106
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
const wrapables = [[]];
|
|
108
|
+
let wrapablesIndex = 0;
|
|
109
|
+
childrenArray.forEach(child => {
|
|
110
|
+
if (isWrapable(child)) {
|
|
111
|
+
// Make this child a child of the current `Text`
|
|
112
|
+
wrapChild(child, wrapables[wrapablesIndex]);
|
|
113
|
+
} else {
|
|
114
|
+
// Close current `Text` and start a new one after this child
|
|
115
|
+
wrapables.push(child, []);
|
|
116
|
+
wrapablesIndex += 2;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const items = wrapables.reduce((flatChildren, group, index) => {
|
|
120
|
+
// Skip nullish children and empty arrays
|
|
121
|
+
if (!group || Array.isArray(group) && !group.length) return flatChildren;
|
|
122
|
+
return [...flatChildren, Array.isArray(group) && group.some(child => isStringOrNumber(child)) ?
|
|
123
|
+
/*#__PURE__*/
|
|
124
|
+
// Wrap text nodes and their wrappable neighbours in Text with as stable a key as possible.
|
|
125
|
+
// Avoid one-item arrays because jest-native's `.toHaveTextContent` is buggy
|
|
126
|
+
// and sometimes fails to match text content in arrays.
|
|
127
|
+
(0, _jsxRuntime.jsx)(_Text.default, { ...textProps,
|
|
128
|
+
children: group.length === 1 ? group[0] : group
|
|
129
|
+
}, combineKeys(group) || index) : group];
|
|
130
|
+
}, []);
|
|
131
|
+
return items.length === 1 ? items[0] : items;
|
|
85
132
|
};
|
|
86
133
|
|
|
87
134
|
exports.wrapStringsInText = wrapStringsInText;
|
package/lib/utils/input.js
CHANGED
|
@@ -23,23 +23,29 @@ const validateProps = ({
|
|
|
23
23
|
const usageError = error => {
|
|
24
24
|
// Errors inside hooks in React Native get incomplete stack traces pointing at parent component only.
|
|
25
25
|
// Help devs out by telling them exactly which hook threw the error as well as why.
|
|
26
|
-
throw new Error(
|
|
26
|
+
throw new Error(`${hookName} ${error}.
|
|
27
|
+
|
|
28
|
+
Consumers of this hook must be one of:
|
|
29
|
+
1. An "uncontrolled" component responding directly to user actions, with an optional \`initialValue${s}\` but no \`value${s}\` prop,
|
|
30
|
+
2. A "controlled" component where an always-defined \`value${s}\` prop is managed by an \`onChange\` handler, with no \`initialValue${s}\`,
|
|
31
|
+
3. A "read-only" component, with \`readOnly\` prop set as \`true\`.
|
|
32
|
+
`);
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
if (value && !onChange && !readOnly) {
|
|
30
|
-
usageError(
|
|
36
|
+
usageError(`has \`value${s}\` prop without \`onChange\` or \`readOnly\``);
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
if (initialValue && value) {
|
|
34
|
-
usageError(
|
|
40
|
+
usageError(`has both \`initialValue${s}\` and \`value${s}\``);
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
if (isControlled && !isCurrentlyControlled) {
|
|
38
|
-
usageError(
|
|
44
|
+
usageError(`stopped receiving \`value${s}\` from parent after delegating state management`);
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
if (!isControlled && isCurrentlyControlled) {
|
|
42
|
-
usageError(
|
|
48
|
+
usageError(`started receiving \`value${s}\` from parent after starting managing own state`);
|
|
43
49
|
}
|
|
44
50
|
};
|
|
45
51
|
/**
|
|
@@ -92,14 +98,14 @@ const useInputValue = (props = {}, hookName = 'useInputValue') => {
|
|
|
92
98
|
}); // Make current value accessible inside useCallback without rememoizing every time the value changes
|
|
93
99
|
|
|
94
100
|
valueRef.current.value = currentValue;
|
|
95
|
-
const setValue = (0, _react.useCallback)(arg => {
|
|
101
|
+
const setValue = (0, _react.useCallback)((arg, event) => {
|
|
96
102
|
if (readOnly) return;
|
|
97
|
-
const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg;
|
|
103
|
+
const newValue = typeof arg === 'function' ? arg(valueRef.current.value) : arg;
|
|
104
|
+
if (!isControlled) setOwnValue(newValue); // Call onChange handler if there's something for it to handle (event or a changed value)
|
|
98
105
|
|
|
99
|
-
if (onChange && valueRef.current.value !== newValue) onChange(newValue);
|
|
100
|
-
if (!isControlled) setOwnValue(newValue);
|
|
106
|
+
if (onChange && (event || valueRef.current.value !== newValue)) onChange(newValue, event);
|
|
101
107
|
}, [isControlled, onChange, readOnly]);
|
|
102
|
-
const resetValue = (0, _react.useCallback)(
|
|
108
|
+
const resetValue = (0, _react.useCallback)(event => setValue(valueRef.current.initial, event), [setValue]);
|
|
103
109
|
return {
|
|
104
110
|
currentValue,
|
|
105
111
|
setValue,
|
|
@@ -154,7 +160,7 @@ const useMultipleInputValues = ({
|
|
|
154
160
|
onChange,
|
|
155
161
|
value: values,
|
|
156
162
|
// if we're controlling our own state, always start with an array
|
|
157
|
-
initialValue: initialValues
|
|
163
|
+
initialValue: initialValues ?? (values === undefined ? [] : undefined)
|
|
158
164
|
}, 'useMultipleInputValues');
|
|
159
165
|
const enforceMaxValues = (0, _react.useCallback)(newValues => {
|
|
160
166
|
if (!maxValues) return newValues;
|
|
@@ -162,14 +168,18 @@ const useMultipleInputValues = ({
|
|
|
162
168
|
return excess > 0 ? newValues.slice(excess) : newValues;
|
|
163
169
|
}, [maxValues]);
|
|
164
170
|
const currentValues = enforceMaxValues(currentValue);
|
|
165
|
-
const setValues = (0, _react.useCallback)(
|
|
171
|
+
const setValues = (0, _react.useCallback)((newValues, event) => {
|
|
172
|
+
const validNewValues = enforceMaxValues(newValues);
|
|
173
|
+
setValue(validNewValues, event);
|
|
174
|
+
}, [setValue, enforceMaxValues]);
|
|
166
175
|
const resetValues = resetValue;
|
|
167
|
-
const unsetValues = (0, _react.useCallback)(
|
|
168
|
-
const toggleOneValue = (0, _react.useCallback)(newValue => {
|
|
169
|
-
if (readOnly) return;
|
|
170
|
-
setValues( // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
|
|
176
|
+
const unsetValues = (0, _react.useCallback)(event => setValues([], event), [setValues]);
|
|
177
|
+
const toggleOneValue = (0, _react.useCallback)((newValue, event) => {
|
|
178
|
+
if (readOnly) return; // This will only work with primitive values (e.g. strings, numbers), swap for .some() and
|
|
171
179
|
// a deepEqual() function if there's any use case for toggling stored objects or arrays.
|
|
172
|
-
|
|
180
|
+
|
|
181
|
+
const newValues = currentValues.includes(newValue) ? currentValues.filter(oldValue => oldValue !== newValue) : [...currentValues, newValue];
|
|
182
|
+
setValues(newValues, event);
|
|
173
183
|
}, [currentValues, readOnly, setValues]);
|
|
174
184
|
return {
|
|
175
185
|
currentValues,
|
package/lib/utils/propTypes.js
CHANGED
|
@@ -11,7 +11,7 @@ var _Linking = _interopRequireDefault(require("react-native-web/dist/cjs/exports
|
|
|
11
11
|
|
|
12
12
|
var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _systemThemeTokens = require("@telus-uds/system-theme-tokens");
|
|
15
15
|
|
|
16
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
17
|
|
|
@@ -60,13 +60,13 @@ const tokenValue = _propTypes.default.oneOfType([_propTypes.default.string, _pro
|
|
|
60
60
|
const tokenValueType = _propTypes.default.oneOfType([tokenValue, _propTypes.default.objectOf(tokenValue)]);
|
|
61
61
|
|
|
62
62
|
const getTokenNames = componentName => {
|
|
63
|
-
const
|
|
63
|
+
const componentTokenSchema = _systemThemeTokens.components[componentName];
|
|
64
64
|
|
|
65
|
-
if (!
|
|
66
|
-
throw new Error(
|
|
65
|
+
if (!componentTokenSchema) {
|
|
66
|
+
throw new Error(`No "${componentName}" tokenKeys in @telus-uds/system-theme-tokens`);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
return
|
|
69
|
+
return Object.keys(componentTokenSchema);
|
|
70
70
|
};
|
|
71
71
|
/**
|
|
72
72
|
* Returns the subset of a set of tokens that may be accepted by the `tokens` prop of a named component
|
|
@@ -104,7 +104,7 @@ exports.getTokenNames = getTokenNames;
|
|
|
104
104
|
const selectTokens = (specifier, tokens, prefix) => {
|
|
105
105
|
const tokenNames = typeof specifier === 'string' ? getTokenNames(specifier) : specifier;
|
|
106
106
|
const filteredTokens = tokenNames.reduce((validTokens, key) => {
|
|
107
|
-
const prefixedKey = prefix ?
|
|
107
|
+
const prefixedKey = prefix ? `${prefix}${key[0].toUpperCase()}${key.slice(1)}` : key;
|
|
108
108
|
const token = tokens[prefixedKey];
|
|
109
109
|
return token !== undefined ? { ...validTokens,
|
|
110
110
|
[key]: token
|
|
@@ -225,7 +225,56 @@ const a11yPropTypes = {
|
|
|
225
225
|
importantForAccessibility: _propTypes.default.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
|
|
226
226
|
onAccessibilityAction: _propTypes.default.func,
|
|
227
227
|
onAccessibilityEscape: _propTypes.default.func,
|
|
228
|
-
onAccessibilityTap: _propTypes.default.func
|
|
228
|
+
onAccessibilityTap: _propTypes.default.func,
|
|
229
|
+
..._Platform.default.select({
|
|
230
|
+
web: {
|
|
231
|
+
// React Native Web adds many a11y props that alias aria-* attributes
|
|
232
|
+
// Types based on https://necolas.github.io/react-native-web/docs/accessibility/
|
|
233
|
+
accessibilityActiveDescendant: _propTypes.default.string,
|
|
234
|
+
accessibilityAtomic: _propTypes.default.bool,
|
|
235
|
+
accessibilityAutoComplete: _propTypes.default.string,
|
|
236
|
+
accessibilityBusy: _propTypes.default.bool,
|
|
237
|
+
accessibilityChecked: _propTypes.default.oneOf([true, false, 'mixed']),
|
|
238
|
+
accessibilityColumnCount: _propTypes.default.number,
|
|
239
|
+
accessibilityColumnIndex: _propTypes.default.number,
|
|
240
|
+
accessibilityColumnSpan: _propTypes.default.number,
|
|
241
|
+
accessibilityControls: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
|
|
242
|
+
accessibilityCurrent: _propTypes.default.oneOf([true, false, 'page', 'step', 'location', 'date', 'time']),
|
|
243
|
+
accessibilityDescribedBy: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
|
|
244
|
+
accessibilityDetails: _propTypes.default.string,
|
|
245
|
+
accessibilityDisabled: _propTypes.default.bool,
|
|
246
|
+
accessibilityErrorMessage: _propTypes.default.string,
|
|
247
|
+
accessibilityExpanded: _propTypes.default.bool,
|
|
248
|
+
accessibilityFlowTo: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
|
|
249
|
+
accessibilityHasPopup: _propTypes.default.string,
|
|
250
|
+
accessibilityHidden: _propTypes.default.bool,
|
|
251
|
+
accessibilityInvalid: _propTypes.default.bool,
|
|
252
|
+
accessibilityKeyShortcuts: _propTypes.default.string,
|
|
253
|
+
accessibilityLabelledBy: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
|
|
254
|
+
accessibilityLevel: _propTypes.default.number,
|
|
255
|
+
accessibilityModal: _propTypes.default.bool,
|
|
256
|
+
accessibilityMultiline: _propTypes.default.bool,
|
|
257
|
+
accessibilityMultiSelectable: _propTypes.default.bool,
|
|
258
|
+
accessibilityOrientation: _propTypes.default.oneOf(['horizontal', 'vertical']),
|
|
259
|
+
accessibilityOwns: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.arrayOf(_propTypes.default.string)]),
|
|
260
|
+
accessibilityPlaceholder: _propTypes.default.string,
|
|
261
|
+
accessibilityPosInSet: _propTypes.default.number,
|
|
262
|
+
accessibilityPressed: _propTypes.default.bool,
|
|
263
|
+
accessibilityReadOnly: _propTypes.default.bool,
|
|
264
|
+
accessibilityRequired: _propTypes.default.bool,
|
|
265
|
+
accessibilityRoleDescription: _propTypes.default.string,
|
|
266
|
+
accessibilityRowCount: _propTypes.default.number,
|
|
267
|
+
accessibilityRowIndex: _propTypes.default.number,
|
|
268
|
+
accessibilityRowSpan: _propTypes.default.number,
|
|
269
|
+
accessibilitySelected: _propTypes.default.bool,
|
|
270
|
+
accessibilitySetSize: _propTypes.default.number,
|
|
271
|
+
accessibilitySort: _propTypes.default.oneOf(['ascending', 'descending', 'none', 'other']),
|
|
272
|
+
accessibilityValueMax: _propTypes.default.number,
|
|
273
|
+
accessibilityValueMin: _propTypes.default.number,
|
|
274
|
+
accessibilityValueNow: _propTypes.default.number,
|
|
275
|
+
accessibilityValueText: _propTypes.default.string
|
|
276
|
+
}
|
|
277
|
+
})
|
|
229
278
|
};
|
|
230
279
|
const a11yProps = {
|
|
231
280
|
/**
|
|
@@ -332,8 +381,11 @@ const pressHandlerPropTypes = {
|
|
|
332
381
|
const pressPropTypes = { ...pressHandlerPropTypes,
|
|
333
382
|
disabled: _propTypes.default.bool,
|
|
334
383
|
..._Platform.default.select({
|
|
335
|
-
|
|
336
|
-
|
|
384
|
+
web: {},
|
|
385
|
+
default: {
|
|
386
|
+
hitSlop: _propTypes.default.number,
|
|
387
|
+
pressRetentionOffset: _propTypes.default.oneOfType([_propTypes.default.number, rectProp.propType])
|
|
388
|
+
}
|
|
337
389
|
})
|
|
338
390
|
};
|
|
339
391
|
const pressProps = {
|
|
@@ -366,7 +418,7 @@ const linkProps = {
|
|
|
366
418
|
select: getPropSelector(linkPropTypes),
|
|
367
419
|
|
|
368
420
|
/**
|
|
369
|
-
* Turn hrefs into press handlers on Native and throw if not given `onPress`
|
|
421
|
+
* Turn hrefs into press handlers on Native and throw if not given `onPress` or `href`.
|
|
370
422
|
*
|
|
371
423
|
* @param {({ onPress?: () => void, href?: string })}
|
|
372
424
|
* @returns {(() => void)|undefined} Returns a press handler, or undefined on web if href
|
|
@@ -376,24 +428,25 @@ const linkProps = {
|
|
|
376
428
|
onPress,
|
|
377
429
|
href
|
|
378
430
|
}) => {
|
|
379
|
-
// TODO: revisit this when integrating routing packages
|
|
380
|
-
// https://github.com/telus/universal-design-system/issues/24
|
|
381
|
-
if (href && onPress) {
|
|
382
|
-
throw new Error("handleHref currently doesn't support both href and onPress");
|
|
383
|
-
}
|
|
384
|
-
|
|
385
431
|
if (!href && !onPress) {
|
|
386
432
|
throw new Error('handleHref requires either href or onPress');
|
|
387
433
|
}
|
|
388
434
|
|
|
389
|
-
return _Platform.default.
|
|
435
|
+
return _Platform.default.select({
|
|
436
|
+
web: onPress,
|
|
437
|
+
default: (...args) => {
|
|
438
|
+
if (onPress) onPress(...args);
|
|
439
|
+
if (href) _Linking.default.openURL(href);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
390
442
|
}
|
|
391
443
|
};
|
|
392
444
|
exports.linkProps = linkProps;
|
|
393
445
|
const viewPropTypes = {
|
|
394
446
|
pointerEvents: _propTypes.default.oneOf(['all', 'none', 'box-only', 'box-none']),
|
|
395
447
|
onLayout: _propTypes.default.func,
|
|
396
|
-
nativeID: _propTypes.default.string
|
|
448
|
+
nativeID: _propTypes.default.string,
|
|
449
|
+
testID: _propTypes.default.string
|
|
397
450
|
};
|
|
398
451
|
const viewProps = {
|
|
399
452
|
/**
|
|
@@ -496,23 +549,24 @@ const spacingProps = {
|
|
|
496
549
|
}
|
|
497
550
|
};
|
|
498
551
|
/**
|
|
499
|
-
* Returns a prop type validator which checks whether a prop is either a component or an array of
|
|
500
|
-
* type, based on their `name`
|
|
552
|
+
* Returns a prop type validator which checks whether a prop is either a component or an array of
|
|
553
|
+
* components of a given type, based on their `name` or `displayName` properties.
|
|
501
554
|
* Use an array of strings for `passedName` to accept more than one component type.
|
|
502
555
|
* For an array the validation fails on the first occurrence of an invalid element.
|
|
503
556
|
*
|
|
504
557
|
* @param {string|string[]} passedName
|
|
505
|
-
* @param {boolean} [checkDisplayName] - if `true` then `displayName` property on the component will be validated instead of `name`
|
|
506
558
|
* @return {function}
|
|
507
559
|
*/
|
|
508
560
|
|
|
509
561
|
exports.spacingProps = spacingProps;
|
|
510
562
|
|
|
511
|
-
const componentPropType =
|
|
563
|
+
const componentPropType = passedName => {
|
|
512
564
|
const passedNames = typeof passedName === 'string' ? [passedName] : passedName;
|
|
513
565
|
|
|
514
566
|
const checkProp = (props, propName, componentName) => {
|
|
515
|
-
|
|
567
|
+
var _props$propName$type, _props$propName$type2;
|
|
568
|
+
|
|
569
|
+
if (props[propName] === undefined || props[propName] === null) {
|
|
516
570
|
return undefined;
|
|
517
571
|
}
|
|
518
572
|
|
|
@@ -522,12 +576,11 @@ const componentPropType = (passedName, checkDisplayName = false) => {
|
|
|
522
576
|
return props[propName].map((_, index) => checkProp(props[propName], index, componentName)).find(Boolean);
|
|
523
577
|
}
|
|
524
578
|
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
const testNameInFunction = () => typeof props[propName] === 'function' && (!checkDisplayName && !passedNames.includes(props[propName].name) || checkDisplayName && !passedNames.includes(props[propName].displayName));
|
|
579
|
+
const nameInProp = ((_props$propName$type = props[propName].type) === null || _props$propName$type === void 0 ? void 0 : _props$propName$type.displayName) || ((_props$propName$type2 = props[propName].type) === null || _props$propName$type2 === void 0 ? void 0 : _props$propName$type2.name);
|
|
528
580
|
|
|
529
|
-
if (
|
|
530
|
-
|
|
581
|
+
if (!nameInProp || !passedNames.includes(nameInProp)) {
|
|
582
|
+
const propDescription = nameInProp ? `Component ${nameInProp}` : typeof props[propName];
|
|
583
|
+
return new Error(`${componentName}: ${propDescription} was passed to \`${propName}\` prop; should be ${passedNames.join(' or ')}`);
|
|
531
584
|
}
|
|
532
585
|
|
|
533
586
|
return undefined;
|
|
@@ -535,7 +588,7 @@ const componentPropType = (passedName, checkDisplayName = false) => {
|
|
|
535
588
|
|
|
536
589
|
const checkRequired = (props, propName, componentName) => {
|
|
537
590
|
if (props[propName] === undefined) {
|
|
538
|
-
return new Error(
|
|
591
|
+
return new Error(`The prop \`${propName}\` is marked as required in \`${componentName}\`, but its value is ${props[propName]}.`);
|
|
539
592
|
}
|
|
540
593
|
|
|
541
594
|
return undefined;
|
package/lib/utils/useCopy.js
CHANGED
|
@@ -40,7 +40,7 @@ function useCopy({
|
|
|
40
40
|
copy = DEFAULT_COPY
|
|
41
41
|
}) {
|
|
42
42
|
if (typeof copy === 'string') {
|
|
43
|
-
return key => dictionary[copy][key];
|
|
43
|
+
return key => key ? dictionary[copy][key] : dictionary[copy];
|
|
44
44
|
} // support overriding the entire copy dictionary with an object for a single language
|
|
45
45
|
|
|
46
46
|
|
package/lib/utils/useHash.js
CHANGED
|
@@ -7,11 +7,15 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
|
|
10
|
-
const doAction = action => {
|
|
10
|
+
const doAction = (action, event) => {
|
|
11
11
|
var _window, _window$location;
|
|
12
12
|
|
|
13
|
-
return typeof action === 'function' && action((_window = window) === null || _window === void 0 ? void 0 : (_window$location = _window.location) === null || _window$location === void 0 ? void 0 : _window$location.hash);
|
|
13
|
+
return typeof action === 'function' && action((_window = window) === null || _window === void 0 ? void 0 : (_window$location = _window.location) === null || _window$location === void 0 ? void 0 : _window$location.hash, event);
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {import('react').SyntheticEvent} SyntheticEvent
|
|
17
|
+
*/
|
|
18
|
+
|
|
15
19
|
/**
|
|
16
20
|
* Calls a function (passing it the current hash) based on the current hash state on mount,
|
|
17
21
|
* and binds it to call it again any time the hash changes.
|
|
@@ -20,7 +24,7 @@ const doAction = action => {
|
|
|
20
24
|
*
|
|
21
25
|
* On Native, this is replaced with a harmless no-op function as there is no direct equivalent.
|
|
22
26
|
*
|
|
23
|
-
* @param {(hash: string) => void} action - a function to act on the current hash on load and when it changes
|
|
27
|
+
* @param {(hash: string, event?: SyntheticEvent) => void} action - a function to act on the current hash on load and when it changes
|
|
24
28
|
* @returns
|
|
25
29
|
*/
|
|
26
30
|
|
|
@@ -37,7 +41,7 @@ const useHash = (action, isReady = true) => {
|
|
|
37
41
|
}, [isToDo, action]); // Bind the action for each hash change; do re-bind if the function changes.
|
|
38
42
|
|
|
39
43
|
(0, _react.useEffect)(() => {
|
|
40
|
-
const handleChange =
|
|
44
|
+
const handleChange = event => doAction(action, event);
|
|
41
45
|
|
|
42
46
|
window.addEventListener('hashchange', handleChange);
|
|
43
47
|
return () => window.removeEventListener('hashchange', handleChange);
|
|
@@ -110,8 +110,6 @@ const resolveSpacingOptions = space => {
|
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
const useSpacingScale = spaceValue => {
|
|
113
|
-
var _spaceValue$space;
|
|
114
|
-
|
|
115
113
|
// In future, may need to consider window height as well as width, particularly for native apps,
|
|
116
114
|
// e.g. to ensure designs don't look lost on large, tall, not-so-wide portrait screens.
|
|
117
115
|
const viewport = (0, _ViewportProvider.useViewport)();
|
|
@@ -121,7 +119,7 @@ const useSpacingScale = spaceValue => {
|
|
|
121
119
|
overridden,
|
|
122
120
|
subtract = 0
|
|
123
121
|
} = resolveSpacingOptions(spaceValue);
|
|
124
|
-
const space = !overridden && ((
|
|
122
|
+
const space = !overridden && ((spaceValue === null || spaceValue === void 0 ? void 0 : spaceValue.space) ?? (0, _useResponsiveProp.resolveResponsiveProp)(spaceValue, viewport, 0));
|
|
125
123
|
const {
|
|
126
124
|
size
|
|
127
125
|
} = (0, _ThemeProvider.useThemeTokens)('spacingScale', tokens, variant, {
|
package/lib/utils/useUniqueId.js
CHANGED