@telus-uds/components-base 0.0.2-prerelease.1 → 0.0.2-prerelease.5
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 +36 -0
- package/__fixtures__/testTheme.js +264 -84
- package/__tests__/Box/Box.test.jsx +81 -58
- package/__tests__/Card/Card.test.jsx +63 -0
- package/__tests__/Divider/Divider.test.jsx +26 -5
- package/__tests__/Feedback/Feedback.test.jsx +42 -0
- package/__tests__/FlexGrid/Col.test.jsx +5 -0
- package/__tests__/Pagination/Pagination.test.jsx +160 -0
- package/__tests__/Spacer/Spacer.test.jsx +63 -0
- package/__tests__/StackView/StackView.test.jsx +242 -0
- package/__tests__/StackView/StackWrap.test.jsx +47 -0
- package/__tests__/StackView/getStackedContent.test.jsx +295 -0
- package/__tests__/TextInput/TextInput.test.jsx +146 -0
- package/__tests__/ThemeProvider/useThemeTokens.test.jsx +5 -3
- package/__tests__/utils/spacing.test.jsx +273 -0
- package/__tests__/utils/useUniqueId.test.js +31 -0
- package/babel.config.json +8 -0
- package/jest.config.js +7 -6
- package/lib/A11yInfoProvider/index.js +2 -2
- package/lib/A11yText/index.js +1 -3
- package/lib/ActivityIndicator/Spinner.web.js +3 -5
- package/lib/Box/Box.js +117 -82
- package/lib/Button/Button.js +1 -3
- package/lib/Button/ButtonBase.js +9 -21
- package/lib/Button/ButtonGroup.js +14 -25
- package/lib/Button/ButtonLink.js +1 -3
- package/lib/Card/Card.js +103 -0
- package/lib/Card/index.js +2 -0
- package/lib/Divider/Divider.js +40 -4
- package/lib/ExpandCollapse/Accordion.js +1 -3
- package/lib/ExpandCollapse/Control.js +3 -5
- package/lib/ExpandCollapse/Panel.js +2 -4
- package/lib/Feedback/Feedback.js +110 -0
- package/lib/Feedback/index.js +2 -0
- package/lib/FlexGrid/Col/Col.js +3 -5
- package/lib/FlexGrid/FlexGrid.js +1 -3
- package/lib/FlexGrid/Row/Row.js +1 -3
- package/lib/FlexGrid/providers/GutterContext.js +1 -1
- package/lib/Icon/Icon.js +1 -1
- package/lib/InputLabel/InputLabel.js +86 -0
- package/lib/InputLabel/LabelContent.native.js +8 -0
- package/lib/InputLabel/LabelContent.web.js +17 -0
- package/lib/InputLabel/index.js +2 -0
- package/lib/Link/ChevronLink.js +1 -3
- package/lib/Link/Link.js +1 -3
- package/lib/Link/LinkBase.js +11 -7
- package/lib/Link/TextButton.js +1 -3
- package/lib/Pagination/PageButton.js +85 -0
- package/lib/Pagination/Pagination.js +118 -0
- package/lib/Pagination/SideButton.js +108 -0
- package/lib/Pagination/dictionary.js +18 -0
- package/lib/Pagination/index.js +2 -0
- package/lib/Pagination/useCopy.js +10 -0
- package/lib/Pagination/usePagination.js +70 -0
- package/lib/SideNav/Item.js +4 -6
- package/lib/SideNav/ItemsGroup.js +11 -11
- package/lib/SideNav/SideNav.js +2 -4
- package/lib/Spacer/Spacer.js +98 -0
- package/lib/Spacer/index.js +2 -0
- package/lib/StackView/StackView.js +105 -0
- package/lib/StackView/StackWrap.js +32 -0
- package/lib/StackView/StackWrap.native.js +3 -0
- package/lib/StackView/StackWrapBox.js +85 -0
- package/lib/StackView/StackWrapGap.js +45 -0
- package/lib/StackView/common.js +30 -0
- package/lib/StackView/getStackedContent.js +111 -0
- package/lib/StackView/index.js +5 -0
- package/lib/TextInput/TextInput.js +337 -0
- package/lib/TextInput/index.js +2 -0
- package/lib/ThemeProvider/ThemeProvider.js +2 -2
- package/lib/ThemeProvider/useThemeTokens.js +34 -6
- package/lib/ThemeProvider/utils/theme-tokens.js +37 -9
- package/lib/ToggleSwitch/ToggleSwitch.js +17 -47
- package/lib/Typography/Typography.js +1 -7
- package/lib/ViewportProvider/index.js +1 -1
- package/lib/index.js +8 -1
- package/lib/utils/index.js +2 -1
- package/lib/utils/input.js +3 -1
- package/lib/utils/propTypes.js +103 -8
- package/lib/utils/spacing/index.js +2 -0
- package/lib/utils/spacing/useSpacingScale.js +102 -0
- package/lib/utils/spacing/utils.js +32 -0
- package/lib/utils/useUniqueId.js +12 -0
- package/package.json +6 -9
- package/release-context.json +4 -4
- package/src/Box/Box.jsx +117 -80
- package/src/Button/ButtonBase.jsx +8 -21
- package/src/Button/ButtonGroup.jsx +13 -17
- package/src/Card/Card.jsx +101 -0
- package/src/Card/index.js +3 -0
- package/src/Divider/Divider.jsx +38 -3
- package/src/ExpandCollapse/Control.jsx +2 -3
- package/src/Feedback/Feedback.jsx +99 -0
- package/src/Feedback/index.js +3 -0
- package/src/FlexGrid/Col/Col.jsx +4 -2
- package/src/Icon/Icon.jsx +2 -1
- package/src/InputLabel/InputLabel.jsx +99 -0
- package/src/InputLabel/LabelContent.native.jsx +6 -0
- package/src/InputLabel/LabelContent.web.jsx +13 -0
- package/src/InputLabel/index.js +3 -0
- package/src/Link/LinkBase.jsx +9 -3
- package/src/Pagination/PageButton.jsx +80 -0
- package/src/Pagination/Pagination.jsx +135 -0
- package/src/Pagination/SideButton.jsx +93 -0
- package/src/Pagination/dictionary.js +18 -0
- package/src/Pagination/index.js +3 -0
- package/src/Pagination/useCopy.js +7 -0
- package/src/Pagination/usePagination.js +69 -0
- package/src/SideNav/Item.jsx +3 -3
- package/src/SideNav/ItemsGroup.jsx +11 -13
- package/src/Spacer/Spacer.jsx +91 -0
- package/src/Spacer/index.js +3 -0
- package/src/StackView/StackView.jsx +103 -0
- package/src/StackView/StackWrap.jsx +33 -0
- package/src/StackView/StackWrap.native.jsx +4 -0
- package/src/StackView/StackWrapBox.jsx +82 -0
- package/src/StackView/StackWrapGap.jsx +39 -0
- package/src/StackView/common.jsx +28 -0
- package/src/StackView/getStackedContent.jsx +106 -0
- package/src/StackView/index.js +6 -0
- package/src/TextInput/TextInput.jsx +325 -0
- package/src/TextInput/index.js +3 -0
- package/src/ThemeProvider/useThemeTokens.js +34 -7
- package/src/ThemeProvider/utils/theme-tokens.js +37 -8
- package/src/ToggleSwitch/ToggleSwitch.jsx +23 -43
- package/src/Typography/Typography.jsx +0 -4
- package/src/index.js +8 -1
- package/src/utils/index.js +1 -0
- package/src/utils/input.js +2 -1
- package/src/utils/propTypes.js +105 -16
- package/src/utils/spacing/index.js +3 -0
- package/src/utils/spacing/useSpacingScale.js +93 -0
- package/src/utils/spacing/utils.js +28 -0
- package/src/utils/useUniqueId.js +14 -0
- package/stories/A11yText/A11yText.stories.jsx +11 -5
- package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
- package/stories/Box/Box.stories.jsx +46 -17
- package/stories/Button/Button.stories.jsx +17 -21
- package/stories/Button/ButtonGroup.stories.jsx +2 -1
- package/stories/Button/ButtonLink.stories.jsx +6 -4
- package/stories/Card/Card.stories.jsx +62 -0
- package/stories/Divider/Divider.stories.jsx +26 -2
- package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
- package/stories/Feedback/Feedback.stories.jsx +97 -0
- package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
- package/stories/Icon/Icon.stories.jsx +11 -3
- package/stories/InputLabel/InputLabel.stories.jsx +37 -0
- package/stories/Link/ChevronLink.stories.jsx +20 -4
- package/stories/Link/Link.stories.jsx +24 -3
- package/stories/Link/TextButton.stories.jsx +24 -3
- package/stories/Pagination/Pagination.stories.jsx +64 -0
- package/stories/SideNav/SideNav.stories.jsx +17 -2
- package/stories/Spacer/Spacer.stories.jsx +33 -0
- package/stories/StackView/StackView.stories.jsx +65 -0
- package/stories/StackView/StackWrap.stories.jsx +52 -0
- package/stories/TextInput/TextInput.stories.jsx +103 -0
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
- package/stories/Typography/Typography.stories.jsx +12 -3
- package/stories/platform-supports.web.jsx +1 -1
- package/stories/supports.jsx +113 -13
- package/babel.config.js +0 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
|
|
5
|
+
import { getTokensPropType, selectTokens, variantProp } from '../utils';
|
|
6
|
+
import LabelContent from './LabelContent';
|
|
7
|
+
|
|
8
|
+
const selectLabelStyles = tokens => applyTextStyles(selectTokens('Typography', tokens));
|
|
9
|
+
|
|
10
|
+
const selectHintStyles = ({
|
|
11
|
+
hintColor,
|
|
12
|
+
hintFontName,
|
|
13
|
+
hintFontSize,
|
|
14
|
+
hintFontWeight,
|
|
15
|
+
hintLineHeight
|
|
16
|
+
}) => applyTextStyles({
|
|
17
|
+
color: hintColor,
|
|
18
|
+
fontName: hintFontName,
|
|
19
|
+
fontSize: hintFontSize,
|
|
20
|
+
fontWeight: hintFontWeight,
|
|
21
|
+
lineHeight: hintLineHeight
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const selectGapStyles = ({
|
|
25
|
+
gap
|
|
26
|
+
}) => ({
|
|
27
|
+
marginRight: gap
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function InputLabel({
|
|
31
|
+
label,
|
|
32
|
+
forId,
|
|
33
|
+
hint,
|
|
34
|
+
hintPosition = 'inline',
|
|
35
|
+
hintId,
|
|
36
|
+
tooltip,
|
|
37
|
+
tokens,
|
|
38
|
+
variant
|
|
39
|
+
}) {
|
|
40
|
+
const themeTokens = useThemeTokens('InputLabel', tokens, variant); // TODO: use the actual Tooltip component here
|
|
41
|
+
|
|
42
|
+
const hasTooltip = tooltip !== undefined;
|
|
43
|
+
const isHintInline = hintPosition === 'inline';
|
|
44
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
45
|
+
style: [staticStyles.container, !isHintInline && staticStyles.containerWithHintBelow]
|
|
46
|
+
}, /*#__PURE__*/React.createElement(Text, {
|
|
47
|
+
style: [selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label]
|
|
48
|
+
}, /*#__PURE__*/React.createElement(LabelContent, {
|
|
49
|
+
forId: forId
|
|
50
|
+
}, label)), hint && isHintInline && /*#__PURE__*/React.createElement(Text, {
|
|
51
|
+
style: [selectHintStyles(themeTokens), hasTooltip && selectGapStyles(themeTokens)],
|
|
52
|
+
nativeID: hintId
|
|
53
|
+
}, hint), tooltip && /*#__PURE__*/React.createElement(Text, null, "?"), hint && !isHintInline && /*#__PURE__*/React.createElement(Text, {
|
|
54
|
+
style: [selectHintStyles(themeTokens), staticStyles.hintBelow],
|
|
55
|
+
nativeID: hintId
|
|
56
|
+
}, hint));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
InputLabel.propTypes = {
|
|
60
|
+
label: PropTypes.string.isRequired,
|
|
61
|
+
forId: PropTypes.string,
|
|
62
|
+
hint: PropTypes.string,
|
|
63
|
+
hintPosition: PropTypes.oneOf(['inline', 'below']),
|
|
64
|
+
hintId: PropTypes.string,
|
|
65
|
+
tooltip: PropTypes.string,
|
|
66
|
+
tokens: getTokensPropType('InputLabel'),
|
|
67
|
+
variant: variantProp.propType
|
|
68
|
+
};
|
|
69
|
+
export default InputLabel;
|
|
70
|
+
const staticStyles = StyleSheet.create({
|
|
71
|
+
container: {
|
|
72
|
+
display: 'flex',
|
|
73
|
+
flexDirection: 'row',
|
|
74
|
+
alignItems: 'center'
|
|
75
|
+
},
|
|
76
|
+
containerWithHintBelow: {
|
|
77
|
+
flexWrap: 'wrap'
|
|
78
|
+
},
|
|
79
|
+
label: {
|
|
80
|
+
flexShrink: 0
|
|
81
|
+
},
|
|
82
|
+
hintBelow: {
|
|
83
|
+
flexBasis: '100%',
|
|
84
|
+
flexShrink: 0
|
|
85
|
+
}
|
|
86
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
function LabelContent({
|
|
5
|
+
children,
|
|
6
|
+
forId
|
|
7
|
+
}) {
|
|
8
|
+
return /*#__PURE__*/React.createElement("label", {
|
|
9
|
+
htmlFor: forId
|
|
10
|
+
}, children);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default LabelContent;
|
|
14
|
+
LabelContent.propTypes = {
|
|
15
|
+
children: PropTypes.string,
|
|
16
|
+
forId: PropTypes.string
|
|
17
|
+
};
|
package/lib/Link/ChevronLink.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
|
|
3
1
|
import React from 'react';
|
|
4
2
|
import PropTypes from 'prop-types';
|
|
5
3
|
import Link from './Link';
|
|
@@ -9,7 +7,7 @@ const ChevronLink = ({
|
|
|
9
7
|
children,
|
|
10
8
|
variant = {},
|
|
11
9
|
...linkProps
|
|
12
|
-
}) => /*#__PURE__*/React.createElement(Link,
|
|
10
|
+
}) => /*#__PURE__*/React.createElement(Link, Object.assign({
|
|
13
11
|
variant: {
|
|
14
12
|
component: 'ChevronLink',
|
|
15
13
|
...variant
|
package/lib/Link/Link.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
|
|
3
1
|
import React from 'react';
|
|
4
2
|
import LinkBase from './LinkBase';
|
|
5
3
|
|
|
@@ -9,7 +7,7 @@ const Link = ({
|
|
|
9
7
|
accessibilityRole = 'link',
|
|
10
8
|
variant = {},
|
|
11
9
|
...linkProps
|
|
12
|
-
}) => /*#__PURE__*/React.createElement(LinkBase,
|
|
10
|
+
}) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
|
|
13
11
|
href: href,
|
|
14
12
|
accessibilityRole: accessibilityRole,
|
|
15
13
|
variant: {
|
package/lib/Link/LinkBase.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
|
|
3
1
|
import React, { useState } from 'react';
|
|
4
2
|
import PropTypes from 'prop-types';
|
|
5
3
|
import { Text, Platform, StyleSheet, Pressable, PixelRatio, View, TouchableWithoutFeedback } from 'react-native';
|
|
@@ -27,7 +25,10 @@ Platform.OS === 'web' ? {
|
|
|
27
25
|
outline: outerBorderOutline,
|
|
28
26
|
borderWidth: outerBorderWidth,
|
|
29
27
|
borderColor: outerBorderColor,
|
|
30
|
-
borderRadius: outerBorderRadius
|
|
28
|
+
borderRadius: outerBorderRadius,
|
|
29
|
+
// Stops focus ring stretching horizontally if parent has display: block
|
|
30
|
+
// width: fit-content isn't supported on Firefox; can't cascade props like CSS `width: fit-content; width: --moz-fit-content;`
|
|
31
|
+
display: 'inline-flex'
|
|
31
32
|
} : {};
|
|
32
33
|
|
|
33
34
|
const selectTextStyles = ({
|
|
@@ -55,10 +56,12 @@ const selectIconStyles = ({
|
|
|
55
56
|
iconGapBefore,
|
|
56
57
|
iconGapAfter,
|
|
57
58
|
iconScale,
|
|
58
|
-
iconTranslateX
|
|
59
|
+
iconTranslateX,
|
|
60
|
+
iconTranslateY
|
|
59
61
|
}) => ({
|
|
60
62
|
scale: iconScale,
|
|
61
63
|
translateX: iconTranslateX,
|
|
64
|
+
translateY: iconTranslateY,
|
|
62
65
|
size: iconSize,
|
|
63
66
|
gapBefore: iconGapBefore,
|
|
64
67
|
gapAfter: iconGapAfter
|
|
@@ -173,7 +176,7 @@ const LinkBase = ({
|
|
|
173
176
|
onPressIn: onPressIn,
|
|
174
177
|
onPressOut: onPressOut,
|
|
175
178
|
onPress: _onPress
|
|
176
|
-
}, /*#__PURE__*/React.createElement(Text,
|
|
179
|
+
}, /*#__PURE__*/React.createElement(Text, Object.assign({}, textProps, {
|
|
177
180
|
style: [outerBorderStyles, blockLeftStyle, contentStyles, blockTextStyles, textStyles]
|
|
178
181
|
}), children))
|
|
179
182
|
);
|
|
@@ -185,7 +188,8 @@ const LinkBase = ({
|
|
|
185
188
|
size: iconStyles.size ? iconStyles.size * iconScale : undefined,
|
|
186
189
|
color: contentStyles.color ?? undefined,
|
|
187
190
|
scale: iconStyles.scale ?? undefined,
|
|
188
|
-
translateX: iconStyles.translateX ?? undefined
|
|
191
|
+
translateX: iconStyles.translateX ?? undefined,
|
|
192
|
+
translateY: iconStyles.translateY ?? undefined
|
|
189
193
|
};
|
|
190
194
|
const iconContent = /*#__PURE__*/React.createElement(IconComponent, {
|
|
191
195
|
tokens: iconTokens,
|
|
@@ -203,7 +207,7 @@ const LinkBase = ({
|
|
|
203
207
|
// properly on Android which purely inline links can't do. Add an isNested case that is similar to
|
|
204
208
|
// https://github.com/telus/universal-design-system/pull/233
|
|
205
209
|
|
|
206
|
-
return /*#__PURE__*/React.createElement(Pressable,
|
|
210
|
+
return /*#__PURE__*/React.createElement(Pressable, Object.assign({}, linkPropSet, {
|
|
207
211
|
style: [outerBorderStyles, blockLeftStyle, staticStyles.rowContainer]
|
|
208
212
|
}), iconPosition === 'left' && iconBlock, /*#__PURE__*/React.createElement(Text, {
|
|
209
213
|
style: [contentStyles, blockTextStyles, textStyles, Platform.select({
|
package/lib/Link/TextButton.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
|
|
3
1
|
import React from 'react';
|
|
4
2
|
import PropTypes from 'prop-types';
|
|
5
3
|
import LinkBase from './LinkBase';
|
|
@@ -10,7 +8,7 @@ const TextButton = ({
|
|
|
10
8
|
variant = {},
|
|
11
9
|
accessibilityRole = 'button',
|
|
12
10
|
...linkProps
|
|
13
|
-
}) => /*#__PURE__*/React.createElement(LinkBase,
|
|
11
|
+
}) => /*#__PURE__*/React.createElement(LinkBase, Object.assign({
|
|
14
12
|
onPress: onPress,
|
|
15
13
|
accessibilityRole: accessibilityRole,
|
|
16
14
|
variant: {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'react-native';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import ButtonBase from '../Button/ButtonBase';
|
|
5
|
+
import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
|
|
6
|
+
import { a11yProps, copyPropTypes, getTokensPropType, hrefAttrsProp, linkProps, selectTokens, variantProp } from '../utils';
|
|
7
|
+
import useCopy from './useCopy';
|
|
8
|
+
import dictionary from './dictionary';
|
|
9
|
+
|
|
10
|
+
const selectTextStyles = ({
|
|
11
|
+
color,
|
|
12
|
+
fontName,
|
|
13
|
+
fontSize,
|
|
14
|
+
fontWeight,
|
|
15
|
+
lineHeight,
|
|
16
|
+
textLine
|
|
17
|
+
}) => applyTextStyles({
|
|
18
|
+
color,
|
|
19
|
+
fontName,
|
|
20
|
+
fontSize,
|
|
21
|
+
fontWeight,
|
|
22
|
+
lineHeight,
|
|
23
|
+
textDecorationLine: textLine
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
function PageButton({
|
|
27
|
+
label,
|
|
28
|
+
onPress,
|
|
29
|
+
href,
|
|
30
|
+
isActive,
|
|
31
|
+
copy,
|
|
32
|
+
variant,
|
|
33
|
+
tokens,
|
|
34
|
+
...props
|
|
35
|
+
}) {
|
|
36
|
+
const getTokens = useThemeTokensCallback('PaginationPageButton', tokens, variant);
|
|
37
|
+
const getCopy = useCopy({
|
|
38
|
+
dictionary,
|
|
39
|
+
copy
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
|
|
43
|
+
|
|
44
|
+
const getTextStyles = buttonState => selectTextStyles(getTokens(buttonState));
|
|
45
|
+
|
|
46
|
+
const activeProps = isActive ? {
|
|
47
|
+
selected: true,
|
|
48
|
+
...a11yProps.nonFocusableProps,
|
|
49
|
+
// a brute fix for the focus state being stuck on an active item since it becomes non-focusable
|
|
50
|
+
// (see https://github.com/telus/universal-design-system/pull/577#issuecomment-931344107)
|
|
51
|
+
key: 'active-item'
|
|
52
|
+
} : {};
|
|
53
|
+
const accessibilityRole = href !== undefined ? 'link' : 'button';
|
|
54
|
+
const activeLabel = isActive ? ` ${getCopy('currentLabel')}` : '';
|
|
55
|
+
const accessibilityLabel = `${getCopy('goToLabel')} ${label}${activeLabel}`;
|
|
56
|
+
const {
|
|
57
|
+
hrefAttrs,
|
|
58
|
+
rest
|
|
59
|
+
} = hrefAttrsProp.bundle(props);
|
|
60
|
+
const buttonProps = {
|
|
61
|
+
accessibilityRole,
|
|
62
|
+
accessibilityLabel,
|
|
63
|
+
onPress,
|
|
64
|
+
href,
|
|
65
|
+
hrefAttrs,
|
|
66
|
+
...rest
|
|
67
|
+
};
|
|
68
|
+
return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, buttonProps, {
|
|
69
|
+
tokens: getButtonTokens
|
|
70
|
+
}, activeProps), buttonState => {
|
|
71
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
72
|
+
style: getTextStyles(buttonState)
|
|
73
|
+
}, label);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
PageButton.propTypes = {
|
|
78
|
+
label: PropTypes.string,
|
|
79
|
+
isActive: PropTypes.bool,
|
|
80
|
+
copy: copyPropTypes,
|
|
81
|
+
variant: variantProp.propType,
|
|
82
|
+
tokens: getTokensPropType('PaginationPageButton'),
|
|
83
|
+
...linkProps.types
|
|
84
|
+
};
|
|
85
|
+
export default PageButton;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import { componentPropType, copyPropTypes, getTokensPropType, variantProp } from '../utils';
|
|
4
|
+
import { applyTextStyles, useThemeTokens } from '../ThemeProvider';
|
|
5
|
+
import { useViewport } from '../ViewportProvider';
|
|
6
|
+
import Box from '../Box';
|
|
7
|
+
import usePagination from './usePagination';
|
|
8
|
+
import PageButton from './PageButton';
|
|
9
|
+
import SideButton from './SideButton';
|
|
10
|
+
|
|
11
|
+
const selectTextStyles = ({
|
|
12
|
+
color,
|
|
13
|
+
fontName,
|
|
14
|
+
fontSize,
|
|
15
|
+
fontWeight,
|
|
16
|
+
lineHeight
|
|
17
|
+
}) => applyTextStyles({
|
|
18
|
+
color,
|
|
19
|
+
fontName,
|
|
20
|
+
fontSize,
|
|
21
|
+
fontWeight,
|
|
22
|
+
lineHeight
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function Pagination({
|
|
26
|
+
children,
|
|
27
|
+
copy = 'en',
|
|
28
|
+
variant,
|
|
29
|
+
tokens,
|
|
30
|
+
sideButtonVariant,
|
|
31
|
+
sideButtonTokens
|
|
32
|
+
}) {
|
|
33
|
+
const viewport = useViewport();
|
|
34
|
+
const {
|
|
35
|
+
truncateAbove,
|
|
36
|
+
gap,
|
|
37
|
+
...themeTokens
|
|
38
|
+
} = useThemeTokens('Pagination', tokens, variant, {
|
|
39
|
+
viewport
|
|
40
|
+
});
|
|
41
|
+
const items = React.Children.toArray(children);
|
|
42
|
+
const {
|
|
43
|
+
isItemActive,
|
|
44
|
+
getItemProps,
|
|
45
|
+
showPrevious,
|
|
46
|
+
showNext,
|
|
47
|
+
nextItemProps,
|
|
48
|
+
previousItemProps,
|
|
49
|
+
shouldRenderButton,
|
|
50
|
+
shouldRenderEllipsis
|
|
51
|
+
} = usePagination({
|
|
52
|
+
items,
|
|
53
|
+
truncateAbove
|
|
54
|
+
});
|
|
55
|
+
const ellipsisTextStyles = selectTextStyles(themeTokens);
|
|
56
|
+
|
|
57
|
+
if (items.length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
} // TODO: replace with a spacing component when https://github.com/telus/universal-design-system/pull/531 is implemented
|
|
60
|
+
// concatenate all items to easily wrap them in identical spacing components
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const buttons = [showPrevious && /*#__PURE__*/React.createElement(SideButton, Object.assign({}, previousItemProps, {
|
|
64
|
+
direction: "previous",
|
|
65
|
+
copy: copy,
|
|
66
|
+
variant: sideButtonVariant,
|
|
67
|
+
tokens: sideButtonTokens
|
|
68
|
+
})), ...items.map((child, itemIndex) => {
|
|
69
|
+
const buttonLabel = `${itemIndex + 1}`;
|
|
70
|
+
const itemProps = getItemProps(itemIndex);
|
|
71
|
+
|
|
72
|
+
if (shouldRenderButton(itemIndex)) {
|
|
73
|
+
return /*#__PURE__*/React.createElement(PageButton, Object.assign({}, itemProps, {
|
|
74
|
+
label: buttonLabel,
|
|
75
|
+
copy: copy,
|
|
76
|
+
isActive: isItemActive(itemIndex)
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (shouldRenderEllipsis(itemIndex)) {
|
|
81
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
82
|
+
style: ellipsisTextStyles
|
|
83
|
+
}, "...");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}), showNext && /*#__PURE__*/React.createElement(SideButton, Object.assign({}, nextItemProps, {
|
|
88
|
+
direction: "next",
|
|
89
|
+
copy: copy,
|
|
90
|
+
variant: sideButtonVariant,
|
|
91
|
+
tokens: sideButtonTokens
|
|
92
|
+
}))];
|
|
93
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
94
|
+
style: staticStyles.container
|
|
95
|
+
}, buttons // keep the keys in-line with the page numbers regardless of which buttons are actually rendered
|
|
96
|
+
.map((element, index) => [element, `page-${index + 1}`]).filter(([element]) => element !== null).map(([element, key]) => /*#__PURE__*/React.createElement(Box, {
|
|
97
|
+
right: gap,
|
|
98
|
+
key: key
|
|
99
|
+
}, element)));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
Pagination.PageButton = PageButton;
|
|
103
|
+
Pagination.propTypes = {
|
|
104
|
+
children: componentPropType('PageButton'),
|
|
105
|
+
copy: copyPropTypes,
|
|
106
|
+
variant: variantProp.propType,
|
|
107
|
+
tokens: getTokensPropType('Pagination'),
|
|
108
|
+
sideButtonVariant: variantProp.propType,
|
|
109
|
+
sideButtonTokens: getTokensPropType('PaginationSideButton')
|
|
110
|
+
};
|
|
111
|
+
const staticStyles = StyleSheet.create({
|
|
112
|
+
container: {
|
|
113
|
+
flexDirection: 'row',
|
|
114
|
+
flexWrap: 'wrap',
|
|
115
|
+
alignItems: 'center'
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
export default Pagination;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, View, Text, PixelRatio } from 'react-native';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import ButtonBase from '../Button/ButtonBase';
|
|
5
|
+
import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
|
|
6
|
+
import { useViewport } from '../ViewportProvider';
|
|
7
|
+
import { copyPropTypes, hrefAttrsProp, linkProps, selectTokens } from '../utils';
|
|
8
|
+
import dictionary from './dictionary';
|
|
9
|
+
import useCopy from './useCopy';
|
|
10
|
+
|
|
11
|
+
const selectTextStyles = ({
|
|
12
|
+
color,
|
|
13
|
+
fontName,
|
|
14
|
+
fontSize,
|
|
15
|
+
fontWeight,
|
|
16
|
+
lineHeight
|
|
17
|
+
}) => applyTextStyles({
|
|
18
|
+
color,
|
|
19
|
+
fontName,
|
|
20
|
+
fontSize,
|
|
21
|
+
fontWeight,
|
|
22
|
+
lineHeight
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const selectIconTokens = ({
|
|
26
|
+
color,
|
|
27
|
+
iconSize,
|
|
28
|
+
iconTranslateX
|
|
29
|
+
}) => {
|
|
30
|
+
// Scale icon with text, but with a cap so text isn't squashed at large scales
|
|
31
|
+
const iconScale = Math.min(PixelRatio.getFontScale(), 2);
|
|
32
|
+
return {
|
|
33
|
+
color,
|
|
34
|
+
size: iconSize * iconScale,
|
|
35
|
+
translateX: iconTranslateX
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function SideButton({
|
|
40
|
+
direction = 'previous',
|
|
41
|
+
onPress,
|
|
42
|
+
href,
|
|
43
|
+
copy,
|
|
44
|
+
variant,
|
|
45
|
+
tokens,
|
|
46
|
+
...props
|
|
47
|
+
}) {
|
|
48
|
+
const viewport = useViewport();
|
|
49
|
+
const buttonVariant = {
|
|
50
|
+
direction,
|
|
51
|
+
viewport,
|
|
52
|
+
...variant
|
|
53
|
+
};
|
|
54
|
+
const getTokens = useThemeTokensCallback('PaginationSideButton', tokens, buttonVariant);
|
|
55
|
+
const getCopy = useCopy({
|
|
56
|
+
dictionary,
|
|
57
|
+
copy
|
|
58
|
+
});
|
|
59
|
+
const {
|
|
60
|
+
icon: IconComponent
|
|
61
|
+
} = getTokens(tokens, buttonVariant);
|
|
62
|
+
|
|
63
|
+
const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
|
|
64
|
+
|
|
65
|
+
const getIconTokens = buttonState => selectIconTokens(getTokens(buttonState));
|
|
66
|
+
|
|
67
|
+
const getTextStyles = buttonState => selectTextStyles(getTokens(buttonState));
|
|
68
|
+
|
|
69
|
+
const label = direction === 'previous' ? getCopy('previousText') : getCopy('nextText');
|
|
70
|
+
const showLabel = viewport !== 'sm' && viewport !== 'xs';
|
|
71
|
+
const accessibilityLabel = direction === 'previous' ? getCopy('previousLabel') : getCopy('nextLabel');
|
|
72
|
+
const accessibilityRole = href !== undefined ? 'link' : 'button';
|
|
73
|
+
const {
|
|
74
|
+
hrefAttrs,
|
|
75
|
+
rest
|
|
76
|
+
} = hrefAttrsProp.bundle(props);
|
|
77
|
+
const buttonProps = {
|
|
78
|
+
accessibilityRole,
|
|
79
|
+
accessibilityLabel,
|
|
80
|
+
onPress,
|
|
81
|
+
href,
|
|
82
|
+
hrefAttrs,
|
|
83
|
+
...rest
|
|
84
|
+
};
|
|
85
|
+
return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, buttonProps, {
|
|
86
|
+
tokens: getButtonTokens
|
|
87
|
+
}), buttonState => /*#__PURE__*/React.createElement(View, {
|
|
88
|
+
style: staticStyles.contentContainer
|
|
89
|
+
}, direction === 'previous' && IconComponent && /*#__PURE__*/React.createElement(IconComponent, {
|
|
90
|
+
tokens: getIconTokens(buttonState)
|
|
91
|
+
}), showLabel && /*#__PURE__*/React.createElement(Text, {
|
|
92
|
+
style: getTextStyles(buttonState)
|
|
93
|
+
}, label), direction === 'next' && IconComponent && /*#__PURE__*/React.createElement(IconComponent, {
|
|
94
|
+
tokens: getIconTokens(buttonState)
|
|
95
|
+
})));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
SideButton.propTypes = {
|
|
99
|
+
direction: PropTypes.oneOf(['previous', 'next']),
|
|
100
|
+
copy: copyPropTypes,
|
|
101
|
+
...linkProps.types
|
|
102
|
+
};
|
|
103
|
+
export default SideButton;
|
|
104
|
+
const staticStyles = StyleSheet.create({
|
|
105
|
+
contentContainer: {
|
|
106
|
+
flexDirection: 'row'
|
|
107
|
+
}
|
|
108
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
en: {
|
|
3
|
+
goToLabel: 'Go to page number',
|
|
4
|
+
currentLabel: '(current)',
|
|
5
|
+
previousLabel: 'Go to previous page',
|
|
6
|
+
previousText: 'Previous',
|
|
7
|
+
nextLabel: 'Go to next page',
|
|
8
|
+
nextText: 'Next'
|
|
9
|
+
},
|
|
10
|
+
fr: {
|
|
11
|
+
goToLabel: 'Aller au page n°',
|
|
12
|
+
currentLabel: '(page actuelle)',
|
|
13
|
+
previousLabel: 'Aller au page précédent',
|
|
14
|
+
previousText: 'Précédent',
|
|
15
|
+
nextLabel: 'Aller au prochain page',
|
|
16
|
+
nextText: 'Suivant'
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles configurable truncating of pagination items.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} props
|
|
5
|
+
* @param {React.Element[]} items
|
|
6
|
+
* @param {number} truncateAbove
|
|
7
|
+
*/
|
|
8
|
+
function usePagination({
|
|
9
|
+
items,
|
|
10
|
+
truncateAbove
|
|
11
|
+
}) {
|
|
12
|
+
const activeItemIndex = Math.max(items.findIndex(item => item.props.isActive), 0 // default to the first item if none is marked as active
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const getItemProps = index => {
|
|
16
|
+
const {
|
|
17
|
+
onPress,
|
|
18
|
+
href,
|
|
19
|
+
hrefAttrs,
|
|
20
|
+
variant,
|
|
21
|
+
tokens
|
|
22
|
+
} = items[index]?.props ?? {};
|
|
23
|
+
return {
|
|
24
|
+
onPress,
|
|
25
|
+
href,
|
|
26
|
+
hrefAttrs,
|
|
27
|
+
variant,
|
|
28
|
+
tokens
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const windowSize = truncateAbove > 4 ? 3 : 1;
|
|
33
|
+
const truncateEnabled = items.length > truncateAbove;
|
|
34
|
+
const truncateWindowStart = windowSize;
|
|
35
|
+
const truncateWindowEnd = items.length - 1 - windowSize; // basically how many items next to the currently active one do we want to render
|
|
36
|
+
|
|
37
|
+
const activeWindowStart = activeItemIndex - (windowSize - 1) / 2;
|
|
38
|
+
const activeWindowEnd = activeItemIndex + (windowSize - 1) / 2;
|
|
39
|
+
const isFirstActive = activeItemIndex === 0;
|
|
40
|
+
const isLastActive = activeItemIndex === items.length - 1; // start truncating when the active item is within the truncate window
|
|
41
|
+
|
|
42
|
+
const isLeftTruncated = truncateEnabled && activeItemIndex >= truncateWindowStart;
|
|
43
|
+
const isRightTruncated = truncateEnabled && activeItemIndex <= truncateWindowEnd;
|
|
44
|
+
|
|
45
|
+
function shouldRenderEllipsis(itemIndex) {
|
|
46
|
+
const isNextToFirst = itemIndex === 1;
|
|
47
|
+
const isNextToLast = itemIndex === items.length - 2;
|
|
48
|
+
return isLeftTruncated && isNextToFirst || isRightTruncated && isNextToLast;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function shouldRenderButton(itemIndex) {
|
|
52
|
+
const isFirst = itemIndex === 0;
|
|
53
|
+
const isLast = itemIndex === items.length - 1;
|
|
54
|
+
const isInActiveWindow = itemIndex >= activeWindowStart && itemIndex <= activeWindowEnd;
|
|
55
|
+
return isFirst || isLast || isInActiveWindow || !isLeftTruncated && itemIndex <= truncateWindowStart || !isRightTruncated && itemIndex >= truncateWindowEnd;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
showPrevious: !isFirstActive,
|
|
60
|
+
showNext: !isLastActive,
|
|
61
|
+
shouldRenderButton,
|
|
62
|
+
shouldRenderEllipsis,
|
|
63
|
+
isItemActive: itemIndex => itemIndex === activeItemIndex,
|
|
64
|
+
getItemProps,
|
|
65
|
+
nextItemProps: getItemProps(activeItemIndex + 1),
|
|
66
|
+
previousItemProps: getItemProps(activeItemIndex - 1)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default usePagination;
|
package/lib/SideNav/Item.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
-
|
|
3
1
|
import React from 'react';
|
|
4
2
|
import { Pressable } from 'react-native';
|
|
5
3
|
import PropTypes from 'prop-types';
|
|
@@ -66,7 +64,7 @@ const Item = ({
|
|
|
66
64
|
...hrefAttrsProp.spread(hrefAttrs),
|
|
67
65
|
...rest
|
|
68
66
|
});
|
|
69
|
-
const getTokens = useThemeTokensCallback('SideNavItem');
|
|
67
|
+
const getTokens = useThemeTokensCallback('SideNavItem', tokens, variant);
|
|
70
68
|
|
|
71
69
|
const getAppearanceState = ({
|
|
72
70
|
hovered
|
|
@@ -76,9 +74,9 @@ const Item = ({
|
|
|
76
74
|
expanded: isExpanded
|
|
77
75
|
});
|
|
78
76
|
|
|
79
|
-
const getPressableStyle = pressableState => selectItemStyles(getTokens(
|
|
77
|
+
const getPressableStyle = pressableState => selectItemStyles(getTokens(getAppearanceState(pressableState)));
|
|
80
78
|
|
|
81
|
-
return /*#__PURE__*/React.createElement(Pressable,
|
|
79
|
+
return /*#__PURE__*/React.createElement(Pressable, Object.assign({
|
|
82
80
|
style: getPressableStyle
|
|
83
81
|
}, linkPropSet, {
|
|
84
82
|
accessibilityState: {
|
|
@@ -86,7 +84,7 @@ const Item = ({
|
|
|
86
84
|
},
|
|
87
85
|
testID: testID
|
|
88
86
|
}), pressableState => {
|
|
89
|
-
const themeTokens = getTokens(
|
|
87
|
+
const themeTokens = getTokens(getAppearanceState(pressableState));
|
|
90
88
|
return /*#__PURE__*/React.createElement(ItemContent, {
|
|
91
89
|
tokens: themeTokens
|
|
92
90
|
}, children);
|