@telus-uds/components-web 2.13.0 → 2.15.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 +32 -2
- package/component-docs.json +46 -19
- package/lib/Badge/Badge.js +10 -1
- package/lib/Card/CardContent.js +6 -5
- package/lib/DatePicker/DatePicker.js +12 -11
- package/lib/QuantitySelector/QuantitySelector.js +67 -66
- package/lib/QuantitySelector/SideButton.js +93 -0
- package/lib/QuantitySelector/styles.js +4 -20
- package/lib/TermsAndConditions/TermsAndConditions.js +21 -4
- package/lib/Testimonial/Testimonial.js +48 -12
- package/lib/Toast/Toast.js +19 -7
- package/lib-module/Badge/Badge.js +10 -1
- package/lib-module/Card/CardContent.js +6 -5
- package/lib-module/DatePicker/DatePicker.js +11 -11
- package/lib-module/QuantitySelector/QuantitySelector.js +68 -67
- package/lib-module/QuantitySelector/SideButton.js +80 -0
- package/lib-module/QuantitySelector/styles.js +3 -15
- package/lib-module/TermsAndConditions/TermsAndConditions.js +20 -4
- package/lib-module/Testimonial/Testimonial.js +49 -13
- package/lib-module/Toast/Toast.js +19 -7
- package/package.json +4 -4
- package/src/Badge/Badge.jsx +14 -2
- package/src/Card/CardContent.jsx +6 -5
- package/src/DatePicker/DatePicker.jsx +11 -13
- package/src/QuantitySelector/QuantitySelector.jsx +60 -76
- package/src/QuantitySelector/SideButton.jsx +74 -0
- package/src/QuantitySelector/styles.js +4 -70
- package/src/TermsAndConditions/TermsAndConditions.jsx +30 -4
- package/src/Testimonial/Testimonial.jsx +73 -11
- package/src/Toast/Toast.jsx +5 -1
|
@@ -5,7 +5,7 @@ import { Box, Divider, selectSystemProps, Typography, useCopy, useThemeTokens, u
|
|
|
5
5
|
import ExpandCollapse from './ExpandCollapse';
|
|
6
6
|
import OrderedListBase from '../OrderedList/OrderedListBase';
|
|
7
7
|
import { htmlAttrs, media, renderStructuredContent } from '../utils';
|
|
8
|
-
import
|
|
8
|
+
import defaultDictionary from './dictionary';
|
|
9
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
10
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
11
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs]);
|
|
@@ -106,6 +106,7 @@ const TermsAndConditions = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
106
106
|
nonIndexedContent,
|
|
107
107
|
tokens,
|
|
108
108
|
variant = {},
|
|
109
|
+
dictionary = defaultDictionary,
|
|
109
110
|
...rest
|
|
110
111
|
} = _ref6;
|
|
111
112
|
const getCopy = useCopy({
|
|
@@ -172,7 +173,13 @@ const TermsAndConditions = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
172
173
|
})]
|
|
173
174
|
});
|
|
174
175
|
});
|
|
175
|
-
TermsAndConditions.displayName = 'TermsAndConditions';
|
|
176
|
+
TermsAndConditions.displayName = 'TermsAndConditions'; // If a language dictionary entry is provided, it must contain every key
|
|
177
|
+
|
|
178
|
+
const dictionaryContentShape = PropTypes.shape({
|
|
179
|
+
headingHide: PropTypes.string.isRequired,
|
|
180
|
+
headingView: PropTypes.string.isRequired,
|
|
181
|
+
nonIndexedTitle: PropTypes.string.isRequired
|
|
182
|
+
});
|
|
176
183
|
TermsAndConditions.propTypes = { ...selectedSystemPropTypes,
|
|
177
184
|
|
|
178
185
|
/**
|
|
@@ -198,11 +205,20 @@ TermsAndConditions.propTypes = { ...selectedSystemPropTypes,
|
|
|
198
205
|
*
|
|
199
206
|
* nonIndexedContent do not have a corresponding superscript and instead apply to the page as a whole.
|
|
200
207
|
*/
|
|
201
|
-
nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string]))
|
|
208
|
+
nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string])),
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Custom dictionary containing the labels to use for `TermsAndConditions`
|
|
212
|
+
*/
|
|
213
|
+
dictionary: PropTypes.shape({
|
|
214
|
+
en: dictionaryContentShape,
|
|
215
|
+
fr: dictionaryContentShape
|
|
216
|
+
})
|
|
202
217
|
};
|
|
203
218
|
TermsAndConditions.defaultProps = {
|
|
204
219
|
copy: 'en',
|
|
205
220
|
indexedContent: [],
|
|
206
|
-
nonIndexedContent: []
|
|
221
|
+
nonIndexedContent: [],
|
|
222
|
+
dictionary: defaultDictionary
|
|
207
223
|
};
|
|
208
224
|
export default TermsAndConditions;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
-
import { Icon, selectSystemProps, Typography, useThemeTokens } from '@telus-uds/components-base';
|
|
4
|
+
import { Icon, selectSystemProps, Typography, useThemeTokens, useViewport } from '@telus-uds/components-base';
|
|
5
5
|
import Image from '../Image';
|
|
6
6
|
import { htmlAttrs } from '../utils';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -83,11 +83,13 @@ const Testimonial = _ref5 => {
|
|
|
83
83
|
imageSrc,
|
|
84
84
|
image = imageSrc,
|
|
85
85
|
additionalInfo,
|
|
86
|
-
testimonialStyle = '
|
|
86
|
+
testimonialStyle = 'large',
|
|
87
87
|
tokens,
|
|
88
|
+
copy = 'en',
|
|
88
89
|
variant = {},
|
|
89
90
|
...rest
|
|
90
91
|
} = _ref5;
|
|
92
|
+
const viewport = useViewport();
|
|
91
93
|
const {
|
|
92
94
|
testimonialContainerGap,
|
|
93
95
|
quoteContainerGap,
|
|
@@ -96,9 +98,34 @@ const Testimonial = _ref5 => {
|
|
|
96
98
|
figcaptionGap,
|
|
97
99
|
textColor,
|
|
98
100
|
icon,
|
|
101
|
+
iconFr,
|
|
99
102
|
iconColor,
|
|
100
|
-
imageSize
|
|
101
|
-
|
|
103
|
+
imageSize,
|
|
104
|
+
testimonialFontSizeLarge,
|
|
105
|
+
testimonialLineHeightLarge,
|
|
106
|
+
testimonialFontWeightLarge,
|
|
107
|
+
testimonialFontSizeHeading,
|
|
108
|
+
testimonialLineHeightHeading,
|
|
109
|
+
testimonialFontNameHeading,
|
|
110
|
+
testimonialFontWeightHeading,
|
|
111
|
+
authorFontSize,
|
|
112
|
+
authorLineHeight,
|
|
113
|
+
authorFontName,
|
|
114
|
+
authorFontWeight,
|
|
115
|
+
additionalFontSize,
|
|
116
|
+
additionalLineHeight,
|
|
117
|
+
additionalFontName,
|
|
118
|
+
additionalFontWeight
|
|
119
|
+
} = useThemeTokens('Testimonial', tokens, variant, {
|
|
120
|
+
viewport
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const getQuoteTestimonial = open => {
|
|
124
|
+
let quote = '';
|
|
125
|
+
if (copy === 'en') quote = open ? '\u201C' : '\u201D';else quote = open ? '\u00AB ' : ' \u00BB';
|
|
126
|
+
return quote;
|
|
127
|
+
};
|
|
128
|
+
|
|
102
129
|
return /*#__PURE__*/_jsxs(TestimonialContainer, {
|
|
103
130
|
testimonialContainerGap: testimonialContainerGap,
|
|
104
131
|
...selectProps(rest),
|
|
@@ -111,7 +138,7 @@ const Testimonial = _ref5 => {
|
|
|
111
138
|
variant: {
|
|
112
139
|
size: 'micro'
|
|
113
140
|
},
|
|
114
|
-
icon: icon
|
|
141
|
+
icon: copy === 'en' ? icon : iconFr
|
|
115
142
|
}), showDivider && /*#__PURE__*/_jsx(Divider, {
|
|
116
143
|
dividerBackgroundColor: dividerBackgroundColor,
|
|
117
144
|
dividerBorder: dividerBorder,
|
|
@@ -124,9 +151,12 @@ const Testimonial = _ref5 => {
|
|
|
124
151
|
},
|
|
125
152
|
tokens: {
|
|
126
153
|
color: textColor,
|
|
127
|
-
|
|
154
|
+
fontSize: testimonialStyle === 'heading' ? testimonialFontSizeHeading : testimonialFontSizeLarge,
|
|
155
|
+
lineHeight: testimonialStyle === 'heading' ? testimonialLineHeightHeading : testimonialLineHeightLarge,
|
|
156
|
+
fontName: testimonialFontNameHeading,
|
|
157
|
+
fontWeight: testimonialStyle === 'heading' ? testimonialFontWeightHeading : testimonialFontWeightLarge
|
|
128
158
|
},
|
|
129
|
-
children:
|
|
159
|
+
children: `${getQuoteTestimonial(true)}${testimonial}${getQuoteTestimonial()}`
|
|
130
160
|
})
|
|
131
161
|
}), (image || title || additionalInfo) && /*#__PURE__*/_jsxs(Figcaption, {
|
|
132
162
|
figcaptionGap: figcaptionGap,
|
|
@@ -139,20 +169,26 @@ const Testimonial = _ref5 => {
|
|
|
139
169
|
}) : image), (title || additionalInfo) && /*#__PURE__*/_jsxs(AuthorInfoContainer, {
|
|
140
170
|
children: [title && /*#__PURE__*/_jsx(Typography, {
|
|
141
171
|
variant: {
|
|
142
|
-
size: 'small'
|
|
143
|
-
colour: 'secondary'
|
|
172
|
+
size: 'small'
|
|
144
173
|
},
|
|
145
174
|
tokens: {
|
|
146
|
-
|
|
175
|
+
color: textColor,
|
|
176
|
+
fontSize: authorFontSize,
|
|
177
|
+
lineHeight: authorLineHeight,
|
|
178
|
+
fontName: authorFontName,
|
|
179
|
+
fontWeight: authorFontWeight
|
|
147
180
|
},
|
|
148
181
|
children: title
|
|
149
182
|
}), additionalInfo && /*#__PURE__*/_jsx(Typography, {
|
|
150
183
|
variant: {
|
|
151
|
-
size: '
|
|
152
|
-
colour: 'secondary'
|
|
184
|
+
size: 'small'
|
|
153
185
|
},
|
|
154
186
|
tokens: {
|
|
155
|
-
|
|
187
|
+
color: textColor,
|
|
188
|
+
fontSize: additionalFontSize,
|
|
189
|
+
lineHeight: additionalLineHeight,
|
|
190
|
+
fontName: additionalFontName,
|
|
191
|
+
fontWeight: additionalFontWeight
|
|
156
192
|
},
|
|
157
193
|
children: additionalInfo
|
|
158
194
|
})]
|
|
@@ -21,19 +21,29 @@ const animation = props => css(["", " 1s ease-in-out 2s forwards,", " 1s ease-in
|
|
|
21
21
|
const ToastContainer = /*#__PURE__*/styled.div.withConfig({
|
|
22
22
|
displayName: "Toast__ToastContainer",
|
|
23
23
|
componentId: "components-web__sc-p78jdh-0"
|
|
24
|
-
})(["display:flex;justify-content:center;align-items:center;flex-wrap:wrap;background:", ";gap:", ";height:0;overflow:hidden;animation:", ";"], _ref => {
|
|
24
|
+
})(["display:flex;justify-content:center;align-items:center;flex-wrap:wrap;padding-left:", "px;padding-right:", "px;background:", ";gap:", ";height:0;overflow:hidden;animation:", ";"], _ref => {
|
|
25
25
|
let {
|
|
26
|
-
|
|
26
|
+
padding
|
|
27
27
|
} = _ref;
|
|
28
|
-
return
|
|
28
|
+
return padding;
|
|
29
29
|
}, _ref2 => {
|
|
30
30
|
let {
|
|
31
|
-
|
|
31
|
+
padding
|
|
32
32
|
} = _ref2;
|
|
33
|
+
return padding;
|
|
34
|
+
}, _ref3 => {
|
|
35
|
+
let {
|
|
36
|
+
containerBackgroundColor
|
|
37
|
+
} = _ref3;
|
|
38
|
+
return containerBackgroundColor;
|
|
39
|
+
}, _ref4 => {
|
|
40
|
+
let {
|
|
41
|
+
containerGap
|
|
42
|
+
} = _ref4;
|
|
33
43
|
return containerGap;
|
|
34
44
|
}, animation);
|
|
35
45
|
|
|
36
|
-
const Toast =
|
|
46
|
+
const Toast = _ref5 => {
|
|
37
47
|
let {
|
|
38
48
|
toast,
|
|
39
49
|
copy,
|
|
@@ -42,7 +52,7 @@ const Toast = _ref3 => {
|
|
|
42
52
|
tokens,
|
|
43
53
|
variant = {},
|
|
44
54
|
...rest
|
|
45
|
-
} =
|
|
55
|
+
} = _ref5;
|
|
46
56
|
// viewport hook added to work adjust the padding to different sizes
|
|
47
57
|
const viewport = useViewport();
|
|
48
58
|
const {
|
|
@@ -58,7 +68,8 @@ const Toast = _ref3 => {
|
|
|
58
68
|
animationBackgroundColorAfter,
|
|
59
69
|
animationColorBefore,
|
|
60
70
|
animationColorAfter,
|
|
61
|
-
chevronlinkFontWeight
|
|
71
|
+
chevronlinkFontWeight,
|
|
72
|
+
...extraTokens
|
|
62
73
|
} = useThemeTokens('Toast', tokens, variant, {
|
|
63
74
|
viewport
|
|
64
75
|
}); // inherit ChevronLink tokens for animation colors
|
|
@@ -91,6 +102,7 @@ const Toast = _ref3 => {
|
|
|
91
102
|
animationColorAfter: animationColorAfter,
|
|
92
103
|
animationFillColorBefore: chevronInverseColor,
|
|
93
104
|
animationFillColorAfter: chevronDefaultColor,
|
|
105
|
+
...extraTokens,
|
|
94
106
|
...selectProps(rest),
|
|
95
107
|
children: [headline && /*#__PURE__*/_jsx(Typography, {
|
|
96
108
|
variant: {
|
package/package.json
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
],
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@gorhom/portal": "^1.0.14",
|
|
8
|
-
"@telus-uds/components-base": "1.
|
|
9
|
-
"@telus-uds/system-constants": "^1.
|
|
8
|
+
"@telus-uds/components-base": "1.56.0",
|
|
9
|
+
"@telus-uds/system-constants": "^1.3.0",
|
|
10
10
|
"fscreen": "^1.2.0",
|
|
11
11
|
"lodash.omit": "^4.5.0",
|
|
12
12
|
"react-dates": "^21.8.0",
|
|
13
13
|
"react-helmet-async": "^1.3.0",
|
|
14
14
|
"react-moment-proptypes": "^1.8.1",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^2.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^2.39.0",
|
|
16
16
|
"prop-types": "^15.7.2",
|
|
17
17
|
"lodash.throttle": "^4.1.1",
|
|
18
18
|
"react-youtube": "^10.1.0",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"skip": true
|
|
64
64
|
},
|
|
65
65
|
"types": "types/index.d.ts",
|
|
66
|
-
"version": "2.
|
|
66
|
+
"version": "2.15.0"
|
|
67
67
|
}
|
package/src/Badge/Badge.jsx
CHANGED
|
@@ -42,7 +42,8 @@ const Badge = ({ children, tokens, variant = {}, ...rest }) => {
|
|
|
42
42
|
paddingTop,
|
|
43
43
|
paddingBottom,
|
|
44
44
|
fontName,
|
|
45
|
-
fontWeight
|
|
45
|
+
fontWeight,
|
|
46
|
+
fontSize
|
|
46
47
|
} = useThemeTokens('Badge', tokens, variant)
|
|
47
48
|
|
|
48
49
|
const semanticGradient = gradient && transformGradient(gradient)
|
|
@@ -56,6 +57,12 @@ const Badge = ({ children, tokens, variant = {}, ...rest }) => {
|
|
|
56
57
|
background = semanticGradient
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
const fontSizeMapping = {
|
|
61
|
+
12: 'micro',
|
|
62
|
+
14: 'small',
|
|
63
|
+
16: 'h6'
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
return (
|
|
60
67
|
<BadgeContainer
|
|
61
68
|
isOutlineOffer={isOutlineOffer}
|
|
@@ -69,7 +76,12 @@ const Badge = ({ children, tokens, variant = {}, ...rest }) => {
|
|
|
69
76
|
border={`${borderWidth}px solid ${borderColor}`}
|
|
70
77
|
{...selectProps(rest)}
|
|
71
78
|
>
|
|
72
|
-
<Typography
|
|
79
|
+
<Typography
|
|
80
|
+
tokens={{ fontName, fontWeight, color }}
|
|
81
|
+
variant={{ size: fontSizeMapping[fontSize] }}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</Typography>
|
|
73
85
|
</BadgeContainer>
|
|
74
86
|
)
|
|
75
87
|
}
|
package/src/Card/CardContent.jsx
CHANGED
|
@@ -23,7 +23,8 @@ const CardContentContainer = styled.div(
|
|
|
23
23
|
contentAlignItems: alignItems,
|
|
24
24
|
contentFlexGrow: flexGrow,
|
|
25
25
|
contentFlexShrink: flexShrink,
|
|
26
|
-
contentJustifyContent: justifyContent
|
|
26
|
+
contentJustifyContent: justifyContent,
|
|
27
|
+
borderWidth
|
|
27
28
|
}) => ({
|
|
28
29
|
// We need to make sure to have sharp corners on the bottom
|
|
29
30
|
// if the card has a footer
|
|
@@ -31,10 +32,10 @@ const CardContentContainer = styled.div(
|
|
|
31
32
|
borderBottomRightRadius: withFooter ? 0 : borderRadius,
|
|
32
33
|
borderTopLeftRadius: borderRadius,
|
|
33
34
|
borderTopRightRadius: borderRadius,
|
|
34
|
-
paddingBottom,
|
|
35
|
-
paddingLeft,
|
|
36
|
-
paddingRight,
|
|
37
|
-
paddingTop,
|
|
35
|
+
paddingBottom: paddingBottom - borderWidth,
|
|
36
|
+
paddingLeft: paddingLeft - borderWidth,
|
|
37
|
+
paddingRight: paddingRight - borderWidth,
|
|
38
|
+
paddingTop: paddingTop - borderWidth,
|
|
38
39
|
display: 'flex',
|
|
39
40
|
flexDirection: 'column',
|
|
40
41
|
alignItems,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
applyTextStyles
|
|
16
16
|
} from '@telus-uds/components-base'
|
|
17
17
|
import moment from 'moment'
|
|
18
|
+
import { isUndefined } from 'lodash'
|
|
18
19
|
import CalendarContainer from './CalendarContainer'
|
|
19
20
|
import dictionary from './dictionary'
|
|
20
21
|
import { htmlAttrs } from '../utils'
|
|
@@ -23,25 +24,17 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
|
|
|
23
24
|
const dateFormat = 'DD / MM / YYYY'
|
|
24
25
|
const dateFormatWithoutSpaces = 'DD/MM/YYYY'
|
|
25
26
|
const getResponsiveDaySize = (inline = false, viewport = 'md') => {
|
|
26
|
-
let responsiveDaySize
|
|
27
27
|
if (viewport === 'xs') {
|
|
28
|
-
|
|
29
|
-
} else {
|
|
30
|
-
responsiveDaySize = inline ? 60 : 44
|
|
28
|
+
return inline ? undefined : 36
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
return responsiveDaySize
|
|
30
|
+
return inline ? 60 : 44
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
const getResponsiveCircleSize = (inline = false, viewport = 'md') => {
|
|
37
|
-
let responsiveCircleSize
|
|
38
34
|
if (viewport === 'xs') {
|
|
39
|
-
|
|
40
|
-
} else {
|
|
41
|
-
responsiveCircleSize = inline ? 44 : 26
|
|
35
|
+
return 26
|
|
42
36
|
}
|
|
43
|
-
|
|
44
|
-
return responsiveCircleSize
|
|
37
|
+
return inline ? 44 : 26
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
const MonthCenterContainer = styled.div({
|
|
@@ -106,7 +99,11 @@ const DatePicker = forwardRef(
|
|
|
106
99
|
const [isClickedInside, setIsClickedInside] = useState(false)
|
|
107
100
|
const getCopy = useCopy({ dictionary, copy })
|
|
108
101
|
useEffect(() => {
|
|
109
|
-
|
|
102
|
+
/**
|
|
103
|
+
* `date` could be passed as `null` to reset the value so explicitly
|
|
104
|
+
* checking for not being `undefined`
|
|
105
|
+
*/
|
|
106
|
+
if (!isUndefined(date) && !moment(date).isSame(inputDate)) {
|
|
110
107
|
setInputDate(date)
|
|
111
108
|
setInputText(date instanceof moment ? date.format(dateFormat) : '')
|
|
112
109
|
}
|
|
@@ -139,6 +136,7 @@ const DatePicker = forwardRef(
|
|
|
139
136
|
const onChange = (value) => {
|
|
140
137
|
setInputDate(value)
|
|
141
138
|
setInputText(value.format(dateFormat))
|
|
139
|
+
setIsFocused(false)
|
|
142
140
|
if (onDateChange) onDateChange(value)
|
|
143
141
|
}
|
|
144
142
|
const onChangeInput = (value) => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import {
|
|
4
|
-
getThemeTokens,
|
|
5
4
|
Box,
|
|
6
5
|
TextInput,
|
|
7
6
|
Spacer,
|
|
@@ -9,13 +8,12 @@ import {
|
|
|
9
8
|
InputLabel,
|
|
10
9
|
useInputValue,
|
|
11
10
|
useCopy,
|
|
12
|
-
|
|
13
|
-
useTheme
|
|
11
|
+
useThemeTokensCallback
|
|
14
12
|
} from '@telus-uds/components-base'
|
|
15
13
|
|
|
16
|
-
import
|
|
17
|
-
import { InputField, InputWrapper, LeftButtonWrapper, RightButtonWrapper } from './styles'
|
|
14
|
+
import { InputField, InputWrapper } from './styles'
|
|
18
15
|
import defaultDictionary from './dictionary'
|
|
16
|
+
import SideButton from './SideButton'
|
|
19
17
|
|
|
20
18
|
const { isNaN } = Number
|
|
21
19
|
|
|
@@ -28,6 +26,7 @@ const QuantitySelector = ({
|
|
|
28
26
|
minNumber,
|
|
29
27
|
maxNumber,
|
|
30
28
|
defaultValue,
|
|
29
|
+
value,
|
|
31
30
|
label,
|
|
32
31
|
hint,
|
|
33
32
|
hintPosition,
|
|
@@ -40,13 +39,8 @@ const QuantitySelector = ({
|
|
|
40
39
|
tokens,
|
|
41
40
|
testID
|
|
42
41
|
}) => {
|
|
43
|
-
const {
|
|
44
|
-
components: { QuantitySelector: componentTheme }
|
|
45
|
-
} = useTheme()
|
|
46
|
-
|
|
47
|
-
const { leftIcon, rightIcon, padding } = useThemeTokens('QuantitySelector', tokens, variant)
|
|
42
|
+
const { disabled } = variant
|
|
48
43
|
const [error, setError] = useState('')
|
|
49
|
-
const { alternative } = variant
|
|
50
44
|
const getCopy = useCopy({ dictionary, copy })
|
|
51
45
|
|
|
52
46
|
const getValidatedNumber = (numberToEvaluate) => {
|
|
@@ -58,16 +52,14 @@ const QuantitySelector = ({
|
|
|
58
52
|
return numberToEvaluate
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
const initialValue = getValidatedNumber(defaultValue)
|
|
62
|
-
|
|
63
55
|
const { currentValue: number, setValue: setNumber } = useInputValue({
|
|
64
|
-
value:
|
|
65
|
-
initialValue,
|
|
56
|
+
value: getValidatedNumber(value),
|
|
57
|
+
initialValue: getValidatedNumber(defaultValue),
|
|
66
58
|
onChange
|
|
67
59
|
})
|
|
68
60
|
|
|
69
|
-
const isDecreaseEnabled = !isNumber(minNumber) || number > minNumber
|
|
70
|
-
const isIncreaseEnabled = !isNumber(maxNumber) || number < maxNumber
|
|
61
|
+
const isDecreaseEnabled = (!disabled && !isNumber(minNumber)) || number > minNumber
|
|
62
|
+
const isIncreaseEnabled = (!disabled && !isNumber(maxNumber)) || number < maxNumber
|
|
71
63
|
const inputValue = isNumber(number) ? number.toString() : ''
|
|
72
64
|
|
|
73
65
|
const updateNumber = (newNumber, originalInputEvent) => {
|
|
@@ -101,24 +93,34 @@ const QuantitySelector = ({
|
|
|
101
93
|
/>
|
|
102
94
|
) : null
|
|
103
95
|
|
|
96
|
+
const getTokens = useThemeTokensCallback('QuantitySelector', tokens, variant)
|
|
97
|
+
|
|
104
98
|
const renderTextInput = () => (
|
|
105
99
|
<TextInput
|
|
106
100
|
nativeID={id}
|
|
107
101
|
value={inputValue}
|
|
102
|
+
defaultValue={defaultValue}
|
|
108
103
|
tokens={(textInputState) => {
|
|
109
|
-
const {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
104
|
+
const {
|
|
105
|
+
inputWidth,
|
|
106
|
+
inputBorderWidth,
|
|
107
|
+
inputBorderColor,
|
|
108
|
+
textColor,
|
|
109
|
+
inputBackgroundColor,
|
|
110
|
+
...rest
|
|
111
|
+
} = getTokens({
|
|
112
|
+
...textInputState
|
|
113
|
+
})
|
|
116
114
|
return {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
...rest,
|
|
116
|
+
order: 1,
|
|
117
|
+
borderWidth: inputBorderWidth,
|
|
118
|
+
backgroundColor: inputBackgroundColor,
|
|
119
|
+
color: textColor,
|
|
120
|
+
width: inputWidth,
|
|
120
121
|
borderColor: inputBorderColor,
|
|
121
|
-
|
|
122
|
+
borderRadius: 0,
|
|
123
|
+
outerBorderWidth: 0
|
|
122
124
|
}
|
|
123
125
|
}}
|
|
124
126
|
onChange={inputChangeHandler}
|
|
@@ -133,58 +135,34 @@ const QuantitySelector = ({
|
|
|
133
135
|
/>
|
|
134
136
|
)
|
|
135
137
|
|
|
136
|
-
const getButtonTokens = (isEnabled) => (buttonState) => {
|
|
137
|
-
const disabled = !isEnabled
|
|
138
|
-
const { ...buttonTokens } = getThemeTokens(componentTheme, tokens, variant, {
|
|
139
|
-
...buttonState,
|
|
140
|
-
disabled
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
...buttonTokens,
|
|
145
|
-
outerBorderGap: 0,
|
|
146
|
-
padding
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const renderLeftButton = () => {
|
|
151
|
-
return (
|
|
152
|
-
<IconButton
|
|
153
|
-
icon={leftIcon}
|
|
154
|
-
tokens={getButtonTokens(isDecreaseEnabled)}
|
|
155
|
-
onPress={(event) => updateNumber(number - 1, event)}
|
|
156
|
-
onDoubleClick={(event) => updateNumber(number - 1, event)}
|
|
157
|
-
accessibilityLabel={getCopy('accessibility').decreaseButton}
|
|
158
|
-
accessibilityDisabled={!isDecreaseEnabled}
|
|
159
|
-
/>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const renderRightButton = () => (
|
|
164
|
-
<IconButton
|
|
165
|
-
icon={rightIcon}
|
|
166
|
-
tokens={getButtonTokens(isIncreaseEnabled)}
|
|
167
|
-
onPress={() => updateNumber(number + 1)}
|
|
168
|
-
onDoubleClick={() => updateNumber(number + 1)}
|
|
169
|
-
accessibilityLabel={getCopy('accessibility').increaseButton}
|
|
170
|
-
accessibilityDisabled={!isIncreaseEnabled}
|
|
171
|
-
/>
|
|
172
|
-
)
|
|
173
|
-
|
|
174
138
|
return (
|
|
175
139
|
<Box space={2} testID={testID}>
|
|
176
140
|
{renderLabel()}
|
|
177
141
|
<Spacer space={2} />
|
|
178
142
|
<InputWrapper>
|
|
179
|
-
<InputField
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
143
|
+
<InputField>{renderTextInput()}</InputField>
|
|
144
|
+
<div style={{ order: 0 }}>
|
|
145
|
+
<SideButton
|
|
146
|
+
isEnabled={isDecreaseEnabled}
|
|
147
|
+
onPress={() => updateNumber(number - 1)}
|
|
148
|
+
onDoubleClick={() => updateNumber(number - 1)}
|
|
149
|
+
tokens={tokens}
|
|
150
|
+
variant={{ decrease: true, ...variant }}
|
|
151
|
+
accessibilityLabel={getCopy('accessibility').decreaseButton}
|
|
152
|
+
accessibilityDisabled={!isDecreaseEnabled}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
<div style={{ order: 2 }}>
|
|
156
|
+
<SideButton
|
|
157
|
+
isEnabled={isIncreaseEnabled}
|
|
158
|
+
onPress={() => updateNumber(number + 1)}
|
|
159
|
+
onDoubleClick={() => updateNumber(number + 1)}
|
|
160
|
+
accessibilityLabel={getCopy('accessibility').increaseButton}
|
|
161
|
+
accessibilityDisabled={!isIncreaseEnabled}
|
|
162
|
+
tokens={tokens}
|
|
163
|
+
variant={{ increase: true, ...variant }}
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
188
166
|
</InputWrapper>
|
|
189
167
|
{error ? (
|
|
190
168
|
<Box vertical={2}>
|
|
@@ -230,6 +208,11 @@ QuantitySelector.propTypes = {
|
|
|
230
208
|
* The default value of the input field
|
|
231
209
|
*/
|
|
232
210
|
defaultValue: PropTypes.number,
|
|
211
|
+
/**
|
|
212
|
+
* If the input's state is to be controlled by a parent component, use this prop
|
|
213
|
+
* together with the `onChange` to pass down and update the lifted state.
|
|
214
|
+
*/
|
|
215
|
+
value: PropTypes.number,
|
|
233
216
|
/**
|
|
234
217
|
* The label of the input field
|
|
235
218
|
*/
|
|
@@ -262,7 +245,8 @@ QuantitySelector.propTypes = {
|
|
|
262
245
|
*/
|
|
263
246
|
copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr'])]),
|
|
264
247
|
variant: PropTypes.exact({
|
|
265
|
-
alternative: PropTypes.bool
|
|
248
|
+
alternative: PropTypes.bool,
|
|
249
|
+
disabled: PropTypes.bool
|
|
266
250
|
}),
|
|
267
251
|
tokens: PropTypes.oneOf([PropTypes.object, PropTypes.func]),
|
|
268
252
|
/**
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { IconButton, useThemeTokensCallback } from '@telus-uds/components-base'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
|
|
5
|
+
const SideButton = ({
|
|
6
|
+
isEnabled,
|
|
7
|
+
onPress,
|
|
8
|
+
onDoubleClick,
|
|
9
|
+
accessibilityLabel,
|
|
10
|
+
accessibilityDisabled,
|
|
11
|
+
tokens,
|
|
12
|
+
variant
|
|
13
|
+
}) => {
|
|
14
|
+
const getTokens = useThemeTokensCallback('QuantitySelectorSideButton', tokens, variant)
|
|
15
|
+
const getButtonTokens = ({ buttonState, disabled }) => {
|
|
16
|
+
const {
|
|
17
|
+
borderRadius,
|
|
18
|
+
borderTopLeftRadius,
|
|
19
|
+
borderTopRightRadius,
|
|
20
|
+
borderBottomLeftRadius,
|
|
21
|
+
borderBottomRightRadius,
|
|
22
|
+
...rest
|
|
23
|
+
} = getTokens({ ...buttonState, disabled })
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
...rest,
|
|
27
|
+
borderRadius,
|
|
28
|
+
borderTopLeftRadius,
|
|
29
|
+
borderTopRightRadius,
|
|
30
|
+
borderBottomLeftRadius,
|
|
31
|
+
borderBottomRightRadius,
|
|
32
|
+
outerBorderRadius: borderRadius,
|
|
33
|
+
outerBorderTopLeftRadius: borderTopLeftRadius,
|
|
34
|
+
outerBorderTopRightRadius: borderTopRightRadius,
|
|
35
|
+
outerBorderBottomLeftRadius: borderBottomLeftRadius,
|
|
36
|
+
outerBorderBottomRightRadius: borderBottomRightRadius,
|
|
37
|
+
outerBorderGap: 0,
|
|
38
|
+
outerBorderWidth: 0
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<IconButton
|
|
44
|
+
tokens={(buttonState) => getButtonTokens({ disabled: !isEnabled, buttonState })}
|
|
45
|
+
onPress={onPress}
|
|
46
|
+
onDoubleClick={onDoubleClick}
|
|
47
|
+
accessibilityLabel={accessibilityLabel}
|
|
48
|
+
accessibilityDisabled={accessibilityDisabled}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
SideButton.displayName = 'QuantitySelectorSideButton'
|
|
54
|
+
|
|
55
|
+
SideButton.defaultProps = {
|
|
56
|
+
isEnabled: true,
|
|
57
|
+
onPress: () => {},
|
|
58
|
+
onDoubleClick: () => {},
|
|
59
|
+
accessibilityLabel: '',
|
|
60
|
+
accessibilityDisabled: false,
|
|
61
|
+
tokens: {},
|
|
62
|
+
variant: {}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
SideButton.propTypes = {
|
|
66
|
+
isEnabled: PropTypes.bool,
|
|
67
|
+
onPress: PropTypes.func,
|
|
68
|
+
onDoubleClick: PropTypes.func,
|
|
69
|
+
accessibilityLabel: PropTypes.string,
|
|
70
|
+
accessibilityDisabled: PropTypes.bool,
|
|
71
|
+
tokens: PropTypes.object,
|
|
72
|
+
variant: PropTypes.object
|
|
73
|
+
}
|
|
74
|
+
export default SideButton
|