@telus-uds/components-base 1.10.0 → 1.12.1

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 (117) hide show
  1. package/CHANGELOG.md +37 -3
  2. package/component-docs.json +413 -62
  3. package/lib/BaseProvider/index.js +7 -2
  4. package/lib/Button/ButtonBase.js +18 -14
  5. package/lib/Carousel/Carousel.js +92 -71
  6. package/lib/Carousel/CarouselContext.js +12 -4
  7. package/lib/Carousel/CarouselItem/CarouselItem.js +24 -9
  8. package/lib/Carousel/CarouselStepTracker/CarouselStepTracker.js +56 -0
  9. package/lib/Carousel/CarouselStepTracker/index.js +13 -0
  10. package/lib/Carousel/dictionary.js +23 -0
  11. package/lib/Checkbox/Checkbox.js +7 -3
  12. package/lib/Checkbox/CheckboxGroup.js +1 -1
  13. package/lib/Feedback/Feedback.js +18 -10
  14. package/lib/Icon/IconText.js +5 -0
  15. package/lib/InputLabel/InputLabel.js +11 -5
  16. package/lib/InputSupports/InputSupports.js +10 -3
  17. package/lib/InputSupports/useInputSupports.js +3 -2
  18. package/lib/Link/LinkBase.js +7 -3
  19. package/lib/List/ListItem.js +7 -3
  20. package/lib/Modal/Modal.js +4 -0
  21. package/lib/Notification/Notification.js +7 -2
  22. package/lib/Pagination/Pagination.js +7 -3
  23. package/lib/RadioCard/RadioCard.js +6 -1
  24. package/lib/Select/Select.js +7 -3
  25. package/lib/Skeleton/Skeleton.js +1 -0
  26. package/lib/StepTracker/Step.js +8 -4
  27. package/lib/StepTracker/StepTracker.js +17 -13
  28. package/lib/Tabs/TabsItem.js +4 -0
  29. package/lib/TextInput/TextInput.js +3 -1
  30. package/lib/TextInput/TextInputBase.js +7 -3
  31. package/lib/ThemeProvider/ThemeProvider.js +20 -3
  32. package/lib/ThemeProvider/utils/styles.js +8 -1
  33. package/lib/ThemeProvider/utils/theme-tokens.js +1 -1
  34. package/lib/Typography/Typography.js +6 -2
  35. package/lib/index.js +9 -0
  36. package/lib/utils/index.js +9 -0
  37. package/lib/utils/props/clickProps.js +2 -2
  38. package/lib/utils/props/handlerProps.js +77 -31
  39. package/lib/utils/useScrollBlocking.js +66 -0
  40. package/lib/utils/useScrollBlocking.native.js +11 -0
  41. package/lib-module/BaseProvider/index.js +7 -2
  42. package/lib-module/Button/ButtonBase.js +7 -3
  43. package/lib-module/Carousel/Carousel.js +85 -70
  44. package/lib-module/Carousel/CarouselContext.js +11 -4
  45. package/lib-module/Carousel/CarouselItem/CarouselItem.js +25 -10
  46. package/lib-module/Carousel/CarouselStepTracker/CarouselStepTracker.js +42 -0
  47. package/lib-module/Carousel/CarouselStepTracker/index.js +2 -0
  48. package/lib-module/Carousel/dictionary.js +16 -0
  49. package/lib-module/Checkbox/Checkbox.js +8 -4
  50. package/lib-module/Checkbox/CheckboxGroup.js +1 -1
  51. package/lib-module/Feedback/Feedback.js +19 -11
  52. package/lib-module/Icon/IconText.js +5 -0
  53. package/lib-module/InputLabel/InputLabel.js +12 -6
  54. package/lib-module/InputSupports/InputSupports.js +10 -3
  55. package/lib-module/InputSupports/useInputSupports.js +3 -2
  56. package/lib-module/Link/LinkBase.js +8 -4
  57. package/lib-module/List/ListItem.js +8 -4
  58. package/lib-module/Modal/Modal.js +3 -0
  59. package/lib-module/Notification/Notification.js +8 -3
  60. package/lib-module/Pagination/Pagination.js +8 -4
  61. package/lib-module/RadioCard/RadioCard.js +7 -2
  62. package/lib-module/Select/Select.js +8 -4
  63. package/lib-module/Skeleton/Skeleton.js +1 -0
  64. package/lib-module/StepTracker/Step.js +9 -5
  65. package/lib-module/StepTracker/StepTracker.js +17 -14
  66. package/lib-module/Tabs/TabsItem.js +5 -1
  67. package/lib-module/TextInput/TextInput.js +3 -1
  68. package/lib-module/TextInput/TextInputBase.js +8 -4
  69. package/lib-module/ThemeProvider/ThemeProvider.js +20 -3
  70. package/lib-module/ThemeProvider/utils/styles.js +8 -1
  71. package/lib-module/ThemeProvider/utils/theme-tokens.js +1 -1
  72. package/lib-module/Typography/Typography.js +7 -3
  73. package/lib-module/index.js +1 -0
  74. package/lib-module/utils/index.js +1 -0
  75. package/lib-module/utils/props/clickProps.js +2 -2
  76. package/lib-module/utils/props/handlerProps.js +78 -31
  77. package/lib-module/utils/useScrollBlocking.js +58 -0
  78. package/lib-module/utils/useScrollBlocking.native.js +2 -0
  79. package/package.json +3 -3
  80. package/src/BaseProvider/index.jsx +6 -3
  81. package/src/Button/ButtonBase.jsx +8 -3
  82. package/src/Carousel/Carousel.jsx +106 -74
  83. package/src/Carousel/CarouselContext.jsx +15 -4
  84. package/src/Carousel/CarouselItem/CarouselItem.jsx +26 -8
  85. package/src/Carousel/CarouselStepTracker/CarouselStepTracker.jsx +36 -0
  86. package/src/Carousel/CarouselStepTracker/index.js +3 -0
  87. package/src/Carousel/dictionary.js +16 -0
  88. package/src/Checkbox/Checkbox.jsx +14 -11
  89. package/src/Checkbox/CheckboxGroup.jsx +1 -1
  90. package/src/Feedback/Feedback.jsx +14 -7
  91. package/src/Icon/IconText.jsx +2 -0
  92. package/src/InputLabel/InputLabel.jsx +13 -12
  93. package/src/InputSupports/InputSupports.jsx +18 -3
  94. package/src/InputSupports/useInputSupports.js +2 -2
  95. package/src/Link/LinkBase.jsx +10 -4
  96. package/src/List/ListItem.jsx +9 -4
  97. package/src/Modal/Modal.jsx +3 -1
  98. package/src/Notification/Notification.jsx +5 -3
  99. package/src/Pagination/Pagination.jsx +6 -4
  100. package/src/RadioCard/RadioCard.jsx +3 -2
  101. package/src/Select/Select.jsx +12 -3
  102. package/src/Skeleton/Skeleton.jsx +1 -0
  103. package/src/StepTracker/Step.jsx +12 -4
  104. package/src/StepTracker/StepTracker.jsx +20 -13
  105. package/src/Tabs/TabsItem.jsx +3 -2
  106. package/src/TextInput/TextInput.jsx +1 -1
  107. package/src/TextInput/TextInputBase.jsx +11 -3
  108. package/src/ThemeProvider/ThemeProvider.jsx +16 -3
  109. package/src/ThemeProvider/utils/styles.js +9 -1
  110. package/src/ThemeProvider/utils/theme-tokens.js +1 -1
  111. package/src/Typography/Typography.jsx +11 -12
  112. package/src/index.js +1 -0
  113. package/src/utils/index.js +1 -0
  114. package/src/utils/props/clickProps.js +2 -2
  115. package/src/utils/props/handlerProps.js +64 -16
  116. package/src/utils/useScrollBlocking.js +57 -0
  117. package/src/utils/useScrollBlocking.native.js +2 -0
@@ -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,7 +9,17 @@ import useInputSupports from './useInputSupports'
9
9
 
10
10
  const InputSupports = forwardRef(
11
11
  (
12
- { children, copy = 'en', label, hint, hintPosition = 'inline', feedback, tooltip, validation },
12
+ {
13
+ children,
14
+ copy = 'en',
15
+ label,
16
+ hint,
17
+ hintPosition = 'inline',
18
+ feedback,
19
+ tooltip,
20
+ validation,
21
+ nativeID
22
+ },
13
23
  ref
14
24
  ) => {
15
25
  const { space } = useThemeTokens('InputSupports')
@@ -18,7 +28,8 @@ const InputSupports = forwardRef(
18
28
  feedback,
19
29
  hint,
20
30
  label,
21
- validation
31
+ validation,
32
+ nativeID
22
33
  })
23
34
 
24
35
  return (
@@ -72,7 +83,11 @@ InputSupports.propTypes = {
72
83
  /**
73
84
  * Use to visually mark an input as valid or invalid.
74
85
  */
75
- validation: PropTypes.oneOf(['error', 'success'])
86
+ validation: PropTypes.oneOf(['error', 'success']),
87
+ /**
88
+ * ID for DOM element on web
89
+ */
90
+ nativeID: PropTypes.string
76
91
  }
77
92
 
78
93
  export default InputSupports
@@ -2,7 +2,7 @@ import useUniqueId from '../utils/useUniqueId'
2
2
 
3
3
  const joinDefined = (array) => array.filter((item) => item !== undefined).join(' ')
4
4
 
5
- const useInputSupports = ({ label, feedback, validation, hint }) => {
5
+ const useInputSupports = ({ label, feedback, validation, hint, nativeID }) => {
6
6
  const hasValidationError = validation === 'error'
7
7
 
8
8
  const inputId = useUniqueId('input')
@@ -20,7 +20,7 @@ const useInputSupports = ({ label, feedback, validation, hint }) => {
20
20
  }
21
21
 
22
22
  return {
23
- inputId,
23
+ inputId: nativeID || inputId,
24
24
  hintId,
25
25
  feedbackId,
26
26
  a11yProps
@@ -15,7 +15,7 @@ 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])
@@ -55,12 +55,16 @@ const selectTextStyles = ({ color }) => ({
55
55
  })
56
56
  })
57
57
 
58
- const selectBlockStyles = ({ blockFontWeight, blockFontSize, blockLineHeight, blockFontName }) =>
58
+ const selectBlockStyles = (
59
+ { blockFontWeight, blockFontSize, blockLineHeight, blockFontName },
60
+ themeOptions
61
+ ) =>
59
62
  applyTextStyles({
60
63
  fontWeight: blockFontWeight,
61
64
  fontSize: blockFontSize,
62
65
  lineHeight: blockLineHeight,
63
- fontName: blockFontName
66
+ fontName: blockFontName,
67
+ themeOptions
64
68
  })
65
69
 
66
70
  const selectDecorationStyles = ({ color, textLine, textLineStyle }) => ({
@@ -138,6 +142,8 @@ const LinkBase = forwardRef(
138
142
  // On web, this makes focus rings wrap only the link, not the entire block
139
143
  const blockLeftStyle = Platform.OS === 'web' && staticStyles.blockLeft
140
144
 
145
+ const { themeOptions } = useTheme()
146
+
141
147
  return (
142
148
  <InlinePressable
143
149
  {...selectedProps}
@@ -162,7 +168,7 @@ const LinkBase = forwardRef(
162
168
 
163
169
  // TODO: may need to apply some smarter text inheritance here if inline to avoid native
164
170
  // issues like double-application of line heights breaking align-items: baseline
165
- const blockTextStyles = selectBlockStyles(themeTokens)
171
+ const blockTextStyles = selectBlockStyles(themeTokens, themeOptions)
166
172
 
167
173
  const IconComponent = icon || themeTokens.icon
168
174
  const { iconSpace } = themeTokens
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef } from 'react'
2
2
  import { View, Platform, StyleSheet } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
- import { useThemeTokens, applyTextStyles } from '../ThemeProvider'
4
+ import { useTheme, useThemeTokens, applyTextStyles } from '../ThemeProvider'
5
5
  import {
6
6
  a11yProps,
7
7
  getTokensPropType,
@@ -41,12 +41,16 @@ const selectBulletPositioningStyles = ({ itemIconSize }) => ({
41
41
  height: itemIconSize
42
42
  })
43
43
 
44
- const selectItemStyles = ({ itemFontWeight, itemFontSize, itemLineHeight, itemFontName }) =>
44
+ const selectItemStyles = (
45
+ { itemFontWeight, itemFontSize, itemLineHeight, itemFontName },
46
+ themeOptions
47
+ ) =>
45
48
  applyTextStyles({
46
49
  fontWeight: itemFontWeight,
47
50
  fontSize: itemFontSize,
48
51
  lineHeight: itemLineHeight,
49
- fontName: itemFontName
52
+ fontName: itemFontName,
53
+ themeOptions
50
54
  })
51
55
 
52
56
  const selectItemBlockStyles = ({ interItemMargin }) => ({
@@ -80,8 +84,9 @@ const ListItem = forwardRef(
80
84
  ref
81
85
  ) => {
82
86
  const themeTokens = useThemeTokens('List', tokens, variant)
87
+ const { themeOptions } = useTheme()
83
88
 
84
- const itemStyles = selectItemStyles(themeTokens)
89
+ const itemStyles = selectItemStyles(themeTokens, themeOptions)
85
90
  const itemBlockStyles = selectItemBlockStyles(themeTokens)
86
91
  const dividerStyles = selectDividerStyles(themeTokens)
87
92
  const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
@@ -22,6 +22,7 @@ import {
22
22
  import { useViewport } from '../ViewportProvider'
23
23
  import IconButton from '../IconButton'
24
24
  import dictionary from './dictionary'
25
+ import useScrollBlocking from '../utils/useScrollBlocking'
25
26
 
26
27
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
27
28
 
@@ -89,6 +90,7 @@ const Modal = forwardRef(
89
90
  ({ children, isOpen, onClose, maxWidth, tokens, variant, copy, closeButton, ...rest }, ref) => {
90
91
  const viewport = useViewport()
91
92
  const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth })
93
+ const modalRef = useScrollBlocking(isOpen)
92
94
 
93
95
  const { closeIcon: CloseIconComponent } = themeTokens
94
96
 
@@ -113,7 +115,7 @@ const Modal = forwardRef(
113
115
 
114
116
  return (
115
117
  <NativeModal transparent {...selectProps(rest)}>
116
- <View style={[staticStyles.positioningContainer]}>
118
+ <View style={[staticStyles.positioningContainer]} ref={modalRef}>
117
119
  <View
118
120
  style={[staticStyles.sizingContainer, selectContainerStyles(themeTokens)]}
119
121
  pointerEvents="box-none" // don't capture backdrop press events
@@ -2,7 +2,7 @@ import React, { forwardRef, useState } from 'react'
2
2
  import { StyleSheet, View } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
5
- import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  getTokensPropType,
@@ -20,7 +20,8 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
20
20
 
21
21
  const selectContainerStyles = (tokens) => ({ ...tokens })
22
22
 
23
- const selectTextStyles = (tokens) => applyTextStyles(selectTokens('Typography', tokens))
23
+ const selectTextStyles = (tokens, themeOptions) =>
24
+ applyTextStyles({ ...selectTokens('Typography', tokens), themeOptions })
24
25
 
25
26
  const selectIconProps = ({ iconSize, iconColor }) => ({
26
27
  size: iconSize,
@@ -96,12 +97,13 @@ const Notification = forwardRef(
96
97
  const [isDismissed, setIsDismissed] = useState(false)
97
98
  const themeTokens = useThemeTokens('Notification', tokens, variant, { system })
98
99
  const getCopy = useCopy({ dictionary, copy })
100
+ const { themeOptions } = useTheme()
99
101
 
100
102
  if (isDismissed) {
101
103
  return null
102
104
  }
103
105
 
104
- const textStyles = selectTextStyles(themeTokens)
106
+ const textStyles = selectTextStyles(themeTokens, themeOptions)
105
107
 
106
108
  const content = wrapStringsInText(
107
109
  typeof children === 'function' ? children({ textStyles }) : children,
@@ -11,7 +11,7 @@ import {
11
11
  viewProps,
12
12
  withLinkRouter
13
13
  } from '../utils'
14
- import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
14
+ import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
15
15
  import { useViewport } from '../ViewportProvider'
16
16
  import Box from '../Box'
17
17
 
@@ -21,13 +21,14 @@ import SideButton from './SideButton'
21
21
 
22
22
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
23
23
 
24
- const selectTextStyles = ({ color, fontName, fontSize, fontWeight, lineHeight }) =>
24
+ const selectTextStyles = ({ color, fontName, fontSize, fontWeight, lineHeight }, themeOptions) =>
25
25
  applyTextStyles({
26
26
  color,
27
27
  fontName,
28
28
  fontSize,
29
29
  fontWeight,
30
- lineHeight
30
+ lineHeight,
31
+ themeOptions
31
32
  })
32
33
 
33
34
  const Pagination = forwardRef(
@@ -49,6 +50,7 @@ const Pagination = forwardRef(
49
50
  const { truncateAbove, gap, ...themeTokens } = useThemeTokens('Pagination', tokens, variant, {
50
51
  viewport
51
52
  })
53
+ const { themeOptions } = useTheme()
52
54
 
53
55
  const items = React.Children.toArray(children)
54
56
 
@@ -66,7 +68,7 @@ const Pagination = forwardRef(
66
68
  truncateAbove
67
69
  })
68
70
 
69
- const ellipsisTextStyles = selectTextStyles(themeTokens)
71
+ const ellipsisTextStyles = selectTextStyles(themeTokens, themeOptions)
70
72
 
71
73
  if (items.length === 0) {
72
74
  return null
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { StyleSheet, Text, View } from 'react-native'
4
4
 
5
- import { useThemeTokensCallback, applyTextStyles } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokensCallback } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  focusHandlerProps,
@@ -96,6 +96,7 @@ const RadioCard = forwardRef(
96
96
 
97
97
  const getTokens = useThemeTokensCallback('RadioCard', tokens, variant)
98
98
  const getCardTokens = (cardState) => selectPressableCardTokens(getTokens(cardState))
99
+ const { themeOptions } = useTheme()
99
100
 
100
101
  return (
101
102
  <PressableCardBase
@@ -112,7 +113,7 @@ const RadioCard = forwardRef(
112
113
  const { radioSpace, contentSpace, ...themeTokens } = getTokens(cardState)
113
114
  const radioTokens = selectRadioButtonTokens(themeTokens, 'radio')
114
115
  const titleTokens = selectTokens('Typography', themeTokens)
115
- const textStyle = applyTextStyles(titleTokens)
116
+ const textStyle = applyTextStyles({ ...titleTokens, themeOptions })
116
117
  return (
117
118
  <StackView direction="row" space={radioSpace}>
118
119
  <View style={[staticStyles.alignWithText, { height: textStyle.lineHeight }]}>
@@ -2,7 +2,7 @@ import React, { forwardRef, useState } from 'react'
2
2
 
3
3
  import { View, Platform, StyleSheet } from 'react-native'
4
4
  import PropTypes from 'prop-types'
5
- import { applyTextStyles, useThemeTokens, applyOuterBorder } from '../ThemeProvider'
5
+ import { applyTextStyles, useThemeTokens, applyOuterBorder, useTheme } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  componentPropType,
@@ -43,6 +43,7 @@ const selectInputStyles = (
43
43
  validationIconSize = 0,
44
44
  height
45
45
  },
46
+ themeOptions,
46
47
  inactive
47
48
  ) => {
48
49
  // Subtract border width from padding so overall input width/height doesn't
@@ -50,7 +51,13 @@ const selectInputStyles = (
50
51
  const offsetBorder = (value) =>
51
52
  typeof value === 'number' ? Math.max(0, value - borderWidth) : value
52
53
 
53
- const textStyles = applyTextStyles({ fontName, fontSize, lineHeight, fontWeight })
54
+ const textStyles = applyTextStyles({
55
+ fontName,
56
+ fontSize,
57
+ lineHeight,
58
+ fontWeight,
59
+ themeOptions
60
+ })
54
61
 
55
62
  const webStyles = Platform.select({
56
63
  web: {
@@ -205,13 +212,15 @@ const Select = forwardRef(
205
212
 
206
213
  const { icon: IconComponent, validationIcon: ValidationIconComponent } = themeTokens
207
214
 
215
+ const { themeOptions } = useTheme()
216
+
208
217
  return (
209
218
  <InputSupports {...supportsProps} {...selectedProps}>
210
219
  {({ inputId, ...props }) => (
211
220
  <View style={selectOuterBorderStyles(themeTokens)}>
212
221
  <Picker
213
222
  ref={ref}
214
- style={selectInputStyles(themeTokens, inactive)}
223
+ style={selectInputStyles(themeTokens, themeOptions, inactive)}
215
224
  onFocus={handleFocus}
216
225
  onBlur={handleBlur}
217
226
  onMouseOver={handleMouseOver}
@@ -22,6 +22,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
22
22
  const selectSkeletonStyles = ({ color, radius, fadeAnimation }) => ({
23
23
  backgroundColor: color,
24
24
  borderRadius: radius,
25
+ maxWidth: '100%',
25
26
  ...fadeAnimation
26
27
  })
27
28
 
@@ -4,7 +4,7 @@ import { StyleSheet, Text, View } from 'react-native'
4
4
  import StackView from '../StackView'
5
5
  import Icon from '../Icon'
6
6
  import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
7
- import { applyTextStyles } from '../ThemeProvider'
7
+ import { applyTextStyles, useTheme } from '../ThemeProvider'
8
8
 
9
9
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
10
10
 
@@ -95,6 +95,7 @@ const selectLabelStyles = (
95
95
  labelFontName,
96
96
  labelLineHeight
97
97
  },
98
+ themeOptions,
98
99
  isCurrent
99
100
  ) =>
100
101
  applyTextStyles({
@@ -102,7 +103,8 @@ const selectLabelStyles = (
102
103
  fontSize: labelFontSize,
103
104
  lineHeight: labelLineHeight,
104
105
  fontWeight: isCurrent ? labelCurrentFontWeight : labelFontWeight,
105
- fontName: labelFontName
106
+ fontName: labelFontName,
107
+ themeOptions
106
108
  })
107
109
  const getStepTestID = (isCompleted, isCurrent) => {
108
110
  let testID = 'StepTracker-Step'
@@ -125,6 +127,7 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
125
127
  const isCompleted = status > stepIndex
126
128
  const isCurrent = status === stepIndex
127
129
  const isActive = isCompleted || isCurrent
130
+ const { themeOptions } = useTheme()
128
131
 
129
132
  return (
130
133
  <StackView
@@ -158,7 +161,12 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
158
161
  {showStepLabel && (
159
162
  <View style={[staticStyles.stepLabelContainer, selectLabelContainerStyles(tokens)]}>
160
163
  {showStepName && (
161
- <Text style={[staticStyles.centeredText, selectLabelStyles(tokens, isCurrent)]}>
164
+ <Text
165
+ style={[
166
+ staticStyles.centeredText,
167
+ selectLabelStyles(tokens, themeOptions, isCurrent)
168
+ ]}
169
+ >
162
170
  {name}
163
171
  </Text>
164
172
  )}
@@ -167,7 +175,7 @@ const Step = ({ label, name, status = 0, stepCount = 0, stepIndex = 0, tokens, .
167
175
  style={[
168
176
  staticStyles.centeredText,
169
177
  tokens.labelDirection === 'column' && staticStyles.wrappingLabel,
170
- selectLabelStyles(tokens, isCurrent)
178
+ selectLabelStyles(tokens, themeOptions, isCurrent)
171
179
  ]}
172
180
  >
173
181
  {label}
@@ -2,7 +2,7 @@ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { StyleSheet, Text, View } from 'react-native'
4
4
  import StackView from '../StackView'
5
- import { applyTextStyles, useThemeTokens } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokens } from '../ThemeProvider'
6
6
  import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
7
7
  import { useViewport } from '../ViewportProvider'
8
8
  import useCopy from '../utils/useCopy'
@@ -25,19 +25,17 @@ const selectContainerStyles = ({
25
25
  const selectStepTrackerLabelContainerStyles = ({ labelMarginTop }) => ({
26
26
  marginTop: labelMarginTop
27
27
  })
28
- const selectStepTrackerLabelStyles = ({
29
- labelColor,
30
- labelFontSize,
31
- labelFontWeight,
32
- labelFontName,
33
- labelLineHeight
34
- }) =>
28
+ const selectStepTrackerLabelStyles = (
29
+ { labelColor, labelFontSize, labelFontWeight, labelFontName, labelLineHeight },
30
+ themeOptions
31
+ ) =>
35
32
  applyTextStyles({
36
33
  color: labelColor,
37
34
  fontSize: labelFontSize,
38
35
  lineHeight: labelLineHeight,
39
36
  fontWeight: labelFontWeight,
40
- fontName: labelFontName
37
+ fontName: labelFontName,
38
+ themeOptions
41
39
  })
42
40
 
43
41
  /**
@@ -106,6 +104,7 @@ const StepTracker = forwardRef(
106
104
  : ''
107
105
  const getStepLabel = (index) =>
108
106
  themeTokens.showStepLabel ? getCopy('stepLabel').replace('%{stepNumber}', index + 1) : ''
107
+ const { themeOptions } = useTheme()
109
108
  if (!steps.length) return null
110
109
  const selectedProps = selectProps({
111
110
  accessibilityLabel: stepTrackerLabel,
@@ -145,7 +144,9 @@ const StepTracker = forwardRef(
145
144
  selectStepTrackerLabelContainerStyles(themeTokens)
146
145
  ]}
147
146
  >
148
- <Text style={selectStepTrackerLabelStyles(themeTokens)}>{stepTrackerLabel}</Text>
147
+ <Text style={selectStepTrackerLabelStyles(themeTokens, themeOptions)}>
148
+ {stepTrackerLabel}
149
+ </Text>
149
150
  </View>
150
151
  )}
151
152
  </StackView>
@@ -155,13 +156,19 @@ const StepTracker = forwardRef(
155
156
  )
156
157
  StepTracker.displayName = 'StepTracker'
157
158
 
159
+ // If a language dictionary entry is provided, it must contain every key
160
+ const dictionaryContentShape = PropTypes.shape({
161
+ stepLabel: PropTypes.string.isRequired,
162
+ stepTrackerLabel: PropTypes.string.isRequired
163
+ })
164
+
158
165
  StepTracker.propTypes = {
159
166
  ...selectedSystemPropTypes,
160
167
  current: PropTypes.number,
161
- copy: PropTypes.oneOf(['en', 'fr']),
168
+ copy: PropTypes.oneOfType([PropTypes.oneOf(['en', 'fr']), dictionaryContentShape]),
162
169
  dictionary: PropTypes.shape({
163
- en: PropTypes.shape({ stepLabel: PropTypes.string, stepTrackerLabel: PropTypes.string }),
164
- fr: PropTypes.shape({ stepLabel: PropTypes.string, stepTrackerLabel: PropTypes.string })
170
+ en: dictionaryContentShape,
171
+ fr: dictionaryContentShape
165
172
  }),
166
173
  steps: PropTypes.arrayOf(PropTypes.string),
167
174
  tokens: getTokensPropType('StepTracker'),
@@ -2,7 +2,7 @@ import React, { forwardRef, useEffect } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'
4
4
 
5
- import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokensCallback } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  clickProps,
@@ -97,7 +97,7 @@ const TabsItem = forwardRef(
97
97
  ) => {
98
98
  // Convert onClick etc to onPress etc if used in an integration
99
99
  const { onPress, ...rest } = clickProps.toPressProps(rawRest)
100
-
100
+ const { themeOptions } = useTheme()
101
101
  const getTokens = useThemeTokensCallback('TabsItem', tokens, variant)
102
102
  const resolveTokens = (pressableState) =>
103
103
  resolvePressableTokens(getTokens, pressableState, { selected })
@@ -165,6 +165,7 @@ const TabsItem = forwardRef(
165
165
  const containerStyles = selectContainerStyles(themeTokens)
166
166
  const textStyles = applyTextStyles({
167
167
  ...selectTokens('Typography', themeTokens),
168
+ themeOptions,
168
169
  textAlign
169
170
  })
170
171
 
@@ -51,7 +51,7 @@ const TextInput = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
51
51
  }
52
52
 
53
53
  return (
54
- <InputSupports {...supportsProps}>
54
+ <InputSupports nativeID={selectedProps.nativeID} {...supportsProps}>
55
55
  {({ inputId, ...props }) => (
56
56
  <TextInputBase ref={ref} {...inputProps} nativeID={inputId} {...props} />
57
57
  )}
@@ -2,7 +2,7 @@ import React, { forwardRef, useEffect, useState } from 'react'
2
2
  import { Platform, StyleSheet, TextInput as NativeTextInput, View } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
5
- import { applyTextStyles, useThemeTokens, applyOuterBorder } from '../ThemeProvider'
5
+ import { applyTextStyles, useTheme, useThemeTokens, applyOuterBorder } from '../ThemeProvider'
6
6
  import {
7
7
  a11yProps,
8
8
  getTokensPropType,
@@ -43,6 +43,7 @@ const selectInputStyles = (
43
43
  width,
44
44
  height
45
45
  },
46
+ themeOptions,
46
47
  inactive
47
48
  ) => {
48
49
  // Subtract border width from padding so overall input width/height doesn't
@@ -50,7 +51,13 @@ const selectInputStyles = (
50
51
  const offsetBorder = (value) =>
51
52
  typeof value === 'number' ? Math.max(0, value - borderWidth) : value
52
53
 
53
- const textStyles = applyTextStyles({ fontName, fontSize, lineHeight, fontWeight })
54
+ const textStyles = applyTextStyles({
55
+ fontName,
56
+ fontSize,
57
+ lineHeight,
58
+ fontWeight,
59
+ themeOptions
60
+ })
54
61
 
55
62
  function linesToHeight(lines) {
56
63
  const { lineHeight: absoluteLineHeight } = textStyles
@@ -197,7 +204,8 @@ const TextInputBase = forwardRef(
197
204
  value: isControlled ? currentValue : undefined
198
205
  }
199
206
 
200
- const nativeInputStyle = selectInputStyles({ ...themeTokens, height }, inactive)
207
+ const { themeOptions } = useTheme()
208
+ const nativeInputStyle = selectInputStyles({ ...themeTokens, height }, themeOptions, inactive)
201
209
 
202
210
  return (
203
211
  <View style={selectOuterBorderStyles(themeTokens)}>
@@ -7,7 +7,12 @@ export const uninitialisedError = new Error('Theme context used outside of Theme
7
7
  export const ThemeContext = createContext(uninitialisedError)
8
8
  export const ThemeSetterContext = createContext(uninitialisedError)
9
9
 
10
- const ThemeProvider = ({ children, defaultTheme }) => {
10
+ const ThemeProvider = ({
11
+ children,
12
+ defaultTheme,
13
+ // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
14
+ themeOptions = { forceAbsoluteFontSizing: true }
15
+ }) => {
11
16
  const [theme, setTheme] = useState(defaultTheme)
12
17
 
13
18
  // Validate the theme tokens version on every render.
@@ -17,7 +22,7 @@ const ThemeProvider = ({ children, defaultTheme }) => {
17
22
 
18
23
  return (
19
24
  <ThemeSetterContext.Provider value={setTheme}>
20
- <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
25
+ <ThemeContext.Provider value={{ ...theme, themeOptions }}>{children}</ThemeContext.Provider>
21
26
  </ThemeSetterContext.Provider>
22
27
  )
23
28
  }
@@ -28,7 +33,15 @@ ThemeProvider.propTypes = {
28
33
  metadata: PropTypes.shape({
29
34
  themeTokensVersion: PropTypes.string.isRequired
30
35
  }).isRequired
31
- }).isRequired
36
+ }).isRequired,
37
+ /**
38
+ * An object containing options allowing to customize the theming experience:
39
+ *
40
+ * - `forceAbsoluteFontSizing`: available on web only; when set to true, allows
41
+ * using absolute font sizing (in pixels, doesn't scale) instead of the
42
+ * relative sizing (in `rem`, scales depending on the browser settings)
43
+ */
44
+ themeOptions: PropTypes.shape({ forceAbsoluteFontSizing: PropTypes.bool })
32
45
  }
33
46
 
34
47
  export default ThemeProvider
@@ -15,13 +15,21 @@ export function applyTextStyles({
15
15
  fontWeight = '400',
16
16
  fontName,
17
17
  fontStyle = 'normal',
18
+ themeOptions = {
19
+ // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
20
+ forceAbsoluteFontSizing: true
21
+ },
18
22
  ...rest
19
23
  }) {
20
24
  const styles = { ...rest }
25
+ const { forceAbsoluteFontSizing } = themeOptions
21
26
 
22
27
  if (fontSize) {
23
28
  // If relative font sizes are needed, catch and calculate them here
24
- styles.fontSize = fontSize
29
+ styles.fontSize =
30
+ Platform.OS === 'web' && !forceAbsoluteFontSizing
31
+ ? `${fontSize / fontBasePixels}rem`
32
+ : fontSize
25
33
  }
26
34
  if (typeof lineHeight === 'number') {
27
35
  // React Native expects absolute line heights but multipliers are better as design tokens