@telus-uds/components-base 0.0.2-prerelease.9 → 1.1.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 (293) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.storybook/main.js +4 -0
  3. package/.storybook/preview.js +37 -0
  4. package/.ultra.cache.json +1 -1
  5. package/CHANGELOG.md +50 -0
  6. package/README.md +4 -2
  7. package/__fixtures__/test-utils.js +25 -0
  8. package/__fixtures__/testTheme.js +4 -2
  9. package/__tests__/Button/ButtonGroup.test.jsx +4 -5
  10. package/__tests__/Checkbox/Checkbox.test.jsx +2 -2
  11. package/__tests__/Checkbox/CheckboxGroup.test.jsx +4 -5
  12. package/__tests__/ExpandCollapse/ExpandCollapse.test.jsx +2 -2
  13. package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +164 -0
  14. package/__tests__/Link/LinkBase.test.jsx +0 -14
  15. package/__tests__/Radio/Radio.test.jsx +2 -2
  16. package/__tests__/Radio/RadioGroup.test.jsx +4 -5
  17. package/__tests__/RadioCard/RadioCard.test.jsx +2 -2
  18. package/__tests__/RadioCard/RadioCardGroup.test.jsx +4 -5
  19. package/__tests__/Search/Search.test.jsx +9 -8
  20. package/__tests__/Select/Select.test.jsx +3 -2
  21. package/__tests__/Tabs/Tabs.test.jsx +1 -161
  22. package/__tests__/Tags/Tags.test.jsx +4 -5
  23. package/__tests__/TextInput/TextArea.test.jsx +3 -2
  24. package/__tests__/TextInput/TextInputBase.test.jsx +10 -5
  25. package/__tests__/ThemeProvider/ThemeProvider.test.jsx +77 -0
  26. package/__tests__/ThemeProvider/useThemeTokens.test.jsx +9 -5
  27. package/__tests__/ThemeProvider/utils/theme-tokens.test.js +41 -0
  28. package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +3 -2
  29. package/__tests__/utils/children.test.jsx +128 -0
  30. package/__tests__/utils/input.test.js +1 -1
  31. package/__tests__/utils/semantics.test.jsx +43 -0
  32. package/babel.config.js +9 -16
  33. package/component-docs.json +10313 -0
  34. package/generate-component-docs.js +56 -0
  35. package/lib/A11yText/index.js +10 -5
  36. package/lib/ActivityIndicator/Spinner.js +16 -13
  37. package/lib/ActivityIndicator/Spinner.native.js +12 -8
  38. package/lib/Box/Box.js +103 -8
  39. package/lib/Button/Button.js +9 -8
  40. package/lib/Button/ButtonBase.js +14 -7
  41. package/lib/Button/ButtonGroup.js +25 -10
  42. package/lib/Button/ButtonLink.js +10 -7
  43. package/lib/Card/Card.js +2 -0
  44. package/lib/Card/CardBase.js +13 -5
  45. package/lib/Card/PressableCardBase.js +12 -8
  46. package/lib/Checkbox/Checkbox.js +25 -14
  47. package/lib/Checkbox/CheckboxGroup.js +22 -12
  48. package/lib/Divider/Divider.js +12 -7
  49. package/lib/ExpandCollapse/Accordion.js +10 -4
  50. package/lib/ExpandCollapse/Control.js +12 -6
  51. package/lib/ExpandCollapse/ExpandCollapse.js +10 -5
  52. package/lib/ExpandCollapse/Panel.js +8 -7
  53. package/lib/Feedback/Feedback.js +10 -5
  54. package/lib/Fieldset/Fieldset.js +10 -5
  55. package/lib/Fieldset/FieldsetContainer.js +10 -5
  56. package/lib/Fieldset/FieldsetContainer.native.js +10 -5
  57. package/lib/Fieldset/Legend.js +10 -5
  58. package/lib/Fieldset/Legend.native.js +10 -5
  59. package/lib/FlexGrid/Col/Col.js +8 -5
  60. package/lib/FlexGrid/FlexGrid.js +31 -6
  61. package/lib/FlexGrid/Row/Row.js +12 -5
  62. package/lib/{Tabs → HorizontalScroll}/HorizontalScroll.js +5 -4
  63. package/lib/{Tabs/TabsScrollButton.js → HorizontalScroll/HorizontalScrollButton.js} +14 -8
  64. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.js +0 -0
  65. package/lib/{Tabs → HorizontalScroll}/ScrollViewEnd.native.js +0 -0
  66. package/lib/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  67. package/lib/HorizontalScroll/index.js +35 -0
  68. package/lib/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  69. package/lib/Icon/Icon.js +16 -9
  70. package/lib/Icon/IconText.js +8 -7
  71. package/lib/IconButton/IconButton.js +10 -5
  72. package/lib/InputLabel/InputLabel.js +33 -5
  73. package/lib/InputLabel/LabelContent.js +22 -12
  74. package/lib/InputLabel/LabelContent.native.js +23 -5
  75. package/lib/InputSupports/InputSupports.js +10 -5
  76. package/lib/Link/ChevronLink.js +12 -5
  77. package/lib/Link/InlinePressable.js +10 -4
  78. package/lib/Link/InlinePressable.native.js +5 -4
  79. package/lib/Link/Link.js +12 -5
  80. package/lib/Link/LinkBase.js +12 -5
  81. package/lib/Link/TextButton.js +10 -5
  82. package/lib/List/List.js +6 -6
  83. package/lib/List/ListItem.js +28 -33
  84. package/lib/List/index.js +15 -0
  85. package/lib/Modal/Modal.js +10 -5
  86. package/lib/Notification/Notification.js +21 -5
  87. package/lib/Pagination/PageButton.js +16 -11
  88. package/lib/Pagination/Pagination.js +12 -7
  89. package/lib/Pagination/SideButton.js +12 -7
  90. package/lib/Pagination/usePagination.js +2 -2
  91. package/lib/Progress/Progress.js +10 -5
  92. package/lib/Progress/ProgressBar.js +21 -10
  93. package/lib/Progress/ProgressBarBackground.js +12 -8
  94. package/lib/Radio/Radio.js +14 -13
  95. package/lib/Radio/RadioButton.js +20 -9
  96. package/lib/Radio/RadioGroup.js +24 -13
  97. package/lib/RadioCard/RadioCard.js +14 -10
  98. package/lib/RadioCard/RadioCardGroup.js +13 -12
  99. package/lib/Search/Search.js +29 -18
  100. package/lib/Select/Picker.js +11 -6
  101. package/lib/Select/Picker.native.js +21 -6
  102. package/lib/Select/Select.js +46 -4
  103. package/lib/SideNav/Item.js +10 -5
  104. package/lib/SideNav/ItemsGroup.js +10 -5
  105. package/lib/SideNav/SideNav.js +11 -7
  106. package/lib/Skeleton/Skeleton.js +15 -15
  107. package/lib/Skeleton/skeletonWebAnimation.js +1 -1
  108. package/lib/Spacer/Spacer.js +19 -7
  109. package/lib/StackView/StackView.js +26 -7
  110. package/lib/StackView/StackWrap.js +24 -13
  111. package/lib/StackView/StackWrapBox.js +34 -8
  112. package/lib/StackView/StackWrapGap.js +16 -7
  113. package/lib/StackView/common.js +4 -2
  114. package/lib/StackView/getStackedContent.js +2 -2
  115. package/lib/StepTracker/StepTracker.js +10 -5
  116. package/lib/Tabs/Tabs.js +26 -19
  117. package/lib/Tabs/TabsItem.js +16 -12
  118. package/lib/Tags/Tags.js +27 -11
  119. package/lib/TextInput/TextArea.js +7 -5
  120. package/lib/TextInput/TextInput.js +12 -6
  121. package/lib/TextInput/TextInputBase.js +12 -8
  122. package/lib/ThemeProvider/ThemeProvider.js +14 -10
  123. package/lib/ThemeProvider/useSetTheme.js +6 -1
  124. package/lib/ThemeProvider/utils/styles.js +2 -2
  125. package/lib/ThemeProvider/utils/theme-tokens.js +39 -8
  126. package/lib/ToggleSwitch/ToggleSwitch.js +11 -6
  127. package/lib/Tooltip/Backdrop.js +10 -2
  128. package/lib/Tooltip/Tooltip.js +5 -4
  129. package/lib/Typography/Typography.js +40 -24
  130. package/lib/index.js +36 -1
  131. package/lib/utils/a11y/index.js +13 -0
  132. package/lib/utils/a11y/propTypes.js +61 -0
  133. package/lib/utils/a11y/propTypes.native.js +47 -0
  134. package/lib/utils/a11y/semantics.js +173 -0
  135. package/lib/utils/animation/useVerticalExpandAnimation.js +1 -1
  136. package/lib/utils/children.js +55 -8
  137. package/lib/utils/input.js +27 -17
  138. package/lib/utils/propTypes.js +40 -68
  139. package/lib/utils/useCopy.js +1 -1
  140. package/lib/utils/useHash.js +8 -4
  141. package/lib/utils/useSpacingScale.js +1 -3
  142. package/lib/utils/useUniqueId.js +1 -1
  143. package/package.json +14 -6
  144. package/release-context.json +4 -4
  145. package/src/A11yText/index.jsx +6 -4
  146. package/src/ActivityIndicator/Spinner.jsx +5 -3
  147. package/src/ActivityIndicator/Spinner.native.jsx +5 -3
  148. package/src/Box/Box.jsx +125 -39
  149. package/src/Button/Button.jsx +7 -4
  150. package/src/Button/ButtonBase.jsx +86 -77
  151. package/src/Button/ButtonGroup.jsx +81 -69
  152. package/src/Button/ButtonLink.jsx +18 -13
  153. package/src/Card/Card.jsx +2 -2
  154. package/src/Card/CardBase.jsx +6 -4
  155. package/src/Card/PressableCardBase.jsx +71 -64
  156. package/src/Checkbox/Checkbox.jsx +118 -108
  157. package/src/Checkbox/CheckboxGroup.jsx +72 -62
  158. package/src/Divider/Divider.jsx +7 -4
  159. package/src/ExpandCollapse/Accordion.jsx +3 -2
  160. package/src/ExpandCollapse/Control.jsx +40 -43
  161. package/src/ExpandCollapse/ExpandCollapse.jsx +26 -23
  162. package/src/ExpandCollapse/Panel.jsx +69 -63
  163. package/src/Feedback/Feedback.jsx +36 -33
  164. package/src/Fieldset/Fieldset.jsx +63 -56
  165. package/src/Fieldset/FieldsetContainer.jsx +14 -5
  166. package/src/Fieldset/FieldsetContainer.native.jsx +7 -4
  167. package/src/Fieldset/Legend.jsx +7 -2
  168. package/src/Fieldset/Legend.native.jsx +7 -2
  169. package/src/FlexGrid/Col/Col.jsx +139 -132
  170. package/src/FlexGrid/FlexGrid.jsx +79 -51
  171. package/src/FlexGrid/Row/Row.jsx +55 -48
  172. package/src/HorizontalScroll/HorizontalScroll.jsx +168 -0
  173. package/src/HorizontalScroll/HorizontalScrollButton.jsx +105 -0
  174. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.jsx +0 -0
  175. package/src/{Tabs → HorizontalScroll}/ScrollViewEnd.native.jsx +0 -0
  176. package/src/{Tabs → HorizontalScroll}/dictionary.js +0 -0
  177. package/src/HorizontalScroll/index.js +17 -0
  178. package/src/{Tabs → HorizontalScroll}/itemPositions.js +0 -0
  179. package/src/Icon/Icon.jsx +37 -35
  180. package/src/Icon/IconText.jsx +22 -17
  181. package/src/IconButton/IconButton.jsx +49 -42
  182. package/src/InputLabel/InputLabel.jsx +53 -38
  183. package/src/InputLabel/LabelContent.jsx +14 -6
  184. package/src/InputLabel/LabelContent.native.jsx +11 -2
  185. package/src/InputSupports/InputSupports.jsx +29 -34
  186. package/src/Link/ChevronLink.jsx +26 -16
  187. package/src/Link/InlinePressable.jsx +5 -3
  188. package/src/Link/InlinePressable.native.jsx +5 -3
  189. package/src/Link/Link.jsx +22 -16
  190. package/src/Link/LinkBase.jsx +67 -58
  191. package/src/Link/TextButton.jsx +30 -23
  192. package/src/List/List.jsx +6 -7
  193. package/src/List/ListItem.jsx +70 -90
  194. package/src/List/index.js +5 -0
  195. package/src/Modal/Modal.jsx +9 -4
  196. package/src/Notification/Notification.jsx +58 -43
  197. package/src/Pagination/PageButton.jsx +37 -34
  198. package/src/Pagination/Pagination.jsx +88 -92
  199. package/src/Pagination/SideButton.jsx +44 -41
  200. package/src/Progress/Progress.jsx +5 -4
  201. package/src/Progress/ProgressBar.jsx +42 -29
  202. package/src/Progress/ProgressBarBackground.jsx +5 -3
  203. package/src/Radio/Radio.jsx +85 -78
  204. package/src/Radio/RadioButton.jsx +54 -43
  205. package/src/Radio/RadioGroup.jsx +74 -63
  206. package/src/RadioCard/RadioCard.jsx +75 -68
  207. package/src/RadioCard/RadioCardGroup.jsx +82 -75
  208. package/src/Search/Search.jsx +127 -106
  209. package/src/Select/Picker.jsx +49 -42
  210. package/src/Select/Picker.native.jsx +56 -49
  211. package/src/Select/Select.jsx +115 -72
  212. package/src/SideNav/Item.jsx +53 -46
  213. package/src/SideNav/ItemsGroup.jsx +50 -43
  214. package/src/SideNav/SideNav.jsx +68 -60
  215. package/src/Skeleton/Skeleton.jsx +9 -13
  216. package/src/Spacer/Spacer.jsx +11 -4
  217. package/src/StackView/StackView.jsx +47 -23
  218. package/src/StackView/StackWrap.jsx +14 -12
  219. package/src/StackView/StackWrapBox.jsx +62 -28
  220. package/src/StackView/StackWrapGap.jsx +46 -24
  221. package/src/StackView/common.jsx +3 -2
  222. package/src/StepTracker/StepTracker.jsx +73 -62
  223. package/src/Tabs/Tabs.jsx +70 -62
  224. package/src/Tabs/TabsItem.jsx +111 -103
  225. package/src/Tags/Tags.jsx +114 -102
  226. package/src/TextInput/TextArea.jsx +5 -4
  227. package/src/TextInput/TextInput.jsx +5 -4
  228. package/src/TextInput/TextInputBase.jsx +84 -77
  229. package/src/ThemeProvider/ThemeProvider.jsx +11 -7
  230. package/src/ThemeProvider/useSetTheme.js +4 -0
  231. package/src/ThemeProvider/utils/theme-tokens.js +28 -0
  232. package/src/ToggleSwitch/ToggleSwitch.jsx +49 -50
  233. package/src/Tooltip/Tooltip.jsx +134 -130
  234. package/src/Typography/Typography.jsx +67 -44
  235. package/src/index.js +3 -1
  236. package/src/utils/a11y/index.js +1 -0
  237. package/src/utils/a11y/propTypes.js +61 -0
  238. package/src/utils/a11y/propTypes.native.js +39 -0
  239. package/src/utils/a11y/semantics.js +162 -0
  240. package/src/utils/children.jsx +60 -7
  241. package/src/utils/input.js +20 -17
  242. package/src/utils/propTypes.js +30 -76
  243. package/src/utils/useCopy.js +1 -1
  244. package/src/utils/useHash.js +8 -3
  245. package/stories/A11yText/A11yText.stories.jsx +3 -3
  246. package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +2 -2
  247. package/stories/Box/Box.stories.jsx +2 -2
  248. package/stories/Button/Button.stories.jsx +3 -3
  249. package/stories/Button/ButtonGroup.stories.jsx +2 -2
  250. package/stories/Button/ButtonLink.stories.jsx +2 -2
  251. package/stories/Card/Card.stories.jsx +2 -2
  252. package/stories/Checkbox/Checkbox.stories.jsx +2 -2
  253. package/stories/Divider/Divider.stories.jsx +2 -2
  254. package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +3 -3
  255. package/stories/Feedback/Feedback.stories.jsx +2 -2
  256. package/stories/FlexGrid/01 FlexGrid.stories.jsx +2 -2
  257. package/stories/FlexGrid/02 Row.stories.jsx +2 -2
  258. package/stories/FlexGrid/03 Col.stories.jsx +2 -2
  259. package/stories/Icon/Icon.stories.jsx +2 -2
  260. package/stories/IconButton/IconButton.stories.jsx +2 -2
  261. package/stories/InputLabel/InputLabel.stories.jsx +2 -2
  262. package/stories/Link/ChevronLink.stories.jsx +2 -2
  263. package/stories/Link/Link.stories.jsx +2 -2
  264. package/stories/Link/TextButton.stories.jsx +2 -2
  265. package/stories/List/List.stories.jsx +2 -2
  266. package/stories/Modal/Modal.stories.jsx +2 -2
  267. package/stories/Notification/Notification.stories.jsx +2 -2
  268. package/stories/Pagination/Pagination.stories.jsx +2 -2
  269. package/stories/Progress/Progress.stories.jsx +2 -2
  270. package/stories/Radio/Radio.stories.jsx +2 -2
  271. package/stories/RadioCard/RadioCard.stories.jsx +2 -2
  272. package/stories/Search/Search.stories.jsx +2 -2
  273. package/stories/Select/Select.stories.jsx +2 -2
  274. package/stories/SideNav/SideNav.stories.jsx +2 -2
  275. package/stories/SideNav/SideNavItem.stories.jsx +2 -2
  276. package/stories/SideNav/SideNavItemsGroup.stories.jsx +2 -2
  277. package/stories/Skeleton/Skeleton.stories.jsx +3 -3
  278. package/stories/Spacer/Spacer.stories.jsx +2 -2
  279. package/stories/StackView/StackView.stories.jsx +2 -2
  280. package/stories/StackView/StackWrap.stories.jsx +2 -2
  281. package/stories/StepTracker/StepTracker.stories.jsx +2 -2
  282. package/stories/Tabs/Tabs.stories.jsx +2 -2
  283. package/stories/Tags/Tags.stories.jsx +2 -2
  284. package/stories/TextInput/TextArea.stories.jsx +2 -2
  285. package/stories/TextInput/TextInput.stories.jsx +2 -2
  286. package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +2 -2
  287. package/stories/Tooltip/Tooltip.stories.jsx +2 -2
  288. package/stories/TooltipButton/TooltipButton.stories.jsx +2 -2
  289. package/stories/Typography/Typography.stories.jsx +2 -2
  290. package/stories/platform-supports.jsx +1 -1
  291. package/stories/supports.jsx +4 -5
  292. package/src/Tabs/HorizontalScroll.jsx +0 -165
  293. package/src/Tabs/TabsScrollButton.jsx +0 -100
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { forwardRef, useEffect, useRef, useState } from 'react'
2
2
  import { Dimensions, Platform, Pressable, StyleSheet, Text, View } from 'react-native'
3
3
 
4
4
  import PropTypes from 'prop-types'
@@ -99,157 +99,161 @@ const defaultControl = (pressableState, variant) => (
99
99
  * - You may use one when the information is useful only to a small percentage of users (ie. tech savvy people wouldn't need this info).
100
100
  * - Tooltips may also be useful when vertical space is an issue.
101
101
  */
102
- const Tooltip = ({ children, content, position = 'auto', copy = 'en', tokens, variant }) => {
103
- const [isOpen, setIsOpen] = useState(false)
102
+ const Tooltip = forwardRef(
103
+ ({ children, content, position = 'auto', copy = 'en', tokens, variant }, ref) => {
104
+ const [isOpen, setIsOpen] = useState(false)
104
105
 
105
- const controlRef = useRef()
106
- const [controlLayout, setControlLayout] = useState(null)
107
- const [tooltipDimensions, setTooltipDimensions] = useState(null)
108
- const [windowDimensions, setWindowDimensions] = useState(Dimensions.get('window'))
109
- const [tooltipPosition, setTooltipPosition] = useState(null)
106
+ const controlRef = useRef()
107
+ const [controlLayout, setControlLayout] = useState(null)
108
+ const [tooltipDimensions, setTooltipDimensions] = useState(null)
109
+ const [windowDimensions, setWindowDimensions] = useState(Dimensions.get('window'))
110
+ const [tooltipPosition, setTooltipPosition] = useState(null)
110
111
 
111
- const getCopy = useCopy({ dictionary, copy })
112
- const themeTokens = useThemeTokens('Tooltip', tokens, variant)
112
+ const getCopy = useCopy({ dictionary, copy })
113
+ const themeTokens = useThemeTokens('Tooltip', tokens, variant)
113
114
 
114
- const { arrowWidth, arrowOffset } = themeTokens
115
+ const { arrowWidth, arrowOffset } = themeTokens
115
116
 
116
- useEffect(() => {
117
- const subscription = Dimensions.addEventListener('change', ({ window }) => {
118
- setWindowDimensions(window)
119
- })
117
+ useEffect(() => {
118
+ const subscription = Dimensions.addEventListener('change', ({ window }) => {
119
+ setWindowDimensions(window)
120
+ })
120
121
 
121
- return () => subscription?.remove()
122
- })
122
+ return () => subscription?.remove()
123
+ })
123
124
 
124
- const toggleIsOpen = () => setIsOpen(!isOpen)
125
- const close = () => setIsOpen(false)
125
+ const toggleIsOpen = () => setIsOpen(!isOpen)
126
+ const close = () => setIsOpen(false)
126
127
 
127
- const getPressableState = ({ pressed, hovered, focused }) => ({
128
- pressed,
129
- hover: hovered,
130
- focus: focused
131
- })
128
+ const getPressableState = ({ pressed, hovered, focused }) => ({
129
+ pressed,
130
+ hover: hovered,
131
+ focus: focused
132
+ })
132
133
 
133
- const onTooltipLayout = ({
134
- nativeEvent: {
135
- layout: { width, height }
136
- }
137
- }) => {
138
- if (
139
- tooltipDimensions === null ||
140
- tooltipDimensions.width !== width ||
141
- tooltipDimensions.height !== height
142
- ) {
143
- setTooltipDimensions({
144
- width: Platform.select({
145
- web: width + 0.3, // avoids often unnecessary line breaks due to subpixel rendering of fonts
146
- native: width
147
- }),
148
- height
149
- })
134
+ const onTooltipLayout = ({
135
+ nativeEvent: {
136
+ layout: { width, height }
137
+ }
138
+ }) => {
139
+ if (
140
+ tooltipDimensions === null ||
141
+ tooltipDimensions.width !== width ||
142
+ tooltipDimensions.height !== height
143
+ ) {
144
+ setTooltipDimensions({
145
+ width: Platform.select({
146
+ web: width + 0.3, // avoids often unnecessary line breaks due to subpixel rendering of fonts
147
+ native: width
148
+ }),
149
+ height
150
+ })
151
+ }
150
152
  }
151
- }
152
153
 
153
- useEffect(() => {
154
- if (isOpen) {
155
- controlRef.current.measureInWindow((x, y, width, height) => {
156
- setControlLayout({ x, y, width, height })
157
- })
158
- } else {
159
- setControlLayout(null)
160
- setTooltipDimensions(null)
161
- setTooltipPosition(null)
162
- }
163
- }, [isOpen])
154
+ useEffect(() => {
155
+ if (isOpen) {
156
+ controlRef.current.measureInWindow((x, y, width, height) => {
157
+ setControlLayout({ x, y, width, height })
158
+ })
159
+ } else {
160
+ setControlLayout(null)
161
+ setTooltipDimensions(null)
162
+ setTooltipPosition(null)
163
+ }
164
+ }, [isOpen])
164
165
 
165
- useEffect(() => {
166
- setIsOpen(false)
167
- }, [windowDimensions])
166
+ useEffect(() => {
167
+ setIsOpen(false)
168
+ }, [windowDimensions])
168
169
 
169
- useEffect(() => {
170
- if (
171
- (tooltipPosition !== null && !tooltipPosition?.isNormalized) ||
172
- !isOpen ||
173
- controlLayout === null ||
174
- tooltipDimensions == null
175
- ) {
176
- return
177
- }
170
+ useEffect(() => {
171
+ if (
172
+ (tooltipPosition !== null && !tooltipPosition?.isNormalized) ||
173
+ !isOpen ||
174
+ controlLayout === null ||
175
+ tooltipDimensions == null
176
+ ) {
177
+ return
178
+ }
178
179
 
179
- const updatedPosition = getTooltipPosition(position, {
180
- controlLayout,
180
+ const updatedPosition = getTooltipPosition(position, {
181
+ controlLayout,
182
+ tooltipDimensions,
183
+ windowDimensions,
184
+ arrowWidth,
185
+ arrowOffset
186
+ })
187
+
188
+ // avoid ending up in an infinite normalization loop
189
+ if (tooltipPosition?.isNormalized && updatedPosition.isNormalized) {
190
+ return
191
+ }
192
+
193
+ setTooltipPosition(updatedPosition)
194
+ }, [
195
+ isOpen,
196
+ position,
181
197
  tooltipDimensions,
198
+ controlLayout,
182
199
  windowDimensions,
183
200
  arrowWidth,
184
- arrowOffset
185
- })
201
+ arrowOffset,
202
+ tooltipPosition
203
+ ])
186
204
 
187
- // avoid ending up in an infinite normalization loop
188
- if (tooltipPosition?.isNormalized && updatedPosition.isNormalized) {
189
- return
190
- }
205
+ const control = children !== undefined ? children : defaultControl
206
+ const pressableStyles =
207
+ control === defaultControl ? Platform.select({ web: { outline: 'none' } }) : undefined
208
+ const pressableHitSlop =
209
+ control === defaultControl ? { top: 10, bottom: 10, left: 10, right: 10 } : undefined
191
210
 
192
- setTooltipPosition(updatedPosition)
193
- }, [
194
- isOpen,
195
- position,
196
- tooltipDimensions,
197
- controlLayout,
198
- windowDimensions,
199
- arrowWidth,
200
- arrowOffset,
201
- tooltipPosition
202
- ])
203
-
204
- const control = children !== undefined ? children : defaultControl
205
- const pressableStyles =
206
- control === defaultControl ? Platform.select({ web: { outline: 'none' } }) : undefined
207
- const pressableHitSlop =
208
- control === defaultControl ? { top: 10, bottom: 10, left: 10, right: 10 } : undefined
209
-
210
- return (
211
- <View style={staticStyles.container}>
212
- <Pressable
213
- onPress={toggleIsOpen}
214
- ref={controlRef}
215
- onBlur={close}
216
- style={pressableStyles}
217
- hitSlop={pressableHitSlop}
218
- accessibilityLabel={getCopy('a11yText')}
219
- accessibilityRole="button"
220
- >
221
- {typeof control === 'function'
222
- ? (pressableState) => control(getPressableState(pressableState), variant)
223
- : control}
224
- </Pressable>
225
- {isOpen && (
226
- <Backdrop onPress={close}>
227
- <View
228
- style={[
229
- staticStyles.tooltip,
230
- selectTooltipShadowStyles(themeTokens), // applied separately so that it doesn't cover the arrow
231
- tooltipPosition && selectTooltipPositionStyles(tooltipPosition),
232
- (tooltipPosition === null || tooltipPosition?.isNormalized) &&
233
- staticStyles.tooltipHidden // visually hide the tooltip until we have a final measurement
234
- ]}
235
- onLayout={onTooltipLayout}
236
- accessibilityRole="alert"
237
- >
211
+ return (
212
+ <View style={staticStyles.container}>
213
+ <Pressable
214
+ onPress={toggleIsOpen}
215
+ ref={controlRef}
216
+ onBlur={close}
217
+ style={pressableStyles}
218
+ hitSlop={pressableHitSlop}
219
+ accessibilityLabel={getCopy('a11yText')}
220
+ accessibilityRole="button"
221
+ >
222
+ {typeof control === 'function'
223
+ ? (pressableState) => control(getPressableState(pressableState), variant)
224
+ : control}
225
+ </Pressable>
226
+ {isOpen && (
227
+ <Backdrop onPress={close}>
238
228
  <View
229
+ ref={ref}
239
230
  style={[
240
- staticStyles.arrow,
241
- tooltipPosition && selectArrowStyles(themeTokens, tooltipPosition)
231
+ staticStyles.tooltip,
232
+ selectTooltipShadowStyles(themeTokens), // applied separately so that it doesn't cover the arrow
233
+ tooltipPosition && selectTooltipPositionStyles(tooltipPosition),
234
+ (tooltipPosition === null || tooltipPosition?.isNormalized) &&
235
+ staticStyles.tooltipHidden // visually hide the tooltip until we have a final measurement
242
236
  ]}
243
- />
244
- <View style={selectTooltipStyles(themeTokens)}>
245
- <Text style={selectTextStyles(themeTokens)}>{content}</Text>
237
+ onLayout={onTooltipLayout}
238
+ accessibilityRole="alert"
239
+ >
240
+ <View
241
+ style={[
242
+ staticStyles.arrow,
243
+ tooltipPosition && selectArrowStyles(themeTokens, tooltipPosition)
244
+ ]}
245
+ />
246
+ <View style={selectTooltipStyles(themeTokens)}>
247
+ <Text style={selectTextStyles(themeTokens)}>{content}</Text>
248
+ </View>
246
249
  </View>
247
- </View>
248
- </Backdrop>
249
- )}
250
- </View>
251
- )
252
- }
250
+ </Backdrop>
251
+ )}
252
+ </View>
253
+ )
254
+ }
255
+ )
256
+ Tooltip.displayName = 'Tooltip'
253
257
 
254
258
  Tooltip.propTypes = {
255
259
  /**
@@ -1,20 +1,22 @@
1
- import React from 'react'
1
+ import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { Text, View } from 'react-native'
4
4
 
5
5
  import { useThemeTokens } from '../ThemeProvider'
6
6
  import { useViewport } from '../ViewportProvider'
7
7
  import { applyTextStyles } from '../ThemeProvider/utils'
8
- import { a11yProps, variantProp, getTokensPropType, getMaxFontMultiplier } from '../utils'
9
-
8
+ import {
9
+ a11yProps,
10
+ variantProp,
11
+ getTokensPropType,
12
+ getMaxFontMultiplier,
13
+ headingTags,
14
+ textTags,
15
+ getA11yPropsFromHtmlTag
16
+ } from '../utils'
10
17
  /**
11
- * If passed a string like 'h1', 'h2' etc, returns the heading number as a string,
12
- * else returns false
18
+ * @typedef {import('../utils/a11y/semantics').TextTag} TextTag
13
19
  */
14
- function getHeadingLevel(heading) {
15
- const match = typeof heading === 'string' && heading.match(/^h(\d)$/)
16
- return match && match[1]
17
- }
18
20
 
19
21
  const selectTextStyles = ({
20
22
  fontWeight,
@@ -38,50 +40,71 @@ const selectTextStyles = ({
38
40
  })
39
41
 
40
42
  // General-purpose flexible theme-neutral base component for text
41
- const Typography = ({
42
- children,
43
- variant,
44
- heading,
45
- block = false,
46
- align = undefined,
47
- accessibilityRole = heading ? 'header' : 'text',
48
- tokens,
49
- ...rest
50
- }) => {
51
- const viewport = useViewport()
52
- const themeTokens = useThemeTokens('Typography', tokens, variant, { viewport })
53
- const textProps = {
54
- style: selectTextStyles(align ? { ...themeTokens, textAlign: align } : themeTokens),
55
- maxFontSizeMultiplier: getMaxFontMultiplier(themeTokens)
56
- }
43
+ const Typography = forwardRef(
44
+ (
45
+ {
46
+ children,
47
+ variant,
48
+ heading,
49
+ tag = typeof heading === 'string' ? heading : undefined,
50
+ accessibilityRole = heading === true ? 'header' : undefined,
51
+ block = false,
52
+ align = undefined,
53
+ tokens,
54
+ dataSet,
55
+ ...rest
56
+ },
57
+ ref
58
+ ) => {
59
+ const viewport = useViewport()
60
+ const themeTokens = useThemeTokens('Typography', tokens, variant, { viewport })
61
+ const textProps = {
62
+ style: selectTextStyles(align ? { ...themeTokens, textAlign: align } : themeTokens),
63
+ dataSet,
64
+ maxFontSizeMultiplier: getMaxFontMultiplier(themeTokens)
65
+ }
57
66
 
58
- const headingLevel = getHeadingLevel(heading)
59
- const a11y = {
60
- ...a11yProps.select(rest),
61
- accessibilityRole,
62
- // On React Native Web, `accessibilityLevel` controls which heading tag (h1, h2 etc) is used.
63
- ...(headingLevel && { accessibilityLevel: headingLevel })
64
- }
67
+ const a11y = {
68
+ ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
69
+ ...a11yProps.select(rest)
70
+ }
65
71
 
66
- return block ? (
67
- <View {...a11y}>
68
- <Text {...textProps}>{children}</Text>
69
- </View>
70
- ) : (
71
- <Text {...textProps} {...a11y}>
72
- {children}
73
- </Text>
74
- )
75
- }
72
+ return block ? (
73
+ <View ref={ref} {...a11y}>
74
+ <Text {...textProps}>{children}</Text>
75
+ </View>
76
+ ) : (
77
+ <Text ref={ref} {...textProps} {...a11y}>
78
+ {children}
79
+ </Text>
80
+ )
81
+ }
82
+ )
83
+ Typography.displayName = 'Typography'
76
84
 
77
85
  Typography.propTypes = {
78
86
  ...a11yProps.types,
79
87
  tokens: getTokensPropType('Typography'),
80
88
  variant: variantProp.propType,
81
89
  /**
82
- * If truthy, will render a heading; if a html heading tag is provided, uses that tag on Web
90
+ * Renders the text as a semantic heading. If a html heading tag is provided, uses that tag on Web.
91
+ *
92
+ * Does not affect styling: <Typography heading="h2"> will render a <h2> that looks like plain text.
93
+ * Use both `heading` and `variant` props to render semantic headings that look like headings.
94
+ *
95
+ * In native apps, if this is `true` or any html heading tag string and accessibilityRole prop
96
+ * is not defined, the accessibilityRole will default to "heading".
97
+ */
98
+ heading: PropTypes.oneOf([...headingTags, true]),
99
+ /**
100
+ * Optional semantic HTML tag, to apply to the text on web. Does not change styling.
101
+ *
102
+ * `tag` and `heading` props set the same thing, so shouldn't be used together (`tag` overrides `heading`).
103
+ *
104
+ * In native apps, if a header tag is provided ("h1", "h2" etc) and accessibilityRole prop
105
+ * is not defined, the accessibilityRole will default to "heading".
83
106
  */
84
- heading: PropTypes.oneOf([true, 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
107
+ tag: PropTypes.oneOf(textTags),
85
108
  /**
86
109
  * If true, forces the element to behave as a View block even if nested in other text
87
110
  */
package/src/index.js CHANGED
@@ -10,12 +10,14 @@ export { default as ExpandCollapse, Accordion } from './ExpandCollapse'
10
10
  export { default as Feedback } from './Feedback'
11
11
  export { default as Fieldset } from './Fieldset'
12
12
  export { default as FlexGrid } from './FlexGrid'
13
+ export { default as HorizontalScroll } from './HorizontalScroll'
14
+ export * from './HorizontalScroll'
13
15
  export { default as Icon } from './Icon'
14
16
  export * from './Icon'
15
17
  export { default as IconButton } from './IconButton'
16
18
  export { default as InputLabel } from './InputLabel'
17
19
  export * from './Link'
18
- export { default as List } from './List'
20
+ export { default as List, ListItem, ListBase } from './List'
19
21
  export { default as Modal } from './Modal'
20
22
  export { default as Notification } from './Notification'
21
23
  export { default as Pagination } from './Pagination'
@@ -1 +1,2 @@
1
1
  export * from './textSize'
2
+ export * from './semantics'
@@ -0,0 +1,61 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ import nativePropTypes from './propTypes.native'
4
+
5
+ export default {
6
+ ...nativePropTypes,
7
+ // React Native Web adds many a11y props that alias aria-* attributes
8
+ // Types based on https://necolas.github.io/react-native-web/docs/accessibility/
9
+ accessibilityActiveDescendant: PropTypes.string,
10
+ accessibilityAtomic: PropTypes.bool,
11
+ accessibilityAutoComplete: PropTypes.string,
12
+ accessibilityBusy: PropTypes.bool,
13
+ accessibilityChecked: PropTypes.oneOf([true, false, 'mixed']),
14
+ accessibilityColumnCount: PropTypes.number,
15
+ accessibilityColumnIndex: PropTypes.number,
16
+ accessibilityColumnSpan: PropTypes.number,
17
+ accessibilityControls: PropTypes.oneOfType([
18
+ PropTypes.string,
19
+ PropTypes.arrayOf(PropTypes.string)
20
+ ]),
21
+ accessibilityCurrent: PropTypes.oneOf([true, false, 'page', 'step', 'location', 'date', 'time']),
22
+ accessibilityDescribedBy: PropTypes.oneOfType([
23
+ PropTypes.string,
24
+ PropTypes.arrayOf(PropTypes.string)
25
+ ]),
26
+ accessibilityDetails: PropTypes.string,
27
+ accessibilityDisabled: PropTypes.bool,
28
+ accessibilityErrorMessage: PropTypes.string,
29
+ accessibilityExpanded: PropTypes.bool,
30
+ accessibilityFlowTo: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
31
+ accessibilityHasPopup: PropTypes.string,
32
+ accessibilityHidden: PropTypes.bool,
33
+ accessibilityInvalid: PropTypes.bool,
34
+ accessibilityKeyShortcuts: PropTypes.string,
35
+ accessibilityLabelledBy: PropTypes.oneOfType([
36
+ PropTypes.string,
37
+ PropTypes.arrayOf(PropTypes.string)
38
+ ]),
39
+ accessibilityLevel: PropTypes.number,
40
+ accessibilityModal: PropTypes.bool,
41
+ accessibilityMultiline: PropTypes.bool,
42
+ accessibilityMultiSelectable: PropTypes.bool,
43
+ accessibilityOrientation: PropTypes.oneOf(['horizontal', 'vertical']),
44
+ accessibilityOwns: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
45
+ accessibilityPlaceholder: PropTypes.string,
46
+ accessibilityPosInSet: PropTypes.number,
47
+ accessibilityPressed: PropTypes.bool,
48
+ accessibilityReadOnly: PropTypes.bool,
49
+ accessibilityRequired: PropTypes.bool,
50
+ accessibilityRoleDescription: PropTypes.string,
51
+ accessibilityRowCount: PropTypes.number,
52
+ accessibilityRowIndex: PropTypes.number,
53
+ accessibilityRowSpan: PropTypes.number,
54
+ accessibilitySelected: PropTypes.bool,
55
+ accessibilitySetSize: PropTypes.number,
56
+ accessibilitySort: PropTypes.oneOf(['ascending', 'descending', 'none', 'other']),
57
+ accessibilityValueMax: PropTypes.number,
58
+ accessibilityValueMin: PropTypes.number,
59
+ accessibilityValueNow: PropTypes.number,
60
+ accessibilityValueText: PropTypes.string
61
+ }
@@ -0,0 +1,39 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ // React Native exports a11y prop definitions as TypeScript Interfaces, but no longer exports PropTypes
4
+ // so we have to define them ourselves.
5
+ export default {
6
+ accessible: PropTypes.bool,
7
+ focusable: PropTypes.bool,
8
+
9
+ accessibilityElementsHidden: PropTypes.bool,
10
+ accessibilityHint: PropTypes.string, // react-native-web ignores `accessibilityHint`
11
+ accessibilityIgnoresInvertColors: PropTypes.bool,
12
+ accessibilityLabel: PropTypes.string,
13
+ accessibilityRole: PropTypes.string,
14
+ accessibilityActions: PropTypes.arrayOf(
15
+ PropTypes.shape({
16
+ name: PropTypes.string.isRequired,
17
+ label: PropTypes.string
18
+ })
19
+ ),
20
+ accessibilityLiveRegion: PropTypes.oneOf(['none', 'polite', 'assertive']),
21
+ accessibilityState: PropTypes.shape({
22
+ disabled: PropTypes.bool,
23
+ selected: PropTypes.bool,
24
+ checked: PropTypes.oneOf([true, false, 'mixed']),
25
+ busy: PropTypes.bool,
26
+ expanded: PropTypes.bool
27
+ }),
28
+ accessibilityValue: PropTypes.shape({
29
+ min: PropTypes.number,
30
+ max: PropTypes.number,
31
+ now: PropTypes.number,
32
+ text: PropTypes.string
33
+ }),
34
+ accessibilityViewIsModal: PropTypes.bool,
35
+ importantForAccessibility: PropTypes.oneOf(['auto', 'yes', 'no', 'no-hide-descendants']),
36
+ onAccessibilityAction: PropTypes.func,
37
+ onAccessibilityEscape: PropTypes.func,
38
+ onAccessibilityTap: PropTypes.func
39
+ }