@telus-uds/components-base 3.28.2 → 3.29.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/lib/cjs/Card/CardBase.js +12 -0
  3. package/lib/cjs/ColourToggle/ColourBubble.js +17 -3
  4. package/lib/cjs/ColourToggle/ColourToggle.js +8 -2
  5. package/lib/cjs/ExpandCollapse/Control.js +17 -3
  6. package/lib/cjs/ExpandCollapse/Panel.js +6 -0
  7. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
  8. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
  9. package/lib/cjs/Link/ChevronLink.js +1 -0
  10. package/lib/cjs/Link/LinkBase.js +29 -13
  11. package/lib/cjs/Link/MobileIconTextContent.js +156 -0
  12. package/lib/cjs/TabBar/TabBar.js +7 -2
  13. package/lib/esm/Card/CardBase.js +12 -0
  14. package/lib/esm/ColourToggle/ColourBubble.js +17 -3
  15. package/lib/esm/ColourToggle/ColourToggle.js +8 -2
  16. package/lib/esm/ExpandCollapse/Control.js +17 -3
  17. package/lib/esm/ExpandCollapse/Panel.js +6 -0
  18. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +14 -2
  19. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +15 -2
  20. package/lib/esm/Link/ChevronLink.js +1 -0
  21. package/lib/esm/Link/LinkBase.js +29 -13
  22. package/lib/esm/Link/MobileIconTextContent.js +147 -0
  23. package/lib/esm/TabBar/TabBar.js +7 -2
  24. package/lib/package.json +1 -1
  25. package/package.json +1 -1
  26. package/src/Card/CardBase.jsx +12 -0
  27. package/src/ColourToggle/ColourBubble.jsx +18 -3
  28. package/src/ColourToggle/ColourToggle.jsx +7 -2
  29. package/src/ExpandCollapse/Control.jsx +24 -4
  30. package/src/ExpandCollapse/Panel.jsx +6 -0
  31. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +23 -3
  32. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +14 -2
  33. package/src/Link/ChevronLink.jsx +1 -0
  34. package/src/Link/LinkBase.jsx +47 -20
  35. package/src/Link/MobileIconTextContent.jsx +129 -0
  36. package/src/TabBar/TabBar.jsx +21 -4
@@ -15,6 +15,7 @@ const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
15
15
  defaultColourId,
16
16
  items,
17
17
  onChange,
18
+ showTooltips,
18
19
  ...rest
19
20
  } = _ref;
20
21
  const [currentColourId, setCurrentColourId] = React.useState(defaultColourId);
@@ -54,7 +55,8 @@ const ColourToggle = /*#__PURE__*/React.forwardRef((_ref, ref) => {
54
55
  isSelected: id === currentColourId,
55
56
  colourHexCode: colourHexCode,
56
57
  colourName: colourName,
57
- onPress: handleChangeColour
58
+ onPress: handleChangeColour,
59
+ showTooltip: showTooltips
58
60
  }, colourBubbleId);
59
61
  })
60
62
  })]
@@ -86,6 +88,10 @@ ColourToggle.propTypes = {
86
88
  /**
87
89
  * 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
88
90
  */
89
- onChange: PropTypes.func
91
+ onChange: PropTypes.func,
92
+ /**
93
+ * When true, displays each colour's name as a tooltip on hover (web only).
94
+ */
95
+ showTooltips: PropTypes.bool
90
96
  };
91
97
  export default ColourToggle;
@@ -66,11 +66,15 @@ function selectIconContainerStyles(_ref2) {
66
66
  }
67
67
  function selectTextContainerStyles(_ref3) {
68
68
  let {
69
- textLine
69
+ textLine,
70
+ controlAlign
70
71
  } = _ref3;
71
72
  return {
72
73
  textDecorationLine: textLine,
73
- flex: 1
74
+ flex: 1,
75
+ ...(controlAlign && {
76
+ alignItems: controlAlign
77
+ })
74
78
  };
75
79
  }
76
80
  function selectIconTokens(tokens) {
@@ -85,6 +89,7 @@ const ExpandCollapseControl = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
85
89
  isExpanded,
86
90
  children,
87
91
  tokens,
92
+ controlAlign,
88
93
  accessibilityRole = 'button',
89
94
  variant,
90
95
  ...rest
@@ -135,7 +140,12 @@ const ExpandCollapseControl = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
135
140
  ...selectIconTokens(themeTokens)
136
141
  })
137
142
  }), /*#__PURE__*/_jsx(View, {
138
- style: [selectTextContainerStyles(themeTokens), staticStyles.bubblePointerEvents],
143
+ style: [selectTextContainerStyles({
144
+ ...themeTokens,
145
+ ...(controlAlign && {
146
+ controlAlign
147
+ })
148
+ }), staticStyles.bubblePointerEvents],
139
149
  children: typeof children === 'function' ? children(getControlState(pressableState)) : children
140
150
  })]
141
151
  });
@@ -161,6 +171,10 @@ ExpandCollapseControl.propTypes = {
161
171
  * Whether the linked ExpandCollapsePanel is opened or closed. Allows themes to set `expanded` styles.
162
172
  */
163
173
  isExpanded: PropTypes.bool,
174
+ /**
175
+ * Optional alignment for control content. Overrides token-driven alignment.
176
+ */
177
+ controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
164
178
  /**
165
179
  * Function called when the ExpandCollapse is pressed.
166
180
  */
@@ -97,6 +97,7 @@ const ExpandCollapsePanel = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
97
97
  onPress,
98
98
  control,
99
99
  controlTokens,
100
+ controlAlign,
100
101
  children,
101
102
  tokens,
102
103
  variant,
@@ -166,6 +167,7 @@ const ExpandCollapsePanel = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
166
167
  ...selectedProps,
167
168
  isExpanded: isExpanded,
168
169
  tokens: controlTokens,
170
+ controlAlign: controlAlign,
169
171
  variant: variant,
170
172
  onPress: handleControlPress,
171
173
  ref: controlRef,
@@ -265,6 +267,10 @@ ExpandCollapsePanel.propTypes = {
265
267
  * Optional theme token overrides that may be passed to the ExpandCollapseControl element.
266
268
  */
267
269
  controlTokens: getTokensPropType('ExpandCollapseControl'),
270
+ /**
271
+ * Optional alignment for control content.
272
+ */
273
+ controlAlign: PropTypes.oneOf(['flex-start', 'center', 'flex-end']),
268
274
  /**
269
275
  * An optional ref to be attached to the control
270
276
  */
@@ -6,6 +6,11 @@ import { variantProp } from '../utils/props';
6
6
  import ExpandCollapseMiniControl from './ExpandCollapseMiniControl';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
8
  const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([contentfulProps]);
9
+ const alignMap = {
10
+ start: 'flex-start',
11
+ middle: 'center',
12
+ end: 'flex-end'
13
+ };
9
14
  const ExpandCollapseMini = /*#__PURE__*/React.forwardRef((_ref, ref) => {
10
15
  let {
11
16
  children,
@@ -14,6 +19,7 @@ const ExpandCollapseMini = /*#__PURE__*/React.forwardRef((_ref, ref) => {
14
19
  nativeID,
15
20
  initialOpen = false,
16
21
  dataSet,
22
+ align,
17
23
  ...rest
18
24
  } = _ref;
19
25
  const expandCollapeMiniPanelId = useUniqueId('ExpandCollapseMiniPanel');
@@ -41,12 +47,14 @@ const ExpandCollapseMini = /*#__PURE__*/React.forwardRef((_ref, ref) => {
41
47
  borderColor: 'transparent',
42
48
  textLine: tokens.textLine ?? 'none',
43
49
  backgroundColor: 'transparent'
44
- }
50
+ },
51
+ controlAlign: align && alignMap[align]
45
52
  // TODO refactor
46
53
  // eslint-disable-next-line react/no-unstable-nested-components
47
54
  ,
48
55
  control: pressableState => /*#__PURE__*/_jsx(ExpandCollapseMiniControl, {
49
56
  pressableState: pressableState,
57
+ align: align,
50
58
  ...rest
51
59
  }),
52
60
  controlRef: ref,
@@ -86,6 +94,10 @@ ExpandCollapseMini.propTypes = {
86
94
  /**
87
95
  * The dataSet prop allows to pass data-* attributes element to the component.
88
96
  */
89
- dataSet: PropTypes.object
97
+ dataSet: PropTypes.object,
98
+ /**
99
+ * Controls the horizontal alignment of the trigger label and icon within the panel width.
100
+ */
101
+ align: PropTypes.oneOf(['start', 'middle', 'end'])
90
102
  };
91
103
  export default ExpandCollapseMini;
@@ -6,6 +6,11 @@ import { useThemeTokens } from '../ThemeProvider';
6
6
  import { htmlAttrs, viewProps, selectSystemProps } from '../utils';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
8
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs, viewProps]);
9
+ const alignSelfMap = {
10
+ start: 'flex-start',
11
+ middle: 'center',
12
+ end: 'flex-end'
13
+ };
9
14
 
10
15
  // The ExpandCollapseControl has all the appropriate role, a11y, press handling etc
11
16
  // and a more appropriate press area, defer interaction handling to it.
@@ -24,6 +29,7 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
24
29
  iconPosition = 'right',
25
30
  tokens,
26
31
  variant = {},
32
+ align,
27
33
  ...rest
28
34
  } = _ref;
29
35
  const {
@@ -92,7 +98,10 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
92
98
  ...getTokens(linkState),
93
99
  iconSize,
94
100
  blockFontSize: fontSize,
95
- blockLineHeight: lineHeight
101
+ blockLineHeight: lineHeight,
102
+ ...(align && {
103
+ alignSelf: alignSelfMap[align]
104
+ })
96
105
  }),
97
106
  ref: ref,
98
107
  ...presentationOnly,
@@ -123,6 +132,10 @@ ExpandCollapseMiniControl.propTypes = {
123
132
  /**
124
133
  * Optional variant object to override the default theme tokens
125
134
  */
126
- variant: PropTypes.object
135
+ variant: PropTypes.object,
136
+ /**
137
+ * Controls the horizontal alignment of the trigger label and icon
138
+ */
139
+ align: PropTypes.oneOf(['start', 'middle', 'end'])
127
140
  };
128
141
  export default ExpandCollapseMiniControl;
@@ -42,6 +42,7 @@ const ChevronLink = /*#__PURE__*/React.forwardRef((_ref, ref) => {
42
42
  const getTokens = useThemeTokensCallback('Link', applyChevronTokens, variant);
43
43
  return /*#__PURE__*/_jsx(LinkBase, {
44
44
  ...otherlinkProps,
45
+ useMeasuredMobileIconLayout: true,
45
46
  iconPosition: direction,
46
47
  tokens: getTokens,
47
48
  dataSet: dataSet,
@@ -7,6 +7,7 @@ import { a11yProps, clickProps, getTokensPropType, hrefAttrsProp, linkProps, sel
7
7
  import { resolvePressableTokens } from '../utils/pressability';
8
8
  import { withLinkRouter } from '../utils';
9
9
  import InlinePressable from './InlinePressable';
10
+ import MobileIconTextContent from './MobileIconTextContent';
10
11
  import { applyTextStyles, applyOuterBorder, useTheme } from '../ThemeProvider';
11
12
  import { IconText, iconComponentPropTypes } from '../Icon';
12
13
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -137,6 +138,7 @@ const LinkBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
137
138
  tokens = {},
138
139
  children,
139
140
  dataSet,
141
+ useMeasuredMobileIconLayout = false,
140
142
  accessibilityRole = 'link',
141
143
  ...rawRest
142
144
  } = _ref6;
@@ -180,8 +182,8 @@ const LinkBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
180
182
  const themeTokens = resolveLinkTokens(linkState);
181
183
  const outerBorderStyles = selectOuterBorderStyles(themeTokens);
182
184
  const decorationStyles = selectDecorationStyles(themeTokens);
183
- const mobileCompensation = null;
184
- return [outerBorderStyles, mobileCompensation, blockLeftStyle, decorationStyles, hasIcon && staticStyles.rowContainer];
185
+ const shouldUseMeasuredMobileContent = Platform.OS !== 'web' && useMeasuredMobileIconLayout;
186
+ return [outerBorderStyles, shouldUseMeasuredMobileContent ? staticStyles.measuredMobileOuterBorderCompensation : null, blockLeftStyle, decorationStyles, hasIcon && staticStyles.rowContainer];
185
187
  },
186
188
  children: linkState => {
187
189
  const themeTokens = resolveLinkTokens(linkState);
@@ -196,19 +198,32 @@ const LinkBase = /*#__PURE__*/React.forwardRef((_ref6, ref) => {
196
198
  } = themeTokens;
197
199
  const isTextOnlyLink = !IconComponent && !icon && accessibilityRole === 'link';
198
200
  const adjustedIconSpace = Platform.OS !== 'web' && isTextOnlyLink ? 0 : iconSpace;
201
+ const shouldUseMeasuredMobileContent = Platform.OS !== 'web' && useMeasuredMobileIconLayout;
202
+ const textBaselineStyle = shouldUseMeasuredMobileContent ? null : staticStyles.baseline;
203
+ const linkTextContent = /*#__PURE__*/_jsx(Text, {
204
+ style: [textStyles, blockTextStyles, textBaselineStyle, staticStyles.bubblePointerEvents],
205
+ children: typeof children === 'function' ? children(linkState) : children
206
+ });
207
+ const sharedIconProps = {
208
+ ...iconProps,
209
+ tokens: iconTokens,
210
+ style: staticStyles.bubblePointerEvents
211
+ };
212
+ if (shouldUseMeasuredMobileContent) {
213
+ return /*#__PURE__*/_jsx(MobileIconTextContent, {
214
+ icon: IconComponent,
215
+ iconPosition: iconPosition,
216
+ space: adjustedIconSpace,
217
+ iconProps: sharedIconProps,
218
+ children: linkTextContent
219
+ });
220
+ }
199
221
  return /*#__PURE__*/_jsx(IconText, {
200
222
  icon: IconComponent,
201
223
  iconPosition: iconPosition,
202
224
  space: adjustedIconSpace,
203
- iconProps: {
204
- ...iconProps,
205
- tokens: iconTokens,
206
- style: staticStyles.bubblePointerEvents
207
- },
208
- children: /*#__PURE__*/_jsx(Text, {
209
- style: [textStyles, blockTextStyles, staticStyles.baseline, staticStyles.bubblePointerEvents],
210
- children: typeof children === 'function' ? children(linkState) : children
211
- })
225
+ iconProps: sharedIconProps,
226
+ children: linkTextContent
212
227
  });
213
228
  }
214
229
  });
@@ -266,11 +281,12 @@ const staticStyles = StyleSheet.create({
266
281
  }
267
282
  })
268
283
  },
269
- outerBorderCompensation: {
284
+ measuredMobileOuterBorderCompensation: {
270
285
  ...(Platform.OS !== 'web' && {
271
286
  marginHorizontal: 2,
287
+ marginVertical: 2,
272
288
  paddingHorizontal: Platform.OS === 'android' ? 2 : 0,
273
- paddingTop: Platform.OS === 'android' ? 2 : 0
289
+ paddingVertical: Platform.OS === 'android' ? 2 : 0
274
290
  })
275
291
  }
276
292
  });
@@ -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;
@@ -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: {
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.0",
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.0",
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
  */