@telus-uds/components-base 1.14.2 → 1.16.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 (161) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/__tests17__/A11yText/A11yText.test.jsx +34 -0
  3. package/__tests17__/ActivityIndicator/ActivityIndicator.test.jsx +68 -0
  4. package/__tests17__/ActivityIndicator/__snapshots__/ActivityIndicator.test.jsx.snap +299 -0
  5. package/__tests17__/Box/Box.test.jsx +111 -0
  6. package/__tests17__/Button/Button.test.jsx +86 -0
  7. package/__tests17__/Button/ButtonBase.test.jsx +82 -0
  8. package/__tests17__/Button/ButtonGroup.test.jsx +347 -0
  9. package/__tests17__/Button/ButtonLink.test.jsx +61 -0
  10. package/__tests17__/Card/Card.test.jsx +63 -0
  11. package/__tests17__/Carousel/Carousel.test.jsx +128 -0
  12. package/__tests17__/Carousel/CarouselTabs.test.jsx +142 -0
  13. package/__tests17__/Checkbox/Checkbox.test.jsx +94 -0
  14. package/__tests17__/Checkbox/CheckboxGroup.test.jsx +246 -0
  15. package/__tests17__/Divider/Divider.test.jsx +91 -0
  16. package/__tests17__/ExpandCollapse/ExpandCollapse.test.jsx +109 -0
  17. package/__tests17__/Feedback/Feedback.test.jsx +42 -0
  18. package/__tests17__/FlexGrid/Col.test.jsx +261 -0
  19. package/__tests17__/FlexGrid/FlexGrid.test.jsx +136 -0
  20. package/__tests17__/FlexGrid/Row.test.jsx +273 -0
  21. package/__tests17__/HorizontalScroll/HorizontalScroll.test.jsx +165 -0
  22. package/__tests17__/Icon/Icon.test.jsx +61 -0
  23. package/__tests17__/IconButton/IconButton.test.jsx +52 -0
  24. package/__tests17__/InputLabel/InputLabel.test.jsx +28 -0
  25. package/__tests17__/InputLabel/__snapshots__/InputLabel.test.jsx.snap +3 -0
  26. package/__tests17__/InputSupports/InputSupports.test.jsx +60 -0
  27. package/__tests17__/Link/Link.test.jsx +63 -0
  28. package/__tests17__/Link/TextButton.test.jsx +35 -0
  29. package/__tests17__/List/List.test.jsx +82 -0
  30. package/__tests17__/Modal/Modal.test.jsx +47 -0
  31. package/__tests17__/Notification/Notification.test.jsx +20 -0
  32. package/__tests17__/Pagination/Pagination.test.jsx +160 -0
  33. package/__tests17__/Progress/Progress.test.jsx +79 -0
  34. package/__tests17__/Radio/Radio.test.jsx +87 -0
  35. package/__tests17__/Radio/RadioGroup.test.jsx +220 -0
  36. package/__tests17__/RadioCard/RadioCard.test.jsx +87 -0
  37. package/__tests17__/RadioCard/RadioCardGroup.test.jsx +246 -0
  38. package/__tests17__/Search/Search.test.jsx +87 -0
  39. package/__tests17__/Select/Select.test.jsx +94 -0
  40. package/__tests17__/SideNav/SideNav.test.jsx +110 -0
  41. package/__tests17__/Skeleton/Skeleton.test.jsx +61 -0
  42. package/__tests17__/SkipLink/SkipLink.test.jsx +61 -0
  43. package/__tests17__/Spacer/Spacer.test.jsx +63 -0
  44. package/__tests17__/StackView/StackView.test.jsx +211 -0
  45. package/__tests17__/StackView/StackWrap.test.jsx +47 -0
  46. package/__tests17__/StackView/getStackedContent.test.jsx +295 -0
  47. package/__tests17__/StepTracker/StepTracker.test.jsx +108 -0
  48. package/__tests17__/Tabs/Tabs.test.jsx +49 -0
  49. package/__tests17__/Tags/Tags.test.jsx +327 -0
  50. package/__tests17__/TextInput/TextArea.test.jsx +35 -0
  51. package/__tests17__/TextInput/TextInputBase.test.jsx +125 -0
  52. package/__tests17__/ThemeProvider/ThemeProvider.test.jsx +80 -0
  53. package/__tests17__/ThemeProvider/useThemeTokens.test.jsx +514 -0
  54. package/__tests17__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  55. package/__tests17__/ToggleSwitch/ToggleSwitch.test.jsx +82 -0
  56. package/__tests17__/ToggleSwitch/ToggleSwitchGroup.test.jsx +192 -0
  57. package/__tests17__/Tooltip/Tooltip.test.jsx +65 -0
  58. package/__tests17__/Tooltip/getTooltipPosition.test.js +79 -0
  59. package/__tests17__/Typography/typography.test.jsx +90 -0
  60. package/__tests17__/utils/children.test.jsx +128 -0
  61. package/__tests17__/utils/containUniqueFields.test.js +25 -0
  62. package/__tests17__/utils/input.test.js +375 -0
  63. package/__tests17__/utils/props.test.js +36 -0
  64. package/__tests17__/utils/semantics.test.jsx +34 -0
  65. package/__tests17__/utils/useCopy.test.js +42 -0
  66. package/__tests17__/utils/useResponsiveProp.test.jsx +202 -0
  67. package/__tests17__/utils/useSpacingScale.test.jsx +273 -0
  68. package/__tests17__/utils/useUniqueId.test.js +31 -0
  69. package/component-docs.json +120 -85
  70. package/lib/A11yInfoProvider/index.js +14 -5
  71. package/lib/Button/ButtonGroup.js +3 -2
  72. package/lib/Carousel/Carousel.js +18 -2
  73. package/lib/Carousel/CarouselTabs/CarouselTabs.js +6 -7
  74. package/lib/Checkbox/Checkbox.js +9 -6
  75. package/lib/ExpandCollapse/Control.js +6 -5
  76. package/lib/ExpandCollapse/Panel.js +5 -4
  77. package/lib/List/ListItem.js +10 -236
  78. package/lib/List/ListItemBase.js +162 -0
  79. package/lib/List/ListItemContent.js +85 -0
  80. package/lib/List/ListItemMark.js +158 -0
  81. package/lib/List/PressableListItemBase.js +147 -0
  82. package/lib/Notification/Notification.js +2 -1
  83. package/lib/Pagination/Pagination.js +4 -3
  84. package/lib/Radio/Radio.js +9 -6
  85. package/lib/RadioCard/RadioCard.js +9 -6
  86. package/lib/Select/Select.js +1 -0
  87. package/lib/Skeleton/Skeleton.js +18 -13
  88. package/lib/Skeleton/useSkeletonNativeAnimation.js +4 -2
  89. package/lib/Tabs/Tabs.js +12 -3
  90. package/lib/Tags/Tags.js +3 -3
  91. package/lib/TextInput/TextInput.js +5 -4
  92. package/lib/ToggleSwitch/ToggleSwitch.js +24 -19
  93. package/lib/ViewportProvider/useViewportListener.js +11 -5
  94. package/lib/utils/hasOwnProperty.js +18 -0
  95. package/lib/utils/props/a11yProps.js +171 -1
  96. package/lib/utils/props/getPropSelector.js +47 -5
  97. package/lib/utils/ssr.js +116 -1
  98. package/lib/utils/useResponsiveProp.js +5 -3
  99. package/lib/utils/withLinkRouter.js +3 -5
  100. package/lib-module/A11yInfoProvider/index.js +14 -4
  101. package/lib-module/Button/ButtonGroup.js +3 -2
  102. package/lib-module/Carousel/Carousel.js +16 -2
  103. package/lib-module/Carousel/CarouselTabs/CarouselTabs.js +7 -6
  104. package/lib-module/Checkbox/Checkbox.js +9 -6
  105. package/lib-module/ExpandCollapse/Control.js +6 -5
  106. package/lib-module/ExpandCollapse/Panel.js +5 -4
  107. package/lib-module/List/ListItem.js +13 -235
  108. package/lib-module/List/ListItemBase.js +139 -0
  109. package/lib-module/List/ListItemContent.js +66 -0
  110. package/lib-module/List/ListItemMark.js +143 -0
  111. package/lib-module/List/PressableListItemBase.js +117 -0
  112. package/lib-module/Notification/Notification.js +2 -1
  113. package/lib-module/Pagination/Pagination.js +5 -3
  114. package/lib-module/Radio/Radio.js +9 -6
  115. package/lib-module/RadioCard/RadioCard.js +9 -6
  116. package/lib-module/Select/Select.js +1 -0
  117. package/lib-module/Skeleton/Skeleton.js +15 -13
  118. package/lib-module/Skeleton/useSkeletonNativeAnimation.js +3 -2
  119. package/lib-module/Tabs/Tabs.js +13 -4
  120. package/lib-module/Tags/Tags.js +3 -3
  121. package/lib-module/TextInput/TextInput.js +5 -4
  122. package/lib-module/ToggleSwitch/ToggleSwitch.js +24 -19
  123. package/lib-module/ViewportProvider/useViewportListener.js +10 -4
  124. package/lib-module/utils/hasOwnProperty.js +11 -0
  125. package/lib-module/utils/props/a11yProps.js +169 -1
  126. package/lib-module/utils/props/getPropSelector.js +44 -5
  127. package/lib-module/utils/ssr.js +106 -0
  128. package/lib-module/utils/useResponsiveProp.js +3 -4
  129. package/lib-module/utils/withLinkRouter.js +3 -5
  130. package/package.json +12 -17
  131. package/src/A11yInfoProvider/index.jsx +20 -4
  132. package/src/Button/ButtonGroup.jsx +4 -2
  133. package/src/Carousel/Carousel.jsx +15 -2
  134. package/src/Carousel/CarouselTabs/CarouselTabs.jsx +5 -3
  135. package/src/Checkbox/Checkbox.jsx +7 -3
  136. package/src/ExpandCollapse/Control.jsx +8 -5
  137. package/src/ExpandCollapse/Panel.jsx +7 -5
  138. package/src/List/ListItem.jsx +12 -191
  139. package/src/List/ListItemBase.jsx +118 -0
  140. package/src/List/ListItemContent.jsx +52 -0
  141. package/src/List/ListItemMark.jsx +99 -0
  142. package/src/List/PressableListItemBase.jsx +102 -0
  143. package/src/Notification/Notification.jsx +1 -1
  144. package/src/Pagination/Pagination.jsx +6 -1
  145. package/src/Radio/Radio.jsx +7 -3
  146. package/src/RadioCard/RadioCard.jsx +7 -3
  147. package/src/Select/Select.jsx +1 -1
  148. package/src/Skeleton/Skeleton.jsx +25 -19
  149. package/src/Skeleton/useSkeletonNativeAnimation.js +3 -3
  150. package/src/Tabs/Tabs.jsx +19 -2
  151. package/src/Tags/Tags.jsx +3 -3
  152. package/src/TextInput/TextInput.jsx +4 -4
  153. package/src/ToggleSwitch/ToggleSwitch.jsx +3 -3
  154. package/src/ViewportProvider/useViewportListener.js +10 -5
  155. package/src/utils/hasOwnProperty.js +11 -0
  156. package/src/utils/props/a11yProps.js +107 -1
  157. package/src/utils/props/getPropSelector.js +45 -4
  158. package/src/utils/ssr.jsx +124 -0
  159. package/src/utils/useResponsiveProp.js +3 -3
  160. package/src/utils/withLinkRouter.jsx +1 -3
  161. package/src/utils/ssr.js +0 -35
@@ -56,11 +56,14 @@ const ExpandCollapseControl = forwardRef(
56
56
  ) => {
57
57
  const getTokens = useThemeTokensCallback('ExpandCollapseControl', tokens, variant)
58
58
 
59
- const selectedProps = selectProps({ ...rest, accessibilityRole })
60
- selectedProps.accessibilityState = {
61
- ...(selectedProps.accessibilityState || {}),
62
- expanded: isExpanded
63
- }
59
+ const selectedProps = selectProps({
60
+ accessibilityRole,
61
+ ...rest,
62
+ accessibilityState: {
63
+ ...(rest.accessibilityState || {}),
64
+ expanded: isExpanded
65
+ }
66
+ })
64
67
 
65
68
  const getControlState = ({ pressed, hovered, focused }) => ({
66
69
  pressed,
@@ -60,11 +60,13 @@ const ExpandCollapsePanel = forwardRef(
60
60
  const [containerHeight, setContainerHeight] = useState(null)
61
61
  const isExpanded = openIds.includes(panelId)
62
62
 
63
- const selectedProps = selectProps(rest)
64
- selectedProps.accessibilityState = {
65
- ...(selectedProps.accessibilityState || {}),
66
- expanded: isExpanded
67
- }
63
+ const selectedProps = selectProps({
64
+ ...rest,
65
+ accessibilityState: {
66
+ ...(rest.accessibilityState || {}),
67
+ expanded: isExpanded
68
+ }
69
+ })
68
70
 
69
71
  const themeTokens = useThemeTokens('ExpandCollapsePanel', tokens, variant, {
70
72
  expanded: isExpanded
@@ -1,204 +1,25 @@
1
1
  import React, { forwardRef } from 'react'
2
- import { View, Platform, StyleSheet } from 'react-native'
3
- import PropTypes from 'prop-types'
4
- import { useTheme, useThemeTokens, applyTextStyles } from '../ThemeProvider'
5
- import {
6
- a11yProps,
7
- getTokensPropType,
8
- selectSystemProps,
9
- variantProp,
10
- viewProps,
11
- wrapStringsInText
12
- } from '../utils'
13
2
 
14
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
15
-
16
- const selectBulletStyles = ({ itemBulletWidth, itemBulletHeight, itemBulletColor }) => ({
17
- width: itemBulletWidth,
18
- height: itemBulletHeight,
19
- borderRadius: itemBulletHeight / 2,
20
- backgroundColor: itemBulletColor
21
- })
22
-
23
- const selectBulletContainerStyles = ({ itemBulletContainerWidth, itemBulletContainerAlign }) => ({
24
- width: itemBulletContainerWidth,
25
- alignItems: itemBulletContainerAlign
26
- })
27
-
28
- const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
29
- size: itemIconSize,
30
- color: itemIconColor
31
- })
32
-
33
- const selectSideItemContainerStyles = ({ listGutter, iconMarginTop }) => ({
34
- marginTop: iconMarginTop,
35
- marginRight: listGutter
36
- })
37
-
38
- // Align bullets with the top line of text the same way icons are aligned
39
- const selectBulletPositioningStyles = ({ itemIconSize }) => ({
40
- width: itemIconSize,
41
- height: itemIconSize
42
- })
43
-
44
- const selectItemStyles = (
45
- { itemFontWeight, itemFontSize, itemLineHeight, itemFontName },
46
- themeOptions
47
- ) =>
48
- applyTextStyles({
49
- fontWeight: itemFontWeight,
50
- fontSize: itemFontSize,
51
- lineHeight: itemLineHeight,
52
- fontName: itemFontName,
53
- themeOptions
54
- })
55
-
56
- const selectItemBlockStyles = ({ interItemMargin }) => ({
57
- marginBottom: interItemMargin
58
- })
59
-
60
- const selectDividerStyles = ({ dividerColor, dividerSize, interItemMarginWithDivider }) => ({
61
- borderBottomWidth: dividerSize,
62
- borderColor: dividerColor,
63
- marginBottom: interItemMarginWithDivider,
64
- paddingBottom: interItemMarginWithDivider
65
- })
3
+ import ListItemBase from './ListItemBase'
4
+ import { useThemeTokens } from '../ThemeProvider'
5
+ import { variantProp } from '../utils'
66
6
 
67
7
  /**
68
8
  * ListItem is responsible for rendering icon or a bullet as side item
69
9
  */
70
- const ListItem = forwardRef(
71
- (
72
- {
73
- tokens,
74
- variant,
75
- icon,
76
- iconColor,
77
- iconSize,
78
- showDivider,
79
- children,
80
- isLastItem,
81
- accessibilityRole = Platform.select({ web: 'listitem', default: undefined }),
82
- ...rest
83
- },
84
- ref
85
- ) => {
86
- const themeTokens = useThemeTokens('List', tokens, variant)
87
- const { themeOptions } = useTheme()
88
-
89
- const itemStyles = selectItemStyles(themeTokens, themeOptions)
90
- const itemBlockStyles = selectItemBlockStyles(themeTokens)
91
- const dividerStyles = selectDividerStyles(themeTokens)
92
- const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
93
- const itemBulletStyles = selectBulletStyles(themeTokens)
94
- const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens)
95
- const iconTokens = selectItemIconTokens(themeTokens)
96
- const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
97
-
98
- const renderItem = () => (
99
- <View style={staticStyles.wrap}>{wrapStringsInText(children, { style: itemStyles })}</View>
100
- )
101
-
102
- /**
103
- * Function responsible returning styling, in case the item is the last shouldn't
104
- * add extra margin on the bottom, if "showDivider" is true it should add a divider
105
- * and custom margin and padding, otherwise just adds a margin to the bottom
106
- */
107
- const getContainerStyle = () => {
108
- if (isLastItem) {
109
- return undefined
110
- }
111
-
112
- if (showDivider) {
113
- return dividerStyles
114
- }
115
-
116
- return itemBlockStyles
117
- }
118
-
119
- /**
120
- * Renders item bullet or Icon in case it's defined
121
- * in case children are string the icon is centered otherwise
122
- * it will align itself at start of the container
123
- */
124
- const renderMarker = () => {
125
- const IconComponent = icon || <></>
126
-
127
- if (icon) {
128
- return (
129
- <View style={sideItemContainerStyles}>
130
- <IconComponent
131
- size={iconSize || iconTokens.size}
132
- color={iconColor || iconTokens.color}
133
- />
134
- </View>
135
- )
136
- }
137
-
138
- return (
139
- <View style={[sideItemContainerStyles, itemBulletContainerStyles]}>
140
- <View style={[staticStyles.bulletPositioning, itemBulletPositioningStyles]}>
141
- <View style={itemBulletStyles} testID="unordered-item-bullet" />
142
- </View>
143
- </View>
144
- )
145
- }
146
-
147
- return (
148
- <View
149
- ref={ref}
150
- style={[staticStyles.itemContainer, getContainerStyle()]}
151
- accessibilityRole={accessibilityRole}
152
- {...selectProps(rest)}
153
- >
154
- {renderMarker()}
155
- {renderItem()}
156
- </View>
157
- )
158
- }
159
- )
160
- ListItem.displayName = 'ListItem'
161
-
162
- const staticStyles = StyleSheet.create({
163
- itemContainer: {
164
- flexDirection: 'row'
165
- },
166
- wrap: {
167
- flex: 1
168
- },
169
- bulletPositioning: {
170
- alignItems: 'center',
171
- justifyContent: 'center'
172
- }
10
+ const ListItem = forwardRef(({ tokens, variant, children, ...listItemProps }, ref) => {
11
+ const themeTokens = useThemeTokens('List', tokens, variant)
12
+ return (
13
+ <ListItemBase tokens={themeTokens} ref={ref} {...listItemProps}>
14
+ {children}
15
+ </ListItemBase>
16
+ )
173
17
  })
18
+ ListItem.displayName = 'ListItem'
174
19
 
175
20
  ListItem.propTypes = {
176
- ...selectedSystemPropTypes,
177
- tokens: getTokensPropType('List'),
178
21
  variant: variantProp.propType,
179
- children: PropTypes.node.isRequired,
180
- /**
181
- * Renders side item icon
182
- */
183
- icon: PropTypes.elementType,
184
- /**
185
- * Will set display icon color
186
- */
187
- iconColor: PropTypes.string,
188
- /**
189
- * Allow the user define the icon size if not defined the theme's file
190
- */
191
- iconSize: PropTypes.number,
192
- /**
193
- * @ignore
194
- * Defined by parent if it's last item on the list
195
- */
196
- isLastItem: PropTypes.bool,
197
- /**
198
- * @ignore
199
- * In case it is not the last item allow display divider
200
- */
201
- showDivider: PropTypes.bool
22
+ ...ListItemBase.propTypes
202
23
  }
203
24
 
204
25
  export default ListItem
@@ -0,0 +1,118 @@
1
+ import React, { forwardRef } from 'react'
2
+ import { View, Platform, StyleSheet } from 'react-native'
3
+ import PropTypes from 'prop-types'
4
+ import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
5
+
6
+ import ListItemContent from './ListItemContent'
7
+ import ListItemMark from './ListItemMark'
8
+
9
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
10
+
11
+ const selectItemBlockStyles = ({ interItemMargin }) => ({
12
+ marginBottom: interItemMargin
13
+ })
14
+
15
+ const selectDividerStyles = ({ dividerColor, dividerSize, interItemMarginWithDivider }) => ({
16
+ borderBottomWidth: dividerSize,
17
+ borderColor: dividerColor,
18
+ marginBottom: interItemMarginWithDivider,
19
+ paddingBottom: interItemMarginWithDivider
20
+ })
21
+
22
+ /**
23
+ * ListItem is responsible for rendering icon or a bullet as side item
24
+ */
25
+ const ListItemBase = forwardRef(
26
+ (
27
+ {
28
+ tokens,
29
+ icon,
30
+ iconColor,
31
+ iconSize,
32
+ showDivider,
33
+ children,
34
+ isLastItem,
35
+ accessibilityRole = Platform.select({ web: 'listitem', default: undefined }),
36
+ ...rest
37
+ },
38
+ ref
39
+ ) => {
40
+ const themeTokens = typeof tokens === 'function' ? tokens() : tokens
41
+
42
+ const itemBlockStyles = selectItemBlockStyles(themeTokens)
43
+ const dividerStyles = selectDividerStyles(themeTokens)
44
+
45
+ /**
46
+ * Function responsible returning styling, in case the item is the last shouldn't
47
+ * add extra margin on the bottom, if "showDivider" is true it should add a divider
48
+ * and custom margin and padding, otherwise just adds a margin to the bottom
49
+ */
50
+ const getContainerStyle = () => {
51
+ if (isLastItem) {
52
+ return undefined
53
+ }
54
+
55
+ if (showDivider) {
56
+ return dividerStyles
57
+ }
58
+
59
+ return itemBlockStyles
60
+ }
61
+
62
+ return (
63
+ <View
64
+ ref={ref}
65
+ style={[staticStyles.itemContainer, getContainerStyle()]}
66
+ accessibilityRole={accessibilityRole}
67
+ {...selectProps(rest)}
68
+ >
69
+ {typeof children === 'function' ? (
70
+ children({ tokens, icon, iconColor, iconSize, isLastItem })
71
+ ) : (
72
+ <>
73
+ <ListItemMark tokens={tokens} icon={icon} iconColor={iconColor} iconSize={iconSize} />
74
+ <ListItemContent tokens={tokens}>{children}</ListItemContent>
75
+ </>
76
+ )}
77
+ </View>
78
+ )
79
+ }
80
+ )
81
+ ListItemBase.displayName = 'ListItem'
82
+
83
+ const staticStyles = StyleSheet.create({
84
+ itemContainer: {
85
+ flexDirection: 'row'
86
+ }
87
+ })
88
+
89
+ ListItemBase.propTypes = {
90
+ ...selectedSystemPropTypes,
91
+ tokens: getTokensPropType('List'),
92
+ variant: variantProp.propType,
93
+ children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
94
+ /**
95
+ * Renders side item icon
96
+ */
97
+ icon: PropTypes.elementType,
98
+ /**
99
+ * Will set display icon color
100
+ */
101
+ iconColor: PropTypes.string,
102
+ /**
103
+ * Allow the user define the icon size if not defined the theme's file
104
+ */
105
+ iconSize: PropTypes.number,
106
+ /**
107
+ * @ignore
108
+ * Defined by parent if it's last item on the list
109
+ */
110
+ isLastItem: PropTypes.bool,
111
+ /**
112
+ * @ignore
113
+ * In case it is not the last item allow display divider
114
+ */
115
+ showDivider: PropTypes.bool
116
+ }
117
+
118
+ export default ListItemBase
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { View, StyleSheet } from 'react-native'
5
+ import { wrapStringsInText } from '../utils'
6
+ import { useTheme, applyTextStyles } from '../ThemeProvider'
7
+
8
+ export const tokenTypes = {
9
+ itemFontWeight: PropTypes.string.isRequired,
10
+ itemFontSize: PropTypes.number.isRequired,
11
+ itemLineHeight: PropTypes.number.isRequired,
12
+ itemFontName: PropTypes.string.isRequired
13
+ }
14
+
15
+ const selectItemTextStyles = (
16
+ { itemFontWeight, itemFontSize, itemLineHeight, itemFontName },
17
+ themeOptions
18
+ ) =>
19
+ applyTextStyles({
20
+ fontWeight: itemFontWeight,
21
+ fontSize: itemFontSize,
22
+ lineHeight: itemLineHeight,
23
+ fontName: itemFontName,
24
+ themeOptions
25
+ })
26
+
27
+ /**
28
+ * Subcomponent used within ListItem and similar components for rendering content that fills
29
+ * and wraps available space in a { flexDirection: row } container alongside a ListIconMark,
30
+ * and that applies text styles to strings via supplied tokens.
31
+ *
32
+ * It's the responsibility of themes to make sure that these text tokens align the first line of
33
+ * text nicely against the bullet or icon rendered by ListIconMark.
34
+ */
35
+ const ListItemContent = ({ tokens, children }) => {
36
+ const { themeOptions } = useTheme()
37
+ const textStyles = selectItemTextStyles(tokens, themeOptions)
38
+ return <View style={staticStyles.wrap}>{wrapStringsInText(children, { style: textStyles })}</View>
39
+ }
40
+
41
+ const staticStyles = StyleSheet.create({
42
+ wrap: {
43
+ flex: 1
44
+ }
45
+ })
46
+
47
+ ListItemContent.propTypes = {
48
+ tokens: PropTypes.shape(tokenTypes).isRequired,
49
+ children: PropTypes.node.isRequired
50
+ }
51
+
52
+ export default ListItemContent
@@ -0,0 +1,99 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { View, StyleSheet } from 'react-native'
5
+
6
+ export const tokenTypes = {
7
+ itemIconSize: PropTypes.number.isRequired,
8
+ itemIconColor: PropTypes.string.isRequired,
9
+ listGutter: PropTypes.number.isRequired,
10
+ iconMarginTop: PropTypes.number.isRequired
11
+ }
12
+
13
+ const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
14
+ size: itemIconSize,
15
+ color: itemIconColor
16
+ })
17
+
18
+ const selectSideItemContainerStyles = ({ listGutter, iconMarginTop }) => ({
19
+ marginTop: iconMarginTop,
20
+ marginRight: listGutter
21
+ })
22
+
23
+ // Align bullets with the top line of text the same way icons are aligned
24
+ const selectBulletPositioningStyles = ({ itemIconSize }) => ({
25
+ width: itemIconSize,
26
+ height: itemIconSize
27
+ })
28
+
29
+ const selectBulletStyles = ({ itemBulletWidth, itemBulletHeight, itemBulletColor }) => ({
30
+ width: itemBulletWidth,
31
+ height: itemBulletHeight,
32
+ borderRadius: itemBulletHeight / 2,
33
+ backgroundColor: itemBulletColor
34
+ })
35
+
36
+ const selectBulletContainerStyles = ({ itemBulletContainerWidth, itemBulletContainerAlign }) => ({
37
+ width: itemBulletContainerWidth,
38
+ alignItems: itemBulletContainerAlign
39
+ })
40
+
41
+ /**
42
+ * Subcomponent used within ListItem and similar components for rendering bullets or icons
43
+ * that sit alongside a ListIconContent in a { flexDirection: row } container.
44
+ *
45
+ * It's the responsibility of themes to make sure that the supplied tokens align the
46
+ * icon or bullet nicely against the first line of text in a ListIconContent.
47
+ */
48
+ const ListItemMark = ({ icon, iconColor, iconSize, tokens = {} }) => {
49
+ const IconComponent = icon || <></>
50
+
51
+ const themeTokens = typeof tokens === 'function' ? tokens() : tokens
52
+
53
+ const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
54
+
55
+ if (icon) {
56
+ const iconTokens = selectItemIconTokens(themeTokens)
57
+ return (
58
+ <View style={sideItemContainerStyles}>
59
+ <IconComponent size={iconSize || iconTokens.size} color={iconColor || iconTokens.color} />
60
+ </View>
61
+ )
62
+ }
63
+
64
+ const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
65
+ const itemBulletStyles = selectBulletStyles(themeTokens)
66
+ const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens)
67
+ return (
68
+ <View style={[sideItemContainerStyles, itemBulletContainerStyles]}>
69
+ <View style={[staticStyles.bulletPositioning, itemBulletPositioningStyles]}>
70
+ <View style={itemBulletStyles} testID="unordered-item-bullet" />
71
+ </View>
72
+ </View>
73
+ )
74
+ }
75
+
76
+ ListItemMark.propTypes = {
77
+ tokens: PropTypes.shape(tokenTypes).isRequired,
78
+ /**
79
+ * Renders side item icon
80
+ */
81
+ icon: PropTypes.elementType,
82
+ /**
83
+ * Will set display icon color
84
+ */
85
+ iconColor: PropTypes.string,
86
+ /**
87
+ * Allow the user define the icon size
88
+ */
89
+ iconSize: PropTypes.number
90
+ }
91
+
92
+ const staticStyles = StyleSheet.create({
93
+ bulletPositioning: {
94
+ alignItems: 'center',
95
+ justifyContent: 'center'
96
+ }
97
+ })
98
+
99
+ export default ListItemMark
@@ -0,0 +1,102 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import ABBPropTypes from 'airbnb-prop-types'
4
+
5
+ import { Pressable, StyleSheet, Platform } from 'react-native'
6
+
7
+ import {
8
+ resolvePressableTokens,
9
+ clickProps,
10
+ linkProps,
11
+ hrefAttrsProp,
12
+ withLinkRouter
13
+ } from '../utils'
14
+
15
+ import ListItemBase from './ListItemBase'
16
+ import ListItemContent, { tokenTypes as contentTokenTypes } from './ListItemContent'
17
+ import ListItemMark, { tokenTypes as markTokenTypes } from './ListItemMark'
18
+
19
+ const selectPressableStyles = ({
20
+ backgroundColor,
21
+ paddingLeft,
22
+ paddingRight,
23
+ paddingTop,
24
+ paddingBottom,
25
+ interItemMargin
26
+ }) => ({
27
+ backgroundColor,
28
+ paddingLeft,
29
+ paddingRight,
30
+ paddingTop,
31
+ paddingBottom,
32
+ marginBottom: interItemMargin,
33
+ ...Platform.select({ web: { outline: 'none' } })
34
+ })
35
+
36
+ const PressableListItemBase = forwardRef(
37
+ ({ href, tokens, icon, children, listItemRef, ...rest }, ref) => {
38
+ const { onPress, ...props } = clickProps.toPressProps(rest)
39
+ const { hrefAttrs, rest: listItemProps } = hrefAttrsProp.bundle(props)
40
+ const handlePress = linkProps.handleHref({ href, onPress })
41
+
42
+ return (
43
+ <ListItemBase ref={listItemRef} tokens={tokens()} {...listItemProps}>
44
+ {({ isLastItem }) => {
45
+ const getTokens = (pressableState) =>
46
+ resolvePressableTokens(tokens, pressableState, { last: isLastItem })
47
+ const getPressableStyle = (pressableState) => [
48
+ staticStyles.itemContainer,
49
+ selectPressableStyles(getTokens(pressableState))
50
+ ]
51
+ return (
52
+ <Pressable
53
+ onPress={handlePress}
54
+ href={href}
55
+ hrefAttrs={hrefAttrs}
56
+ style={getPressableStyle}
57
+ ref={ref}
58
+ >
59
+ {(pressableState) => {
60
+ const themeTokens = getTokens(pressableState)
61
+ return (
62
+ <>
63
+ <ListItemMark icon={icon} tokens={themeTokens} />
64
+ <ListItemContent tokens={themeTokens}>
65
+ {typeof children === 'function' ? children(pressableState) : children}
66
+ </ListItemContent>
67
+ </>
68
+ )
69
+ }}
70
+ </Pressable>
71
+ )
72
+ }}
73
+ </ListItemBase>
74
+ )
75
+ }
76
+ )
77
+
78
+ PressableListItemBase.displayName = 'PressableListItemBase'
79
+
80
+ const staticStyles = StyleSheet.create({
81
+ itemContainer: {
82
+ flexDirection: 'row',
83
+ flex: 1
84
+ },
85
+ tokens: {
86
+ ...contentTokenTypes,
87
+ ...markTokenTypes
88
+ }
89
+ })
90
+
91
+ PressableListItemBase.propTypes = {
92
+ href: PropTypes.string,
93
+ onPress: PropTypes.func,
94
+ // TODO - type this better, maybe import the subcomponent token types and run it through util
95
+ // eslint-disable-next-line react/forbid-prop-types
96
+ tokens: PropTypes.any,
97
+ icon: PropTypes.elementType,
98
+ children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
99
+ listItemRef: ABBPropTypes.ref()
100
+ }
101
+
102
+ export default withLinkRouter(PressableListItemBase)
@@ -112,7 +112,7 @@ const Notification = forwardRef(
112
112
  const textStyles = selectTextStyles(themeTokens, themeOptions)
113
113
 
114
114
  const content = wrapStringsInText(
115
- typeof children === 'function' ? children({ textStyles }) : children,
115
+ typeof children === 'function' ? children({ textStyles, variant }) : children,
116
116
  { style: textStyles }
117
117
  )
118
118
 
@@ -103,12 +103,17 @@ const Pagination = forwardRef(
103
103
  label={buttonLabel}
104
104
  copy={copy}
105
105
  isActive={isItemActive(itemIndex)}
106
+ key={buttonLabel}
106
107
  />
107
108
  )
108
109
  }
109
110
 
110
111
  if (shouldRenderEllipsis(itemIndex)) {
111
- return <Text style={ellipsisTextStyles}>...</Text>
112
+ return (
113
+ <Text key="..." style={ellipsisTextStyles}>
114
+ ...
115
+ </Text>
116
+ )
112
117
  }
113
118
 
114
119
  return null
@@ -151,15 +151,19 @@ const Radio = forwardRef(
151
151
  const uniqueId = useUniqueId('radio')
152
152
  const inputId = id ?? uniqueId
153
153
 
154
+ const selectedProps = selectProps({
155
+ accessibilityRole: 'radio',
156
+ accessibilityState: { checked: isChecked, disabled: inactive },
157
+ ...rest
158
+ })
159
+
154
160
  return (
155
161
  <Pressable
156
162
  ref={ref}
157
163
  disabled={inactive}
158
164
  onKeyDown={handleKeyDown}
159
165
  onPress={handleChange}
160
- accessibilityRole="radio"
161
- accessibilityState={{ checked: isChecked, disabled: inactive }}
162
- {...selectProps(rest)}
166
+ {...selectedProps}
163
167
  >
164
168
  {({ focused: focus, hovered: hover, pressed }) => {
165
169
  const stateTokens = getTokens({ focus, hover, pressed })