@telus-uds/components-base 4.0.0-beta.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import Pressable from "react-native-web/dist/exports/Pressable";
5
5
  import Platform from "react-native-web/dist/exports/Platform";
6
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
7
  import { resolvePressableTokens } from '../utils/pressability';
7
8
  import { applyShadowToken } from '../ThemeProvider/utils/styles';
8
9
  import { getTokensPropType } from '../utils/props/tokens';
9
10
  import { Tooltip } from '../Tooltip/Tooltip.native';
10
- import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { UNAVAILABLE_VARIANT } from './constants';
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
13
  const selectGeneralBubbleTokens = _ref => {
12
14
  let {
13
15
  outerBubbleHeight,
@@ -63,37 +65,70 @@ const selectBorderBubbleTokens = _ref3 => {
63
65
  borderRadius: bubbleBorderRadius
64
66
  };
65
67
  };
66
- export const ColourBubble = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
68
+ const selectOverlayStyles = _ref4 => {
67
69
  let {
68
- tokens = {},
70
+ overlayColor,
71
+ innerBubbleBorderRadius
72
+ } = _ref4;
73
+ return {
74
+ borderRadius: innerBubbleBorderRadius,
75
+ backgroundColor: overlayColor
76
+ };
77
+ };
78
+ const selectSlashStyles = (_ref5, unavailable) => {
79
+ let {
80
+ slashLightColor,
81
+ slashDarkColor,
82
+ slashWidth,
83
+ slashOffset
84
+ } = _ref5;
85
+ return {
86
+ width: slashWidth,
87
+ backgroundColor: unavailable === UNAVAILABLE_VARIANT.DARK ? slashDarkColor : slashLightColor,
88
+ marginLeft: slashOffset
89
+ };
90
+ };
91
+ export const ColourBubble = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
92
+ let {
93
+ tokens = () => ({}),
69
94
  id,
70
95
  colourHexCode,
71
96
  colourName,
72
97
  isSelected,
73
98
  onPress,
74
- showTooltip
75
- } = _ref4;
99
+ showTooltip,
100
+ unavailable,
101
+ isDisabled
102
+ } = _ref6;
76
103
  const defaultTokens = tokens({
77
104
  selected: isSelected
78
105
  });
79
- const resolveColourBubbleTokens = pressState => resolvePressableTokens(tokens, pressState, {});
80
- const themeTokens = React.useMemo(() => tokens(), [tokens]);
81
- const pressable = /*#__PURE__*/_jsx(Pressable, {
106
+ const resolveColourBubbleTokens = pressState => resolvePressableTokens(tokens, isDisabled ? {} : pressState, {});
107
+ const themeTokens = tokens();
108
+ const pressable = /*#__PURE__*/_jsxs(Pressable, {
82
109
  style: state => [selectGeneralBubbleTokens(resolveColourBubbleTokens(state)), isSelected && selectBorderBubbleTokens(defaultTokens)],
83
110
  onPress: onPress,
111
+ disabled: isDisabled,
112
+ focusable: !isDisabled,
84
113
  accessible: true,
85
114
  accessibilityRole: "radio",
86
115
  accessibilityLabel: colourName,
87
116
  accessibilityState: {
88
- checked: isSelected
117
+ checked: isSelected,
118
+ disabled: isDisabled
89
119
  },
90
120
  ref: ref,
91
121
  testID: id,
92
- children: /*#__PURE__*/_jsx(View, {
122
+ children: [/*#__PURE__*/_jsx(View, {
93
123
  style: [selectInnerBubbleTokens(themeTokens), {
94
124
  backgroundColor: colourHexCode
95
- }]
96
- })
125
+ }],
126
+ children: unavailable && /*#__PURE__*/_jsx(View, {
127
+ style: [StyleSheet.absoluteFillObject, selectOverlayStyles(themeTokens)]
128
+ })
129
+ }), unavailable && /*#__PURE__*/_jsx(View, {
130
+ style: [staticStyles.indicator, selectSlashStyles(themeTokens, unavailable)]
131
+ })]
97
132
  });
98
133
  if (showTooltip) {
99
134
  return /*#__PURE__*/_jsx(Tooltip, {
@@ -135,5 +170,23 @@ ColourBubble.propTypes = {
135
170
  /**
136
171
  * When true, wraps the bubble in a Tooltip that displays the colourName on hover (web only).
137
172
  */
138
- showTooltip: PropTypes.bool
139
- };
173
+ showTooltip: PropTypes.bool,
174
+ /**
175
+ * When set, renders a diagonal indicator over the bubble. Accepts either 'dark' or 'light' to determine the colour of the indicator.
176
+ */
177
+ unavailable: PropTypes.oneOf(Object.values(UNAVAILABLE_VARIANT)),
178
+ /**
179
+ * When true, disables interaction on the bubble.
180
+ */
181
+ isDisabled: PropTypes.bool
182
+ };
183
+ const staticStyles = StyleSheet.create({
184
+ indicator: {
185
+ position: 'absolute',
186
+ top: '50%',
187
+ height: 2,
188
+ transform: [{
189
+ rotate: '-45deg'
190
+ }]
191
+ }
192
+ });
@@ -7,9 +7,12 @@ import { getTokensPropType } from '../utils/props/tokens';
7
7
  import { selectSystemProps } from '../utils/props/selectSystemProps';
8
8
  import { variantProp } from '../utils/props/variantProp';
9
9
  import { viewProps } from '../utils/props/viewProps';
10
+ import { useCopy } from '../utils/useCopy';
10
11
  import { StackWrap } from '../StackView/StackWrap';
11
12
  import { Typography } from '../Typography/Typography';
12
13
  import { ColourBubble } from './ColourBubble';
14
+ import { UNAVAILABLE_VARIANT, DICTIONARY_CONTENT_SHAPE } from './constants';
15
+ import { DEFAULT_COLOUR_TOGGLE_DICTIONARY } from './dictionary';
13
16
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
17
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
15
18
  export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
@@ -20,37 +23,49 @@ export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
20
23
  items,
21
24
  onChange,
22
25
  showTooltips,
26
+ copy = 'en',
27
+ dictionary = DEFAULT_COLOUR_TOGGLE_DICTIONARY,
23
28
  ...rest
24
29
  } = _ref;
25
30
  const [currentColourId, setCurrentColourId] = React.useState(defaultColourId);
26
31
  const getTokens = useThemeTokensCallback('ColourToggle', tokens, variant);
32
+ const getCopy = useCopy({
33
+ dictionary,
34
+ copy
35
+ });
27
36
  const {
28
37
  space
29
38
  } = getTokens();
30
- const {
31
- colourName: currentColourName = ''
32
- } = items.find(item => item.id === currentColourId) || '';
39
+ const currentItem = items.find(_ref2 => {
40
+ let {
41
+ id
42
+ } = _ref2;
43
+ return id === currentColourId;
44
+ });
33
45
  return /*#__PURE__*/_jsxs(View, {
34
46
  ref: ref,
35
47
  ...selectProps(rest),
36
48
  children: [/*#__PURE__*/_jsx(Typography, {
37
- children: currentColourName
49
+ children: currentItem?.unavailable ? `${currentItem.colourName} ${getCopy('unavailable')}` : currentItem?.colourName ?? ''
38
50
  }), /*#__PURE__*/_jsx(StackWrap, {
39
51
  space: space,
40
52
  accessibilityRole: "radiogroup",
41
- children: items.map((_ref2, index) => {
53
+ children: items.map((_ref3, index) => {
42
54
  let {
43
55
  id,
44
56
  colourHexCode,
45
- colourName
46
- } = _ref2;
57
+ colourName,
58
+ unavailable,
59
+ disabled
60
+ } = _ref3;
47
61
  const colourBubbleId = id || `ColourBubble[${index}]`;
48
62
  const handleChangeColour = event => {
49
63
  setCurrentColourId(id);
50
64
  onChange?.(event, {
51
65
  id,
52
66
  colourHexCode,
53
- colourName
67
+ colourName,
68
+ unavailable
54
69
  });
55
70
  };
56
71
  return /*#__PURE__*/_jsx(ColourBubble, {
@@ -60,7 +75,9 @@ export const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
60
75
  colourHexCode: colourHexCode,
61
76
  colourName: colourName,
62
77
  onPress: handleChangeColour,
63
- showTooltip: showTooltips
78
+ showTooltip: showTooltips,
79
+ unavailable: unavailable,
80
+ isDisabled: !!disabled
64
81
  }, colourBubbleId);
65
82
  })
66
83
  })]
@@ -87,7 +104,9 @@ ColourToggle.propTypes = {
87
104
  items: PropTypes.arrayOf(PropTypes.exact({
88
105
  colourHexCode: PropTypes.string,
89
106
  colourName: PropTypes.string,
90
- id: PropTypes.string
107
+ id: PropTypes.string,
108
+ unavailable: PropTypes.oneOf(Object.values(UNAVAILABLE_VARIANT)),
109
+ disabled: PropTypes.bool
91
110
  })),
92
111
  /**
93
112
  * If provided, this function is called when the current selection of the color is changed of all currently `items`. Receives two parameters: item object selected and the event
@@ -96,5 +115,16 @@ ColourToggle.propTypes = {
96
115
  /**
97
116
  * When true, displays each colour's name as a tooltip on hover (web only).
98
117
  */
99
- showTooltips: PropTypes.bool
118
+ showTooltips: PropTypes.bool,
119
+ /**
120
+ * Select English or French copy.
121
+ */
122
+ copy: PropTypes.oneOf(['en', 'fr']),
123
+ /**
124
+ * Override default labels.
125
+ */
126
+ dictionary: PropTypes.shape({
127
+ en: DICTIONARY_CONTENT_SHAPE,
128
+ fr: DICTIONARY_CONTENT_SHAPE
129
+ })
100
130
  };
@@ -0,0 +1,8 @@
1
+ import PropTypes from 'prop-types';
2
+ export const UNAVAILABLE_VARIANT = {
3
+ DARK: 'dark',
4
+ LIGHT: 'light'
5
+ };
6
+ export const DICTIONARY_CONTENT_SHAPE = PropTypes.shape({
7
+ unavailable: PropTypes.string
8
+ });
@@ -0,0 +1,8 @@
1
+ export const DEFAULT_COLOUR_TOGGLE_DICTIONARY = {
2
+ en: {
3
+ unavailable: '(Out of stock)'
4
+ },
5
+ fr: {
6
+ unavailable: '(En rupture de stock)'
7
+ }
8
+ };
@@ -200,7 +200,11 @@ export const Validator = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
200
200
 
201
201
  // Sync external value prop to internal state
202
202
  React.useEffect(() => {
203
- if (value && Number(value).toString() !== 'NaN') {
203
+ if (value === '') {
204
+ setCodes(Array(validatorsLength).fill(''));
205
+ return;
206
+ }
207
+ if (Number(value).toString() !== 'NaN') {
204
208
  const digits = value.split('').slice(0, validatorsLength);
205
209
  const newCodes = Array(validatorsLength).fill('');
206
210
  digits.forEach((digit, i) => {
package/lib/package.json CHANGED
@@ -11,8 +11,8 @@
11
11
  "@floating-ui/react-native": "^0.10.7",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@react-native-picker/picker": "^2.11.4",
14
- "@telus-uds/system-constants": "^4.0.0-alpha.0",
15
- "@telus-uds/system-theme-tokens": "^5.0.0-alpha.2",
14
+ "@telus-uds/system-constants": "^4.0.0",
15
+ "@telus-uds/system-theme-tokens": "^5.0.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-document-picker": "^14.0.8",
18
18
  "expo-linear-gradient": "^15.0.8",
@@ -81,6 +81,6 @@
81
81
  "standard-engine": {
82
82
  "skip": true
83
83
  },
84
- "version": "4.0.0-beta.0",
84
+ "version": "4.0.0",
85
85
  "types": "types/index.d.ts"
86
86
  }
package/package.json CHANGED
@@ -11,8 +11,8 @@
11
11
  "@floating-ui/react-native": "^0.10.7",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@react-native-picker/picker": "^2.11.4",
14
- "@telus-uds/system-constants": "^4.0.0-alpha.0",
15
- "@telus-uds/system-theme-tokens": "^5.0.0-alpha.2",
14
+ "@telus-uds/system-constants": "^4.0.0",
15
+ "@telus-uds/system-theme-tokens": "^5.0.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-document-picker": "^14.0.8",
18
18
  "expo-linear-gradient": "^15.0.8",
@@ -81,6 +81,6 @@
81
81
  "standard-engine": {
82
82
  "skip": true
83
83
  },
84
- "version": "4.0.0-beta.0",
84
+ "version": "4.0.0",
85
85
  "types": "types/index.d.ts"
86
86
  }
@@ -25,6 +25,8 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
25
25
  pressProps,
26
26
  viewProps
27
27
  ])
28
+ const EQUAL_WIDTH = 'equal'
29
+ const RESPONSIVE_WIDTH = 'responsive'
28
30
 
29
31
  export const ButtonGroup = React.forwardRef(
30
32
  (
@@ -67,6 +69,9 @@ export const ButtonGroup = React.forwardRef(
67
69
  const themeButtonTokensCallback = useThemeTokensCallback('ButtonGroupItem', tokens, variant)
68
70
  const gapValue = useSpacingScale(gap || space)
69
71
 
72
+ const isWeb = Platform.OS === 'web'
73
+ const buttonWidthVariant = variant?.width
74
+
70
75
  const getButtonTokens = useCallback(
71
76
  (state) => {
72
77
  const themeButtonTokens = themeButtonTokensCallback(state)
@@ -74,36 +79,93 @@ export const ButtonGroup = React.forwardRef(
74
79
  const shouldUseTransparentBackground =
75
80
  isMobileNonContained && !state.selected && !state.pressed && !state.hover && !state.focus
76
81
 
82
+ let widthStyle
83
+
84
+ switch (buttonWidthVariant) {
85
+ case EQUAL_WIDTH:
86
+ widthStyle = staticStyles.equalWidth
87
+ break
88
+ case RESPONSIVE_WIDTH:
89
+ widthStyle = staticStyles.responsiveWidth
90
+ break
91
+ // no default
92
+ }
93
+
77
94
  return {
78
95
  ...themeButtonTokens,
79
- ...(variant?.width === 'equal' && staticStyles.equalWidth),
96
+ ...widthStyle,
80
97
  ...(shouldUseTransparentBackground && { backgroundColor: 'transparent' }),
81
98
  alignSelf: themeButtonTokens.width ? 'flex-start' : 'center'
82
99
  }
83
100
  },
84
- [themeButtonTokensCallback, isMobileNonContained, variant?.width]
101
+ [themeButtonTokensCallback, isMobileNonContained, buttonWidthVariant]
85
102
  )
86
103
 
87
- const fieldsetStyles = useMemo(
88
- () => ({
89
- ...staticStyles.fieldsetBase,
104
+ const fieldsetStyles = useMemo(() => {
105
+ let fieldSetBase
106
+ switch (buttonWidthVariant) {
107
+ case EQUAL_WIDTH:
108
+ case RESPONSIVE_WIDTH:
109
+ fieldSetBase = staticStyles.fieldSetResponsive
110
+ break
111
+ default:
112
+ fieldSetBase = staticStyles.fieldsetBase
113
+ break
114
+ }
115
+
116
+ return {
117
+ ...fieldSetBase,
90
118
  borderRadius,
91
119
  backgroundColor: isMobileNonContained ? 'transparent' : backgroundColor || 'transparent',
92
- padding,
93
- width: variant?.width === 'equal' ? '100%' : 'auto'
94
- }),
95
- [borderRadius, backgroundColor, padding, variant?.width, isMobileNonContained]
96
- )
120
+ padding
121
+ }
122
+ }, [borderRadius, backgroundColor, padding, isMobileNonContained, buttonWidthVariant])
97
123
 
98
- const viewStyles = useMemo(
99
- () => ({
100
- ...staticStyles.viewBase,
124
+ const buttonsContainerStyles = useMemo(() => {
125
+ let flexWrap
126
+ if (isWeb) {
127
+ flexWrap = 'wrap'
128
+ } else {
129
+ switch (buttonWidthVariant) {
130
+ case EQUAL_WIDTH:
131
+ case RESPONSIVE_WIDTH:
132
+ flexWrap = 'nowrap'
133
+ break
134
+ default:
135
+ flexWrap = 'wrap'
136
+ break
137
+ }
138
+ }
139
+
140
+ let justifyContent
141
+ switch (buttonWidthVariant) {
142
+ case EQUAL_WIDTH:
143
+ justifyContent = 'space-evenly'
144
+ break
145
+ default:
146
+ justifyContent = 'flex-start'
147
+ break
148
+ }
149
+
150
+ let viewBaseStyle
151
+ switch (buttonWidthVariant) {
152
+ case EQUAL_WIDTH:
153
+ case RESPONSIVE_WIDTH:
154
+ viewBaseStyle = {}
155
+ break
156
+ default:
157
+ viewBaseStyle = staticStyles.viewBase
158
+ break
159
+ }
160
+
161
+ return {
162
+ ...viewBaseStyle,
163
+ flexWrap,
101
164
  flexDirection: direction === 'column' ? 'column' : 'row',
102
165
  gap: gapValue || 0,
103
- justifyContent: variant?.width === 'equal' ? 'space-evenly' : 'flex-start'
104
- }),
105
- [direction, gapValue, variant?.width]
106
- )
166
+ justifyContent
167
+ }
168
+ }, [direction, gapValue, buttonWidthVariant, isWeb])
107
169
 
108
170
  const { currentValues, toggleOneValue } = useMultipleInputValues({
109
171
  initialValues,
@@ -125,7 +187,7 @@ export const ButtonGroup = React.forwardRef(
125
187
  }
126
188
 
127
189
  // Some web screenreaders e.g. MacOS Voiceover don't handle radiogroups properly unless radio is direct child of radiogroup
128
- const innerRole =
190
+ const buttonsWrapperRole =
129
191
  Platform.OS === 'web' && accessibilityRole === 'radiogroup' ? accessibilityRole : undefined
130
192
 
131
193
  return (
@@ -146,7 +208,7 @@ export const ButtonGroup = React.forwardRef(
146
208
  style={fieldsetStyles}
147
209
  {...selectProps(rest)}
148
210
  >
149
- <View accessibilityRole={innerRole} style={viewStyles}>
211
+ <View accessibilityRole={buttonsWrapperRole} style={buttonsContainerStyles}>
150
212
  {items.map(
151
213
  (
152
214
  {
@@ -326,17 +388,24 @@ ButtonGroup.propTypes = {
326
388
  }
327
389
 
328
390
  const staticStyles = StyleSheet.create({
391
+ equalWidth: {
392
+ width: '100%',
393
+ flex: 1
394
+ },
395
+ responsiveWidth: {
396
+ width: 'auto'
397
+ },
329
398
  fieldsetBase: {
330
399
  alignSelf: 'flex-start',
331
- display: 'inline'
400
+ display: 'inline',
401
+ width: 'auto'
402
+ },
403
+ fieldSetResponsive: {
404
+ width: '100%'
332
405
  },
333
406
  viewBase: {
334
407
  flexWrap: 'wrap',
335
408
  alignContent: 'stretch',
336
409
  alignItems: 'center'
337
- },
338
- equalWidth: {
339
- width: '100%',
340
- flex: 1
341
410
  }
342
411
  })