@telus-uds/components-base 1.3.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +13 -0
- package/CHANGELOG.json +142 -1
- package/CHANGELOG.md +51 -2
- package/__tests__/FlexGrid/Row.test.jsx +100 -25
- package/__tests__/utils/containUniqueFields.test.js +25 -0
- package/component-docs.json +50 -15
- package/lib/Button/ButtonBase.js +1 -1
- package/lib/Button/ButtonGroup.js +20 -12
- package/lib/Card/PressableCardBase.js +1 -1
- package/lib/Checkbox/Checkbox.js +27 -16
- package/lib/Checkbox/CheckboxGroup.js +19 -5
- package/lib/ExpandCollapse/Panel.js +10 -10
- package/lib/FlexGrid/Col/Col.js +13 -3
- package/lib/FlexGrid/Row/Row.js +8 -2
- package/lib/HorizontalScroll/HorizontalScroll.js +0 -1
- package/lib/HorizontalScroll/HorizontalScrollButton.js +23 -49
- package/lib/InputLabel/InputLabel.js +27 -25
- package/lib/Link/LinkBase.js +19 -6
- package/lib/Modal/Modal.js +18 -18
- package/lib/Notification/Notification.js +5 -6
- package/lib/Radio/Radio.js +23 -12
- package/lib/Radio/RadioGroup.js +12 -3
- package/lib/RadioCard/RadioCard.js +1 -1
- package/lib/RadioCard/RadioCardGroup.js +11 -2
- package/lib/Select/Select.js +2 -3
- package/lib/Tags/Tags.js +23 -17
- package/lib/TextInput/TextArea.js +2 -2
- package/lib/TextInput/TextInput.js +12 -2
- package/lib/TextInput/TextInputBase.js +1 -1
- package/lib/TextInput/propTypes.js +8 -1
- package/lib/ToggleSwitch/ToggleSwitch.js +5 -2
- package/lib/ToggleSwitch/ToggleSwitchGroup.js +20 -12
- package/lib/utils/containUniqueFields.js +34 -0
- package/lib/utils/index.js +10 -1
- package/lib/utils/props/handlerProps.js +72 -0
- package/lib/utils/props/index.js +14 -0
- package/lib/utils/props/inputSupportsProps.js +3 -5
- package/lib-module/Button/ButtonBase.js +2 -2
- package/lib-module/Button/ButtonGroup.js +15 -6
- package/lib-module/Card/PressableCardBase.js +2 -2
- package/lib-module/Checkbox/Checkbox.js +28 -17
- package/lib-module/Checkbox/CheckboxGroup.js +20 -7
- package/lib-module/ExpandCollapse/Panel.js +10 -10
- package/lib-module/FlexGrid/Col/Col.js +13 -3
- package/lib-module/FlexGrid/Row/Row.js +8 -2
- package/lib-module/HorizontalScroll/HorizontalScroll.js +0 -1
- package/lib-module/HorizontalScroll/HorizontalScrollButton.js +24 -49
- package/lib-module/InputLabel/InputLabel.js +28 -25
- package/lib-module/Link/LinkBase.js +19 -6
- package/lib-module/Modal/Modal.js +19 -19
- package/lib-module/Notification/Notification.js +6 -6
- package/lib-module/Radio/Radio.js +24 -13
- package/lib-module/Radio/RadioGroup.js +13 -4
- package/lib-module/RadioCard/RadioCard.js +2 -2
- package/lib-module/RadioCard/RadioCardGroup.js +12 -3
- package/lib-module/Select/Select.js +2 -3
- package/lib-module/Tags/Tags.js +18 -11
- package/lib-module/TextInput/TextArea.js +3 -3
- package/lib-module/TextInput/TextInput.js +11 -3
- package/lib-module/TextInput/TextInputBase.js +2 -2
- package/lib-module/TextInput/propTypes.js +7 -1
- package/lib-module/ToggleSwitch/ToggleSwitch.js +6 -3
- package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +15 -6
- package/lib-module/utils/containUniqueFields.js +26 -0
- package/lib-module/utils/index.js +2 -1
- package/lib-module/utils/props/handlerProps.js +59 -0
- package/lib-module/utils/props/index.js +1 -0
- package/lib-module/utils/props/inputSupportsProps.js +3 -5
- package/package.json +5 -5
- package/src/Button/ButtonBase.jsx +8 -2
- package/src/Button/ButtonGroup.jsx +51 -34
- package/src/Card/PressableCardBase.jsx +6 -1
- package/src/Checkbox/Checkbox.jsx +35 -23
- package/src/Checkbox/CheckboxGroup.jsx +52 -22
- package/src/ExpandCollapse/Panel.jsx +9 -9
- package/src/FlexGrid/Col/Col.jsx +11 -2
- package/src/FlexGrid/Row/Row.jsx +8 -2
- package/src/HorizontalScroll/HorizontalScroll.jsx +1 -1
- package/src/HorizontalScroll/HorizontalScrollButton.jsx +21 -58
- package/src/InputLabel/InputLabel.jsx +36 -27
- package/src/Link/LinkBase.jsx +20 -4
- package/src/Modal/Modal.jsx +30 -26
- package/src/Notification/Notification.jsx +7 -4
- package/src/Radio/Radio.jsx +26 -14
- package/src/Radio/RadioGroup.jsx +39 -21
- package/src/RadioCard/RadioCard.jsx +6 -1
- package/src/RadioCard/RadioCardGroup.jsx +17 -1
- package/src/Select/Select.jsx +2 -2
- package/src/Tags/Tags.jsx +23 -9
- package/src/TextInput/TextArea.jsx +5 -1
- package/src/TextInput/TextInput.jsx +13 -3
- package/src/TextInput/TextInputBase.jsx +6 -1
- package/src/TextInput/propTypes.js +7 -1
- package/src/ToggleSwitch/ToggleSwitch.jsx +11 -2
- package/src/ToggleSwitch/ToggleSwitchGroup.jsx +19 -6
- package/src/utils/containUniqueFields.js +32 -0
- package/src/utils/index.js +1 -0
- package/src/utils/props/handlerProps.js +47 -0
- package/src/utils/props/index.js +1 -0
- package/src/utils/props/inputSupportsProps.js +3 -4
- package/stories/InputLabel/InputLabel.stories.jsx +25 -28
- package/stories/Modal/Modal.stories.jsx +25 -0
- package/stories/Search/Search.stories.jsx +4 -1
- package/stories/TextInput/TextInput.stories.jsx +40 -2
package/src/FlexGrid/Col/Col.jsx
CHANGED
|
@@ -23,6 +23,7 @@ const Col = forwardRef(
|
|
|
23
23
|
mdOffset,
|
|
24
24
|
lgOffset,
|
|
25
25
|
xlOffset,
|
|
26
|
+
flex,
|
|
26
27
|
...viewProps
|
|
27
28
|
},
|
|
28
29
|
ref
|
|
@@ -106,7 +107,10 @@ const Col = forwardRef(
|
|
|
106
107
|
|
|
107
108
|
let hidingStyles = {}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
// TODO: consider setting this to always 'flex' in a major release.
|
|
111
|
+
// `display: block` is invalid in native apps.
|
|
112
|
+
// See https://telusdigital.atlassian.net/browse/UDS1-92
|
|
113
|
+
const shown = !flex && Platform.OS === 'web' ? 'block' : 'flex'
|
|
110
114
|
|
|
111
115
|
if (viewPort === viewports.xs) {
|
|
112
116
|
hidingStyles = {
|
|
@@ -267,7 +271,12 @@ Col.propTypes = {
|
|
|
267
271
|
*/
|
|
268
272
|
horizontalAlign: responsiveProps.getTypeOptionallyByViewport(
|
|
269
273
|
PropTypes.oneOf(['left', 'center', 'right'])
|
|
270
|
-
)
|
|
274
|
+
),
|
|
275
|
+
/**
|
|
276
|
+
* (web only) Stretches the column to fill vertical space using `display: flex`.
|
|
277
|
+
* In native apps, FlexGrid.Col behaves as if this is always true (as do all `View`s).
|
|
278
|
+
*/
|
|
279
|
+
flex: PropTypes.bool
|
|
271
280
|
}
|
|
272
281
|
|
|
273
282
|
export default Col
|
package/src/FlexGrid/Row/Row.jsx
CHANGED
|
@@ -72,21 +72,27 @@ const Row = forwardRef(
|
|
|
72
72
|
const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
|
|
73
73
|
|
|
74
74
|
let flexDirection = ''
|
|
75
|
+
let flexWrap = ''
|
|
75
76
|
|
|
76
77
|
if (viewPort === viewports.xs) {
|
|
77
78
|
flexDirection = reverseLevel[0] ? 'row-reverse' : 'row'
|
|
79
|
+
flexWrap = reverseLevel[0] ? 'wrap-reverse' : 'wrap'
|
|
78
80
|
}
|
|
79
81
|
if (viewPort === viewports.sm) {
|
|
80
82
|
flexDirection = reverseLevel[1] ? 'row-reverse' : 'row'
|
|
83
|
+
flexWrap = reverseLevel[1] ? 'wrap-reverse' : 'wrap'
|
|
81
84
|
}
|
|
82
85
|
if (viewPort === viewports.md) {
|
|
83
86
|
flexDirection = reverseLevel[2] ? 'row-reverse' : 'row'
|
|
87
|
+
flexWrap = reverseLevel[2] ? 'wrap-reverse' : 'wrap'
|
|
84
88
|
}
|
|
85
89
|
if (viewPort === viewports.lg) {
|
|
86
90
|
flexDirection = reverseLevel[3] ? 'row-reverse' : 'row'
|
|
91
|
+
flexWrap = reverseLevel[3] ? 'wrap-reverse' : 'wrap'
|
|
87
92
|
}
|
|
88
93
|
if (viewPort === viewports.xl) {
|
|
89
94
|
flexDirection = reverseLevel[4] ? 'row-reverse' : 'row'
|
|
95
|
+
flexWrap = reverseLevel[4] ? 'wrap-reverse' : 'wrap'
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
return (
|
|
@@ -97,6 +103,7 @@ const Row = forwardRef(
|
|
|
97
103
|
styles.row,
|
|
98
104
|
{
|
|
99
105
|
flexDirection,
|
|
106
|
+
flexWrap,
|
|
100
107
|
...horizontalAlignStyles(horizontalAlign),
|
|
101
108
|
...verticalAlignStyles(verticalAlign),
|
|
102
109
|
...distributeStyles(distribute)
|
|
@@ -118,8 +125,7 @@ const styles = StyleSheet.create({
|
|
|
118
125
|
flexGrow: 0,
|
|
119
126
|
flexShrink: 1,
|
|
120
127
|
flexBasis: 'auto',
|
|
121
|
-
flexDirection: 'row'
|
|
122
|
-
flexWrap: 'wrap'
|
|
128
|
+
flexDirection: 'row'
|
|
123
129
|
}
|
|
124
130
|
})
|
|
125
131
|
|
|
@@ -129,7 +129,7 @@ const HorizontalScroll = forwardRef(
|
|
|
129
129
|
showsHorizontalScrollIndicator={false}
|
|
130
130
|
contentContainerStyle={[
|
|
131
131
|
staticStyles.scrollContainer,
|
|
132
|
-
{
|
|
132
|
+
{ borderBottomWidth, borderBottomColor }
|
|
133
133
|
]}
|
|
134
134
|
{...selectProps(rest)}
|
|
135
135
|
>
|
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
resolvePressableTokens,
|
|
8
|
-
selectTokens,
|
|
9
|
-
variantProp,
|
|
10
|
-
getTokensPropType,
|
|
11
|
-
useCopy,
|
|
12
|
-
copyPropTypes,
|
|
13
|
-
a11yProps
|
|
14
|
-
} from '../utils'
|
|
15
|
-
import Icon from '../Icon'
|
|
16
|
-
import Typography from '../Typography'
|
|
3
|
+
import { StyleSheet, View } from 'react-native'
|
|
4
|
+
import { variantProp, getTokensPropType, useCopy, copyPropTypes, a11yProps } from '../utils'
|
|
5
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
6
|
+
import IconButton from '../IconButton'
|
|
17
7
|
import dictionary from './dictionary'
|
|
18
8
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
) => [
|
|
23
|
-
staticStyles.absolute,
|
|
24
|
-
staticStyles[direction],
|
|
25
|
-
{
|
|
26
|
-
borderRadius,
|
|
27
|
-
backgroundColor,
|
|
28
|
-
borderColor,
|
|
29
|
-
borderWidth,
|
|
30
|
-
padding
|
|
31
|
-
}
|
|
32
|
-
]
|
|
9
|
+
const selectContainerStyles = ({ offset }) => ({
|
|
10
|
+
marginTop: offset ? -offset : 0
|
|
11
|
+
})
|
|
33
12
|
|
|
34
13
|
/**
|
|
35
14
|
* Button within a Tabs component showing users that content is available to the left or
|
|
@@ -40,41 +19,25 @@ const selectButtonStyles = (
|
|
|
40
19
|
* @TODO when IconButton is complete and stable revisit this and update interaction state styles.
|
|
41
20
|
*/
|
|
42
21
|
const HorizontalScrollButton = forwardRef(
|
|
43
|
-
({ direction = 'next', icon,
|
|
44
|
-
const
|
|
45
|
-
const resolveButtonTokens = (pressableState) =>
|
|
46
|
-
resolvePressableTokens(getTokens, pressableState)
|
|
47
|
-
const getPressableStyle = (pressableState) => [
|
|
48
|
-
selectButtonStyles(resolveButtonTokens(pressableState), direction),
|
|
49
|
-
{ marginTop: -1 * (offset || 0) }
|
|
50
|
-
]
|
|
51
|
-
|
|
22
|
+
({ direction = 'next', icon, offset, onPress, variant, tokens, copy }, ref) => {
|
|
23
|
+
const themeTokens = useThemeTokens('HorizontalScrollButton', tokens, variant)
|
|
52
24
|
const getCopy = useCopy({ dictionary, copy })
|
|
53
25
|
const label = direction === 'previous' ? getCopy('previousText') : getCopy('nextText')
|
|
54
26
|
|
|
55
27
|
return (
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
style={getPressableStyle}
|
|
59
|
-
onPress={onPress}
|
|
60
|
-
accessibilityLabel={label}
|
|
61
|
-
accessibilityRole="button"
|
|
62
|
-
// For keyboard-tab or screenreader-swipe navigation, users can just go through all items
|
|
63
|
-
{...a11yProps.nonFocusableProps}
|
|
28
|
+
<View
|
|
29
|
+
style={[staticStyles.absolute, staticStyles[direction], selectContainerStyles({ offset })]}
|
|
64
30
|
>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
}}
|
|
77
|
-
</Pressable>
|
|
31
|
+
<IconButton
|
|
32
|
+
accessibilityLabel={label}
|
|
33
|
+
icon={icon}
|
|
34
|
+
onPress={onPress}
|
|
35
|
+
ref={ref}
|
|
36
|
+
tokens={themeTokens}
|
|
37
|
+
variant={variant}
|
|
38
|
+
{...a11yProps.nonFocusableProps}
|
|
39
|
+
/>
|
|
40
|
+
</View>
|
|
78
41
|
)
|
|
79
42
|
}
|
|
80
43
|
)
|
|
@@ -47,35 +47,46 @@ const InputLabel = forwardRef(
|
|
|
47
47
|
const isHintInline = hintPosition === 'inline'
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
|
|
51
|
-
ref={ref}
|
|
52
|
-
style={[staticStyles.container, !isHintInline && staticStyles.containerWithHintBelow]}
|
|
53
|
-
{...selectProps(rest)}
|
|
54
|
-
>
|
|
55
|
-
<Text
|
|
56
|
-
style={[selectLabelStyles(themeTokens), selectGapStyles(themeTokens), staticStyles.label]}
|
|
57
|
-
>
|
|
58
|
-
<LabelContent forId={forId}>{label}</LabelContent>
|
|
59
|
-
</Text>
|
|
60
|
-
{hint && isHintInline && (
|
|
50
|
+
<>
|
|
51
|
+
<View ref={ref} style={staticStyles.container} {...selectProps(rest)}>
|
|
61
52
|
<Text
|
|
62
|
-
style={[
|
|
63
|
-
|
|
53
|
+
style={[
|
|
54
|
+
selectLabelStyles(themeTokens),
|
|
55
|
+
selectGapStyles(themeTokens),
|
|
56
|
+
staticStyles.label
|
|
57
|
+
]}
|
|
64
58
|
>
|
|
65
|
-
{
|
|
59
|
+
<LabelContent forId={forId}>{label}</LabelContent>
|
|
66
60
|
</Text>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
{hint && isHintInline && (
|
|
62
|
+
<Text
|
|
63
|
+
style={[
|
|
64
|
+
selectHintStyles(themeTokens),
|
|
65
|
+
hasTooltip && selectGapStyles(themeTokens),
|
|
66
|
+
staticStyles.label
|
|
67
|
+
]}
|
|
68
|
+
nativeID={hintId}
|
|
69
|
+
>
|
|
70
|
+
{hint}
|
|
71
|
+
</Text>
|
|
72
|
+
)}
|
|
73
|
+
{hasTooltip && (
|
|
74
|
+
<View
|
|
75
|
+
style={[
|
|
76
|
+
staticStyles.tooltipAlign,
|
|
77
|
+
{ height: themeTokens.fontSize * themeTokens.lineHeight }
|
|
78
|
+
]}
|
|
79
|
+
>
|
|
80
|
+
<Tooltip content={tooltip} />
|
|
81
|
+
</View>
|
|
82
|
+
)}
|
|
83
|
+
</View>
|
|
73
84
|
{hint && !isHintInline && (
|
|
74
85
|
<Text style={[selectHintStyles(themeTokens), staticStyles.hintBelow]} nativeID={hintId}>
|
|
75
86
|
{hint}
|
|
76
87
|
</Text>
|
|
77
88
|
)}
|
|
78
|
-
|
|
89
|
+
</>
|
|
79
90
|
)
|
|
80
91
|
}
|
|
81
92
|
)
|
|
@@ -115,21 +126,19 @@ export default InputLabel
|
|
|
115
126
|
|
|
116
127
|
const staticStyles = StyleSheet.create({
|
|
117
128
|
container: {
|
|
118
|
-
|
|
129
|
+
flexShrink: 1,
|
|
119
130
|
flexDirection: 'row',
|
|
120
131
|
alignItems: 'baseline'
|
|
121
132
|
},
|
|
122
|
-
containerWithHintBelow: {
|
|
123
|
-
flexWrap: 'wrap'
|
|
124
|
-
},
|
|
125
133
|
label: {
|
|
126
|
-
flexShrink:
|
|
134
|
+
flexShrink: 1
|
|
127
135
|
},
|
|
128
136
|
hintBelow: {
|
|
129
137
|
flexBasis: '100%',
|
|
130
138
|
flexShrink: 0
|
|
131
139
|
},
|
|
132
140
|
tooltipAlign: {
|
|
133
|
-
alignSelf: '
|
|
141
|
+
alignSelf: 'flex-start',
|
|
142
|
+
justifyContent: 'center'
|
|
134
143
|
}
|
|
135
144
|
})
|
package/src/Link/LinkBase.jsx
CHANGED
|
@@ -45,10 +45,8 @@ const selectOuterBorderStyles = ({
|
|
|
45
45
|
}
|
|
46
46
|
: {}
|
|
47
47
|
|
|
48
|
-
const selectTextStyles = ({ color
|
|
48
|
+
const selectTextStyles = ({ color }) => ({
|
|
49
49
|
color,
|
|
50
|
-
textDecorationLine: textLine,
|
|
51
|
-
textDecorationStyle: textLineStyle,
|
|
52
50
|
...Platform.select({
|
|
53
51
|
web: {
|
|
54
52
|
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
@@ -65,6 +63,18 @@ const selectBlockStyles = ({ blockFontWeight, blockFontSize, blockLineHeight, bl
|
|
|
65
63
|
fontName: blockFontName
|
|
66
64
|
})
|
|
67
65
|
|
|
66
|
+
const selectDecorationStyles = ({ color, textLine, textLineStyle }) => ({
|
|
67
|
+
color,
|
|
68
|
+
textDecorationLine: textLine,
|
|
69
|
+
textDecorationStyle: textLineStyle,
|
|
70
|
+
...Platform.select({
|
|
71
|
+
web: {
|
|
72
|
+
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
73
|
+
transition: 'color 200ms'
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
68
78
|
const selectIconTokens = ({ color, iconSize, iconTranslateX, iconTranslateY }) => ({
|
|
69
79
|
color,
|
|
70
80
|
translateX: iconTranslateX,
|
|
@@ -132,8 +142,14 @@ const LinkBase = forwardRef(
|
|
|
132
142
|
style={(linkState) => {
|
|
133
143
|
const themeTokens = resolveLinkTokens(linkState)
|
|
134
144
|
const outerBorderStyles = selectOuterBorderStyles(themeTokens)
|
|
145
|
+
const decorationStyles = selectDecorationStyles(themeTokens)
|
|
135
146
|
const hasIcon = Boolean(icon || themeTokens.icon)
|
|
136
|
-
return [
|
|
147
|
+
return [
|
|
148
|
+
outerBorderStyles,
|
|
149
|
+
blockLeftStyle,
|
|
150
|
+
decorationStyles,
|
|
151
|
+
hasIcon && staticStyles.rowContainer
|
|
152
|
+
]
|
|
137
153
|
}}
|
|
138
154
|
>
|
|
139
155
|
{(linkState) => {
|
package/src/Modal/Modal.jsx
CHANGED
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
selectSystemProps,
|
|
17
17
|
useCopy,
|
|
18
18
|
variantProp,
|
|
19
|
-
viewProps
|
|
19
|
+
viewProps,
|
|
20
|
+
selectTokens
|
|
20
21
|
} from '../utils'
|
|
21
22
|
import { useViewport } from '../ViewportProvider'
|
|
22
|
-
import
|
|
23
|
+
import IconButton from '../IconButton'
|
|
23
24
|
import dictionary from './dictionary'
|
|
24
25
|
|
|
25
26
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
@@ -70,11 +71,6 @@ const selectCloseButtonContainerStyles = ({ paddingRight, paddingTop }) => ({
|
|
|
70
71
|
paddingTop
|
|
71
72
|
})
|
|
72
73
|
|
|
73
|
-
const selectCloseIconProps = ({ closeIconSize, closeIconColor }) => ({
|
|
74
|
-
size: closeIconSize,
|
|
75
|
-
color: closeIconColor
|
|
76
|
-
})
|
|
77
|
-
|
|
78
74
|
/**
|
|
79
75
|
* A modal window is a secondary window that opens on top of the main one.
|
|
80
76
|
* Users have to interact with it before they can carry out their task and return to the main window.
|
|
@@ -90,7 +86,7 @@ const selectCloseIconProps = ({ closeIconSize, closeIconColor }) => ({
|
|
|
90
86
|
* - Don’t use modals consecutively
|
|
91
87
|
*/
|
|
92
88
|
const Modal = forwardRef(
|
|
93
|
-
({ children, isOpen, onClose, maxWidth, tokens, variant, copy, ...rest }, ref) => {
|
|
89
|
+
({ children, isOpen, onClose, maxWidth, tokens, variant, copy, closeButton, ...rest }, ref) => {
|
|
94
90
|
const viewport = useViewport()
|
|
95
91
|
const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth })
|
|
96
92
|
|
|
@@ -107,11 +103,14 @@ const Modal = forwardRef(
|
|
|
107
103
|
if (event.key === 'Escape') onClose()
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
// Show the custom react node passed to `closedButton` or the default close button if `closeButton` is `undefined`.
|
|
107
|
+
// Hide the close button if `closeButton` is `null`.
|
|
108
|
+
const showCloseButton = closeButton !== null
|
|
109
|
+
|
|
110
110
|
if (!isOpen) {
|
|
111
111
|
return null
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// TODO: replace the close button with IconButton when implemented (https://github.com/telus/universal-design-system/issues/281)
|
|
115
114
|
return (
|
|
116
115
|
<NativeModal transparent {...selectProps(rest)}>
|
|
117
116
|
<View style={[staticStyles.positioningContainer]}>
|
|
@@ -124,23 +123,24 @@ const Modal = forwardRef(
|
|
|
124
123
|
style={[staticStyles.modal, selectModalStyles(themeTokens)]}
|
|
125
124
|
onKeyUp={handleKeyUp}
|
|
126
125
|
>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<ButtonBase
|
|
134
|
-
onPress={handleClose}
|
|
135
|
-
accessibilityRole="button"
|
|
136
|
-
accessibilityLabel={closeLabel}
|
|
126
|
+
{showCloseButton && (
|
|
127
|
+
<View
|
|
128
|
+
style={[
|
|
129
|
+
staticStyles.closeButtonContainer,
|
|
130
|
+
selectCloseButtonContainerStyles(themeTokens)
|
|
131
|
+
]}
|
|
137
132
|
>
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
{closeButton || (
|
|
134
|
+
<IconButton
|
|
135
|
+
onPress={handleClose}
|
|
136
|
+
icon={CloseIconComponent}
|
|
137
|
+
accessibilityRole="button"
|
|
138
|
+
accessibilityLabel={closeLabel}
|
|
139
|
+
tokens={selectTokens('IconButton', themeTokens, 'close')}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
142
|
+
</View>
|
|
143
|
+
)}
|
|
144
144
|
{children}
|
|
145
145
|
</View>
|
|
146
146
|
</View>
|
|
@@ -164,7 +164,11 @@ Modal.propTypes = {
|
|
|
164
164
|
onClose: PropTypes.func,
|
|
165
165
|
maxWidth: PropTypes.bool,
|
|
166
166
|
tokens: getTokensPropType('Modal'),
|
|
167
|
-
variant: variantProp.propType
|
|
167
|
+
variant: variantProp.propType,
|
|
168
|
+
/**
|
|
169
|
+
* Pass a react node to override the default close button or pass `null` to hide the close button.
|
|
170
|
+
*/
|
|
171
|
+
closeButton: PropTypes.node
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
export default Modal
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef, useState } from 'react'
|
|
2
|
-
import { StyleSheet,
|
|
2
|
+
import { StyleSheet, View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
import PropTypes from 'prop-types'
|
|
5
5
|
import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
selectTokens,
|
|
11
11
|
variantProp,
|
|
12
|
-
viewProps
|
|
12
|
+
viewProps,
|
|
13
|
+
wrapStringsInText
|
|
13
14
|
} from '../utils'
|
|
14
15
|
import ButtonBase from '../Button/ButtonBase'
|
|
15
16
|
import useCopy from '../utils/useCopy'
|
|
@@ -102,8 +103,10 @@ const Notification = forwardRef(
|
|
|
102
103
|
|
|
103
104
|
const textStyles = selectTextStyles(themeTokens)
|
|
104
105
|
|
|
105
|
-
const content =
|
|
106
|
-
typeof children === '
|
|
106
|
+
const content = wrapStringsInText(
|
|
107
|
+
typeof children === 'function' ? children({ textStyles }) : children,
|
|
108
|
+
{ style: textStyles }
|
|
109
|
+
)
|
|
107
110
|
|
|
108
111
|
const { icon: IconComponent, dismissIcon: DismissIconComponent } = themeTokens
|
|
109
112
|
|
package/src/Radio/Radio.jsx
CHANGED
|
@@ -7,6 +7,7 @@ import RadioButton, { selectRadioButtonTokens } from './RadioButton'
|
|
|
7
7
|
import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
|
|
8
8
|
import {
|
|
9
9
|
a11yProps,
|
|
10
|
+
focusHandlerProps,
|
|
10
11
|
getTokensPropType,
|
|
11
12
|
selectSystemProps,
|
|
12
13
|
useInputValue,
|
|
@@ -16,7 +17,11 @@ import {
|
|
|
16
17
|
} from '../utils'
|
|
17
18
|
import StackView from '../StackView'
|
|
18
19
|
|
|
19
|
-
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
20
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
21
|
+
a11yProps,
|
|
22
|
+
focusHandlerProps,
|
|
23
|
+
viewProps
|
|
24
|
+
])
|
|
20
25
|
|
|
21
26
|
const selectContainerStyles = ({
|
|
22
27
|
containerBackgroundColor,
|
|
@@ -154,23 +159,29 @@ const Radio = forwardRef(
|
|
|
154
159
|
>
|
|
155
160
|
{({ focused: focus, hovered: hover, pressed }) => {
|
|
156
161
|
const stateTokens = getTokens({ focus, hover, pressed })
|
|
162
|
+
const labelStyles = selectLabelStyles(stateTokens)
|
|
163
|
+
const alignWithLabel = label
|
|
164
|
+
? [staticStyles.alignWithLabel, { height: labelStyles.lineHeight }]
|
|
165
|
+
: null
|
|
157
166
|
|
|
158
167
|
return (
|
|
159
168
|
<StackView space={0}>
|
|
160
169
|
<View style={[staticStyles.container, selectContainerStyles(stateTokens)]}>
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
<View style={alignWithLabel}>
|
|
171
|
+
<RadioButton
|
|
172
|
+
tokens={selectRadioButtonTokens(stateTokens)}
|
|
173
|
+
isControlled={isControlled}
|
|
174
|
+
isChecked={isChecked}
|
|
175
|
+
inactive={inactive}
|
|
176
|
+
defaultChecked={defaultChecked}
|
|
177
|
+
inputId={inputId}
|
|
178
|
+
handleChange={handleChange}
|
|
179
|
+
name={inputName}
|
|
180
|
+
value={value}
|
|
181
|
+
/>
|
|
182
|
+
</View>
|
|
172
183
|
{Boolean(label) && (
|
|
173
|
-
<Text style={
|
|
184
|
+
<Text style={labelStyles}>
|
|
174
185
|
<RadioLabel forId={inputId}>{label}</RadioLabel>
|
|
175
186
|
</Text>
|
|
176
187
|
)}
|
|
@@ -242,5 +253,6 @@ Radio.propTypes = {
|
|
|
242
253
|
export default Radio
|
|
243
254
|
|
|
244
255
|
const staticStyles = StyleSheet.create({
|
|
245
|
-
container: { flexDirection: 'row', alignItems: 'center' }
|
|
256
|
+
container: { flexDirection: 'row', alignItems: 'center' },
|
|
257
|
+
alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' }
|
|
246
258
|
})
|
package/src/Radio/RadioGroup.jsx
CHANGED
|
@@ -6,6 +6,8 @@ import { useViewport } from '../ViewportProvider'
|
|
|
6
6
|
import { useThemeTokens } from '../ThemeProvider'
|
|
7
7
|
import {
|
|
8
8
|
a11yProps,
|
|
9
|
+
containUniqueFields,
|
|
10
|
+
focusHandlerProps,
|
|
9
11
|
getTokensPropType,
|
|
10
12
|
selectSystemProps,
|
|
11
13
|
useInputValue,
|
|
@@ -17,6 +19,11 @@ import Radio from './Radio'
|
|
|
17
19
|
import Fieldset from '../Fieldset'
|
|
18
20
|
|
|
19
21
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
22
|
+
const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
|
|
23
|
+
a11yProps,
|
|
24
|
+
focusHandlerProps,
|
|
25
|
+
viewProps
|
|
26
|
+
])
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
29
|
* A group of Radios that behave as a radio group. Use when users select a single choice from mutually
|
|
@@ -100,28 +107,38 @@ const RadioGroup = forwardRef(
|
|
|
100
107
|
onChange,
|
|
101
108
|
readOnly: readOnly || inactive
|
|
102
109
|
})
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
|
|
111
|
+
const uniqueFields = ['id', 'label']
|
|
112
|
+
if (!containUniqueFields(items, uniqueFields)) {
|
|
113
|
+
throw new Error(`RadioGroup items must have unique ${uniqueFields.join(', ')}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const radios = items.map(
|
|
117
|
+
({ label, id, onChange: itemOnChange, ref: itemRef, ...itemRest }, index) => {
|
|
118
|
+
const radioId = id || `Radio[${index}]`
|
|
119
|
+
const isChecked = currentValue === radioId
|
|
120
|
+
const handleChange = (newCheckedState, event) => {
|
|
121
|
+
if (typeof itemOnChange === 'function') itemOnChange(newCheckedState, event)
|
|
122
|
+
if (newCheckedState) setValue(radioId, event)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Radio
|
|
127
|
+
ref={itemRef}
|
|
128
|
+
key={radioId}
|
|
129
|
+
id={radioId}
|
|
130
|
+
checked={isChecked}
|
|
131
|
+
onChange={handleChange}
|
|
132
|
+
inactive={inactive}
|
|
133
|
+
label={label}
|
|
134
|
+
name={inputGroupName}
|
|
135
|
+
tokens={radioTokens}
|
|
136
|
+
variant={variant}
|
|
137
|
+
{...selectItemProps(itemRest)}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
109
140
|
}
|
|
110
|
-
|
|
111
|
-
<Radio
|
|
112
|
-
ref={itemRef}
|
|
113
|
-
key={radioId}
|
|
114
|
-
id={radioId}
|
|
115
|
-
checked={isChecked}
|
|
116
|
-
onChange={handleChange}
|
|
117
|
-
inactive={inactive}
|
|
118
|
-
label={label}
|
|
119
|
-
name={inputGroupName}
|
|
120
|
-
tokens={radioTokens}
|
|
121
|
-
variant={variant}
|
|
122
|
-
/>
|
|
123
|
-
)
|
|
124
|
-
})
|
|
141
|
+
)
|
|
125
142
|
|
|
126
143
|
return (
|
|
127
144
|
<Fieldset
|
|
@@ -163,6 +180,7 @@ RadioGroup.propTypes = {
|
|
|
163
180
|
*/
|
|
164
181
|
items: PropTypes.arrayOf(
|
|
165
182
|
PropTypes.exact({
|
|
183
|
+
...selectedItemPropTypes,
|
|
166
184
|
label: PropTypes.string,
|
|
167
185
|
id: PropTypes.string,
|
|
168
186
|
onChange: PropTypes.func,
|
|
@@ -5,6 +5,7 @@ import { StyleSheet, Text, View } from 'react-native'
|
|
|
5
5
|
import { useThemeTokensCallback, applyTextStyles } from '../ThemeProvider'
|
|
6
6
|
import {
|
|
7
7
|
a11yProps,
|
|
8
|
+
focusHandlerProps,
|
|
8
9
|
getTokensPropType,
|
|
9
10
|
selectSystemProps,
|
|
10
11
|
selectTokens,
|
|
@@ -17,7 +18,11 @@ import { PressableCardBase, selectPressableCardTokens } from '../Card'
|
|
|
17
18
|
import StackView from '../StackView'
|
|
18
19
|
import RadioButton, { selectRadioButtonTokens } from '../Radio/RadioButton'
|
|
19
20
|
|
|
20
|
-
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
21
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
22
|
+
a11yProps,
|
|
23
|
+
focusHandlerProps,
|
|
24
|
+
viewProps
|
|
25
|
+
])
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* A Card that behaves like a radio button. Use when users select a single choice from mutually exclusive options
|