@telus-uds/components-base 2.3.0 → 2.5.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/CHANGELOG.md +34 -2
- package/lib/A11yInfoProvider/index.js +2 -2
- package/lib/Autocomplete/Autocomplete.js +22 -32
- package/lib/Autocomplete/Suggestions.js +1 -1
- package/lib/BaseProvider/HydrationContext.js +1 -2
- package/lib/BaseProvider/index.js +1 -2
- package/lib/Button/ButtonDropdown.js +1 -1
- package/lib/Card/Card.js +12 -13
- package/lib/Card/CardBase.js +1 -1
- package/lib/Card/PressableCardBase.js +1 -1
- package/lib/CardGroup/CardGroup.js +3 -3
- package/lib/Carousel/Carousel.js +5 -6
- package/lib/Carousel/CarouselStepTracker/CarouselStepTracker.js +3 -0
- package/lib/Carousel/CarouselTabs/CarouselTabs.js +1 -2
- package/lib/Carousel/CarouselTabs/CarouselTabsPanel.js +1 -1
- package/lib/Carousel/CarouselTabs/CarouselTabsPanelItem.js +1 -1
- package/lib/Carousel/CarouselThumbnail.js +1 -1
- package/lib/Checkbox/Checkbox.js +1 -1
- package/lib/ColourToggle/ColourToggle.js +1 -1
- package/lib/ExpandCollapseMini/ExpandCollapseMini.js +77 -0
- package/lib/ExpandCollapseMini/ExpandCollapseMiniControl.js +126 -0
- package/lib/ExpandCollapseMini/index.js +2 -0
- package/lib/Footnote/Footnote.js +4 -4
- package/lib/HorizontalScroll/HorizontalScroll.js +1 -2
- package/lib/Icon/Icon.js +1 -1
- package/lib/Icon/IconText.js +2 -3
- package/lib/IconButton/IconButton.js +1 -2
- package/lib/InputLabel/InputLabel.js +36 -2
- package/lib/InputSupports/InputSupports.js +31 -8
- package/lib/InputSupports/dictionary.js +12 -0
- package/lib/InputSupports/useInputSupports.js +12 -3
- package/lib/Link/LinkBase.js +25 -18
- package/lib/List/List.js +1 -2
- package/lib/List/ListItemContent.js +1 -1
- package/lib/Listbox/Listbox.js +5 -8
- package/lib/Listbox/PressableItem.js +4 -4
- package/lib/Modal/Modal.js +4 -7
- package/lib/MultiSelectFilter/MultiSelectFilter.js +55 -42
- package/lib/Notification/Notification.js +15 -13
- package/lib/OrderedList/OrderedList.js +2 -3
- package/lib/Pagination/usePagination.js +1 -2
- package/lib/PriceLockup/utils/renderFootnoteContent.js +2 -2
- package/lib/PriceLockup/utils/renderFootnoteLinks.js +2 -2
- package/lib/PriceLockup/utils/renderPrice.js +2 -2
- package/lib/ProductCard/ProductCard.js +2 -3
- package/lib/Progress/ProgressBarBackground.js +2 -2
- package/lib/QuickLinksFeature/QuickLinksFeature.js +1 -2
- package/lib/Radio/Radio.js +1 -1
- package/lib/Search/Search.js +41 -11
- package/lib/Select/Picker.js +2 -2
- package/lib/Select/Picker.native.js +8 -4
- package/lib/Select/constants.js +4 -2
- package/lib/StackView/StackWrap.js +1 -4
- package/lib/StackView/getStackedContent.js +1 -2
- package/lib/StepTracker/StepTracker.js +1 -2
- package/lib/TabBar/TabBar.js +1 -1
- package/lib/Tabs/Tabs.js +1 -1
- package/lib/Tabs/TabsItem.js +2 -2
- package/lib/TextInput/TextArea.js +7 -6
- package/lib/TextInput/TextInput.js +7 -6
- package/lib/TextInput/TextInputBase.js +57 -25
- package/lib/ThemeProvider/utils/theme-tokens.js +2 -4
- package/lib/Timeline/Timeline.js +1 -2
- package/lib/Tooltip/Tooltip.native.js +4 -4
- package/lib/Typography/Typography.js +4 -5
- package/lib/Validator/Validator.js +9 -14
- package/lib/ViewportProvider/useViewportListener.js +1 -1
- package/lib/index.js +1 -0
- package/lib/utils/children.js +2 -6
- package/lib/utils/input.js +1 -1
- package/lib/utils/props/componentPropType.js +1 -2
- package/lib/utils/props/inputSupportsProps.js +15 -3
- package/lib/utils/props/selectSystemProps.js +2 -2
- package/lib/utils/ssr-media-query/create-stylesheet/create-stylesheet-mobile.js +1 -1
- package/lib/utils/ssr-media-query/create-stylesheet/index.js +2 -3
- package/lib/utils/ssr-media-query/utils/inject.js +3 -5
- package/lib/utils/useHash.js +1 -4
- package/lib/utils/useOverlaidPosition.js +25 -4
- package/lib/utils/useScrollBlocking.js +2 -4
- package/lib/utils/useSpacingScale.js +2 -2
- package/package.json +2 -2
- package/src/Carousel/CarouselStepTracker/CarouselStepTracker.jsx +3 -0
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +76 -0
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +119 -0
- package/src/ExpandCollapseMini/index.js +3 -0
- package/src/InputLabel/InputLabel.jsx +39 -2
- package/src/InputSupports/InputSupports.jsx +33 -7
- package/src/InputSupports/dictionary.js +12 -0
- package/src/InputSupports/useInputSupports.js +24 -3
- package/src/Link/LinkBase.jsx +25 -18
- package/src/Modal/Modal.jsx +1 -1
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +55 -27
- package/src/Notification/Notification.jsx +9 -3
- package/src/Search/Search.jsx +52 -24
- package/src/Select/Picker.native.jsx +10 -4
- package/src/Select/constants.js +4 -1
- package/src/TextInput/TextArea.jsx +12 -5
- package/src/TextInput/TextInput.jsx +13 -6
- package/src/TextInput/TextInputBase.jsx +57 -10
- package/src/index.js +1 -0
- package/src/utils/props/inputSupportsProps.js +15 -3
- package/src/utils/useOverlaidPosition.js +23 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
4
|
+
import { Link } from '../Link';
|
|
5
|
+
import { useThemeTokens } from '../ThemeProvider';
|
|
6
|
+
import { htmlAttrs, viewProps, selectSystemProps } from '../utils';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps]);
|
|
9
|
+
|
|
10
|
+
// The ExpandCollapseControl has all the appropriate role, a11y, press handling etc
|
|
11
|
+
// and a more appropriate press area, defer interaction handling to it.
|
|
12
|
+
const presentationOnly = {
|
|
13
|
+
accessibilityRole: null,
|
|
14
|
+
// Treat as regular flow content with the Control
|
|
15
|
+
pointerEvents: 'none',
|
|
16
|
+
// Stop RNW from stopping clicks from bubbling to Control
|
|
17
|
+
focusable: false // Stop RNW from setting tabIndex={0}: focus goes to Control only
|
|
18
|
+
};
|
|
19
|
+
const selectLinkTokens = _ref => {
|
|
20
|
+
let {
|
|
21
|
+
color,
|
|
22
|
+
textLine,
|
|
23
|
+
lineHeight,
|
|
24
|
+
fontSize
|
|
25
|
+
} = _ref;
|
|
26
|
+
return {
|
|
27
|
+
color,
|
|
28
|
+
textLine,
|
|
29
|
+
blockLineHeight: lineHeight,
|
|
30
|
+
blockFontSize: fontSize
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
34
|
+
let {
|
|
35
|
+
pressableState,
|
|
36
|
+
collapseTitle,
|
|
37
|
+
expandTitle = collapseTitle,
|
|
38
|
+
iconPosition = 'right',
|
|
39
|
+
tokens,
|
|
40
|
+
variant = {},
|
|
41
|
+
...rest
|
|
42
|
+
} = _ref2;
|
|
43
|
+
const {
|
|
44
|
+
expanded,
|
|
45
|
+
hover,
|
|
46
|
+
focus
|
|
47
|
+
} = pressableState || {};
|
|
48
|
+
// we only want focus outline when focusing, if user is pressing we don't want the border.
|
|
49
|
+
const {
|
|
50
|
+
outerBorderColor
|
|
51
|
+
} = useThemeTokens('Link', {}, {}, {
|
|
52
|
+
focus: Platform.OS !== 'web' ? expanded : focus
|
|
53
|
+
});
|
|
54
|
+
const {
|
|
55
|
+
size,
|
|
56
|
+
icon,
|
|
57
|
+
...themeTokens
|
|
58
|
+
} = useThemeTokens('ExpandCollapseMiniControl', tokens, variant, {
|
|
59
|
+
expanded,
|
|
60
|
+
focus
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Choose hover styles when any part of Control is hoverred
|
|
64
|
+
const appearance = {
|
|
65
|
+
...variant,
|
|
66
|
+
hover
|
|
67
|
+
};
|
|
68
|
+
const getTokens = linkState => {
|
|
69
|
+
const {
|
|
70
|
+
hover: linkHover
|
|
71
|
+
} = linkState || {};
|
|
72
|
+
const isHovered = hover || linkHover;
|
|
73
|
+
if (Platform.OS !== 'web') {
|
|
74
|
+
return {
|
|
75
|
+
iconTranslateY: -1
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (isHovered) {
|
|
79
|
+
// Include vertical icon animation on hover alongside built-in Link theme, the size is size4
|
|
80
|
+
return {
|
|
81
|
+
iconTranslateY: (expanded ? -1 : 1) * size
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {};
|
|
85
|
+
};
|
|
86
|
+
return /*#__PURE__*/_jsx(Link, {
|
|
87
|
+
variant: appearance,
|
|
88
|
+
icon: icon,
|
|
89
|
+
iconPosition: iconPosition,
|
|
90
|
+
tokens: linkState => ({
|
|
91
|
+
...getTokens(linkState),
|
|
92
|
+
...selectLinkTokens(themeTokens),
|
|
93
|
+
outerBorderColor
|
|
94
|
+
}),
|
|
95
|
+
ref: ref,
|
|
96
|
+
...presentationOnly,
|
|
97
|
+
...selectProps(rest),
|
|
98
|
+
children: expanded ? expandTitle : collapseTitle
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
ExpandCollapseMiniControl.displayName = 'ExpandCollapseMiniControl';
|
|
102
|
+
ExpandCollapseMiniControl.propTypes = {
|
|
103
|
+
...selectedSystemPropTypes,
|
|
104
|
+
...Link.propTypes,
|
|
105
|
+
/**
|
|
106
|
+
* Optional function to call on pressing the panel's control, in addition to opening or closing the panel
|
|
107
|
+
*/
|
|
108
|
+
onPress: PropTypes.func,
|
|
109
|
+
/**
|
|
110
|
+
* ExpandCollapseMiniControl title when expanded
|
|
111
|
+
*/
|
|
112
|
+
expandTitle: PropTypes.string.isRequired,
|
|
113
|
+
/**
|
|
114
|
+
* ExpandCollapseMiniControl title when collapsed
|
|
115
|
+
*/
|
|
116
|
+
collapseTitle: PropTypes.string.isRequired,
|
|
117
|
+
/**
|
|
118
|
+
* React Native's `Pressable`'s state object
|
|
119
|
+
*/
|
|
120
|
+
pressableState: PropTypes.object,
|
|
121
|
+
/**
|
|
122
|
+
* Optional variant object to override the default theme tokens
|
|
123
|
+
*/
|
|
124
|
+
variant: PropTypes.object
|
|
125
|
+
};
|
|
126
|
+
export default ExpandCollapseMiniControl;
|
package/lib/Footnote/Footnote.js
CHANGED
|
@@ -206,8 +206,8 @@ const Footnote = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
|
|
|
206
206
|
style: [selectFootnoteHeaderContentStyle(themeTokens), staticStyles.headerContent],
|
|
207
207
|
children: [/*#__PURE__*/_jsx(Typography, {
|
|
208
208
|
tokens: {
|
|
209
|
-
fontSize: themeTokens
|
|
210
|
-
lineHeight: themeTokens
|
|
209
|
+
fontSize: themeTokens?.headerFontSize,
|
|
210
|
+
lineHeight: themeTokens?.headerLineHeight
|
|
211
211
|
},
|
|
212
212
|
variant: {
|
|
213
213
|
size: 'h4'
|
|
@@ -219,9 +219,9 @@ const Footnote = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
|
|
|
219
219
|
children: /*#__PURE__*/_jsx(View, {
|
|
220
220
|
style: [selectFootnoteCloseButtonStyle(themeTokens), staticStyles.closeButton],
|
|
221
221
|
children: /*#__PURE__*/_jsx(Icon, {
|
|
222
|
-
icon: themeTokens
|
|
222
|
+
icon: themeTokens?.closeIcon,
|
|
223
223
|
tokens: {
|
|
224
|
-
size: themeTokens
|
|
224
|
+
size: themeTokens?.closeButtonIconSize
|
|
225
225
|
}
|
|
226
226
|
})
|
|
227
227
|
})
|
|
@@ -74,8 +74,7 @@ const HorizontalScroll = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
74
74
|
const showPreviousButton = scrollOffset > 0;
|
|
75
75
|
const scrollRef = React.useRef(null);
|
|
76
76
|
const scrollTo = targetX => {
|
|
77
|
-
|
|
78
|
-
if (typeof ((_scrollRef$current = scrollRef.current) === null || _scrollRef$current === void 0 ? void 0 : _scrollRef$current.scrollTo) === 'function') {
|
|
77
|
+
if (typeof scrollRef.current?.scrollTo === 'function') {
|
|
79
78
|
const x = getItemPositionScrollTarget(targetX, scrollMax, itemPositions.positions, buttonClearance);
|
|
80
79
|
scrollRef.current.scrollTo({
|
|
81
80
|
x,
|
package/lib/Icon/Icon.js
CHANGED
|
@@ -22,7 +22,7 @@ const Icon = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
22
22
|
size: size,
|
|
23
23
|
color: themeTokens.color
|
|
24
24
|
});
|
|
25
|
-
const paddingStyles = variant
|
|
25
|
+
const paddingStyles = variant?.padding ? {
|
|
26
26
|
padding: themeTokens.width,
|
|
27
27
|
width: themeTokens.size + themeTokens.width * 2,
|
|
28
28
|
// sets the diameter of the circle which is the size of the icon plus twice the general padding established to obtain a perfect circle
|
package/lib/Icon/IconText.js
CHANGED
|
@@ -18,7 +18,6 @@ import { spacingProps } from '../utils';
|
|
|
18
18
|
*/
|
|
19
19
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
20
|
const IconText = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
21
|
-
var _iconProps$tokens, _iconProps$tokens2;
|
|
22
21
|
let {
|
|
23
22
|
space,
|
|
24
23
|
iconPosition = 'left',
|
|
@@ -35,8 +34,8 @@ const IconText = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
35
34
|
|
|
36
35
|
// Inline images on Android are always baseline-aligned which makes them look misaligned - offset it.
|
|
37
36
|
// See abandoned issue https://github.com/facebook/react-native/issues/6529
|
|
38
|
-
const size =
|
|
39
|
-
const valueTranslateY = iconProps
|
|
37
|
+
const size = iconProps?.tokens?.size ?? 0;
|
|
38
|
+
const valueTranslateY = iconProps?.tokens?.translateY;
|
|
40
39
|
/**
|
|
41
40
|
* These calculations were carried out using a set of linear equations to calculate that the
|
|
42
41
|
* position of the icon "->"" is aligned to the first line of the tooltip text on IOS and Android.
|
|
@@ -134,13 +134,12 @@ const IconButton = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
134
134
|
accessibilityRole
|
|
135
135
|
});
|
|
136
136
|
const handlePress = () => {
|
|
137
|
-
var _ref$current;
|
|
138
137
|
linkProps.handleHref({
|
|
139
138
|
href,
|
|
140
139
|
onPress
|
|
141
140
|
})({
|
|
142
141
|
nativeEvent: {
|
|
143
|
-
target: ref
|
|
142
|
+
target: ref?.current?.id
|
|
144
143
|
}
|
|
145
144
|
});
|
|
146
145
|
};
|
|
@@ -49,6 +49,8 @@ const InputLabel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
49
49
|
tooltip,
|
|
50
50
|
tokens,
|
|
51
51
|
variant,
|
|
52
|
+
characterCount,
|
|
53
|
+
maxCharacterAllowed,
|
|
52
54
|
...rest
|
|
53
55
|
} = _ref3;
|
|
54
56
|
const themeTokens = useThemeTokens('InputLabel', tokens, variant);
|
|
@@ -81,11 +83,26 @@ const InputLabel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
81
83
|
content: tooltip,
|
|
82
84
|
copy: copy
|
|
83
85
|
})
|
|
86
|
+
}), maxCharacterAllowed && isHintInline && /*#__PURE__*/_jsxs(Text, {
|
|
87
|
+
style: [selectHintStyles({
|
|
88
|
+
...themeTokens
|
|
89
|
+
}), staticStyles.characterCountlabel],
|
|
90
|
+
children: [characterCount, "/", maxCharacterAllowed]
|
|
84
91
|
})]
|
|
85
|
-
}), hint && !isHintInline && /*#__PURE__*/_jsx(Text, {
|
|
92
|
+
}), hint && !maxCharacterAllowed && !isHintInline && /*#__PURE__*/_jsx(Text, {
|
|
86
93
|
style: [selectHintStyles(themeTokens), staticStyles.hintBelow],
|
|
87
94
|
nativeID: hintId,
|
|
88
95
|
children: hint
|
|
96
|
+
}), hint && maxCharacterAllowed && !isHintInline && /*#__PURE__*/_jsxs(View, {
|
|
97
|
+
style: staticStyles.container,
|
|
98
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
99
|
+
style: [selectHintStyles(themeTokens), staticStyles.flexHintBelow],
|
|
100
|
+
nativeID: hintId,
|
|
101
|
+
children: hint
|
|
102
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
103
|
+
style: [selectHintStyles(themeTokens), staticStyles.characterCountlabel],
|
|
104
|
+
children: [characterCount, "/", maxCharacterAllowed]
|
|
105
|
+
})]
|
|
89
106
|
})]
|
|
90
107
|
});
|
|
91
108
|
});
|
|
@@ -120,6 +137,14 @@ InputLabel.propTypes = {
|
|
|
120
137
|
* Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
|
|
121
138
|
*/
|
|
122
139
|
tooltip: PropTypes.string,
|
|
140
|
+
/**
|
|
141
|
+
* Current number of characterts of an input text.
|
|
142
|
+
*/
|
|
143
|
+
characterCount: PropTypes.number,
|
|
144
|
+
/**
|
|
145
|
+
* Max number of characters that allows an input text.
|
|
146
|
+
*/
|
|
147
|
+
maxCharacterAllowed: PropTypes.number,
|
|
123
148
|
tokens: getTokensPropType('InputLabel'),
|
|
124
149
|
variant: variantProp.propType
|
|
125
150
|
};
|
|
@@ -129,13 +154,22 @@ const staticStyles = StyleSheet.create({
|
|
|
129
154
|
flexShrink: 1,
|
|
130
155
|
flexDirection: 'row'
|
|
131
156
|
},
|
|
157
|
+
characterCountlabel: {
|
|
158
|
+
marginLeft: 'auto',
|
|
159
|
+
marginTop: 'auto'
|
|
160
|
+
},
|
|
132
161
|
label: {
|
|
133
|
-
flexShrink: 1
|
|
162
|
+
flexShrink: 1,
|
|
163
|
+
alignSelf: 'center'
|
|
134
164
|
},
|
|
135
165
|
hintBelow: {
|
|
136
166
|
flexBasis: '100%',
|
|
137
167
|
flexShrink: 0
|
|
138
168
|
},
|
|
169
|
+
flexHintBelow: {
|
|
170
|
+
flexBasis: '100%',
|
|
171
|
+
flexShrink: 1
|
|
172
|
+
},
|
|
139
173
|
tooltipAlign: {
|
|
140
174
|
alignSelf: 'center',
|
|
141
175
|
justifyContent: 'center'
|
|
@@ -5,7 +5,8 @@ import Feedback from '../Feedback';
|
|
|
5
5
|
import StackView from '../StackView';
|
|
6
6
|
import { useThemeTokens } from '../ThemeProvider';
|
|
7
7
|
import useInputSupports from './useInputSupports';
|
|
8
|
-
import { getTokensPropType } from '../utils';
|
|
8
|
+
import { getTokensPropType, useCopy } from '../utils';
|
|
9
|
+
import dictionary from './dictionary';
|
|
9
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
11
|
const InputSupports = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
11
12
|
let {
|
|
@@ -19,11 +20,19 @@ const InputSupports = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
19
20
|
feedbackProps = {},
|
|
20
21
|
tooltip,
|
|
21
22
|
validation,
|
|
23
|
+
maxCharacterAllowed,
|
|
24
|
+
inputValue,
|
|
22
25
|
nativeID
|
|
23
26
|
} = _ref;
|
|
24
27
|
const {
|
|
25
28
|
space
|
|
26
29
|
} = useThemeTokens('InputSupports');
|
|
30
|
+
const getCopy = useCopy({
|
|
31
|
+
dictionary,
|
|
32
|
+
copy
|
|
33
|
+
});
|
|
34
|
+
const maxCharsReachedErrorMessage = inputValue?.length > maxCharacterAllowed ? getCopy('maxCharsMessage').replace(/%\{charCount\}/g, maxCharacterAllowed) : '';
|
|
35
|
+
const feedbackValidation = inputValue?.length > maxCharacterAllowed ? 'error' : validation;
|
|
27
36
|
const {
|
|
28
37
|
inputId,
|
|
29
38
|
hintId,
|
|
@@ -34,7 +43,10 @@ const InputSupports = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
34
43
|
hint,
|
|
35
44
|
label,
|
|
36
45
|
validation,
|
|
37
|
-
nativeID
|
|
46
|
+
nativeID,
|
|
47
|
+
copy,
|
|
48
|
+
maxCharacterAllowed,
|
|
49
|
+
charactersCount: maxCharacterAllowed - inputValue?.length
|
|
38
50
|
});
|
|
39
51
|
return /*#__PURE__*/_jsxs(StackView, {
|
|
40
52
|
space: space,
|
|
@@ -46,14 +58,17 @@ const InputSupports = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
46
58
|
hintPosition: hintPosition,
|
|
47
59
|
hintId: hintId,
|
|
48
60
|
tooltip: tooltip,
|
|
49
|
-
forId: inputId
|
|
61
|
+
forId: inputId,
|
|
62
|
+
characterCount: inputValue?.length,
|
|
63
|
+
maxCharacterAllowed: maxCharacterAllowed
|
|
50
64
|
}), typeof children === 'function' ? children({
|
|
51
65
|
inputId,
|
|
52
|
-
...a11yProps
|
|
53
|
-
|
|
66
|
+
...a11yProps,
|
|
67
|
+
validation: feedbackValidation
|
|
68
|
+
}) : children, feedback || maxCharsReachedErrorMessage ? /*#__PURE__*/_jsx(Feedback, {
|
|
54
69
|
id: feedbackId,
|
|
55
|
-
title: feedback,
|
|
56
|
-
validation:
|
|
70
|
+
title: feedback || maxCharsReachedErrorMessage,
|
|
71
|
+
validation: feedbackValidation,
|
|
57
72
|
tokens: feedbackTokens,
|
|
58
73
|
variant: {
|
|
59
74
|
icon: feedbackProps.showFeedbackIcon
|
|
@@ -105,6 +120,14 @@ InputSupports.propTypes = {
|
|
|
105
120
|
/**
|
|
106
121
|
* ID for DOM element on web
|
|
107
122
|
*/
|
|
108
|
-
nativeID: PropTypes.string
|
|
123
|
+
nativeID: PropTypes.string,
|
|
124
|
+
/**
|
|
125
|
+
* The text value of a TextArea or TextInput
|
|
126
|
+
*/
|
|
127
|
+
inputValue: PropTypes.string,
|
|
128
|
+
/**
|
|
129
|
+
* Max number of characters that allows an input text.
|
|
130
|
+
*/
|
|
131
|
+
maxCharacterAllowed: PropTypes.number
|
|
109
132
|
};
|
|
110
133
|
export default InputSupports;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
en: {
|
|
3
|
+
maxCharacters: 'Maximum %{charCount} characters',
|
|
4
|
+
charactersRemaining: '%{charCount} characters remaining',
|
|
5
|
+
maxCharsMessage: 'Must not exceed %{charCount} characters'
|
|
6
|
+
},
|
|
7
|
+
fr: {
|
|
8
|
+
maxCharacters: '%{charCount} caractères maximum',
|
|
9
|
+
charactersRemaining: '%{charCount} caractères restants',
|
|
10
|
+
maxCharsMessage: 'Ne doit pas dépasser %{charCount} caractères'
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import useUniqueId from '../utils/useUniqueId';
|
|
2
|
+
import { useCopy } from '../utils';
|
|
3
|
+
import dictionary from './dictionary';
|
|
2
4
|
const joinDefined = array => array.filter(item => item !== undefined).join(' ');
|
|
3
5
|
const useInputSupports = _ref => {
|
|
4
6
|
let {
|
|
@@ -6,19 +8,26 @@ const useInputSupports = _ref => {
|
|
|
6
8
|
feedback,
|
|
7
9
|
validation,
|
|
8
10
|
hint,
|
|
9
|
-
nativeID
|
|
11
|
+
nativeID,
|
|
12
|
+
copy,
|
|
13
|
+
maxCharacterAllowed,
|
|
14
|
+
charactersCount
|
|
10
15
|
} = _ref;
|
|
16
|
+
const getCopy = useCopy({
|
|
17
|
+
dictionary,
|
|
18
|
+
copy
|
|
19
|
+
});
|
|
11
20
|
const hasValidationError = validation === 'error';
|
|
12
21
|
const inputId = useUniqueId('input');
|
|
13
22
|
const hintId = useUniqueId('input-hint');
|
|
14
23
|
const feedbackId = useUniqueId('input-feedback');
|
|
15
24
|
const a11yProps = {
|
|
16
25
|
accessibilityLabel: label,
|
|
17
|
-
accessibilityHint: joinDefined([!hasValidationError && feedback, hint]),
|
|
26
|
+
accessibilityHint: joinDefined([!hasValidationError && feedback, hint, maxCharacterAllowed ? getCopy('maxCharacters').replace(/%\{charCount\}/g, maxCharacterAllowed) : undefined]),
|
|
18
27
|
// native only -> replaced with describedBy on web
|
|
19
28
|
accessibilityDescribedBy: joinDefined([!hasValidationError && feedback && feedbackId,
|
|
20
29
|
// feedback receives a11yRole=alert on error, so there's no need to include it here
|
|
21
|
-
hint && hintId]),
|
|
30
|
+
hint && hintId, charactersCount ? getCopy('charactersRemaining').replace(/%\{charCount\}/g, charactersCount) : undefined]),
|
|
22
31
|
accessibilityInvalid: hasValidationError
|
|
23
32
|
};
|
|
24
33
|
return {
|
package/lib/Link/LinkBase.js
CHANGED
|
@@ -19,20 +19,15 @@ const selectOuterBorderStyles = _ref => {
|
|
|
19
19
|
borderRadius,
|
|
20
20
|
outerBorderOutline
|
|
21
21
|
} = _ref;
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
outerBorderGap
|
|
32
|
-
}),
|
|
33
|
-
borderRadius
|
|
34
|
-
} : {}
|
|
35
|
-
);
|
|
22
|
+
return {
|
|
23
|
+
outline: outerBorderOutline,
|
|
24
|
+
...applyOuterBorder({
|
|
25
|
+
outerBorderColor,
|
|
26
|
+
outerBorderWidth,
|
|
27
|
+
outerBorderGap
|
|
28
|
+
}),
|
|
29
|
+
borderRadius
|
|
30
|
+
};
|
|
36
31
|
};
|
|
37
32
|
const selectTextStyles = _ref2 => {
|
|
38
33
|
let {
|
|
@@ -92,7 +87,8 @@ const selectIconTokens = _ref5 => {
|
|
|
92
87
|
color,
|
|
93
88
|
iconSize,
|
|
94
89
|
blockFontSize,
|
|
95
|
-
iconTranslateX
|
|
90
|
+
iconTranslateX,
|
|
91
|
+
iconTranslateY
|
|
96
92
|
} = _ref5;
|
|
97
93
|
/**
|
|
98
94
|
* These calculations were carried out using a set of linear equations to calculate that the
|
|
@@ -100,11 +96,11 @@ const selectIconTokens = _ref5 => {
|
|
|
100
96
|
* The base equation is: X/4 + Y/4 - 4 - |X - Y| = Z
|
|
101
97
|
* where X = blockFontSize, Y = iconSize and Z = translateY
|
|
102
98
|
*/
|
|
103
|
-
const translateY = blockFontSize / 4 + iconSize / 4 - 4 - Math.abs(iconSize - blockFontSize);
|
|
99
|
+
const translateY = iconTranslateY ?? blockFontSize / 4 + iconSize / 4 - 4 - Math.abs(iconSize - blockFontSize);
|
|
104
100
|
return {
|
|
105
101
|
color,
|
|
106
102
|
translateX: iconTranslateX,
|
|
107
|
-
translateY
|
|
103
|
+
translateY,
|
|
108
104
|
size: iconSize
|
|
109
105
|
};
|
|
110
106
|
};
|
|
@@ -182,7 +178,7 @@ const LinkBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
|
|
|
182
178
|
const themeTokens = resolveLinkTokens(linkState);
|
|
183
179
|
const outerBorderStyles = selectOuterBorderStyles(themeTokens);
|
|
184
180
|
const decorationStyles = selectDecorationStyles(themeTokens);
|
|
185
|
-
return [outerBorderStyles, blockLeftStyle, decorationStyles, hasIcon && staticStyles.rowContainer];
|
|
181
|
+
return [outerBorderStyles, staticStyles.outerBorderStyles, blockLeftStyle, decorationStyles, hasIcon && staticStyles.rowContainer];
|
|
186
182
|
},
|
|
187
183
|
children: linkState => {
|
|
188
184
|
const themeTokens = resolveLinkTokens(linkState);
|
|
@@ -264,6 +260,17 @@ const staticStyles = StyleSheet.create({
|
|
|
264
260
|
pointerEvents: 'none'
|
|
265
261
|
}
|
|
266
262
|
})
|
|
263
|
+
},
|
|
264
|
+
outerBorderStyles: {
|
|
265
|
+
...(Platform.OS !== 'web' && {
|
|
266
|
+
margin: 0,
|
|
267
|
+
marginHorizontal: 2,
|
|
268
|
+
padding: 0
|
|
269
|
+
}),
|
|
270
|
+
...(Platform.OS === 'android' && {
|
|
271
|
+
paddingHorizontal: 2,
|
|
272
|
+
paddingTop: 2
|
|
273
|
+
})
|
|
267
274
|
}
|
|
268
275
|
});
|
|
269
276
|
export default withLinkRouter(LinkBase);
|
package/lib/List/List.js
CHANGED
|
@@ -7,8 +7,7 @@ import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps
|
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
9
9
|
const isListItem = element => {
|
|
10
|
-
|
|
11
|
-
const elementName = (element === null || element === void 0 || (_element$type = element.type) === null || _element$type === void 0 ? void 0 : _element$type.displayName) || (element === null || element === void 0 || (_element$type2 = element.type) === null || _element$type2 === void 0 ? void 0 : _element$type2.name);
|
|
10
|
+
const elementName = element?.type?.displayName || element?.type?.name;
|
|
12
11
|
// Match our own ListItem, and also, custom list items
|
|
13
12
|
return Boolean(elementName.match(/Item/));
|
|
14
13
|
};
|
|
@@ -49,7 +49,7 @@ const ListItemContent = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
49
49
|
} = useTheme();
|
|
50
50
|
const textStyles = selectItemTextStyles(tokens, themeOptions);
|
|
51
51
|
return /*#__PURE__*/_jsx(View, {
|
|
52
|
-
style: [staticStyles.wrap,
|
|
52
|
+
style: [staticStyles.wrap, tokens?.itemUnderline && {
|
|
53
53
|
textDecorationLine: tokens.itemUnderline,
|
|
54
54
|
textDecorationColor: tokens.itemFontColor
|
|
55
55
|
}],
|
package/lib/Listbox/Listbox.js
CHANGED
|
@@ -42,36 +42,33 @@ const Listbox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
42
42
|
// We need to keep track of each item's ref in order to be able to
|
|
43
43
|
// focus on a specific item via keyboard navigation
|
|
44
44
|
const itemRefs = React.useRef([]);
|
|
45
|
-
if (firstItemRef
|
|
45
|
+
if (firstItemRef?.current) itemRefs.current[0] = firstItemRef.current;
|
|
46
46
|
const [focusedIndex, setFocusedIndex] = React.useState(0);
|
|
47
47
|
const handleKeydown = React.useCallback(event => {
|
|
48
48
|
const nextItemRef = itemRefs.current[focusedIndex + 1];
|
|
49
49
|
const prevItemRef = itemRefs.current[focusedIndex - 1];
|
|
50
50
|
if (event.key === 'ArrowUp' || event.shiftKey && event.key === 'Tab') {
|
|
51
|
-
var _parentRef$current;
|
|
52
51
|
// Move the focus to the previous item or to the parent one if on the first
|
|
53
52
|
if (prevItemRef) {
|
|
54
53
|
event.preventDefault();
|
|
55
54
|
prevItemRef.focus();
|
|
56
|
-
} else if (parentRef)
|
|
55
|
+
} else if (parentRef) parentRef.current?.focus();
|
|
57
56
|
setFocusedIndex(focusedIndex - 1);
|
|
58
57
|
} else if ((event.key === 'ArrowDown' || event.key === 'Tab') && nextItemRef) {
|
|
59
58
|
event.preventDefault();
|
|
60
59
|
setFocusedIndex(focusedIndex + 1);
|
|
61
60
|
nextItemRef.focus();
|
|
62
61
|
} else if (event.key === 'Escape') {
|
|
63
|
-
var _parentRef$current2, _parentRef$current3;
|
|
64
62
|
// Close the dropdown
|
|
65
|
-
parentRef
|
|
63
|
+
parentRef?.current?.click();
|
|
66
64
|
// Return focus to the dropdown control after leaving the last item
|
|
67
|
-
parentRef
|
|
65
|
+
parentRef?.current?.focus();
|
|
68
66
|
if (onClose) onClose(event);
|
|
69
67
|
} else if (!nextItemRef && firstItemRef) {
|
|
70
|
-
var _firstItemRef$current;
|
|
71
68
|
// If the last item is focused, move the focus to the first one
|
|
72
69
|
event.preventDefault();
|
|
73
70
|
setFocusedIndex(0);
|
|
74
|
-
|
|
71
|
+
firstItemRef.current?.focus();
|
|
75
72
|
}
|
|
76
73
|
}, [focusedIndex, onClose, parentRef, firstItemRef]);
|
|
77
74
|
|
|
@@ -90,11 +90,11 @@ const PressableItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
|
90
90
|
return themeTokens;
|
|
91
91
|
};
|
|
92
92
|
const handleKeyPress = event => {
|
|
93
|
-
if (['Enter', ' '].includes(event
|
|
94
|
-
onPress
|
|
95
|
-
} else if (
|
|
93
|
+
if (['Enter', ' '].includes(event?.key)) {
|
|
94
|
+
onPress?.(event);
|
|
95
|
+
} else if (event?.key === 'ArrowDown') {
|
|
96
96
|
nextItemRef.current.focus();
|
|
97
|
-
} else if (
|
|
97
|
+
} else if (event?.key === 'ArrowUp' && prevItemRef?.current) {
|
|
98
98
|
prevItemRef.current.focus();
|
|
99
99
|
}
|
|
100
100
|
};
|
package/lib/Modal/Modal.js
CHANGED
|
@@ -100,7 +100,6 @@ const selectScrollViewStyles = () => ({
|
|
|
100
100
|
* - Don’t use modals consecutively
|
|
101
101
|
*/
|
|
102
102
|
const Modal = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
|
|
103
|
-
var _modalContentRef$curr2;
|
|
104
103
|
let {
|
|
105
104
|
children,
|
|
106
105
|
isOpen,
|
|
@@ -147,12 +146,11 @@ const Modal = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
|
|
|
147
146
|
};
|
|
148
147
|
const manageFocus = React.useCallback(event => {
|
|
149
148
|
if (event.key === 'Tab') {
|
|
150
|
-
|
|
151
|
-
const focusableElements = Array.from(modalBodyRef === null || modalBodyRef === void 0 || (_modalBodyRef$current = modalBodyRef.current) === null || _modalBodyRef$current === void 0 ? void 0 : _modalBodyRef$current.querySelectorAll(`
|
|
149
|
+
const focusableElements = Array.from(modalBodyRef?.current?.querySelectorAll(`
|
|
152
150
|
a[href], button, textarea, input, select,
|
|
153
151
|
[tabindex]:not([tabindex="-1"]),
|
|
154
152
|
[contenteditable="true"]
|
|
155
|
-
`));
|
|
153
|
+
`) || []);
|
|
156
154
|
const firstElement = focusableElements[0];
|
|
157
155
|
const lastElement = focusableElements[focusableElements.length - 1];
|
|
158
156
|
if (event.shiftKey && document.activeElement === firstElement) {
|
|
@@ -184,10 +182,9 @@ const Modal = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
|
|
|
184
182
|
}, [manageFocus]);
|
|
185
183
|
React.useEffect(() => {
|
|
186
184
|
if (isOpen) {
|
|
187
|
-
|
|
188
|
-
modalContentRef === null || modalContentRef === void 0 || (_modalContentRef$curr = modalContentRef.current) === null || _modalContentRef$curr === void 0 || _modalContentRef$curr.focus();
|
|
185
|
+
modalContentRef?.current?.focus();
|
|
189
186
|
}
|
|
190
|
-
}, [isOpen, modalContentRef
|
|
187
|
+
}, [isOpen, modalContentRef?.current?.focus]);
|
|
191
188
|
if (!isOpen) {
|
|
192
189
|
return null;
|
|
193
190
|
}
|