@telus-uds/components-base 1.11.0 → 1.13.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 (132) hide show
  1. package/CHANGELOG.md +36 -2
  2. package/component-docs.json +796 -33
  3. package/lib/BaseProvider/index.js +7 -2
  4. package/lib/Button/ButtonBase.js +18 -14
  5. package/lib/Button/ButtonGroup.js +7 -0
  6. package/lib/Carousel/Carousel.js +83 -58
  7. package/lib/Carousel/CarouselContext.js +22 -8
  8. package/lib/Carousel/CarouselFirstFocus/CarouselFirstFocus.js +73 -0
  9. package/lib/Carousel/CarouselStepTracker/CarouselStepTracker.js +56 -0
  10. package/lib/Carousel/CarouselStepTracker/index.js +13 -0
  11. package/lib/Carousel/CarouselTabs/CarouselTabs.js +70 -0
  12. package/lib/Carousel/CarouselTabs/CarouselTabsPanel.js +95 -0
  13. package/lib/Carousel/CarouselTabs/CarouselTabsPanelItem.js +148 -0
  14. package/lib/Carousel/CarouselTabs/index.js +13 -0
  15. package/lib/Carousel/CarouselThumbnail.js +99 -0
  16. package/lib/Carousel/CarouselThumbnailNavigation.js +87 -0
  17. package/lib/Carousel/dictionary.js +4 -2
  18. package/lib/Carousel/index.js +10 -1
  19. package/lib/Checkbox/Checkbox.js +7 -3
  20. package/lib/Checkbox/CheckboxGroup.js +8 -1
  21. package/lib/Feedback/Feedback.js +18 -10
  22. package/lib/Icon/IconText.js +5 -0
  23. package/lib/InputLabel/InputLabel.js +11 -5
  24. package/lib/Link/InlinePressable.js +1 -8
  25. package/lib/Link/LinkBase.js +12 -9
  26. package/lib/List/ListItem.js +7 -3
  27. package/lib/Notification/Notification.js +7 -2
  28. package/lib/Pagination/Pagination.js +7 -3
  29. package/lib/Radio/RadioGroup.js +8 -0
  30. package/lib/RadioCard/RadioCard.js +6 -1
  31. package/lib/RadioCard/RadioCardGroup.js +7 -0
  32. package/lib/Select/Select.js +7 -3
  33. package/lib/SkipLink/SkipLink.js +216 -0
  34. package/lib/SkipLink/index.js +13 -0
  35. package/lib/StepTracker/Step.js +8 -4
  36. package/lib/StepTracker/StepTracker.js +7 -3
  37. package/lib/Tabs/TabsItem.js +4 -0
  38. package/lib/TextInput/TextInputBase.js +7 -3
  39. package/lib/ThemeProvider/ThemeProvider.js +25 -3
  40. package/lib/ThemeProvider/utils/styles.js +8 -1
  41. package/lib/ThemeProvider/utils/theme-tokens.js +1 -1
  42. package/lib/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  43. package/lib/Typography/Typography.js +6 -2
  44. package/lib/index.js +9 -0
  45. package/lib/utils/index.js +9 -0
  46. package/lib-module/BaseProvider/index.js +7 -2
  47. package/lib-module/Button/ButtonBase.js +7 -3
  48. package/lib-module/Button/ButtonGroup.js +7 -0
  49. package/lib-module/Carousel/Carousel.js +80 -57
  50. package/lib-module/Carousel/CarouselContext.js +21 -8
  51. package/lib-module/Carousel/CarouselFirstFocus/CarouselFirstFocus.js +51 -0
  52. package/lib-module/Carousel/CarouselStepTracker/CarouselStepTracker.js +42 -0
  53. package/lib-module/Carousel/CarouselStepTracker/index.js +2 -0
  54. package/lib-module/Carousel/CarouselTabs/CarouselTabs.js +50 -0
  55. package/lib-module/Carousel/CarouselTabs/CarouselTabsPanel.js +76 -0
  56. package/lib-module/Carousel/CarouselTabs/CarouselTabsPanelItem.js +126 -0
  57. package/lib-module/Carousel/CarouselTabs/index.js +2 -0
  58. package/lib-module/Carousel/CarouselThumbnail.js +85 -0
  59. package/lib-module/Carousel/CarouselThumbnailNavigation.js +66 -0
  60. package/lib-module/Carousel/dictionary.js +4 -2
  61. package/lib-module/Carousel/index.js +2 -1
  62. package/lib-module/Checkbox/Checkbox.js +8 -4
  63. package/lib-module/Checkbox/CheckboxGroup.js +8 -1
  64. package/lib-module/Feedback/Feedback.js +19 -11
  65. package/lib-module/Icon/IconText.js +5 -0
  66. package/lib-module/InputLabel/InputLabel.js +12 -6
  67. package/lib-module/Link/InlinePressable.js +1 -8
  68. package/lib-module/Link/LinkBase.js +13 -10
  69. package/lib-module/List/ListItem.js +8 -4
  70. package/lib-module/Notification/Notification.js +8 -3
  71. package/lib-module/Pagination/Pagination.js +8 -4
  72. package/lib-module/Radio/RadioGroup.js +8 -0
  73. package/lib-module/RadioCard/RadioCard.js +7 -2
  74. package/lib-module/RadioCard/RadioCardGroup.js +7 -0
  75. package/lib-module/Select/Select.js +8 -4
  76. package/lib-module/SkipLink/SkipLink.js +188 -0
  77. package/lib-module/SkipLink/index.js +2 -0
  78. package/lib-module/StepTracker/Step.js +9 -5
  79. package/lib-module/StepTracker/StepTracker.js +8 -4
  80. package/lib-module/Tabs/TabsItem.js +5 -1
  81. package/lib-module/TextInput/TextInputBase.js +8 -4
  82. package/lib-module/ThemeProvider/ThemeProvider.js +24 -3
  83. package/lib-module/ThemeProvider/utils/styles.js +8 -1
  84. package/lib-module/ThemeProvider/utils/theme-tokens.js +1 -1
  85. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +7 -0
  86. package/lib-module/Typography/Typography.js +7 -3
  87. package/lib-module/index.js +1 -0
  88. package/lib-module/utils/index.js +1 -0
  89. package/package.json +46 -47
  90. package/src/BaseProvider/index.jsx +6 -3
  91. package/src/Button/ButtonBase.jsx +8 -3
  92. package/src/Button/ButtonGroup.jsx +6 -0
  93. package/src/Carousel/Carousel.jsx +91 -64
  94. package/src/Carousel/CarouselContext.jsx +29 -5
  95. package/src/Carousel/CarouselFirstFocus/CarouselFirstFocus.jsx +49 -0
  96. package/src/Carousel/CarouselStepTracker/CarouselStepTracker.jsx +36 -0
  97. package/src/Carousel/CarouselStepTracker/index.js +3 -0
  98. package/src/Carousel/CarouselTabs/CarouselTabs.jsx +37 -0
  99. package/src/Carousel/CarouselTabs/CarouselTabsPanel.jsx +69 -0
  100. package/src/Carousel/CarouselTabs/CarouselTabsPanelItem.jsx +119 -0
  101. package/src/Carousel/CarouselTabs/index.js +3 -0
  102. package/src/Carousel/CarouselThumbnail.jsx +77 -0
  103. package/src/Carousel/CarouselThumbnailNavigation.jsx +53 -0
  104. package/src/Carousel/dictionary.js +4 -2
  105. package/src/Carousel/index.js +1 -0
  106. package/src/Checkbox/Checkbox.jsx +14 -11
  107. package/src/Checkbox/CheckboxGroup.jsx +8 -1
  108. package/src/Feedback/Feedback.jsx +14 -7
  109. package/src/Icon/IconText.jsx +2 -0
  110. package/src/InputLabel/InputLabel.jsx +13 -12
  111. package/src/Link/InlinePressable.jsx +2 -8
  112. package/src/Link/LinkBase.jsx +17 -20
  113. package/src/List/ListItem.jsx +9 -4
  114. package/src/Notification/Notification.jsx +5 -3
  115. package/src/Pagination/Pagination.jsx +6 -4
  116. package/src/Radio/RadioGroup.jsx +7 -0
  117. package/src/RadioCard/RadioCard.jsx +3 -2
  118. package/src/RadioCard/RadioCardGroup.jsx +6 -0
  119. package/src/Select/Select.jsx +12 -3
  120. package/src/SkipLink/SkipLink.jsx +179 -0
  121. package/src/SkipLink/index.js +3 -0
  122. package/src/StepTracker/Step.jsx +12 -4
  123. package/src/StepTracker/StepTracker.jsx +11 -10
  124. package/src/Tabs/TabsItem.jsx +3 -2
  125. package/src/TextInput/TextInputBase.jsx +11 -3
  126. package/src/ThemeProvider/ThemeProvider.jsx +22 -3
  127. package/src/ThemeProvider/utils/styles.js +9 -1
  128. package/src/ThemeProvider/utils/theme-tokens.js +1 -1
  129. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +6 -0
  130. package/src/Typography/Typography.jsx +11 -12
  131. package/src/index.js +1 -0
  132. package/src/utils/index.js +1 -0
@@ -0,0 +1,69 @@
1
+ import React, { forwardRef, useRef } from 'react'
2
+ import { View } from 'react-native'
3
+
4
+ import PropTypes from 'prop-types'
5
+ import StackView from '../../StackView'
6
+
7
+ import { useCarousel } from '../CarouselContext'
8
+ import CarouselTabsPanelItem from './CarouselTabsPanelItem'
9
+
10
+ const CarouselTabsPanel = forwardRef(({ items }, ref) => {
11
+ const { activeIndex, goTo } = useCarousel()
12
+ const nextFocusRef = useRef()
13
+ const firstTabRef = useRef()
14
+
15
+ // TODO: figure out a better cross-brand way to specify subcomponent variants.
16
+ // For now, this picks an Allium variant, and does nothing in brands that lack it.
17
+ // See similar comment in Carousel and https://github.com/telus/universal-design-system/issues/1549
18
+ const dividerVariant = { decorative: true }
19
+
20
+ const lastTabSelected = activeIndex === items.length - 1
21
+
22
+ return (
23
+ <>
24
+ <View
25
+ focusable
26
+ accessible
27
+ onFocus={(event) => {
28
+ // When user forward-tabs into this section, focus the next tab; if they backwards-tab
29
+ // (shift-tab) back into the carousel content, don't interfere.
30
+ const previousWebFocus = event.relatedTarget
31
+ if (previousWebFocus !== firstTabRef.current) nextFocusRef.current.focus()
32
+ }}
33
+ />
34
+ <StackView direction="row" space={3} divider={{ variant: dividerVariant }} ref={ref}>
35
+ {items.map(({ title, onPress, ...panelItemProps }, index) => {
36
+ const selected = index === activeIndex
37
+ const isNext = index === activeIndex + 1
38
+
39
+ // Selected item should be always unfocusable and unpressable
40
+ const handlePress = selected
41
+ ? undefined
42
+ : (event) => {
43
+ if (typeof onPress === 'function') onPress(event, index)
44
+ goTo(index)
45
+ }
46
+
47
+ return (
48
+ <CarouselTabsPanelItem
49
+ ref={(isNext && nextFocusRef) || (index === 0 && firstTabRef) || null}
50
+ key={title}
51
+ title={title}
52
+ selected={selected}
53
+ onPress={handlePress}
54
+ {...panelItemProps}
55
+ />
56
+ )
57
+ })}
58
+ </StackView>
59
+ {/* TODO: integrate with skiplink, replace this with focusing skiplink target */}
60
+ <View focusable accessible ref={lastTabSelected ? nextFocusRef : null} />
61
+ </>
62
+ )
63
+ })
64
+ CarouselTabsPanel.displayName = 'CarouselTabsPanel'
65
+ CarouselTabsPanel.propTypes = {
66
+ items: PropTypes.arrayOf(PropTypes.shape(CarouselTabsPanelItem.propTypes || {}))
67
+ }
68
+
69
+ export default CarouselTabsPanel
@@ -0,0 +1,119 @@
1
+ import React, { forwardRef, useState } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Pressable, Text, Platform } from 'react-native'
4
+
5
+ import { applyTextStyles, useThemeTokensCallback } from '../../ThemeProvider'
6
+ import {
7
+ selectSystemProps,
8
+ a11yProps,
9
+ pressProps,
10
+ viewProps,
11
+ textProps,
12
+ getTokensPropType,
13
+ variantProp,
14
+ resolvePressableState
15
+ } from '../../utils'
16
+
17
+ const [selectPressProps, pressPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps])
18
+ const [selectTextProps, textPropTypes] = selectSystemProps([textProps])
19
+
20
+ const selectContainerStyles = ({
21
+ paddingLeft,
22
+ paddingRight,
23
+ paddingTop,
24
+ paddingBottom = 0,
25
+ borderBottomColor,
26
+ borderBottomWidth = 0,
27
+ borderBottomStyle,
28
+ flex,
29
+ alignItems,
30
+ justifyContent
31
+ }) => ({
32
+ paddingLeft,
33
+ paddingRight,
34
+ paddingTop,
35
+ paddingBottom: paddingBottom - borderBottomWidth,
36
+ borderBottomColor,
37
+ borderBottomWidth,
38
+ borderBottomStyle,
39
+ flex,
40
+ alignItems,
41
+ justifyContent,
42
+ ...Platform.select({
43
+ // Removes the default browser :focus outline
44
+ web: { outline: 'none' }
45
+ })
46
+ })
47
+
48
+ const selectTextStyles = ({
49
+ fontSize,
50
+ fontScaleCap,
51
+ lineHeight,
52
+ letterSpacing,
53
+ fontWeight,
54
+ fontName,
55
+ color
56
+ }) =>
57
+ applyTextStyles({
58
+ fontSize,
59
+ fontScaleCap,
60
+ lineHeight,
61
+ letterSpacing,
62
+ fontWeight,
63
+ fontName,
64
+ color
65
+ })
66
+
67
+ const CarouselTabsPanelItem = forwardRef(
68
+ ({ title, selected, inactive, variant, tokens, accessibilityRole = 'tab', ...rest }, ref) => {
69
+ // Workaround for React Native Web https://github.com/necolas/react-native-web/issues/2357
70
+ // Don't allow disabled to be set while focus is true else focus state gets locked `true`
71
+ // (must refocus _after_ calling `goTo`, else focus target content is not up to date)
72
+ const [isFocused, setIsFocused] = useState(false)
73
+ const disabled = (inactive || selected) && !isFocused
74
+
75
+ const getTokens = useThemeTokensCallback('CarouselTabsPanelItem', tokens, variant)
76
+ const resolveTokens = (pressState) => getTokens(resolvePressableState(pressState, { selected }))
77
+
78
+ const getContainerStyle = (pressState) => selectContainerStyles(resolveTokens(pressState))
79
+
80
+ const getTextStyle = (pressState) => selectTextStyles(resolveTokens(pressState))
81
+
82
+ const { onPress, ...selectedPressProps } = selectPressProps(rest)
83
+ const handleKeyDown = (event) => {
84
+ // Allow using the spacebar for navigation
85
+ if (event?.key === ' ') onPress(event)
86
+ }
87
+
88
+ return (
89
+ <Pressable
90
+ onPress={onPress}
91
+ style={getContainerStyle}
92
+ accessibilityRole={accessibilityRole}
93
+ ref={ref}
94
+ onKeyDown={handleKeyDown}
95
+ onFocus={() => setIsFocused(true)}
96
+ onBlur={() => setIsFocused(false)}
97
+ disabled={disabled}
98
+ {...selectedPressProps}
99
+ >
100
+ {(pressState) => (
101
+ <Text style={getTextStyle(pressState)} {...selectTextProps(rest)}>
102
+ {title}
103
+ </Text>
104
+ )}
105
+ </Pressable>
106
+ )
107
+ }
108
+ )
109
+ CarouselTabsPanelItem.displayName = 'CarouselTabsPanelItem'
110
+ CarouselTabsPanelItem.propTypes = {
111
+ ...pressPropTypes,
112
+ ...textPropTypes,
113
+ title: PropTypes.string.isRequired,
114
+ selected: PropTypes.bool,
115
+ tokens: getTokensPropType('CarouselTabsPanelItem'),
116
+ variant: variantProp.propType
117
+ }
118
+
119
+ export default CarouselTabsPanelItem
@@ -0,0 +1,3 @@
1
+ import CarouselTabs from './CarouselTabs'
2
+
3
+ export default CarouselTabs
@@ -0,0 +1,77 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Pressable, Image } from 'react-native'
4
+ import { useCarousel } from './CarouselContext'
5
+
6
+ /**
7
+ * `Carousel.Thumbnail` is used to wrap the content of an individual slide and is suppsoed to be the
8
+ * only top-level component passed to the `Carousel`
9
+ */
10
+ const CarouselThumbnail = ({ accessibilityLabel, alt, index, src }) => {
11
+ const { activeIndex, itemLabel, totalItems, getCopyWithPlaceholders, goTo, themeTokens } =
12
+ useCarousel()
13
+ const thumbnailTitle =
14
+ alt ??
15
+ getCopyWithPlaceholders('stepTrackerLabel')
16
+ .replace(/%\{itemLabel\}/g, itemLabel)
17
+ .replace(/%\{stepNumber\}/g, index)
18
+ .replace(/%\{stepCount\}/g, totalItems)
19
+ const handlePress = () => goTo(index)
20
+ const handleKeyDown = (event) => {
21
+ // Allow using the spacebar for navigation
22
+ if (event?.key === ' ') goTo(index)
23
+ }
24
+ const {
25
+ thumbnailBorderColor,
26
+ thumbnailBorderRadius,
27
+ thumbnailBorderWidth,
28
+ thumbnailMargin,
29
+ thumbnailPadding,
30
+ thumbnailSelectedBorderColor,
31
+ thumbnailSelectedBorderWidth,
32
+ thumbnailSize
33
+ } = themeTokens
34
+ const styles = {
35
+ pressable: {
36
+ borderColor: thumbnailBorderColor,
37
+ borderRadius: thumbnailBorderRadius,
38
+ borderWidth: thumbnailBorderWidth,
39
+ margin: thumbnailMargin,
40
+ padding: thumbnailPadding
41
+ },
42
+ image: {
43
+ height: thumbnailSize,
44
+ width: thumbnailSize
45
+ },
46
+ selected: {
47
+ borderColor: thumbnailSelectedBorderColor,
48
+ borderWidth: thumbnailSelectedBorderWidth,
49
+ padding: thumbnailPadding - thumbnailSelectedBorderWidth + thumbnailBorderWidth
50
+ }
51
+ }
52
+
53
+ return (
54
+ <Pressable
55
+ key={src}
56
+ onKeyDown={handleKeyDown}
57
+ onPress={handlePress}
58
+ style={[styles.pressable, index === activeIndex && styles.selected]}
59
+ >
60
+ <Image
61
+ accessibilityIgnoresInvertColors
62
+ accessibilityLabel={accessibilityLabel ?? alt}
63
+ source={src}
64
+ style={styles.image}
65
+ title={thumbnailTitle}
66
+ />
67
+ </Pressable>
68
+ )
69
+ }
70
+ CarouselThumbnail.propTypes = {
71
+ accessibilityLabel: PropTypes.string,
72
+ alt: PropTypes.string,
73
+ index: PropTypes.number,
74
+ src: PropTypes.string
75
+ }
76
+
77
+ export default CarouselThumbnail
@@ -0,0 +1,53 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { View } from 'react-native'
4
+ import { useCarousel } from './CarouselContext'
5
+ import CarouselThumbnail from './CarouselThumbnail'
6
+ import { StackWrap } from '../StackView'
7
+
8
+ const CarouselThumbnailNavigation = forwardRef(({ thumbnails = [] }, ref) => {
9
+ const { totalItems, themeTokens } = useCarousel()
10
+ if (thumbnails.length !== totalItems) {
11
+ throw new Error('Thumbnail set provided does not match the number of slides in the carousel')
12
+ }
13
+ const { thumbnailContainerPaddingTop, thumbnailMargin } = themeTokens
14
+ const stackWrapTokens = {
15
+ justifyContent: 'flex-start'
16
+ }
17
+ const containerStyles = {
18
+ justifyContent: 'center',
19
+ alignItems: 'center',
20
+ paddingTop: thumbnailContainerPaddingTop - thumbnailMargin
21
+ }
22
+
23
+ return (
24
+ <View style={containerStyles}>
25
+ <StackWrap direction="row" tokens={stackWrapTokens} ref={ref}>
26
+ {thumbnails.map(({ accessibilityLabel, alt, src }, index) => (
27
+ <CarouselThumbnail
28
+ accessibilityLabel={accessibilityLabel}
29
+ alt={alt}
30
+ index={index}
31
+ key={src}
32
+ src={src}
33
+ />
34
+ ))}
35
+ </StackWrap>
36
+ </View>
37
+ )
38
+ })
39
+ CarouselThumbnailNavigation.displayName = 'CarouselThumbnailNavigation'
40
+ CarouselThumbnailNavigation.propTypes = {
41
+ /**
42
+ * An array of objects containing information on the thumbnail images.
43
+ */
44
+ thumbnails: PropTypes.arrayOf(
45
+ PropTypes.shape({
46
+ accessibilityLabel: PropTypes.string,
47
+ alt: PropTypes.string,
48
+ src: PropTypes.string
49
+ })
50
+ ).isRequired
51
+ }
52
+
53
+ export default CarouselThumbnailNavigation
@@ -4,13 +4,15 @@ export default {
4
4
  carouselLabel: '%{stepCount} items',
5
5
  iconButtonLabel: 'Show %{itemLabel} %{targetStep} of %{stepCount}',
6
6
  stepLabel: '%{itemLabel} %{stepNumber}',
7
- stepTrackerLabel: '%{itemLabel} %{stepNumber} of %{stepCount}'
7
+ stepTrackerLabel: '%{itemLabel} %{stepNumber} of %{stepCount}',
8
+ skipLink: 'Skip %{title}'
8
9
  },
9
10
  fr: {
10
11
  // TODO: French translations here
11
12
  carouselLabel: '(fr) %{stepCount} items',
12
13
  iconButtonLabel: '(fr) Show %{itemLabel} %{targetStep} of %{stepCount}',
13
14
  stepLabel: '(fr) %{itemLabel} %{stepNumber}',
14
- stepTrackerLabel: '(fr) %{itemLabel} %{stepNumber} of %{stepCount}'
15
+ stepTrackerLabel: '(fr) %{itemLabel} %{stepNumber} of %{stepCount}',
16
+ skipLink: '(fr) Skip %{title}'
15
17
  }
16
18
  }
@@ -1,2 +1,3 @@
1
1
  export * from './CarouselContext'
2
2
  export { default as Carousel } from './Carousel'
3
+ export { default as CarouselTabs } from './CarouselTabs'
@@ -6,7 +6,12 @@ import CheckboxInput from './CheckboxInput'
6
6
  import CheckboxLabel from '../InputLabel/LabelContent'
7
7
  import Feedback from '../Feedback'
8
8
  import StackView from '../StackView'
9
- import { applyShadowToken, applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
9
+ import {
10
+ applyShadowToken,
11
+ applyTextStyles,
12
+ useTheme,
13
+ useThemeTokensCallback
14
+ } from '../ThemeProvider'
10
15
  import {
11
16
  a11yProps,
12
17
  focusHandlerProps,
@@ -54,21 +59,18 @@ const selectInputStyles = (
54
59
  }
55
60
  })
56
61
  })
57
- const selectLabelStyles = ({
58
- labelColor,
59
- labelFontName,
60
- labelFontSize,
61
- labelFontWeight,
62
- labelMarginLeft,
63
- labelLineHeight
64
- }) => ({
62
+ const selectLabelStyles = (
63
+ { labelColor, labelFontName, labelFontSize, labelFontWeight, labelMarginLeft, labelLineHeight },
64
+ themeOptions
65
+ ) => ({
65
66
  marginLeft: labelMarginLeft,
66
67
  ...applyTextStyles({
67
68
  color: labelColor,
68
69
  fontName: labelFontName,
69
70
  fontWeight: labelFontWeight,
70
71
  fontSize: labelFontSize,
71
- lineHeight: labelLineHeight
72
+ lineHeight: labelLineHeight,
73
+ themeOptions
72
74
  })
73
75
  })
74
76
  const selectIconTokens = ({ icon, iconColor, iconSize }) => ({
@@ -172,6 +174,7 @@ const Checkbox = forwardRef(
172
174
  }
173
175
  const uniqueId = useUniqueId('checkbox')
174
176
  const inputId = id ?? uniqueId
177
+ const { themeOptions } = useTheme()
175
178
 
176
179
  return (
177
180
  <View style={staticStyles.wrapper} ref={ref}>
@@ -187,7 +190,7 @@ const Checkbox = forwardRef(
187
190
  {({ focused: focus, hovered: hover, pressed }) => {
188
191
  const { icon: IconComponent, ...stateTokens } = getTokens({ focus, hover, pressed })
189
192
  const iconTokens = selectIconTokens(stateTokens)
190
- const labelStyles = selectLabelStyles(stateTokens)
193
+ const labelStyles = selectLabelStyles(stateTokens, themeOptions)
191
194
  const alignWithLabel = label
192
195
  ? [staticStyles.alignWithLabel, { height: labelStyles.lineHeight }]
193
196
  : null
@@ -61,7 +61,7 @@ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
61
61
  * @example
62
62
  * ```jsx
63
63
  * <CheckboxGroup
64
- * initialCheckedId="check1"
64
+ * initialCheckedIds="check1"
65
65
  * items={[
66
66
  * { label: 'Checkbox 1', id: 'check1' },
67
67
  * { label: 'Checkbox 2', id: 'check2' },
@@ -82,6 +82,8 @@ const CheckboxGroup = forwardRef(
82
82
  legend,
83
83
  tooltip,
84
84
  hint,
85
+ hintPosition = 'inline',
86
+
85
87
  validation,
86
88
  feedback,
87
89
  initialCheckedIds,
@@ -145,6 +147,7 @@ const CheckboxGroup = forwardRef(
145
147
  legend={legend}
146
148
  tooltip={tooltip}
147
149
  hint={hint}
150
+ hintPosition={hintPosition}
148
151
  space={fieldSpace}
149
152
  feedback={feedback}
150
153
  inactive={inactive}
@@ -192,6 +195,10 @@ CheckboxGroup.propTypes = {
192
195
  * Optional additional text giving more detail to help a user make a choice.
193
196
  */
194
197
  hint: PropTypes.string,
198
+ /**
199
+ * Position of the hint relative to label. Use `below` to display a larger hint below the label.
200
+ */
201
+ hintPosition: PropTypes.oneOf(['inline', 'below']),
195
202
  /**
196
203
  * Optional tooltip text content to include alongside the legend and hint.
197
204
  */
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
2
2
  import { StyleSheet, Text, View } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
- import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  getTokensPropType,
@@ -17,10 +17,16 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
17
17
 
18
18
  const selectStyles = (tokens) => selectTokens('Feedback', tokens)
19
19
 
20
- const selectTitleTextStyles = ({ titleFontSize, ...tokens }) =>
21
- applyTextStyles(selectTokens('Typography', { ...tokens, fontSize: titleFontSize }))
22
- const selectContentTextStyles = ({ contentFontSize, ...tokens }) =>
23
- applyTextStyles(selectTokens('Typography', { ...tokens, fontSize: contentFontSize }))
20
+ const selectTitleTextStyles = ({ titleFontSize, ...tokens }, themeOptions) =>
21
+ applyTextStyles({
22
+ ...selectTokens('Typography', { ...tokens, fontSize: titleFontSize, themeOptions }),
23
+ themeOptions
24
+ })
25
+ const selectContentTextStyles = ({ contentFontSize, ...tokens }, themeOptions) =>
26
+ applyTextStyles({
27
+ ...selectTokens('Typography', { ...tokens, fontSize: contentFontSize }),
28
+ themeOptions
29
+ })
24
30
 
25
31
  const selectIconTokens = ({ iconSize, iconColor }) => ({
26
32
  size: iconSize,
@@ -56,8 +62,9 @@ const Feedback = forwardRef(
56
62
 
57
63
  const { icon: IconComponent } = themeTokens
58
64
 
59
- const titleTextStyles = selectTitleTextStyles(themeTokens)
60
- const contentTextStyles = selectContentTextStyles(themeTokens)
65
+ const { themeOptions } = useTheme()
66
+ const titleTextStyles = selectTitleTextStyles(themeTokens, themeOptions)
67
+ const contentTextStyles = selectContentTextStyles(themeTokens, themeOptions)
61
68
 
62
69
  const content =
63
70
  typeof children === 'string' ? <Text style={contentTextStyles}>{children}</Text> : children
@@ -39,6 +39,7 @@ const IconText = forwardRef(
39
39
  IconText.displayName = 'IconText'
40
40
 
41
41
  IconText.propTypes = {
42
+ /* eslint-disable react/no-unused-prop-types */ // eslint is having hard time seeing these props through forwardRef
42
43
  /**
43
44
  * Amount of space to separate the text content and icon. Uses the themes's spacing scale
44
45
  * (see useSpacingScale for more info).
@@ -63,6 +64,7 @@ IconText.propTypes = {
63
64
  * `<Typography>` component, or a component that renders `<Text>`.
64
65
  */
65
66
  children: PropTypes.node
67
+ /* eslint-enable react/no-unused-prop-types */
66
68
  }
67
69
 
68
70
  export default IconText
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
2
2
  import { StyleSheet, Text, View } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
- import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  getTokensPropType,
@@ -17,21 +17,20 @@ import Tooltip from '../Tooltip'
17
17
 
18
18
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
19
19
 
20
- const selectLabelStyles = (tokens) => applyTextStyles(selectTokens('Typography', tokens))
20
+ const selectLabelStyles = (tokens, themeOptions) =>
21
+ applyTextStyles({ ...selectTokens('Typography', tokens), themeOptions })
21
22
 
22
- const selectHintStyles = ({
23
- hintColor,
24
- hintFontName,
25
- hintFontSize,
26
- hintFontWeight,
27
- hintLineHeight
28
- }) =>
23
+ const selectHintStyles = (
24
+ { hintColor, hintFontName, hintFontSize, hintFontWeight, hintLineHeight },
25
+ themeOptions
26
+ ) =>
29
27
  applyTextStyles({
30
28
  color: hintColor,
31
29
  fontName: hintFontName,
32
30
  fontSize: hintFontSize,
33
31
  fontWeight: hintFontWeight,
34
- lineHeight: hintLineHeight
32
+ lineHeight: hintLineHeight,
33
+ themeOptions
35
34
  })
36
35
 
37
36
  const selectGapStyles = ({ gap }) => ({ marginRight: gap })
@@ -57,12 +56,14 @@ const InputLabel = forwardRef(
57
56
  const hasTooltip = tooltip !== undefined
58
57
  const isHintInline = hintPosition === 'inline'
59
58
 
59
+ const { themeOptions } = useTheme()
60
+
60
61
  return (
61
62
  <>
62
63
  <View ref={ref} style={staticStyles.container} {...selectProps(rest)}>
63
64
  <Text
64
65
  style={[
65
- selectLabelStyles(themeTokens),
66
+ selectLabelStyles(themeTokens, themeOptions),
66
67
  selectGapStyles(themeTokens),
67
68
  staticStyles.label
68
69
  ]}
@@ -72,7 +73,7 @@ const InputLabel = forwardRef(
72
73
  {hint && isHintInline && (
73
74
  <Text
74
75
  style={[
75
- selectHintStyles(themeTokens),
76
+ selectHintStyles(themeTokens, themeOptions),
76
77
  hasTooltip && selectGapStyles(themeTokens),
77
78
  staticStyles.label
78
79
  ]}
@@ -9,17 +9,15 @@ import { Pressable, StyleSheet } from 'react-native'
9
9
  * InlinePressable is an alternative to React Native's Pressable that works better when nested
10
10
  * inline inside Text. It accepts the same props as React Native's Pressable.
11
11
  *
12
- * On Web it simply passes its props to Pressable and defaults to `inline-flex` instead of `flex`.
13
- *
14
12
  * @param {PressableProps} PressableProps
15
13
  */
16
14
  // React Native exports prop Types but not propTypes, use JSDoc types here rather than duplicate RN
17
15
  // eslint-disable-next-line react/prop-types
18
- const InlinePressable = forwardRef(({ children, style, inline = false, ...props }, ref) => (
16
+ const InlinePressable = forwardRef(({ children, style, ...props }, ref) => (
19
17
  <Pressable
20
18
  ref={ref}
21
19
  style={(pressState) => [
22
- staticStyles[inline ? 'inline' : 'inlineFlex'],
20
+ staticStyles.inline,
23
21
  typeof style === 'function' ? style(pressState) : style
24
22
  ]}
25
23
  {...props}
@@ -31,11 +29,7 @@ InlinePressable.displayName = 'InlinePressable'
31
29
 
32
30
  const staticStyles = StyleSheet.create({
33
31
  inline: {
34
- // Stop Pressable defaulting to (block) flex
35
32
  display: 'inline'
36
- },
37
- inlineFlex: {
38
- display: 'inline-flex'
39
33
  }
40
34
  })
41
35
 
@@ -15,18 +15,15 @@ import { resolvePressableTokens } from '../utils/pressability'
15
15
  import { withLinkRouter } from '../utils'
16
16
 
17
17
  import InlinePressable from './InlinePressable'
18
- import { applyTextStyles, applyOuterBorder } from '../ThemeProvider'
18
+ import { applyTextStyles, applyOuterBorder, useTheme } from '../ThemeProvider'
19
19
  import { IconText, iconComponentPropTypes } from '../Icon'
20
20
 
21
21
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, linkProps, viewProps])
22
22
 
23
- const selectOuterBorderStyles = ({
24
- outerBorderColor,
25
- outerBorderWidth,
26
- outerBorderGap,
27
- borderRadius,
28
- outerBorderOutline
29
- }) =>
23
+ const selectOuterBorderStyles = (
24
+ { outerBorderColor, outerBorderWidth, outerBorderGap, borderRadius, outerBorderOutline },
25
+ hasIcon
26
+ ) =>
30
27
  // A view wrapper with a border on native messes up inline text alignment
31
28
  // so for now make focus styles strictly web-only
32
29
  Platform.OS === 'web'
@@ -41,7 +38,7 @@ const selectOuterBorderStyles = ({
41
38
  }),
42
39
  // Stops focus ring stretching horizontally if parent has display: block
43
40
  // width: fit-content isn't supported on Firefox; can't cascade props like CSS `width: fit-content; width: --moz-fit-content;`
44
- display: 'inline-flex'
41
+ display: hasIcon ? 'inline-flex' : 'inline' // Stop Pressable defaulting to (block) flex
45
42
  }
46
43
  : {}
47
44
 
@@ -55,12 +52,16 @@ const selectTextStyles = ({ color }) => ({
55
52
  })
56
53
  })
57
54
 
58
- const selectBlockStyles = ({ blockFontWeight, blockFontSize, blockLineHeight, blockFontName }) =>
55
+ const selectBlockStyles = (
56
+ { blockFontWeight, blockFontSize, blockLineHeight, blockFontName },
57
+ themeOptions
58
+ ) =>
59
59
  applyTextStyles({
60
60
  fontWeight: blockFontWeight,
61
61
  fontSize: blockFontSize,
62
62
  lineHeight: blockLineHeight,
63
- fontName: blockFontName
63
+ fontName: blockFontName,
64
+ themeOptions
64
65
  })
65
66
 
66
67
  const selectDecorationStyles = ({ color, textLine, textLineStyle }) => ({
@@ -138,21 +139,17 @@ const LinkBase = forwardRef(
138
139
  // On web, this makes focus rings wrap only the link, not the entire block
139
140
  const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft
140
141
 
142
+ const { themeOptions } = useTheme()
143
+
141
144
  return (
142
145
  <InlinePressable
143
146
  {...selectedProps}
144
- inline={hasIcon} // assuming links without icons should be inline (even if they are long)
145
147
  ref={ref}
146
148
  style={(linkState) => {
147
149
  const themeTokens = resolveLinkTokens(linkState)
148
- const outerBorderStyles = selectOuterBorderStyles(themeTokens)
150
+ const outerBorderStyles = selectOuterBorderStyles(themeTokens, hasIcon)
149
151
  const decorationStyles = selectDecorationStyles(themeTokens)
150
- return [
151
- outerBorderStyles,
152
- blockLeftStyle,
153
- decorationStyles,
154
- hasIcon && staticStyles.rowContainer
155
- ]
152
+ return [outerBorderStyles, blockLeftStyle, decorationStyles, staticStyles.rowContainer]
156
153
  }}
157
154
  >
158
155
  {(linkState) => {
@@ -162,7 +159,7 @@ const LinkBase = forwardRef(
162
159
 
163
160
  // TODO: may need to apply some smarter text inheritance here if inline to avoid native
164
161
  // issues like double-application of line heights breaking align-items: baseline
165
- const blockTextStyles = selectBlockStyles(themeTokens)
162
+ const blockTextStyles = selectBlockStyles(themeTokens, themeOptions)
166
163
 
167
164
  const IconComponent = icon || themeTokens.icon
168
165
  const { iconSpace } = themeTokens