@telus-uds/components-base 3.28.2 → 3.29.1

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.
Files changed (93) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/jest.config.cjs +1 -0
  3. package/lib/cjs/ActionCard/ActionCard.js +4 -4
  4. package/lib/cjs/ActivityIndicator/Dots.native.js +1 -2
  5. package/lib/cjs/ActivityIndicator/Spinner.native.js +1 -2
  6. package/lib/cjs/Box/Box.js +1 -2
  7. package/lib/cjs/Button/Button.js +1 -2
  8. package/lib/cjs/Button/ButtonBase.js +8 -8
  9. package/lib/cjs/Button/ButtonDropdown.js +1 -2
  10. package/lib/cjs/Button/ButtonGroup.js +1 -2
  11. package/lib/cjs/Button/ButtonLink.js +1 -2
  12. package/lib/cjs/Card/Card.js +1 -2
  13. package/lib/cjs/Card/CardBase.js +12 -0
  14. package/lib/cjs/Card/PressableCardBase.js +1 -2
  15. package/lib/cjs/Card/index.js +1 -2
  16. package/lib/cjs/CardGroup/CardGroup.js +1 -2
  17. package/lib/cjs/Carousel/Carousel.js +6 -6
  18. package/lib/cjs/CheckboxCard/CheckboxCard.js +1 -2
  19. package/lib/cjs/CheckboxCardGroup/CheckboxCardGroup.js +1 -2
  20. package/lib/cjs/ColourToggle/ColourBubble.js +17 -3
  21. package/lib/cjs/ColourToggle/ColourToggle.js +8 -2
  22. package/lib/cjs/ExpandCollapse/Control.js +17 -3
  23. package/lib/cjs/ExpandCollapse/Panel.js +6 -0
  24. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
  25. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
  26. package/lib/cjs/HorizontalScroll/index.js +1 -2
  27. package/lib/cjs/Icon/IconText.js +1 -2
  28. package/lib/cjs/Icon/index.js +1 -2
  29. package/lib/cjs/InputSupports/InputSupports.js +1 -2
  30. package/lib/cjs/InputSupports/useInputSupports.js +1 -3
  31. package/lib/cjs/Link/ChevronLink.js +1 -0
  32. package/lib/cjs/Link/LinkBase.js +29 -13
  33. package/lib/cjs/Link/MobileIconTextContent.js +155 -0
  34. package/lib/cjs/Listbox/Listbox.js +1 -2
  35. package/lib/cjs/Modal/Modal.js +1 -1
  36. package/lib/cjs/Pagination/PageButton.js +1 -2
  37. package/lib/cjs/Pagination/Pagination.js +1 -2
  38. package/lib/cjs/QuickLinks/QuickLinks.js +7 -0
  39. package/lib/cjs/Radio/Radio.js +1 -2
  40. package/lib/cjs/RadioCard/RadioCard.js +1 -2
  41. package/lib/cjs/RadioCard/RadioCardGroup.js +1 -2
  42. package/lib/cjs/Shortcuts/Shortcuts.js +1 -2
  43. package/lib/cjs/SideNav/SideNav.js +1 -2
  44. package/lib/cjs/StackView/StackWrapBox.js +9 -1
  45. package/lib/cjs/StackView/StackWrapGap.js +3 -1
  46. package/lib/cjs/StackView/getStackedContent.js +21 -12
  47. package/lib/cjs/TabBar/TabBar.js +7 -2
  48. package/lib/cjs/Tabs/Tabs.js +1 -2
  49. package/lib/cjs/Tooltip/Tooltip.native.js +2 -2
  50. package/lib/cjs/index.js +1 -2
  51. package/lib/cjs/utils/index.js +1 -2
  52. package/lib/esm/ActionCard/ActionCard.js +4 -4
  53. package/lib/esm/Button/ButtonBase.js +8 -8
  54. package/lib/esm/Card/CardBase.js +12 -0
  55. package/lib/esm/Carousel/Carousel.js +6 -6
  56. package/lib/esm/ColourToggle/ColourBubble.js +17 -3
  57. package/lib/esm/ColourToggle/ColourToggle.js +8 -2
  58. package/lib/esm/ExpandCollapse/Control.js +17 -3
  59. package/lib/esm/ExpandCollapse/Panel.js +6 -0
  60. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
  61. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
  62. package/lib/esm/InputSupports/InputSupports.js +1 -2
  63. package/lib/esm/InputSupports/useInputSupports.js +1 -3
  64. package/lib/esm/Link/ChevronLink.js +1 -0
  65. package/lib/esm/Link/LinkBase.js +29 -13
  66. package/lib/esm/Link/MobileIconTextContent.js +147 -0
  67. package/lib/esm/Modal/Modal.js +1 -1
  68. package/lib/esm/QuickLinks/QuickLinks.js +7 -0
  69. package/lib/esm/StackView/StackWrapBox.js +9 -1
  70. package/lib/esm/StackView/StackWrapGap.js +3 -1
  71. package/lib/esm/StackView/getStackedContent.js +20 -10
  72. package/lib/esm/TabBar/TabBar.js +7 -2
  73. package/lib/esm/Tooltip/Tooltip.native.js +2 -2
  74. package/lib/package.json +1 -1
  75. package/package.json +1 -1
  76. package/src/Card/CardBase.jsx +12 -0
  77. package/src/ColourToggle/ColourBubble.jsx +18 -3
  78. package/src/ColourToggle/ColourToggle.jsx +7 -2
  79. package/src/ExpandCollapse/Control.jsx +24 -4
  80. package/src/ExpandCollapse/Panel.jsx +6 -0
  81. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +23 -3
  82. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +14 -2
  83. package/src/InputSupports/InputSupports.jsx +1 -6
  84. package/src/InputSupports/useInputSupports.js +1 -1
  85. package/src/Link/ChevronLink.jsx +1 -0
  86. package/src/Link/LinkBase.jsx +47 -20
  87. package/src/Link/MobileIconTextContent.jsx +129 -0
  88. package/src/Modal/Modal.jsx +1 -1
  89. package/src/QuickLinks/QuickLinks.jsx +8 -0
  90. package/src/StackView/StackWrapBox.jsx +13 -1
  91. package/src/StackView/StackWrapGap.jsx +2 -1
  92. package/src/StackView/getStackedContent.jsx +22 -8
  93. package/src/TabBar/TabBar.jsx +21 -4
@@ -0,0 +1,147 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Text from "react-native-web/dist/exports/Text";
4
+ import View from "react-native-web/dist/exports/View";
5
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
+ import Icon, { iconComponentPropTypes } from '../Icon/Icon';
7
+ import { spacingProps } from '../utils';
8
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ const MobileIconTextContent = /*#__PURE__*/React.forwardRef((_ref, ref) => {
10
+ let {
11
+ space = 0,
12
+ iconPosition = 'left',
13
+ icon: IconComponent,
14
+ iconProps = {},
15
+ children
16
+ } = _ref;
17
+ const [translateY, setTranslateY] = React.useState(0);
18
+ const latestTranslateYRef = React.useRef(0);
19
+ const layoutsRef = React.useRef({
20
+ container: null,
21
+ text: null,
22
+ icon: null
23
+ });
24
+ const applyAlignment = React.useCallback(() => {
25
+ const {
26
+ container,
27
+ text,
28
+ icon
29
+ } = layoutsRef.current;
30
+ if (!container || !icon || !icon.height) return;
31
+ const targetY = text ? text.y + text.height / 2 : container.height / 2;
32
+ const iconY = icon.y + icon.height / 2;
33
+ const nextTranslateY = Math.round((targetY - iconY) * 100) / 100;
34
+ if (!Number.isFinite(nextTranslateY)) return;
35
+ if (Math.abs(nextTranslateY - latestTranslateYRef.current) < 0.5) return;
36
+ latestTranslateYRef.current = nextTranslateY;
37
+ setTranslateY(nextTranslateY);
38
+ }, []);
39
+ const handleContainerLayout = React.useCallback(_ref2 => {
40
+ let {
41
+ nativeEvent: {
42
+ layout
43
+ }
44
+ } = _ref2;
45
+ layoutsRef.current.container = layout;
46
+ applyAlignment();
47
+ }, [applyAlignment]);
48
+ const handleTextLayout = React.useCallback(_ref3 => {
49
+ let {
50
+ nativeEvent: {
51
+ layout
52
+ }
53
+ } = _ref3;
54
+ layoutsRef.current.text = layout;
55
+ applyAlignment();
56
+ }, [applyAlignment]);
57
+ const handleIconLayout = React.useCallback(_ref4 => {
58
+ let {
59
+ nativeEvent: {
60
+ layout
61
+ }
62
+ } = _ref4;
63
+ layoutsRef.current.icon = layout;
64
+ applyAlignment();
65
+ }, [applyAlignment]);
66
+ const iconContent = IconComponent ? /*#__PURE__*/_jsx(Icon, {
67
+ ref: ref,
68
+ icon: IconComponent,
69
+ scalesWithText: true,
70
+ ...iconProps
71
+ }) : null;
72
+ const iconWrapper = IconComponent ? /*#__PURE__*/_jsx(View, {
73
+ onLayout: handleIconLayout,
74
+ style: [staticStyles.iconContainer, {
75
+ transform: [{
76
+ translateY
77
+ }]
78
+ }],
79
+ children: iconContent
80
+ }) : null;
81
+ if (iconPosition === 'inline') {
82
+ return /*#__PURE__*/_jsxs(Text, {
83
+ onLayout: handleContainerLayout,
84
+ children: [/*#__PURE__*/_jsx(Text, {
85
+ onLayout: handleTextLayout,
86
+ children: children
87
+ }), ' ', /*#__PURE__*/_jsx(View, {
88
+ style: staticStyles.inlineIconContainer,
89
+ children: iconWrapper
90
+ })]
91
+ });
92
+ }
93
+ const iconSpaceStyle = iconPosition === 'left' ? {
94
+ marginRight: space
95
+ } : {
96
+ marginLeft: space
97
+ };
98
+ return /*#__PURE__*/_jsxs(View, {
99
+ onLayout: handleContainerLayout,
100
+ style: staticStyles.rowContainer,
101
+ children: [iconPosition === 'left' && /*#__PURE__*/_jsx(View, {
102
+ style: iconSpaceStyle,
103
+ children: iconWrapper
104
+ }), /*#__PURE__*/_jsx(View, {
105
+ onLayout: handleTextLayout,
106
+ children: children
107
+ }), iconPosition === 'right' && /*#__PURE__*/_jsx(View, {
108
+ style: iconSpaceStyle,
109
+ children: iconWrapper
110
+ })]
111
+ });
112
+ });
113
+ MobileIconTextContent.displayName = 'MobileIconTextContent';
114
+ MobileIconTextContent.propTypes = {
115
+ /**
116
+ * Amount of space between text and icon. Uses the theme spacing scale.
117
+ */
118
+ space: spacingProps.types.spacingValue,
119
+ /**
120
+ * Position of the icon relative to text.
121
+ */
122
+ iconPosition: PropTypes.oneOf(['left', 'right', 'inline']),
123
+ /**
124
+ * A valid UDS icon component imported from a UDS palette.
125
+ */
126
+ icon: PropTypes.elementType,
127
+ /**
128
+ * Props passed to the icon component.
129
+ */
130
+ iconProps: PropTypes.exact(iconComponentPropTypes),
131
+ /**
132
+ * Content rendered alongside the icon.
133
+ */
134
+ children: PropTypes.node
135
+ };
136
+ const staticStyles = StyleSheet.create({
137
+ rowContainer: {
138
+ flexDirection: 'row'
139
+ },
140
+ iconContainer: {
141
+ alignSelf: 'flex-start'
142
+ },
143
+ inlineIconContainer: {
144
+ position: 'absolute'
145
+ }
146
+ });
147
+ export default MobileIconTextContent;
@@ -239,7 +239,7 @@ const Modal = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
239
239
  })]
240
240
  })
241
241
  }), /*#__PURE__*/_jsx(TouchableWithoutFeedback, {
242
- onPress: isBackdropClickable && handleClose,
242
+ onPress: isBackdropClickable ? handleClose : undefined,
243
243
  children: /*#__PURE__*/_jsx(View, {
244
244
  style: [staticStyles.backdrop, selectBackdropStyles(themeTokens)]
245
245
  })
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import Platform from "react-native-web/dist/exports/Platform";
3
4
  import { useThemeTokens } from '../ThemeProvider';
4
5
  import { useViewport } from '../ViewportProvider';
5
6
  import { getTokensPropType, variantProp } from '../utils';
7
+ import { tagsToRoles } from '../utils/a11y/semantics';
6
8
  import List from '../List';
7
9
  import StackWrap from '../StackView/StackWrap';
8
10
  import QuickLinksCard from './QuickLinksCard';
@@ -35,6 +37,10 @@ const QuickLinks = /*#__PURE__*/React.forwardRef((_ref, ref) => {
35
37
  } = useThemeTokens('QuickLinks', tokens, variant, {
36
38
  viewport
37
39
  });
40
+ const itemAccessibilityRole = Platform.select({
41
+ web: ['ul', 'ol'].includes(tag) ? tagsToRoles.li : undefined,
42
+ default: undefined
43
+ });
38
44
  const content = list && /*#__PURE__*/_jsx(List, {
39
45
  ref: ref,
40
46
  tokens: listTokens,
@@ -49,6 +55,7 @@ const QuickLinks = /*#__PURE__*/React.forwardRef((_ref, ref) => {
49
55
  justifyContent: stackJustify
50
56
  },
51
57
  tag: tag,
58
+ itemAccessibilityRole: itemAccessibilityRole,
52
59
  ...rest,
53
60
  children: children
54
61
  });
@@ -43,6 +43,7 @@ const StackWrapBox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
43
43
  variant,
44
44
  tag,
45
45
  accessibilityRole,
46
+ itemAccessibilityRole,
46
47
  ...rest
47
48
  } = _ref;
48
49
  const viewport = useViewport();
@@ -69,7 +70,8 @@ const StackWrapBox = /*#__PURE__*/React.forwardRef((_ref, ref) => {
69
70
  const content = getStackedContent(children, {
70
71
  direction,
71
72
  space: 0,
72
- box: boxProps
73
+ box: boxProps,
74
+ itemAccessibilityRole
73
75
  });
74
76
  return /*#__PURE__*/_jsx(View, {
75
77
  ref: ref,
@@ -105,6 +107,12 @@ StackWrapBox.propTypes = {
105
107
  * is not defined, the accessibilityRole will default to "heading".
106
108
  */
107
109
  tag: PropTypes.oneOf(layoutTags),
110
+ /**
111
+ * Optional accessibility role to apply to each item in the stack.
112
+ * On web, items are wrapped (or cloned) with this role, enabling correct list semantics
113
+ * when the container tag is "ul" or "ol".
114
+ */
115
+ itemAccessibilityRole: PropTypes.string,
108
116
  /**
109
117
  * A StackWrap may take any children, but will have no effect if it is only passed one child or is passed children
110
118
  * wrapped in a component. If necessary, children may be wrapped in one React Fragment.
@@ -28,6 +28,7 @@ const StackWrapGap = /*#__PURE__*/React.forwardRef((_ref, ref) => {
28
28
  children,
29
29
  tag,
30
30
  accessibilityRole,
31
+ itemAccessibilityRole,
31
32
  ...rest
32
33
  } = _ref;
33
34
  const viewport = useViewport();
@@ -47,7 +48,8 @@ const StackWrapGap = /*#__PURE__*/React.forwardRef((_ref, ref) => {
47
48
  };
48
49
  const content = getStackedContent(children, {
49
50
  direction,
50
- space: 0
51
+ space: 0,
52
+ itemAccessibilityRole
51
53
  });
52
54
  return /*#__PURE__*/_jsx(View, {
53
55
  ref: ref,
@@ -36,7 +36,8 @@ const getStackedContent = (children, _ref) => {
36
36
  space,
37
37
  direction = 'column',
38
38
  box,
39
- preserveFragments = false
39
+ preserveFragments = false,
40
+ itemAccessibilityRole
40
41
  } = _ref;
41
42
  const boxProps = box && typeof box === 'object' ? box : {
42
43
  space
@@ -48,15 +49,24 @@ const getStackedContent = (children, _ref) => {
48
49
  const validChildren = React.Children.toArray(topLevelChildren).filter(Boolean);
49
50
  const content = validChildren.reduce((newChildren, child, index) => {
50
51
  const boxID = `Stack-Box-${index}`;
51
- const item = box ?
52
- /*#__PURE__*/
53
- // If wrapped in Box, that Box needs a key.
54
- // If possible, use an existing content key; use an index-based key only if necessary.
55
- _createElement(Box, {
56
- ...boxProps,
57
- key: child.key || boxID,
58
- testID: boxID
59
- }, child) : child;
52
+ let item;
53
+ if (box) {
54
+ // If wrapped in Box, that Box needs a key.
55
+ // If possible, use an existing content key; use an index-based key only if necessary.
56
+ item = /*#__PURE__*/_createElement(Box, {
57
+ ...boxProps,
58
+ accessibilityRole: itemAccessibilityRole,
59
+ key: child.key || boxID,
60
+ testID: boxID
61
+ }, child);
62
+ } else if (itemAccessibilityRole) {
63
+ item = /*#__PURE__*/React.cloneElement(child, {
64
+ accessibilityRole: itemAccessibilityRole,
65
+ key: child.key || boxID
66
+ });
67
+ } else {
68
+ item = child;
69
+ }
60
70
  if (!index || !space && !divider) return [...newChildren, item];
61
71
  const testID = `Stack-${divider ? 'Divider' : 'Spacer'}-${index}`;
62
72
  const commonProps = {
@@ -51,6 +51,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
51
51
  * items={items}
52
52
  * initiallySelectedItem="1"
53
53
  * onChange={(itemId) => console.log(itemId)}
54
+ * accessibilityLabel="Main navigation"
54
55
  * />
55
56
  * )
56
57
  */
@@ -62,6 +63,7 @@ const TabBar = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
62
63
  onChange,
63
64
  variant,
64
65
  tokens,
66
+ accessibilityLabel,
65
67
  ...rest
66
68
  } = _ref3;
67
69
  const [isSelected, setIsSelected] = React.useState(initiallySelectedItem);
@@ -76,6 +78,8 @@ const TabBar = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
76
78
  ...selectProps(rest),
77
79
  children: /*#__PURE__*/_jsx(View, {
78
80
  style: [styles.tabBarItem, selectTabBarItemContainerStyles(themeTokens)],
81
+ accessibilityRole: "tablist",
82
+ accessibilityLabel: accessibilityLabel,
79
83
  children: items.map((item, index) => /*#__PURE__*/_jsx(TabBarItem, {
80
84
  label: item.label,
81
85
  href: item.href,
@@ -84,7 +88,6 @@ const TabBar = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
84
88
  iconActive: item.iconActive,
85
89
  onPress: () => handlePress(item.id),
86
90
  id: `tab-item-${index}`,
87
- accessibilityRole: "tablist",
88
91
  tokens: item.tokens
89
92
  }, item.id))
90
93
  })
@@ -109,7 +112,9 @@ TabBar.propTypes = {
109
112
  /** Variant of TabBar for styling purposes. */
110
113
  variant: variantProp.propType,
111
114
  /** Tokens for theming and styling. */
112
- tokens: getTokensPropType('TabBar')
115
+ tokens: getTokensPropType('TabBar'),
116
+ /** Accessible label for the tab bar navigation region, used by screen readers to identify the tablist. */
117
+ accessibilityLabel: PropTypes.string
113
118
  };
114
119
  const styles = StyleSheet.create({
115
120
  tabBar: {
@@ -206,7 +206,7 @@ const Tooltip = /*#__PURE__*/React.forwardRef((_ref7, ref) => {
206
206
  focus: focused
207
207
  };
208
208
  };
209
- const onTooltipLayout = _ref10 => {
209
+ const onTooltipLayout = _ref0 => {
210
210
  let {
211
211
  nativeEvent: {
212
212
  layout: {
@@ -214,7 +214,7 @@ const Tooltip = /*#__PURE__*/React.forwardRef((_ref7, ref) => {
214
214
  height
215
215
  }
216
216
  }
217
- } = _ref10;
217
+ } = _ref0;
218
218
  if (tooltipDimensions === null || tooltipDimensions.width !== width || tooltipDimensions.height !== height) {
219
219
  setTooltipDimensions({
220
220
  width: Platform.select({
package/lib/package.json CHANGED
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.28.2",
87
+ "version": "3.29.1",
88
88
  "types": "types/index.d.ts"
89
89
  }
package/package.json CHANGED
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.28.2",
87
+ "version": "3.29.1",
88
88
  "types": "types/index.d.ts"
89
89
  }
@@ -59,9 +59,21 @@ const setBackgroundImage = ({
59
59
  case 'left-center':
60
60
  backgroundPosition = 'left center'
61
61
  break
62
+ case 'left-start':
63
+ backgroundPosition = 'left top'
64
+ break
65
+ case 'left-end':
66
+ backgroundPosition = 'left bottom'
67
+ break
62
68
  case 'right-center':
63
69
  backgroundPosition = 'right center'
64
70
  break
71
+ case 'right-start':
72
+ backgroundPosition = 'right top'
73
+ break
74
+ case 'right-end':
75
+ backgroundPosition = 'right bottom'
76
+ break
65
77
  default:
66
78
  backgroundPosition = 'center center'
67
79
  }
@@ -5,6 +5,7 @@ import { View, Pressable, Platform } from 'react-native'
5
5
  import { resolvePressableTokens } from '../utils/pressability'
6
6
  import { applyShadowToken } from '../ThemeProvider'
7
7
  import { getTokensPropType } from '../utils'
8
+ import Tooltip from '../Tooltip'
8
9
 
9
10
  const selectGeneralBubbleTokens = ({
10
11
  outerBubbleHeight,
@@ -52,14 +53,14 @@ const selectBorderBubbleTokens = ({
52
53
  })
53
54
 
54
55
  const ColourBubble = React.forwardRef(
55
- ({ tokens = {}, id, colourHexCode, colourName, isSelected, onPress }, ref) => {
56
+ ({ tokens = {}, id, colourHexCode, colourName, isSelected, onPress, showTooltip }, ref) => {
56
57
  const defaultTokens = tokens({ selected: isSelected })
57
58
 
58
59
  const resolveColourBubbleTokens = (pressState) => resolvePressableTokens(tokens, pressState, {})
59
60
 
60
61
  const themeTokens = React.useMemo(() => tokens(), [tokens])
61
62
 
62
- return (
63
+ const pressable = (
63
64
  <Pressable
64
65
  style={(state) => [
65
66
  selectGeneralBubbleTokens(resolveColourBubbleTokens(state)),
@@ -76,6 +77,16 @@ const ColourBubble = React.forwardRef(
76
77
  <View style={[selectInnerBubbleTokens(themeTokens), { backgroundColor: colourHexCode }]} />
77
78
  </Pressable>
78
79
  )
80
+
81
+ if (showTooltip) {
82
+ return (
83
+ <Tooltip content={colourName} activateOnHover>
84
+ {pressable}
85
+ </Tooltip>
86
+ )
87
+ }
88
+
89
+ return pressable
79
90
  }
80
91
  )
81
92
  ColourBubble.displayName = 'ColourBubble'
@@ -106,7 +117,11 @@ ColourBubble.propTypes = {
106
117
  * of the color is changed of all currently `items`.
107
118
  * Receives two parameters: item object selected and the event
108
119
  */
109
- onPress: PropTypes.func
120
+ onPress: PropTypes.func,
121
+ /**
122
+ * When true, wraps the bubble in a Tooltip that displays the colourName on hover (web only).
123
+ */
124
+ showTooltip: PropTypes.bool
110
125
  }
111
126
 
112
127
  export default ColourBubble
@@ -11,7 +11,7 @@ import ColourBubble from './ColourBubble'
11
11
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
12
12
 
13
13
  const ColourToggle = React.forwardRef(
14
- ({ tokens, variant, defaultColourId, items, onChange, ...rest }, ref) => {
14
+ ({ tokens, variant, defaultColourId, items, onChange, showTooltips, ...rest }, ref) => {
15
15
  const [currentColourId, setCurrentColourId] = React.useState(defaultColourId)
16
16
  const getTokens = useThemeTokensCallback('ColourToggle', tokens, variant)
17
17
 
@@ -40,6 +40,7 @@ const ColourToggle = React.forwardRef(
40
40
  colourHexCode={colourHexCode}
41
41
  colourName={colourName}
42
42
  onPress={handleChangeColour}
43
+ showTooltip={showTooltips}
43
44
  />
44
45
  )
45
46
  })}
@@ -77,7 +78,11 @@ ColourToggle.propTypes = {
77
78
  /**
78
79
  * 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
79
80
  */
80
- onChange: PropTypes.func
81
+ onChange: PropTypes.func,
82
+ /**
83
+ * When true, displays each colour's name as a tooltip on hover (web only).
84
+ */
85
+ showTooltips: PropTypes.bool
81
86
  }
82
87
 
83
88
  export default ColourToggle
@@ -54,10 +54,11 @@ function selectIconContainerStyles({ iconGap, iconPaddingTop, iconPosition }) {
54
54
  }
55
55
  }
56
56
 
57
- function selectTextContainerStyles({ textLine }) {
57
+ function selectTextContainerStyles({ textLine, controlAlign }) {
58
58
  return {
59
59
  textDecorationLine: textLine,
60
- flex: 1
60
+ flex: 1,
61
+ ...(controlAlign && { alignItems: controlAlign })
61
62
  }
62
63
  }
63
64
 
@@ -70,7 +71,16 @@ function selectIconTokens(tokens) {
70
71
 
71
72
  const ExpandCollapseControl = React.forwardRef(
72
73
  (
73
- { onPress, isExpanded, children, tokens, accessibilityRole = 'button', variant, ...rest },
74
+ {
75
+ onPress,
76
+ isExpanded,
77
+ children,
78
+ tokens,
79
+ controlAlign,
80
+ accessibilityRole = 'button',
81
+ variant,
82
+ ...rest
83
+ },
74
84
  ref
75
85
  ) => {
76
86
  const getTokens = useThemeTokensCallback('ExpandCollapseControl', tokens, variant)
@@ -110,7 +120,13 @@ const ExpandCollapseControl = React.forwardRef(
110
120
  </View>
111
121
  )}
112
122
  <View
113
- style={[selectTextContainerStyles(themeTokens), staticStyles.bubblePointerEvents]}
123
+ style={[
124
+ selectTextContainerStyles({
125
+ ...themeTokens,
126
+ ...(controlAlign && { controlAlign })
127
+ }),
128
+ staticStyles.bubblePointerEvents
129
+ ]}
114
130
  >
115
131
  {typeof children === 'function'
116
132
  ? children(getControlState(pressableState))
@@ -144,6 +160,10 @@ ExpandCollapseControl.propTypes = {
144
160
  * Whether the linked ExpandCollapsePanel is opened or closed. Allows themes to set `expanded` styles.
145
161
  */
146
162
  isExpanded: PropTypes.bool,
163
+ /**
164
+ * Optional alignment for control content. Overrides token-driven alignment.
165
+ */
166
+ controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
147
167
  /**
148
168
  * Function called when the ExpandCollapse is pressed.
149
169
  */
@@ -90,6 +90,7 @@ const ExpandCollapsePanel = React.forwardRef(
90
90
  onPress,
91
91
  control,
92
92
  controlTokens,
93
+ controlAlign,
93
94
  children,
94
95
  tokens,
95
96
  variant,
@@ -174,6 +175,7 @@ const ExpandCollapsePanel = React.forwardRef(
174
175
  {...selectedProps}
175
176
  isExpanded={isExpanded}
176
177
  tokens={controlTokens}
178
+ controlAlign={controlAlign}
177
179
  variant={variant}
178
180
  onPress={handleControlPress}
179
181
  ref={controlRef}
@@ -284,6 +286,10 @@ ExpandCollapsePanel.propTypes = {
284
286
  * Optional theme token overrides that may be passed to the ExpandCollapseControl element.
285
287
  */
286
288
  controlTokens: getTokensPropType('ExpandCollapseControl'),
289
+ /**
290
+ * Optional alignment for control content.
291
+ */
292
+ controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
287
293
  /**
288
294
  * An optional ref to be attached to the control
289
295
  */
@@ -7,9 +7,24 @@ import ExpandCollapseMiniControl from './ExpandCollapseMiniControl'
7
7
 
8
8
  const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([contentfulProps])
9
9
 
10
+ const alignMap = {
11
+ start: 'flex-start',
12
+ middle: 'center',
13
+ end: 'flex-end'
14
+ }
15
+
10
16
  const ExpandCollapseMini = React.forwardRef(
11
17
  (
12
- { children, onToggle = () => {}, tokens = {}, nativeID, initialOpen = false, dataSet, ...rest },
18
+ {
19
+ children,
20
+ onToggle = () => {},
21
+ tokens = {},
22
+ nativeID,
23
+ initialOpen = false,
24
+ dataSet,
25
+ align,
26
+ ...rest
27
+ },
13
28
  ref
14
29
  ) => {
15
30
  const expandCollapeMiniPanelId = useUniqueId('ExpandCollapseMiniPanel')
@@ -40,10 +55,11 @@ const ExpandCollapseMini = React.forwardRef(
40
55
  textLine: tokens.textLine ?? 'none',
41
56
  backgroundColor: 'transparent'
42
57
  }}
58
+ controlAlign={align && alignMap[align]}
43
59
  // TODO refactor
44
60
  // eslint-disable-next-line react/no-unstable-nested-components
45
61
  control={(pressableState) => (
46
- <ExpandCollapseMiniControl pressableState={pressableState} {...rest} />
62
+ <ExpandCollapseMiniControl pressableState={pressableState} align={align} {...rest} />
47
63
  )}
48
64
  controlRef={ref}
49
65
  nativeID={nativeID}
@@ -87,7 +103,11 @@ ExpandCollapseMini.propTypes = {
87
103
  /**
88
104
  * The dataSet prop allows to pass data-* attributes element to the component.
89
105
  */
90
- dataSet: PropTypes.object
106
+ dataSet: PropTypes.object,
107
+ /**
108
+ * Controls the horizontal alignment of the trigger label and icon within the panel width.
109
+ */
110
+ align: PropTypes.oneOf(['start', 'middle', 'end'])
91
111
  }
92
112
 
93
113
  export default ExpandCollapseMini
@@ -7,6 +7,12 @@ import { htmlAttrs, viewProps, selectSystemProps } from '../utils'
7
7
 
8
8
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps])
9
9
 
10
+ const alignSelfMap = {
11
+ start: 'flex-start',
12
+ middle: 'center',
13
+ end: 'flex-end'
14
+ }
15
+
10
16
  // The ExpandCollapseControl has all the appropriate role, a11y, press handling etc
11
17
  // and a more appropriate press area, defer interaction handling to it.
12
18
  const presentationOnly = {
@@ -24,6 +30,7 @@ const ExpandCollapseMiniControl = React.forwardRef(
24
30
  iconPosition = 'right',
25
31
  tokens,
26
32
  variant = {},
33
+ align,
27
34
  ...rest
28
35
  },
29
36
  ref
@@ -96,7 +103,8 @@ const ExpandCollapseMiniControl = React.forwardRef(
96
103
  ...getTokens(linkState),
97
104
  iconSize,
98
105
  blockFontSize: fontSize,
99
- blockLineHeight: lineHeight
106
+ blockLineHeight: lineHeight,
107
+ ...(align && { alignSelf: alignSelfMap[align] })
100
108
  })}
101
109
  ref={ref}
102
110
  {...presentationOnly}
@@ -132,7 +140,11 @@ ExpandCollapseMiniControl.propTypes = {
132
140
  /**
133
141
  * Optional variant object to override the default theme tokens
134
142
  */
135
- variant: PropTypes.object
143
+ variant: PropTypes.object,
144
+ /**
145
+ * Controls the horizontal alignment of the trigger label and icon
146
+ */
147
+ align: PropTypes.oneOf(['start', 'middle', 'end'])
136
148
  }
137
149
 
138
150
  export default ExpandCollapseMiniControl
@@ -66,12 +66,7 @@ const InputSupports = React.forwardRef(
66
66
  />
67
67
  )}
68
68
  {typeof children === 'function'
69
- ? children({
70
- inputId,
71
- ...a11yProps,
72
- validation: feedbackValidation,
73
- accessibilityDescribedBy: feedbackId
74
- })
69
+ ? children({ inputId, ...a11yProps, validation: feedbackValidation })
75
70
  : children}
76
71
  {feedback || maxCharsReachedErrorMessage ? (
77
72
  <Feedback
@@ -31,8 +31,8 @@ const useInputSupports = ({
31
31
  : undefined
32
32
  ]), // native only -> replaced with describedBy on web
33
33
  accessibilityDescribedBy: joinDefined([
34
- !hasValidationError && feedback && feedbackId, // feedback receives a11yRole=alert on error, so there's no need to include it here
35
34
  hint && hintId,
35
+ (feedback && feedbackId) || undefined,
36
36
  charactersCount
37
37
  ? getCopy('charactersRemaining').replace(/%\{charCount\}/g, charactersCount)
38
38
  : undefined