@telus-uds/components-base 0.0.2-prerelease.6 → 0.0.2-prerelease.7

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 (155) hide show
  1. package/.ultra.cache.json +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/__fixtures__/testTheme.js +424 -37
  4. package/__tests__/Button/ButtonBase.test.jsx +2 -31
  5. package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
  6. package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
  7. package/__tests__/List/List.test.jsx +60 -0
  8. package/__tests__/Radio/Radio.test.jsx +87 -0
  9. package/__tests__/Select/Select.test.jsx +93 -0
  10. package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
  11. package/__tests__/Tags/Tags.test.jsx +328 -0
  12. package/__tests__/TextInput/TextArea.test.jsx +34 -0
  13. package/__tests__/TextInput/{TextInput.test.jsx → TextInputBase.test.jsx} +20 -46
  14. package/jest.config.js +3 -1
  15. package/lib/Button/Button.js +10 -3
  16. package/lib/Button/ButtonBase.js +73 -59
  17. package/lib/Button/ButtonGroup.js +11 -27
  18. package/lib/Button/ButtonLink.js +5 -0
  19. package/lib/Checkbox/Checkbox.js +308 -0
  20. package/lib/Checkbox/CheckboxInput.native.js +6 -0
  21. package/lib/Checkbox/CheckboxInput.web.js +57 -0
  22. package/lib/Checkbox/index.js +2 -0
  23. package/lib/Feedback/Feedback.js +20 -3
  24. package/lib/Icon/Icon.js +8 -5
  25. package/lib/Icon/IconText.js +72 -0
  26. package/lib/Icon/index.js +2 -1
  27. package/lib/InputSupports/InputSupports.js +90 -0
  28. package/lib/InputSupports/index.js +2 -0
  29. package/lib/InputSupports/propTypes.js +55 -0
  30. package/lib/Link/ChevronLink.js +23 -20
  31. package/lib/Link/InlinePressable.native.js +78 -0
  32. package/lib/Link/InlinePressable.web.js +32 -0
  33. package/lib/Link/Link.js +11 -10
  34. package/lib/Link/LinkBase.js +62 -123
  35. package/lib/Link/TextButton.js +20 -9
  36. package/lib/Link/index.js +2 -1
  37. package/lib/List/List.js +52 -0
  38. package/lib/List/ListItem.js +207 -0
  39. package/lib/List/index.js +2 -0
  40. package/lib/Pagination/PageButton.js +2 -25
  41. package/lib/Pagination/SideButton.js +27 -37
  42. package/lib/Radio/Radio.js +291 -0
  43. package/lib/Radio/RadioInput.native.js +6 -0
  44. package/lib/Radio/RadioInput.web.js +59 -0
  45. package/lib/Radio/index.js +2 -0
  46. package/lib/Select/Group.native.js +14 -0
  47. package/lib/Select/Group.web.js +18 -0
  48. package/lib/Select/Item.native.js +9 -0
  49. package/lib/Select/Item.web.js +15 -0
  50. package/lib/Select/Picker.native.js +87 -0
  51. package/lib/Select/Picker.web.js +63 -0
  52. package/lib/Select/Select.js +272 -0
  53. package/lib/Select/index.js +6 -0
  54. package/lib/Skeleton/Skeleton.js +119 -0
  55. package/lib/Skeleton/index.js +2 -0
  56. package/lib/Tags/Tags.js +217 -0
  57. package/lib/Tags/index.js +2 -0
  58. package/lib/TextInput/TextArea.js +82 -0
  59. package/lib/TextInput/TextInput.js +23 -304
  60. package/lib/TextInput/TextInputBase.js +229 -0
  61. package/lib/TextInput/index.js +2 -1
  62. package/lib/TextInput/propTypes.js +31 -0
  63. package/lib/ThemeProvider/useThemeTokens.js +54 -3
  64. package/lib/ToggleSwitch/ToggleSwitch.js +1 -1
  65. package/lib/Typography/Typography.js +4 -19
  66. package/lib/index.js +8 -1
  67. package/lib/utils/a11y/index.js +1 -0
  68. package/lib/utils/a11y/textSize.js +33 -0
  69. package/lib/utils/index.js +3 -0
  70. package/lib/utils/info/index.js +7 -0
  71. package/lib/utils/info/platform/index.js +11 -0
  72. package/lib/utils/info/platform/platform.android.js +1 -0
  73. package/lib/utils/info/platform/platform.ios.js +1 -0
  74. package/lib/utils/info/platform/platform.native.js +4 -0
  75. package/lib/utils/info/platform/platform.web.js +1 -0
  76. package/lib/utils/info/versions.js +5 -0
  77. package/lib/utils/pressability.js +92 -0
  78. package/lib/utils/propTypes.js +78 -17
  79. package/package.json +5 -4
  80. package/release-context.json +4 -4
  81. package/src/Button/Button.jsx +6 -3
  82. package/src/Button/ButtonBase.jsx +66 -57
  83. package/src/Button/ButtonGroup.jsx +9 -22
  84. package/src/Button/ButtonLink.jsx +11 -2
  85. package/src/Checkbox/Checkbox.jsx +275 -0
  86. package/src/Checkbox/CheckboxInput.native.jsx +6 -0
  87. package/src/Checkbox/CheckboxInput.web.jsx +55 -0
  88. package/src/Checkbox/index.js +3 -0
  89. package/src/Feedback/Feedback.jsx +13 -4
  90. package/src/Icon/Icon.jsx +9 -5
  91. package/src/Icon/IconText.jsx +63 -0
  92. package/src/Icon/index.js +2 -1
  93. package/src/InputSupports/InputSupports.jsx +86 -0
  94. package/src/InputSupports/index.js +3 -0
  95. package/src/InputSupports/propTypes.js +44 -0
  96. package/src/Link/ChevronLink.jsx +20 -17
  97. package/src/Link/InlinePressable.native.jsx +73 -0
  98. package/src/Link/InlinePressable.web.jsx +37 -0
  99. package/src/Link/Link.jsx +17 -13
  100. package/src/Link/LinkBase.jsx +57 -140
  101. package/src/Link/TextButton.jsx +25 -11
  102. package/src/Link/index.js +2 -1
  103. package/src/List/List.jsx +47 -0
  104. package/src/List/ListItem.jsx +187 -0
  105. package/src/List/index.js +3 -0
  106. package/src/Pagination/PageButton.jsx +2 -16
  107. package/src/Pagination/SideButton.jsx +23 -34
  108. package/src/Radio/Radio.jsx +270 -0
  109. package/src/Radio/RadioInput.native.jsx +6 -0
  110. package/src/Radio/RadioInput.web.jsx +57 -0
  111. package/src/Radio/index.js +3 -0
  112. package/src/Select/Group.native.jsx +14 -0
  113. package/src/Select/Group.web.jsx +15 -0
  114. package/src/Select/Item.native.jsx +10 -0
  115. package/src/Select/Item.web.jsx +11 -0
  116. package/src/Select/Picker.native.jsx +95 -0
  117. package/src/Select/Picker.web.jsx +67 -0
  118. package/src/Select/Select.jsx +265 -0
  119. package/src/Select/index.js +8 -0
  120. package/src/Skeleton/Skeleton.jsx +101 -0
  121. package/src/Skeleton/index.js +3 -0
  122. package/src/Tags/Tags.jsx +206 -0
  123. package/src/Tags/index.js +3 -0
  124. package/src/TextInput/TextArea.jsx +78 -0
  125. package/src/TextInput/TextInput.jsx +17 -284
  126. package/src/TextInput/TextInputBase.jsx +220 -0
  127. package/src/TextInput/index.js +2 -1
  128. package/src/TextInput/propTypes.js +29 -0
  129. package/src/ThemeProvider/useThemeTokens.js +54 -3
  130. package/src/ToggleSwitch/ToggleSwitch.jsx +1 -1
  131. package/src/Typography/Typography.jsx +4 -15
  132. package/src/index.js +8 -1
  133. package/src/utils/a11y/index.js +1 -0
  134. package/src/utils/a11y/textSize.js +30 -0
  135. package/src/utils/index.js +3 -0
  136. package/src/utils/info/index.js +8 -0
  137. package/src/utils/info/platform/index.js +11 -0
  138. package/src/utils/info/platform/platform.android.js +1 -0
  139. package/src/utils/info/platform/platform.ios.js +1 -0
  140. package/src/utils/info/platform/platform.native.js +4 -0
  141. package/src/utils/info/platform/platform.web.js +1 -0
  142. package/src/utils/info/versions.js +6 -0
  143. package/src/utils/pressability.js +92 -0
  144. package/src/utils/propTypes.js +97 -22
  145. package/stories/Button/Button.stories.jsx +5 -0
  146. package/stories/Checkbox/Checkbox.stories.jsx +71 -0
  147. package/stories/Feedback/Feedback.stories.jsx +5 -6
  148. package/stories/Link/Link.stories.jsx +15 -1
  149. package/stories/List/List.stories.jsx +117 -0
  150. package/stories/Radio/Radio.stories.jsx +113 -0
  151. package/stories/Select/Select.stories.jsx +55 -0
  152. package/stories/Skeleton/Skeleton.stories.jsx +36 -0
  153. package/stories/Tags/Tags.stories.jsx +69 -0
  154. package/stories/TextInput/TextArea.stories.jsx +100 -0
  155. package/stories/supports.jsx +1 -1
@@ -1,14 +1,6 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import {
4
- Text,
5
- Platform,
6
- StyleSheet,
7
- Pressable,
8
- PixelRatio,
9
- View,
10
- TouchableWithoutFeedback
11
- } from 'react-native'
3
+ import { Text, Platform, StyleSheet } from 'react-native'
12
4
  import {
13
5
  a11yProps,
14
6
  hrefAttrsProp,
@@ -16,12 +8,11 @@ import {
16
8
  linkProps,
17
9
  getTokensPropType
18
10
  } from '../utils/propTypes'
11
+ import { resolvePressableTokens } from '../utils/pressability'
19
12
 
20
- import { useThemeTokens, applyTextStyles } from '../ThemeProvider'
21
-
22
- const selectContentStyles = ({ color }) => ({
23
- color
24
- })
13
+ import InlinePressable from './InlinePressable'
14
+ import { applyTextStyles } from '../ThemeProvider'
15
+ import { IconText, iconComponentPropTypes } from '../Icon'
25
16
 
26
17
  const selectOuterBorderStyles = ({
27
18
  outerBorderColor,
@@ -47,9 +38,16 @@ const selectOuterBorderStyles = ({
47
38
  }
48
39
  : {}
49
40
 
50
- const selectTextStyles = ({ textLine, textLineStyle }) => ({
41
+ const selectTextStyles = ({ color, textLine, textLineStyle }) => ({
42
+ color,
51
43
  textDecorationLine: textLine,
52
- textDecorationStyle: textLineStyle
44
+ textDecorationStyle: textLineStyle,
45
+ ...Platform.select({
46
+ web: {
47
+ // TODO: https://github.com/telus/universal-design-system/issues/487
48
+ transition: 'color 200ms'
49
+ }
50
+ })
53
51
  })
54
52
 
55
53
  const selectBlockStyles = ({ blockFontWeight, blockFontSize, blockLineHeight, blockFontName }) =>
@@ -60,20 +58,11 @@ const selectBlockStyles = ({ blockFontWeight, blockFontSize, blockLineHeight, bl
60
58
  fontName: blockFontName
61
59
  })
62
60
 
63
- const selectIconStyles = ({
64
- iconSize,
65
- iconGapBefore,
66
- iconGapAfter,
67
- iconScale,
68
- iconTranslateX,
69
- iconTranslateY
70
- }) => ({
71
- scale: iconScale,
61
+ const selectIconTokens = ({ color, iconSize, iconTranslateX, iconTranslateY }) => ({
62
+ color,
72
63
  translateX: iconTranslateX,
73
64
  translateY: iconTranslateY,
74
- size: iconSize,
75
- gapBefore: iconGapBefore,
76
- gapAfter: iconGapAfter
65
+ size: iconSize
77
66
  })
78
67
 
79
68
  /**
@@ -103,136 +92,61 @@ const LinkBase = ({
103
92
  onPress,
104
93
  icon,
105
94
  iconPosition = icon ? 'left' : undefined,
106
- iconVariant,
95
+ iconProps,
107
96
  variant,
108
- tokens,
97
+ tokens = {},
109
98
  children,
110
99
  accessibilityRole = 'link',
111
100
  ...props
112
101
  }) => {
113
- const handlePress = linkProps.handleHref({ href, onPress })
114
-
115
- const [isFocused, setIsFocused] = useState(false)
116
- const handleFocus = () => setIsFocused(true)
117
- const handleBlur = () => setIsFocused(false)
118
-
119
- const [isHovered, setIsHovered] = useState(false)
120
- const handleHoverIn = () => setIsHovered(true)
121
- const handleHoverOut = () => setIsHovered(false)
122
-
123
- const [isPressed, setIsPressed] = useState(false)
124
- const handlePressIn = () => setIsPressed(true)
125
- const handlePressOut = () => setIsPressed(false)
126
-
127
102
  const { hrefAttrs, rest } = hrefAttrsProp.bundle(props)
128
103
  const linkPropSet = linkProps.select({
129
104
  accessibilityRole,
130
105
  href,
131
- onPress: handlePress,
132
- onPressIn: handlePressIn,
133
- onPressOut: handlePressOut,
134
- onFocus: handleFocus,
135
- onBlur: handleBlur,
136
- onMouseEnter: handleHoverIn,
137
- onMouseLeave: handleHoverOut,
106
+ onPress: linkProps.handleHref({ href, onPress }),
138
107
  ...hrefAttrsProp.spread(hrefAttrs),
139
108
  ...rest
140
109
  })
141
110
 
142
- const themeTokens = useThemeTokens('Link', tokens, variant, {
143
- focus: isFocused,
144
- hover: isHovered,
145
- pressed: isPressed,
146
- iconPosition
147
- })
148
- const IconComponent = icon || themeTokens.icon
149
- const hasIcon = !!IconComponent
150
-
151
- const textStyles = selectTextStyles(themeTokens)
152
- const outerBorderStyles = selectOuterBorderStyles(themeTokens)
153
- const contentStyles = selectContentStyles(themeTokens)
154
- const blockStyles = selectBlockStyles(themeTokens)
155
- const iconStyles = selectIconStyles(themeTokens)
156
-
157
- // TODO: re-apply support for inline links
158
- const isNested = false
111
+ const resolveTokens = (pressState) => resolvePressableTokens(tokens, pressState, { iconPosition })
159
112
 
160
113
  // On web, this makes focus rings wrap only the link, not the entire block
161
114
  const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft
162
115
 
163
- // Apply typographic text styles if not inheriting, and block positioning if a block
164
- const blockTextStyles = !isNested && blockStyles
165
-
166
- if (!hasIcon) {
167
- // onPressIn / onPressOut only work if on TouchableWithoutFeedback, onFocus only works if on Text
168
- const { onPress: _onPress, onPressIn, onPressOut, ...textProps } = linkPropSet
169
- return (
170
- // TouchableWithoutFeedback modifies its child so this stack is all (touchable) Text: no Views
171
- <TouchableWithoutFeedback onPressIn={onPressIn} onPressOut={onPressOut} onPress={_onPress}>
172
- <Text
173
- {...textProps}
174
- style={[outerBorderStyles, blockLeftStyle, contentStyles, blockTextStyles, textStyles]}
175
- >
176
- {children}
177
- </Text>
178
- </TouchableWithoutFeedback>
179
- )
180
- }
181
-
182
- // Scale icon with text, but with a cap so text isn't squashed at large scales
183
- const iconScale = Math.min(PixelRatio.getFontScale(), 2)
184
-
185
- const iconTokens = {
186
- size: iconStyles.size ? iconStyles.size * iconScale : undefined,
187
- color: contentStyles.color ?? undefined,
188
- scale: iconStyles.scale ?? undefined,
189
- translateX: iconStyles.translateX ?? undefined,
190
- translateY: iconStyles.translateY ?? undefined
191
- }
192
-
193
- const iconContent = <IconComponent tokens={iconTokens} variant={iconVariant} />
194
-
195
- const iconBlock = isNested ? (
196
- // TODO: when reimplementing inline support, insert an appropriate space character
197
- // based on the value of gapBefore and gapAfter on native (no inline margins/padding)
198
- iconContent
199
- ) : (
200
- <View
201
- style={[
202
- iconPosition === 'left' && { marginRight: iconStyles.gapAfter * iconScale },
203
- iconPosition === 'right' && { marginLeft: iconStyles.gapBefore * iconScale }
204
- ]}
205
- >
206
- {iconContent}
207
- </View>
208
- )
209
-
210
- // TODO: this doesn't work well when nested inline but does allow block-links to have icons align
211
- // properly on Android which purely inline links can't do. Add an isNested case that is similar to
212
- // https://github.com/telus/universal-design-system/pull/233
213
116
  return (
214
- <Pressable
117
+ <InlinePressable
215
118
  {...linkPropSet}
216
- style={[outerBorderStyles, blockLeftStyle, staticStyles.rowContainer]}
119
+ style={(linkState) => {
120
+ const themeTokens = resolveTokens(linkState)
121
+ const outerBorderStyles = selectOuterBorderStyles(themeTokens)
122
+ const hasIcon = Boolean(icon || themeTokens.icon)
123
+ return [outerBorderStyles, blockLeftStyle, hasIcon && staticStyles.rowContainer]
124
+ }}
217
125
  >
218
- {iconPosition === 'left' && iconBlock}
219
- <Text
220
- style={[
221
- contentStyles,
222
- blockTextStyles,
223
- textStyles,
224
- Platform.select({
225
- web: {
226
- // TODO: https://github.com/telus/universal-design-system/issues/487
227
- transition: 'color 200ms'
228
- }
229
- })
230
- ]}
231
- >
232
- {children}
233
- </Text>
234
- {iconPosition === 'right' && iconBlock}
235
- </Pressable>
126
+ {(linkState) => {
127
+ const themeTokens = resolveTokens(linkState)
128
+ const textStyles = selectTextStyles(themeTokens)
129
+ const iconTokens = selectIconTokens(themeTokens)
130
+
131
+ // TODO: may need to apply some smarter text inheritance here if inline to avoid native
132
+ // issues like double-application of line heights breaking align-items: baseline
133
+ const blockTextStyles = selectBlockStyles(themeTokens)
134
+
135
+ const IconComponent = icon || themeTokens.icon
136
+ const { iconSpace } = themeTokens
137
+
138
+ return (
139
+ <IconText
140
+ icon={IconComponent}
141
+ iconPosition={iconPosition}
142
+ space={iconSpace}
143
+ iconProps={{ ...iconProps, tokens: iconTokens }}
144
+ >
145
+ <Text style={[textStyles, blockTextStyles, staticStyles.baseline]}>{children}</Text>
146
+ </IconText>
147
+ )
148
+ }}
149
+ </InlinePressable>
236
150
  )
237
151
  }
238
152
 
@@ -244,7 +158,7 @@ LinkBase.propTypes = {
244
158
  /**
245
159
  * Optional variant that may be passed down to the link's icon if there is one
246
160
  */
247
- iconVariant: variantProp.propType,
161
+ iconProps: PropTypes.exact(iconComponentPropTypes),
248
162
  /**
249
163
  * A function component for an SVG icon to render inside the link. Inherits size and color from
250
164
  * the link and any Typography the link is nested inside.
@@ -269,6 +183,9 @@ const staticStyles = StyleSheet.create({
269
183
  flexDirection: 'row',
270
184
  alignItems: 'center',
271
185
  justifyContent: 'flex-start'
186
+ },
187
+ baseline: {
188
+ alignSelf: 'baseline'
272
189
  }
273
190
  })
274
191
 
@@ -1,24 +1,36 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
+ import { useThemeTokensCallback } from '../ThemeProvider'
4
5
  import LinkBase from './LinkBase'
5
6
 
7
+ /**
8
+ * `TextButton` is a button that looks like a Link. It uses the same theming and variants as
9
+ * Link but has the accessibility role of a `Button`. It should be used for actions that
10
+ * take place on the current page, or for navigation within an app.
11
+ */
6
12
  const TextButton = ({
7
13
  onPress,
8
14
  children,
9
- variant = {},
15
+ variant,
16
+ tokens,
17
+ // TODO: this may need to use `link` role on Web in the case of being passed both `href` and
18
+ // `onPress` in an omniplatform app that uses React Navigation's useLinkProps for internal nav.
10
19
  accessibilityRole = 'button',
11
20
  ...linkProps
12
- }) => (
13
- <LinkBase
14
- onPress={onPress}
15
- accessibilityRole={accessibilityRole}
16
- variant={{ component: 'TextButton', ...variant }}
17
- {...linkProps}
18
- >
19
- {children}
20
- </LinkBase>
21
- )
21
+ }) => {
22
+ const getTokens = useThemeTokensCallback('Link', tokens, variant)
23
+ return (
24
+ <LinkBase
25
+ onPress={onPress}
26
+ accessibilityRole={accessibilityRole}
27
+ tokens={getTokens}
28
+ {...linkProps}
29
+ >
30
+ {children}
31
+ </LinkBase>
32
+ )
33
+ }
22
34
 
23
35
  TextButton.propTypes = {
24
36
  ...LinkBase.propTypes,
@@ -26,6 +38,8 @@ TextButton.propTypes = {
26
38
  }
27
39
 
28
40
  // Remove incompatible Link prop (if this build includes propTypes)
41
+ // TODO: test if this works with web navigation in omniplatform React Navigation
42
+ // https://github.com/telus/universal-design-system/issues/665
29
43
  // eslint-disable-next-line react/forbid-foreign-prop-types
30
44
  if (TextButton.propTypes?.href) delete TextButton.propTypes.href
31
45
 
package/src/Link/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import ChevronLink from './ChevronLink'
2
2
  import Link from './Link'
3
+ import LinkBase from './LinkBase'
3
4
  import TextButton from './TextButton'
4
5
 
5
- export { ChevronLink, Link, TextButton }
6
+ export { ChevronLink, Link, LinkBase, TextButton }
@@ -0,0 +1,47 @@
1
+ import React, { cloneElement, Children } from 'react'
2
+ import { View, Platform } from 'react-native'
3
+ import PropTypes from 'prop-types'
4
+ import { getTokensPropType, variantProp, componentPropType } from '../utils'
5
+ import { a11yProps } from '../utils/propTypes'
6
+ import ListItem from './ListItem'
7
+
8
+ /**
9
+ * A Unordered List component has a child a ListItem that
10
+ * allows icon, dividers and customized typography
11
+ */
12
+ const List = ({ children, showDivider, tokens, variant, ...rest }) => {
13
+ const accessibilityRole = Platform.select({ web: 'list', default: 'none' })
14
+ const a11y = a11yProps.select(rest)
15
+ const items = Children.map(children, (child, index) => {
16
+ if (child.type.name === ListItem.name) {
17
+ return cloneElement(child, {
18
+ showDivider,
19
+ isLastItem: index + 1 === Children.count(children),
20
+ tokens,
21
+ variant
22
+ })
23
+ }
24
+ return child
25
+ })
26
+
27
+ return (
28
+ <View accessibilityRole={accessibilityRole} {...a11y}>
29
+ {items}
30
+ </View>
31
+ )
32
+ }
33
+
34
+ List.Item = ListItem
35
+
36
+ List.propTypes = {
37
+ ...a11yProps.types,
38
+ tokens: getTokensPropType('List'),
39
+ variant: variantProp.propType,
40
+ children: componentPropType('ListItem'),
41
+ /**
42
+ * In case it is not the last item allow display divider
43
+ */
44
+ showDivider: PropTypes.bool
45
+ }
46
+
47
+ export default List
@@ -0,0 +1,187 @@
1
+ import React from 'react'
2
+ import { View, Platform, Text, StyleSheet } from 'react-native'
3
+ import PropTypes from 'prop-types'
4
+ import { useThemeTokens, applyTextStyles } from '../ThemeProvider'
5
+ import { getTokensPropType, variantProp } from '../utils'
6
+
7
+ const selectBulletStyles = ({ itemBulletWidth, itemBulletHeight, itemBulletColor }) => ({
8
+ width: itemBulletWidth,
9
+ height: itemBulletHeight,
10
+ borderRadius: itemBulletHeight / 2,
11
+ backgroundColor: itemBulletColor
12
+ })
13
+
14
+ const selectBulletContainerStyles = ({ itemBulletContainerWidth, itemBulletContainerAlign }) => ({
15
+ width: itemBulletContainerWidth,
16
+ alignItems: itemBulletContainerAlign,
17
+ justifyContent: itemBulletContainerAlign
18
+ })
19
+
20
+ const selectItemIconTokens = ({ itemIconSize, itemIconColor }) => ({
21
+ size: itemIconSize,
22
+ color: itemIconColor
23
+ })
24
+
25
+ const selectSideItemContainerStyles = ({ listGutter }) => ({
26
+ marginRight: listGutter
27
+ })
28
+
29
+ const selectItemStyles = ({ itemFontWeight, itemFontSize, itemLineHeight, itemFontName }) =>
30
+ applyTextStyles({
31
+ fontWeight: itemFontWeight,
32
+ fontSize: itemFontSize,
33
+ lineHeight: itemLineHeight,
34
+ fontName: itemFontName
35
+ })
36
+
37
+ const selectItemBlockStyles = ({ interItemMargin }) => ({
38
+ marginBottom: interItemMargin
39
+ })
40
+
41
+ const selectDividerStyles = ({ dividerColor, dividerSize, interItemMarginWithDivider }) => ({
42
+ borderBottomWidth: dividerSize,
43
+ borderColor: dividerColor,
44
+ marginBottom: interItemMarginWithDivider,
45
+ paddingBottom: interItemMarginWithDivider
46
+ })
47
+
48
+ /**
49
+ * ListItem is responsible for rendering icon or a bullet as side item
50
+ */
51
+ const ListItem = ({
52
+ tokens,
53
+ variant,
54
+ icon,
55
+ iconColor,
56
+ iconSize,
57
+ showDivider,
58
+ children,
59
+ isLastItem
60
+ }) => {
61
+ const themeTokens = useThemeTokens('List', tokens, variant)
62
+
63
+ const itemStyles = selectItemStyles(themeTokens)
64
+ const itemBlockStyles = selectItemBlockStyles(themeTokens)
65
+ const dividerStyles = selectDividerStyles(themeTokens)
66
+ const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
67
+ const itemBulletStyles = selectBulletStyles(themeTokens)
68
+ const iconTokens = selectItemIconTokens(themeTokens)
69
+ const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
70
+ const accessibilityRole = Platform.select({ web: 'listitem', default: 'item' })
71
+
72
+ const areChildrenStrings = () => {
73
+ if (Array.isArray(children)) {
74
+ return children.every((child) => typeof child === 'string')
75
+ }
76
+
77
+ return typeof children === 'string'
78
+ }
79
+
80
+ const renderItem = () => {
81
+ if (areChildrenStrings()) {
82
+ return <Text style={itemStyles}>{children}</Text>
83
+ }
84
+
85
+ return <View>{children}</View>
86
+ }
87
+
88
+ /**
89
+ * Function responsible returning styling, in case the item is the last shouldn't
90
+ * add extra margin on the bottom, if "showDivider" is true it should add a divider
91
+ * and custom margin and padding, otherwise just adds a margin to the bottom
92
+ */
93
+ const getContainerStyle = () => {
94
+ if (isLastItem) {
95
+ return undefined
96
+ }
97
+
98
+ if (showDivider) {
99
+ return dividerStyles
100
+ }
101
+
102
+ return itemBlockStyles
103
+ }
104
+
105
+ /**
106
+ * Renders item bullet or Icon in case it's defined
107
+ * in case children are string the icon is centered otherwise
108
+ * it will align itself at start of the container
109
+ */
110
+ const renderMarker = () => {
111
+ const IconComponent = icon || <></>
112
+
113
+ if (icon) {
114
+ return (
115
+ <View
116
+ style={[
117
+ sideItemContainerStyles,
118
+ areChildrenStrings() ? staticStyles.centeredIcons : undefined
119
+ ]}
120
+ >
121
+ <IconComponent
122
+ tokens={{
123
+ ...iconTokens,
124
+ size: iconSize || iconTokens.size,
125
+ color: iconColor || iconTokens.color
126
+ }}
127
+ />
128
+ </View>
129
+ )
130
+ }
131
+
132
+ return (
133
+ <View style={[sideItemContainerStyles, itemBulletContainerStyles]}>
134
+ <View style={itemBulletStyles} testID="unordered-item-bullet" />
135
+ </View>
136
+ )
137
+ }
138
+
139
+ return (
140
+ <View
141
+ style={[staticStyles.itemContainer, getContainerStyle()]}
142
+ accessibilityRole={accessibilityRole}
143
+ >
144
+ {renderMarker()}
145
+ {renderItem()}
146
+ </View>
147
+ )
148
+ }
149
+
150
+ const staticStyles = StyleSheet.create({
151
+ itemContainer: {
152
+ flexDirection: 'row'
153
+ },
154
+ centeredIcons: {
155
+ justifyContent: 'center'
156
+ }
157
+ })
158
+
159
+ ListItem.propTypes = {
160
+ tokens: getTokensPropType('List'),
161
+ variant: variantProp.propType,
162
+ children: PropTypes.node.isRequired,
163
+ /**
164
+ * Renders side item icon
165
+ */
166
+ icon: PropTypes.func,
167
+ /**
168
+ * Will set display icon color
169
+ */
170
+ iconColor: PropTypes.string,
171
+ /**
172
+ * Allow the user define the icon size if not defined the theme's file
173
+ */
174
+ iconSize: PropTypes.number,
175
+ /**
176
+ * @ignore
177
+ * Defined by parent if it's last item on the list
178
+ */
179
+ isLastItem: PropTypes.bool,
180
+ /**
181
+ * @ignore
182
+ * In case it is not the last item allow display divider
183
+ */
184
+ showDivider: PropTypes.bool
185
+ }
186
+
187
+ export default ListItem
@@ -0,0 +1,3 @@
1
+ import List from './List'
2
+
3
+ export default List
@@ -1,9 +1,8 @@
1
1
  import React from 'react'
2
- import { Text } from 'react-native'
3
2
  import PropTypes from 'prop-types'
4
3
 
5
4
  import ButtonBase from '../Button/ButtonBase'
6
- import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
5
+ import { useThemeTokensCallback } from '../ThemeProvider'
7
6
  import {
8
7
  a11yProps,
9
8
  copyPropTypes,
@@ -17,23 +16,12 @@ import {
17
16
  import useCopy from '../utils/useCopy'
18
17
  import dictionary from './dictionary'
19
18
 
20
- const selectTextStyles = ({ color, fontName, fontSize, fontWeight, lineHeight, textLine }) =>
21
- applyTextStyles({
22
- color,
23
- fontName,
24
- fontSize,
25
- fontWeight,
26
- lineHeight,
27
- textDecorationLine: textLine
28
- })
29
-
30
19
  function PageButton({ label, onPress, href, isActive, copy, variant, tokens, ...props }) {
31
20
  const getTokens = useThemeTokensCallback('PaginationPageButton', tokens, variant)
32
21
 
33
22
  const getCopy = useCopy({ dictionary, copy })
34
23
 
35
24
  const getButtonTokens = (buttonState) => selectTokens('Button', getTokens(buttonState))
36
- const getTextStyles = (buttonState) => selectTextStyles(getTokens(buttonState))
37
25
 
38
26
  const activeProps = isActive
39
27
  ? {
@@ -61,9 +49,7 @@ function PageButton({ label, onPress, href, isActive, copy, variant, tokens, ...
61
49
 
62
50
  return (
63
51
  <ButtonBase {...buttonProps} tokens={getButtonTokens} {...activeProps}>
64
- {(buttonState) => {
65
- return <Text style={getTextStyles(buttonState)}>{label}</Text>
66
- }}
52
+ {label}
67
53
  </ButtonBase>
68
54
  )
69
55
  }