@telus-uds/components-base 1.86.0 → 1.88.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 (53) hide show
  1. package/CHANGELOG.md +34 -2
  2. package/lib/ActionCard/ActionCard.js +350 -0
  3. package/lib/ActionCard/index.js +10 -0
  4. package/lib/Card/Card.js +3 -3
  5. package/lib/Card/PressableCardBase.js +1 -1
  6. package/lib/Checkbox/Checkbox.js +4 -0
  7. package/lib/List/List.js +9 -2
  8. package/lib/Modal/WebModal.js +2 -1
  9. package/lib/Notification/Notification.js +20 -15
  10. package/lib/OrderedList/Item.js +14 -23
  11. package/lib/Radio/Radio.js +4 -0
  12. package/lib/Radio/RadioGroup.js +1 -1
  13. package/lib/Tabs/Tabs.js +4 -1
  14. package/lib/Tabs/TabsItem.js +7 -1
  15. package/lib/Validator/Validator.js +18 -9
  16. package/lib/index.js +8 -0
  17. package/lib/utils/animation/useVerticalExpandAnimation.js +3 -3
  18. package/lib/utils/props/tokens.js +2 -2
  19. package/lib-module/ActionCard/ActionCard.js +343 -0
  20. package/lib-module/ActionCard/index.js +2 -0
  21. package/lib-module/Card/Card.js +4 -4
  22. package/lib-module/Card/PressableCardBase.js +1 -1
  23. package/lib-module/Checkbox/Checkbox.js +4 -0
  24. package/lib-module/List/List.js +9 -2
  25. package/lib-module/Modal/WebModal.js +2 -1
  26. package/lib-module/Notification/Notification.js +20 -15
  27. package/lib-module/OrderedList/Item.js +14 -23
  28. package/lib-module/Radio/Radio.js +4 -0
  29. package/lib-module/Radio/RadioGroup.js +1 -1
  30. package/lib-module/Tabs/Tabs.js +4 -1
  31. package/lib-module/Tabs/TabsItem.js +7 -1
  32. package/lib-module/Validator/Validator.js +18 -9
  33. package/lib-module/index.js +1 -0
  34. package/lib-module/utils/animation/useVerticalExpandAnimation.js +3 -3
  35. package/lib-module/utils/props/tokens.js +2 -2
  36. package/package.json +2 -2
  37. package/src/ActionCard/ActionCard.jsx +306 -0
  38. package/src/ActionCard/index.js +3 -0
  39. package/src/Card/Card.jsx +6 -4
  40. package/src/Card/PressableCardBase.jsx +1 -1
  41. package/src/Checkbox/Checkbox.jsx +3 -1
  42. package/src/List/List.jsx +7 -2
  43. package/src/Modal/WebModal.jsx +2 -1
  44. package/src/Notification/Notification.jsx +36 -16
  45. package/src/OrderedList/Item.jsx +12 -16
  46. package/src/Radio/Radio.jsx +3 -1
  47. package/src/Radio/RadioGroup.jsx +1 -1
  48. package/src/Tabs/Tabs.jsx +4 -1
  49. package/src/Tabs/TabsItem.jsx +5 -1
  50. package/src/Validator/Validator.jsx +21 -9
  51. package/src/index.js +1 -0
  52. package/src/utils/animation/useVerticalExpandAnimation.js +3 -3
  53. package/src/utils/props/tokens.js +4 -2
@@ -0,0 +1,306 @@
1
+ import React from 'react'
2
+ import { Animated, View, StyleSheet, Text } from 'react-native'
3
+ import PropTypes from 'prop-types'
4
+
5
+ import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
6
+ import {
7
+ a11yProps,
8
+ getTokensPropType,
9
+ selectSystemProps,
10
+ variantProp,
11
+ viewProps,
12
+ wrapStringsInText,
13
+ clickProps,
14
+ hrefAttrsProp,
15
+ linkProps
16
+ } from '../utils'
17
+ import Icon from '../Icon'
18
+ import Card from '../Card'
19
+
20
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, linkProps, viewProps])
21
+
22
+ const selectCardStyles = (styles) =>
23
+ ['borderColor', 'borderRadius', 'borderWidth'].reduce((acc, key) => {
24
+ if (styles[key]) acc[key] = styles[key]
25
+ return acc
26
+ }, {})
27
+
28
+ const selectInteractiveCardStyles = ({
29
+ backgroundColor,
30
+ paddingBottom,
31
+ paddingLeft,
32
+ paddingRight,
33
+ paddingTop
34
+ }) => ({
35
+ ...(backgroundColor && { backgroundColor }),
36
+ paddingBottom,
37
+ paddingLeft,
38
+ paddingRight,
39
+ paddingTop
40
+ })
41
+
42
+ const selectIconStyles = ({
43
+ iconMarginBottom,
44
+ iconMarginLeft,
45
+ iconMarginRight,
46
+ iconMarginTop
47
+ }) => ({
48
+ marginBottom: iconMarginBottom,
49
+ marginLeft: iconMarginLeft,
50
+ marginRight: iconMarginRight,
51
+ marginTop: iconMarginTop
52
+ })
53
+
54
+ const selectIconProps = ({ icon, iconBackgroundColor, iconColor }) => ({
55
+ icon,
56
+ variant: { background: true, padding: 'small' },
57
+ tokens: { backgroundColor: iconBackgroundColor, color: iconColor }
58
+ })
59
+
60
+ const selectTitleStyles = ({
61
+ titleMarginBottom,
62
+ titleMarginLeft,
63
+ titleMarginRight,
64
+ titleMarginTop
65
+ }) => ({
66
+ marginBottom: titleMarginBottom,
67
+ marginLeft: titleMarginLeft,
68
+ marginRight: titleMarginRight,
69
+ marginTop: titleMarginTop
70
+ })
71
+
72
+ const selectTitleTextStyles = ({
73
+ titleFontColor,
74
+ titleFontName,
75
+ titleFontSize,
76
+ titleFontWeight,
77
+ titleLineHeight
78
+ }) =>
79
+ applyTextStyles({
80
+ fontColor: titleFontColor,
81
+ fontName: titleFontName,
82
+ fontSize: titleFontSize,
83
+ fontWeight: titleFontWeight,
84
+ lineHeight: titleLineHeight
85
+ })
86
+
87
+ const selectContentStyles = ({
88
+ contentMarginBottom,
89
+ contentMarginLeft,
90
+ contentMarginRight,
91
+ contentMarginTop
92
+ }) => ({
93
+ marginBottom: contentMarginBottom,
94
+ marginLeft: contentMarginLeft,
95
+ marginRight: contentMarginRight,
96
+ marginTop: contentMarginTop
97
+ })
98
+
99
+ const selectContentTextStyles = ({
100
+ contentFontColor,
101
+ contentFontName,
102
+ contentFontSize,
103
+ contentFontWeight,
104
+ contentLineHeight
105
+ }) =>
106
+ applyTextStyles({
107
+ fontColor: contentFontColor,
108
+ fontName: contentFontName,
109
+ fontSize: contentFontSize,
110
+ fontWeight: contentFontWeight,
111
+ lineHeight: contentLineHeight
112
+ })
113
+
114
+ const selectStatusIconProps = ({ statusIcon, statusIconColor }) => ({
115
+ icon: statusIcon,
116
+ tokens: { color: statusIconColor },
117
+ variant: { size: 'micro' }
118
+ })
119
+
120
+ const selectActionIconStyles = ({
121
+ actionIconMarginBottom,
122
+ actionIconMarginLeft,
123
+ actionIconMarginRight,
124
+ actionIconMarginTop
125
+ }) => ({
126
+ marginBottom: actionIconMarginBottom,
127
+ marginLeft: actionIconMarginLeft,
128
+ marginRight: actionIconMarginRight,
129
+ marginTop: actionIconMarginTop
130
+ })
131
+
132
+ const selectActionIconProps = ({ actionIcon, actionIconColor }) => ({
133
+ icon: actionIcon,
134
+ tokens: { color: actionIconColor }
135
+ })
136
+
137
+ const ACTION_ICON_ANIMATION_DURATION = 100
138
+
139
+ /**
140
+ * ActionCard component represents a card with an action that can be triggered by the user.
141
+ *
142
+ * @component
143
+ * @example
144
+ * return (
145
+ * <ActionCard
146
+ * icon={<Icon icon={Icons.EyeMasked} />}
147
+ * title="Like"
148
+ * href="/like"
149
+ * accessibilityRole="link"
150
+ * tokens={themeTokens}
151
+ * variant={{validation: 'warning'}}
152
+ * onPress={() => handleLike()}
153
+ * >
154
+ * Click here to like the post
155
+ * </ActionCard>
156
+ * )
157
+ */
158
+ const ActionCard = React.forwardRef(
159
+ (
160
+ { icon, title, children, href, direction = true, accessibilityRole, tokens, variant, ...rest },
161
+ ref
162
+ ) => {
163
+ const themeTokens = useThemeTokens('ActionCard', tokens, variant)
164
+
165
+ const actionIconAnimation = React.useRef(new Animated.Value(0)).current
166
+
167
+ const { onPress, ...props } = clickProps.toPressProps(rest)
168
+ const { hrefAttrs, rawRest } = hrefAttrsProp.bundle(props)
169
+ const selectedProps = selectProps({
170
+ accessibilityRole,
171
+ href,
172
+ onPress: linkProps.handleHref({ href, onPress }),
173
+ hrefAttrs,
174
+ ...rawRest
175
+ })
176
+
177
+ return (
178
+ <Card
179
+ ref={ref}
180
+ interactiveCard={{
181
+ body: (pressableState) => {
182
+ const animate = pressableState.pressed || pressableState.hover || pressableState.focus
183
+ Animated.timing(actionIconAnimation, {
184
+ toValue: animate ? themeTokens.actionIconTranslate : 0,
185
+ duration: ACTION_ICON_ANIMATION_DURATION,
186
+ useNativeDriver: false
187
+ }).start()
188
+
189
+ return (
190
+ <View style={staticStyles.container}>
191
+ {icon && (
192
+ <View style={selectIconStyles(themeTokens)}>
193
+ <Icon
194
+ {...selectIconProps({
195
+ icon,
196
+ ...themeTokens
197
+ })}
198
+ />
199
+ </View>
200
+ )}
201
+ <View style={staticStyles.content}>
202
+ <View style={staticStyles.header}>
203
+ <View style={[selectTitleStyles(themeTokens), staticStyles.title]}>
204
+ <Text>
205
+ {wrapStringsInText(title, { style: selectTitleTextStyles(themeTokens) })}
206
+ {themeTokens.statusIcon && (
207
+ <View style={staticStyles.statusIcon}>
208
+ <Icon {...selectStatusIconProps(themeTokens)} />
209
+ </View>
210
+ )}
211
+ </Text>
212
+ </View>
213
+ <View style={staticStyles.icons}>
214
+ {direction && (
215
+ <Animated.View
216
+ style={[
217
+ selectActionIconStyles(themeTokens),
218
+ { transform: [{ translateX: actionIconAnimation }] }
219
+ ]}
220
+ >
221
+ <Icon {...selectActionIconProps(themeTokens)} />
222
+ </Animated.View>
223
+ )}
224
+ </View>
225
+ </View>
226
+ <View style={[staticStyles.body, selectContentStyles(themeTokens)]}>
227
+ {typeof children === 'string'
228
+ ? wrapStringsInText(children, {
229
+ style: selectContentTextStyles(themeTokens)
230
+ })
231
+ : children}
232
+ </View>
233
+ </View>
234
+ </View>
235
+ )
236
+ },
237
+ tokens: selectInteractiveCardStyles(themeTokens)
238
+ }}
239
+ tokens={selectCardStyles(themeTokens)}
240
+ {...selectedProps}
241
+ />
242
+ )
243
+ }
244
+ )
245
+
246
+ ActionCard.displayName = 'ActionCard'
247
+
248
+ ActionCard.propTypes = {
249
+ ...selectedSystemPropTypes,
250
+ tokens: getTokensPropType('ActionCard'),
251
+ variant: variantProp.propType,
252
+ /**
253
+ * Icon for the ActionCard
254
+ */
255
+ icon: PropTypes.elementType,
256
+ /**
257
+ * Title for the ActionCard
258
+ */
259
+ title: PropTypes.string,
260
+ /**
261
+ * Children for the ActionCard
262
+ */
263
+ children: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
264
+ /**
265
+ * href for the ActionCard
266
+ */
267
+ href: PropTypes.string,
268
+ /**
269
+ * Enable or disable the direction of the ActionCard
270
+ */
271
+ direction: PropTypes.bool,
272
+ /**
273
+ * AccesibilityRole for the ActionCard
274
+ */
275
+ accessibilityRole: PropTypes.string
276
+ }
277
+
278
+ const staticStyles = StyleSheet.create({
279
+ body: {
280
+ flexDirection: 'column'
281
+ },
282
+ container: {
283
+ flex: 1,
284
+ flexDirection: 'row'
285
+ },
286
+ content: {
287
+ flex: 1,
288
+ flexDirection: 'column'
289
+ },
290
+ icons: {
291
+ flexDirection: 'row',
292
+ alignItems: 'center'
293
+ },
294
+ header: {
295
+ flexDirection: 'row',
296
+ justifyContent: 'space-between'
297
+ },
298
+ title: {
299
+ flexShrink: 1
300
+ },
301
+ statusIcon: {
302
+ marginLeft: 4
303
+ }
304
+ })
305
+
306
+ export default ActionCard
@@ -0,0 +1,3 @@
1
+ import ActionCard from './ActionCard'
2
+
3
+ export default ActionCard
package/src/Card/Card.jsx CHANGED
@@ -9,13 +9,13 @@ import {
9
9
  } from '../ThemeProvider'
10
10
  import { getTokensPropType, variantProp, StyleSheet, createMediaQueryStyles } from '../utils'
11
11
  import { useViewport } from '../ViewportProvider'
12
- import { a11yProps, selectSystemProps, viewProps } from '../utils/props'
12
+ import { a11yProps, linkProps, selectSystemProps, viewProps } from '../utils/props'
13
13
  import CardBase from './CardBase'
14
14
  import PressableCardBase from './PressableCardBase'
15
15
  import CheckboxButton from '../Checkbox/CheckboxButton'
16
16
  import RadioButton from '../Radio/RadioButton'
17
17
 
18
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
18
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps, linkProps])
19
19
 
20
20
  const SelectionType = {
21
21
  Checkbox: 'checkbox',
@@ -253,7 +253,9 @@ const Card = React.forwardRef(
253
253
  onPress
254
254
  })
255
255
  )}
256
- {interactiveCard?.body}
256
+ {typeof interactiveCard?.body === 'function'
257
+ ? interactiveCard.body(cardState)
258
+ : interactiveCard.body}
257
259
  </>
258
260
  )
259
261
  }}
@@ -304,7 +306,7 @@ Card.propTypes = {
304
306
  * - variant: The variant to be used for the interactive card
305
307
  */
306
308
  interactiveCard: PropTypes.shape({
307
- body: PropTypes.node,
309
+ body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
308
310
  tokens: getTokensPropType('Card'),
309
311
  selectionType: PropTypes.oneOf(Object.values(SelectionType)),
310
312
  variant: variantProp.propType
@@ -135,7 +135,7 @@ PressableCardBase.displayName = 'PressableCardBase'
135
135
  PressableCardBase.propTypes = {
136
136
  ...selectedSystemPropTypes,
137
137
  children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
138
- tokens: getTokensSetPropType(tokenKeys, { requireAll: true, allowFunction: true }),
138
+ tokens: getTokensSetPropType(tokenKeys, { partial: true, allowFunction: true }),
139
139
  variant: variantProp.propType
140
140
  }
141
141
 
@@ -192,6 +192,7 @@ const Checkbox = React.forwardRef(
192
192
  onKeyDown={handleKeyDown}
193
193
  onPress={handleChange}
194
194
  {...selectedProps}
195
+ style={staticStyles.removeOutline}
195
196
  >
196
197
  {({ focused: focus, hovered: hover, pressed }) => {
197
198
  const { icon: IconComponent, ...stateTokens } = getTokens({ focus, hover, pressed })
@@ -311,5 +312,6 @@ const staticStyles = StyleSheet.create({
311
312
  wrapper: { backgroundColor: 'transparent' },
312
313
  container: { flexDirection: 'row', alignItems: 'center' },
313
314
  defaultInputStyles: { alignItems: 'center', justifyContent: 'center' },
314
- alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' }
315
+ alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' },
316
+ removeOutline: { outlineStyle: 'none' }
315
317
  })
package/src/List/List.jsx CHANGED
@@ -41,20 +41,25 @@ const List = React.forwardRef(
41
41
  return child
42
42
  })
43
43
 
44
- return (
44
+ const content = (
45
45
  <View
46
- ref={ref}
47
46
  style={styles.list}
48
47
  accessibilityRole={accessibilityRole}
48
+ ref={ref}
49
49
  {...selectProps(rest)}
50
50
  >
51
51
  {items}
52
52
  </View>
53
53
  )
54
+
55
+ return Platform.OS === 'web' ? <div style={styles.container}>{content}</div> : content
54
56
  }
55
57
  )
56
58
 
57
59
  const styles = StyleSheet.create({
60
+ container: {
61
+ display: 'block'
62
+ },
58
63
  list: {
59
64
  flex: 1,
60
65
  flexShrink: 1
@@ -62,7 +62,8 @@ const staticStyles = StyleSheet.create({
62
62
  flexShrink: 1,
63
63
  flexBasis: 0,
64
64
  zIndex: 1000,
65
- display: 'flex'
65
+ display: 'flex',
66
+ overflowY: 'auto'
66
67
  }
67
68
  })
68
69
 
@@ -61,14 +61,24 @@ const selectDismissIconProps = ({ dismissIconSize, dismissIconColor }) => ({
61
61
 
62
62
  const selectDismissButtonContainerStyles = ({ dismissButtonGap }) => ({
63
63
  paddingLeft: dismissButtonGap,
64
- placeContent: 'center'
64
+ placeContent: 'start'
65
65
  })
66
66
 
67
- const selectContentContainerStyle = (maxWidth) => ({
68
- maxWidth: maxWidth || '100%'
67
+ const selectContentContainerStyle = (themeTokens, maxWidth, viewport, system) => ({
68
+ maxWidth: viewport === 'xl' && system === true ? maxWidth : 'auto',
69
+ minWidth: viewport === 'xl' && system === true ? maxWidth : 'auto',
70
+ paddingRight: themeTokens?.containerPaddingRight,
71
+ paddingLeft: themeTokens?.containerPaddingLeft
69
72
  })
70
73
 
71
- const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, dismissible) => {
74
+ const getMediaQueryStyles = (
75
+ themeTokens,
76
+ themeOptions,
77
+ mediaIdsRef,
78
+ dismissible,
79
+ viewport,
80
+ system
81
+ ) => {
72
82
  const transformedSelectContainerStyles = Object.entries(themeTokens).reduce(
73
83
  (acc, [vp, viewportTokens]) => {
74
84
  acc[vp] = selectContainerStyles({ ...viewportTokens })
@@ -80,7 +90,10 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
80
90
  const selectContainerMediaQueryStyles = createMediaQueryStyles(transformedSelectContainerStyles)
81
91
 
82
92
  const { ids: containerIds, styles: containerStyles } = StyleSheet.create({
83
- container: { flexDirection: 'row', ...selectContainerMediaQueryStyles }
93
+ container: {
94
+ flexDirection: system === true && viewport === 'xl' ? 'row' : 'inherit',
95
+ ...selectContainerMediaQueryStyles
96
+ }
84
97
  })
85
98
 
86
99
  const { ids: contentContainerIds, styles: contentContainerStyles } = StyleSheet.create({
@@ -89,11 +102,11 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
89
102
  flexShrink: 1,
90
103
  justifyContent: 'space-between',
91
104
  ...createMediaQueryStyles({
92
- xs: { width: themeOptions?.contentMaxWidth.xs || '100%' },
93
- md: { width: themeOptions?.contentMaxWidth.md || '100%' },
94
- lg: { width: themeOptions?.contentMaxWidth.lg || '100%' },
95
- sm: { width: themeOptions?.contentMaxWidth.sm || '100%' },
96
- xl: { width: themeOptions?.contentMaxWidth.xl || '100%' }
105
+ xs: { width: themeOptions?.contentMaxWidth?.xs || '100%' },
106
+ md: { width: themeOptions?.contentMaxWidth?.md || '100%' },
107
+ lg: { width: themeOptions?.contentMaxWidth?.lg || '100%' },
108
+ sm: { width: themeOptions?.contentMaxWidth?.sm || '100%' },
109
+ xl: { width: themeOptions?.contentMaxWidth?.xl || '100%' }
97
110
  })
98
111
  }
99
112
  })
@@ -146,16 +159,20 @@ const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, d
146
159
  }
147
160
  }
148
161
 
149
- const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible) => ({
162
+ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system) => ({
150
163
  containerStyles: {
151
- container: { flexDirection: 'row', ...selectContainerStyles(themeTokens) }
164
+ container: {
165
+ flexDirection: system === true && viewport === 'xl' ? 'row' : 'inherit',
166
+ ...selectContainerStyles(themeTokens)
167
+ }
152
168
  },
153
169
  contentContainerStyles: {
154
170
  contentContainer: {
171
+ flex: 1,
155
172
  flexDirection: 'row',
156
173
  flexShrink: 1,
157
174
  justifyContent: 'space-between',
158
- ...selectContentContainerStyle(maxWidth)
175
+ ...selectContentContainerStyle(themeTokens, maxWidth, viewport, system)
159
176
  }
160
177
  },
161
178
  staticContentContainerStyles: {
@@ -260,16 +277,19 @@ const Notification = React.forwardRef(
260
277
  notificationComponentRef.current = getMediaQueryStyles(
261
278
  themeTokens,
262
279
  themeOptions,
263
- viewport,
264
280
  mediaIdsRef,
265
- dismissible
281
+ dismissible,
282
+ viewport,
283
+ system
266
284
  )
267
285
  } else {
268
286
  notificationComponentRef.current = getDefaultStyles(
269
287
  themeTokens,
270
288
  themeOptions,
271
289
  maxWidth,
272
- dismissible
290
+ dismissible,
291
+ viewport,
292
+ system
273
293
  )
274
294
  }
275
295
 
@@ -11,7 +11,6 @@ import {
11
11
  wrapStringsInText
12
12
  } from '../utils'
13
13
  import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
14
- import StackView from '../StackView'
15
14
  import Typography from '../Typography'
16
15
  import ItemBase from './ItemBase'
17
16
 
@@ -32,19 +31,17 @@ const selectItemTextStyles = (
32
31
 
33
32
  const selectItemCounterStyles = ({
34
33
  itemBulletContainerWidth,
35
- itemBulletContainerAlign,
34
+ itemBulletTextAlign,
36
35
  itemFontWeight,
37
36
  itemFontSize,
38
37
  itemFontName,
39
38
  itemLineHeight,
40
39
  themeOptions,
41
- listGutter,
42
40
  itemColor
43
41
  }) => ({
44
42
  color: itemColor,
45
43
  width: itemBulletContainerWidth,
46
- paddingRight: listGutter,
47
- textAlign: itemBulletContainerAlign,
44
+ textAlign: itemBulletTextAlign,
48
45
  ...applyTextStyles({
49
46
  fontWeight: itemFontWeight,
50
47
  fontSize: itemFontSize,
@@ -54,8 +51,9 @@ const selectItemCounterStyles = ({
54
51
  lineHeight: itemLineHeight * itemFontSize
55
52
  })
56
53
 
57
- const selectItemContentStyles = ({ interItemMargin, ...themeTokens }, isLastChild) => ({
54
+ const selectItemContentStyles = ({ interItemMargin, listGutter, ...themeTokens }, isLastChild) => ({
58
55
  ...themeTokens,
56
+ marginLeft: listGutter,
59
57
  marginBottom: !isLastChild ? interItemMargin : 0
60
58
  })
61
59
 
@@ -87,17 +85,15 @@ const Item = React.forwardRef(
87
85
  <View style={[staticStyles.itemCounter, selectItemCounterStyles(themeTokens)]}>
88
86
  {itemCounter}
89
87
  </View>
90
- <View style={staticStyles.itemContent}>
91
- {title ? (
92
- <StackView tokens={{ flexShrink: 1 }} space={0}>
88
+ <View style={[staticStyles.itemContent, selectItemContentStyles(themeTokens, isLastChild)]}>
89
+ {title && (
90
+ <View>
93
91
  <Typography variant={{ size: 'h4' }} tokens={headingTokens}>
94
92
  {title}
95
93
  </Typography>
96
- <View style={selectItemContentStyles(themeTokens, isLastChild)}>{itemContent}</View>
97
- </StackView>
98
- ) : (
99
- <View style={selectItemContentStyles(themeTokens, isLastChild)}>{itemContent}</View>
94
+ </View>
100
95
  )}
96
+ <View>{itemContent}</View>
101
97
  </View>
102
98
  </ItemBase>
103
99
  )
@@ -138,13 +134,13 @@ export default Item
138
134
 
139
135
  const staticStyles = StyleSheet.create({
140
136
  container: {
137
+ flex: 1,
141
138
  flexDirection: 'row'
142
139
  },
143
140
  itemCounter: {
144
- flexWrap: 'wrap'
141
+ alignItems: 'flex-end'
145
142
  },
146
143
  itemContent: {
147
- flexDirection: 'column',
148
- marginLeft: 8
144
+ flexShrink: 1
149
145
  }
150
146
  })
@@ -173,6 +173,7 @@ const Radio = React.forwardRef(
173
173
  onKeyDown={handleKeyDown}
174
174
  onPress={handleChange}
175
175
  {...selectedProps}
176
+ style={staticStyles.removeOutline}
176
177
  >
177
178
  {({ focused: focus, hovered: hover, pressed }) => {
178
179
  const stateTokens = getTokens({ focus, hover, pressed })
@@ -278,5 +279,6 @@ export default Radio
278
279
 
279
280
  const staticStyles = StyleSheet.create({
280
281
  container: { flexDirection: 'row', alignItems: 'center' },
281
- alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' }
282
+ alignWithLabel: { alignSelf: 'flex-start', justifyContent: 'center' },
283
+ removeOutline: { outlineStyle: 'none' }
282
284
  })
@@ -131,7 +131,7 @@ const RadioGroup = React.forwardRef(
131
131
  readOnly: readOnly || inactive
132
132
  })
133
133
 
134
- const uniqueFields = ['id', 'label']
134
+ const uniqueFields = ['id']
135
135
  if (!containUniqueFields(items, uniqueFields)) {
136
136
  throw new Error(`RadioGroup items must have unique ${uniqueFields.join(', ')}`)
137
137
  }
package/src/Tabs/Tabs.jsx CHANGED
@@ -124,6 +124,7 @@ const Tabs = React.forwardRef(
124
124
  ref: itemRef,
125
125
  LinkRouter: ItemLinkRouter = LinkRouter,
126
126
  linkRouterProps: itemLinkRouterProps,
127
+ render,
127
128
  ...itemRest
128
129
  },
129
130
  index
@@ -151,6 +152,7 @@ const Tabs = React.forwardRef(
151
152
  accessibilityRole={accessibilityRole}
152
153
  LinkRouter={ItemLinkRouter}
153
154
  linkRouterProps={{ ...linkRouterProps, ...itemLinkRouterProps }}
155
+ render={render}
154
156
  {...itemProps}
155
157
  >
156
158
  {label}
@@ -178,7 +180,8 @@ Tabs.propTypes = {
178
180
  href: PropTypes.string,
179
181
  label: PropTypes.string,
180
182
  id: PropTypes.string,
181
- ref: ABBPropTypes.ref()
183
+ ref: ABBPropTypes.ref(),
184
+ render: PropTypes.func
182
185
  })
183
186
  ),
184
187
  /**
@@ -92,6 +92,7 @@ const TabsItem = React.forwardRef(
92
92
  : undefined,
93
93
  accessibilityState = accessibilityRole === 'tab' ? { selected } : undefined,
94
94
  id,
95
+ render,
95
96
  ...rawRest
96
97
  },
97
98
  ref
@@ -144,6 +145,8 @@ const TabsItem = React.forwardRef(
144
145
  // itemPositions is a ref object so this should only re-run when `selected` (or `index`) change
145
146
  }, [selected, index, itemPositions])
146
147
 
148
+ if (render) return render({ selected, handlePress })
149
+
147
150
  return (
148
151
  <Pressable
149
152
  ref={ref}
@@ -202,7 +205,8 @@ TabsItem.propTypes = {
202
205
  selected: PropTypes.bool,
203
206
  itemPositions: itemPositionsPropType,
204
207
  children: PropTypes.string,
205
- id: PropTypes.string
208
+ id: PropTypes.string,
209
+ render: PropTypes.func
206
210
  }
207
211
 
208
212
  const staticStyles = StyleSheet.create({