@telus-uds/components-base 0.0.2-prerelease.3 → 0.0.2-prerelease.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ultra.cache.json +1 -0
- package/CHANGELOG.md +55 -0
- package/__fixtures__/testTheme.js +528 -42
- package/__tests__/Button/ButtonBase.test.jsx +3 -32
- package/__tests__/Checkbox/Checkbox.test.jsx +94 -0
- package/__tests__/Divider/Divider.test.jsx +26 -5
- package/__tests__/Feedback/Feedback.test.jsx +42 -0
- package/__tests__/FlexGrid/Col.test.jsx +5 -0
- package/__tests__/InputSupports/InputSupports.test.jsx +50 -0
- package/__tests__/List/List.test.jsx +60 -0
- package/__tests__/Radio/Radio.test.jsx +87 -0
- package/__tests__/Select/Select.test.jsx +93 -0
- package/__tests__/Skeleton/Skeleton.test.jsx +61 -0
- package/__tests__/Spacer/Spacer.test.jsx +63 -0
- package/__tests__/StackView/StackView.test.jsx +216 -0
- package/__tests__/StackView/StackWrap.test.jsx +47 -0
- package/__tests__/StackView/getStackedContent.test.jsx +295 -0
- package/__tests__/Tags/Tags.test.jsx +328 -0
- package/__tests__/TextInput/TextArea.test.jsx +34 -0
- package/__tests__/TextInput/TextInputBase.test.jsx +120 -0
- package/__tests__/Tooltip/Tooltip.test.jsx +65 -0
- package/__tests__/Tooltip/getTooltipPosition.test.js +79 -0
- package/__tests__/utils/useCopy.test.js +31 -0
- package/__tests__/utils/useResponsiveProp.test.jsx +202 -0
- package/__tests__/utils/{spacing.test.jsx → useSpacingScale.test.jsx} +1 -1
- package/__tests__/utils/useUniqueId.test.js +31 -0
- package/jest.config.js +8 -2
- package/lib/Box/Box.js +7 -2
- package/lib/Button/Button.js +10 -3
- package/lib/Button/ButtonBase.js +79 -75
- package/lib/Button/ButtonGroup.js +24 -49
- package/lib/Button/ButtonLink.js +5 -0
- package/lib/Checkbox/Checkbox.js +308 -0
- package/lib/Checkbox/CheckboxInput.native.js +6 -0
- package/lib/Checkbox/CheckboxInput.web.js +57 -0
- package/lib/Checkbox/index.js +2 -0
- package/lib/Divider/Divider.js +40 -2
- package/lib/Feedback/Feedback.js +132 -0
- package/lib/Feedback/index.js +2 -0
- package/lib/Icon/Icon.js +9 -6
- package/lib/Icon/IconText.js +72 -0
- package/lib/Icon/index.js +2 -1
- package/lib/InputLabel/InputLabel.js +88 -0
- package/lib/InputLabel/LabelContent.native.js +8 -0
- package/lib/InputLabel/LabelContent.web.js +17 -0
- package/lib/InputLabel/index.js +2 -0
- package/lib/InputSupports/InputSupports.js +90 -0
- package/lib/InputSupports/index.js +2 -0
- package/lib/InputSupports/propTypes.js +55 -0
- package/lib/Link/ChevronLink.js +35 -10
- package/lib/Link/InlinePressable.native.js +78 -0
- package/lib/Link/InlinePressable.web.js +32 -0
- package/lib/Link/Link.js +11 -10
- package/lib/Link/LinkBase.js +69 -124
- package/lib/Link/TextButton.js +20 -9
- package/lib/Link/index.js +2 -1
- package/lib/List/List.js +52 -0
- package/lib/List/ListItem.js +207 -0
- package/lib/List/index.js +2 -0
- package/lib/Pagination/PageButton.js +3 -26
- package/lib/Pagination/SideButton.js +32 -42
- package/lib/Radio/Radio.js +291 -0
- package/lib/Radio/RadioInput.native.js +6 -0
- package/lib/Radio/RadioInput.web.js +59 -0
- package/lib/Radio/index.js +2 -0
- package/lib/Select/Group.native.js +14 -0
- package/lib/Select/Group.web.js +18 -0
- package/lib/Select/Item.native.js +9 -0
- package/lib/Select/Item.web.js +15 -0
- package/lib/Select/Picker.native.js +87 -0
- package/lib/Select/Picker.web.js +63 -0
- package/lib/Select/Select.js +272 -0
- package/lib/Select/index.js +6 -0
- package/lib/Skeleton/Skeleton.js +119 -0
- package/lib/Skeleton/index.js +2 -0
- package/lib/Spacer/Spacer.js +98 -0
- package/lib/Spacer/index.js +2 -0
- package/lib/StackView/StackView.js +107 -0
- package/lib/StackView/StackWrap.js +32 -0
- package/lib/StackView/StackWrap.native.js +3 -0
- package/lib/StackView/StackWrapBox.js +90 -0
- package/lib/StackView/StackWrapGap.js +50 -0
- package/lib/StackView/common.js +30 -0
- package/lib/StackView/getStackedContent.js +111 -0
- package/lib/StackView/index.js +5 -0
- package/lib/Tags/Tags.js +217 -0
- package/lib/Tags/index.js +2 -0
- package/lib/TextInput/TextArea.js +82 -0
- package/lib/TextInput/TextInput.js +54 -0
- package/lib/TextInput/TextInputBase.js +229 -0
- package/lib/TextInput/index.js +3 -0
- package/lib/TextInput/propTypes.js +31 -0
- package/lib/ThemeProvider/useThemeTokens.js +54 -3
- package/lib/ToggleSwitch/ToggleSwitch.js +1 -1
- package/lib/Tooltip/Backdrop.native.js +35 -0
- package/lib/Tooltip/Backdrop.web.js +52 -0
- package/lib/Tooltip/Tooltip.js +315 -0
- package/lib/Tooltip/dictionary.js +8 -0
- package/lib/Tooltip/getTooltipPosition.js +164 -0
- package/lib/Tooltip/index.js +2 -0
- package/lib/TooltipButton/TooltipButton.js +64 -0
- package/lib/TooltipButton/index.js +2 -0
- package/lib/Typography/Typography.js +4 -23
- package/lib/ViewportProvider/ViewportProvider.js +25 -0
- package/lib/ViewportProvider/index.js +2 -43
- package/lib/ViewportProvider/useViewport.js +3 -0
- package/lib/ViewportProvider/useViewportListener.js +43 -0
- package/lib/index.js +15 -1
- package/lib/utils/a11y/index.js +1 -0
- package/lib/utils/a11y/textSize.js +33 -0
- package/lib/utils/index.js +7 -1
- package/lib/utils/info/index.js +7 -0
- package/lib/utils/info/platform/index.js +11 -0
- package/lib/utils/info/platform/platform.android.js +1 -0
- package/lib/utils/info/platform/platform.ios.js +1 -0
- package/lib/utils/info/platform/platform.native.js +4 -0
- package/lib/utils/info/platform/platform.web.js +1 -0
- package/lib/utils/info/versions.js +5 -0
- package/lib/utils/input.js +3 -1
- package/lib/utils/pressability.js +92 -0
- package/lib/utils/propTypes.js +77 -8
- package/lib/utils/useCopy.js +16 -0
- package/lib/utils/useResponsiveProp.js +47 -0
- package/lib/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +30 -9
- package/lib/utils/useUniqueId.js +12 -0
- package/package.json +7 -5
- package/release-context.json +4 -4
- package/src/Box/Box.jsx +4 -2
- package/src/Button/Button.jsx +6 -3
- package/src/Button/ButtonBase.jsx +72 -75
- package/src/Button/ButtonGroup.jsx +22 -39
- package/src/Button/ButtonLink.jsx +11 -2
- package/src/Checkbox/Checkbox.jsx +275 -0
- package/src/Checkbox/CheckboxInput.native.jsx +6 -0
- package/src/Checkbox/CheckboxInput.web.jsx +55 -0
- package/src/Checkbox/index.js +3 -0
- package/src/Divider/Divider.jsx +38 -3
- package/src/Feedback/Feedback.jsx +108 -0
- package/src/Feedback/index.js +3 -0
- package/src/Icon/Icon.jsx +11 -6
- package/src/Icon/IconText.jsx +63 -0
- package/src/Icon/index.js +2 -1
- package/src/InputLabel/InputLabel.jsx +99 -0
- package/src/InputLabel/LabelContent.native.jsx +6 -0
- package/src/InputLabel/LabelContent.web.jsx +13 -0
- package/src/InputLabel/index.js +3 -0
- package/src/InputSupports/InputSupports.jsx +86 -0
- package/src/InputSupports/index.js +3 -0
- package/src/InputSupports/propTypes.js +44 -0
- package/src/Link/ChevronLink.jsx +28 -7
- package/src/Link/InlinePressable.native.jsx +73 -0
- package/src/Link/InlinePressable.web.jsx +37 -0
- package/src/Link/Link.jsx +17 -13
- package/src/Link/LinkBase.jsx +62 -139
- package/src/Link/TextButton.jsx +25 -11
- package/src/Link/index.js +2 -1
- package/src/List/List.jsx +47 -0
- package/src/List/ListItem.jsx +187 -0
- package/src/List/index.js +3 -0
- package/src/Pagination/PageButton.jsx +3 -17
- package/src/Pagination/SideButton.jsx +27 -38
- package/src/Radio/Radio.jsx +270 -0
- package/src/Radio/RadioInput.native.jsx +6 -0
- package/src/Radio/RadioInput.web.jsx +57 -0
- package/src/Radio/index.js +3 -0
- package/src/Select/Group.native.jsx +14 -0
- package/src/Select/Group.web.jsx +15 -0
- package/src/Select/Item.native.jsx +10 -0
- package/src/Select/Item.web.jsx +11 -0
- package/src/Select/Picker.native.jsx +95 -0
- package/src/Select/Picker.web.jsx +67 -0
- package/src/Select/Select.jsx +265 -0
- package/src/Select/index.js +8 -0
- package/src/Skeleton/Skeleton.jsx +101 -0
- package/src/Skeleton/index.js +3 -0
- package/src/Spacer/Spacer.jsx +91 -0
- package/src/Spacer/index.js +3 -0
- package/src/StackView/StackView.jsx +104 -0
- package/src/StackView/StackWrap.jsx +33 -0
- package/src/StackView/StackWrap.native.jsx +4 -0
- package/src/StackView/StackWrapBox.jsx +93 -0
- package/src/StackView/StackWrapGap.jsx +49 -0
- package/src/StackView/common.jsx +28 -0
- package/src/StackView/getStackedContent.jsx +106 -0
- package/src/StackView/index.js +6 -0
- package/src/Tags/Tags.jsx +206 -0
- package/src/Tags/index.js +3 -0
- package/src/TextInput/TextArea.jsx +78 -0
- package/src/TextInput/TextInput.jsx +52 -0
- package/src/TextInput/TextInputBase.jsx +220 -0
- package/src/TextInput/index.js +4 -0
- package/src/TextInput/propTypes.js +29 -0
- package/src/ThemeProvider/useThemeTokens.js +54 -3
- package/src/ToggleSwitch/ToggleSwitch.jsx +1 -1
- package/src/Tooltip/Backdrop.native.jsx +33 -0
- package/src/Tooltip/Backdrop.web.jsx +60 -0
- package/src/Tooltip/Tooltip.jsx +294 -0
- package/src/Tooltip/dictionary.js +8 -0
- package/src/Tooltip/getTooltipPosition.js +161 -0
- package/src/Tooltip/index.js +3 -0
- package/src/TooltipButton/TooltipButton.jsx +53 -0
- package/src/TooltipButton/index.js +3 -0
- package/src/Typography/Typography.jsx +4 -19
- package/src/ViewportProvider/ViewportProvider.jsx +21 -0
- package/src/ViewportProvider/index.jsx +2 -41
- package/src/ViewportProvider/useViewport.js +5 -0
- package/src/ViewportProvider/useViewportListener.js +43 -0
- package/src/index.js +15 -1
- package/src/utils/a11y/index.js +1 -0
- package/src/utils/a11y/textSize.js +30 -0
- package/src/utils/index.js +8 -1
- package/src/utils/info/index.js +8 -0
- package/src/utils/info/platform/index.js +11 -0
- package/src/utils/info/platform/platform.android.js +1 -0
- package/src/utils/info/platform/platform.ios.js +1 -0
- package/src/utils/info/platform/platform.native.js +4 -0
- package/src/utils/info/platform/platform.web.js +1 -0
- package/src/utils/info/versions.js +6 -0
- package/src/utils/input.js +2 -1
- package/src/utils/pressability.js +92 -0
- package/src/utils/propTypes.js +97 -13
- package/src/utils/useCopy.js +13 -0
- package/src/utils/useResponsiveProp.js +50 -0
- package/src/utils/{spacing/useSpacingScale.js → useSpacingScale.js} +25 -10
- package/src/utils/useUniqueId.js +14 -0
- package/stories/A11yText/A11yText.stories.jsx +11 -5
- package/stories/ActivityIndicator/ActivityIndicator.stories.jsx +11 -2
- package/stories/Box/Box.stories.jsx +29 -2
- package/stories/Button/Button.stories.jsx +21 -20
- package/stories/Button/ButtonGroup.stories.jsx +2 -1
- package/stories/Button/ButtonLink.stories.jsx +6 -4
- package/stories/Card/Card.stories.jsx +13 -1
- package/stories/Checkbox/Checkbox.stories.jsx +71 -0
- package/stories/Divider/Divider.stories.jsx +26 -2
- package/stories/ExpandCollapse/ExpandCollapse.stories.jsx +74 -79
- package/stories/Feedback/Feedback.stories.jsx +96 -0
- package/stories/FlexGrid/01 FlexGrid.stories.jsx +20 -7
- package/stories/Icon/Icon.stories.jsx +11 -3
- package/stories/InputLabel/InputLabel.stories.jsx +42 -0
- package/stories/Link/ChevronLink.stories.jsx +20 -4
- package/stories/Link/Link.stories.jsx +39 -3
- package/stories/Link/TextButton.stories.jsx +24 -2
- package/stories/List/List.stories.jsx +117 -0
- package/stories/Pagination/Pagination.stories.jsx +28 -14
- package/stories/Radio/Radio.stories.jsx +113 -0
- package/stories/Select/Select.stories.jsx +55 -0
- package/stories/SideNav/SideNav.stories.jsx +17 -2
- package/stories/Skeleton/Skeleton.stories.jsx +36 -0
- package/stories/Spacer/Spacer.stories.jsx +38 -0
- package/stories/StackView/StackView.stories.jsx +75 -0
- package/stories/StackView/StackWrap.stories.jsx +64 -0
- package/stories/Tags/Tags.stories.jsx +69 -0
- package/stories/TextInput/TextArea.stories.jsx +100 -0
- package/stories/TextInput/TextInput.stories.jsx +103 -0
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +16 -3
- package/stories/Tooltip/Tooltip.stories.jsx +81 -0
- package/stories/TooltipButton/TooltipButton.stories.jsx +11 -0
- package/stories/Typography/Typography.stories.jsx +12 -3
- package/stories/platform-supports.web.jsx +1 -1
- package/stories/supports.jsx +110 -14
- package/lib/Pagination/useCopy.js +0 -10
- package/lib/utils/spacing/index.js +0 -2
- package/lib/utils/spacing/utils.js +0 -32
- package/src/Pagination/useCopy.js +0 -7
- package/src/utils/spacing/index.js +0 -3
- package/src/utils/spacing/utils.js +0 -28
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { renderHook } from '@testing-library/react-hooks'
|
|
3
|
+
import useResponsiveProp from '../../src/utils/useResponsiveProp'
|
|
4
|
+
import Viewport from '../../__fixtures__/Viewport'
|
|
5
|
+
|
|
6
|
+
const wrapper = ({ children, viewport }) => <Viewport viewport={viewport}>{children}</Viewport>
|
|
7
|
+
|
|
8
|
+
describe('useResponsiveProp', () => {
|
|
9
|
+
it('returns viewport indexes that explicitly match current value', () => {
|
|
10
|
+
const responsiveProp = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }
|
|
11
|
+
const { rerender, result } = renderHook((args) => useResponsiveProp(args.responsiveProp), {
|
|
12
|
+
wrapper,
|
|
13
|
+
initialProps: { viewport: 'xs', responsiveProp }
|
|
14
|
+
})
|
|
15
|
+
expect(result.current).toBe(1)
|
|
16
|
+
rerender({ viewport: 'sm', responsiveProp })
|
|
17
|
+
expect(result.current).toBe(2)
|
|
18
|
+
rerender({ viewport: 'md', responsiveProp })
|
|
19
|
+
expect(result.current).toBe(3)
|
|
20
|
+
rerender({ viewport: 'lg', responsiveProp })
|
|
21
|
+
expect(result.current).toBe(4)
|
|
22
|
+
rerender({ viewport: 'xl', responsiveProp })
|
|
23
|
+
expect(result.current).toBe(5)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('returns viewport indexes that implicitly match current value', () => {
|
|
27
|
+
const responsiveProp = { sm: 2, lg: 4 }
|
|
28
|
+
const { rerender, result } = renderHook((args) => useResponsiveProp(args.responsiveProp), {
|
|
29
|
+
wrapper,
|
|
30
|
+
initialProps: { viewport: 'xs', responsiveProp }
|
|
31
|
+
})
|
|
32
|
+
expect(result.current).toBe(undefined)
|
|
33
|
+
rerender({ viewport: 'sm', responsiveProp })
|
|
34
|
+
expect(result.current).toBe(2)
|
|
35
|
+
rerender({ viewport: 'md', responsiveProp })
|
|
36
|
+
expect(result.current).toBe(2)
|
|
37
|
+
rerender({ viewport: 'lg', responsiveProp })
|
|
38
|
+
expect(result.current).toBe(4)
|
|
39
|
+
rerender({ viewport: 'xl', responsiveProp })
|
|
40
|
+
expect(result.current).toBe(4)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('returns provided defaultValue where there is no match', () => {
|
|
44
|
+
const responsiveProp = { md: 2, lg: 4 }
|
|
45
|
+
const defaultValue = 'some default value'
|
|
46
|
+
const { rerender, result } = renderHook(
|
|
47
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
48
|
+
{
|
|
49
|
+
wrapper,
|
|
50
|
+
initialProps: { viewport: 'xs', responsiveProp, defaultValue }
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
expect(result.current).toBe(defaultValue)
|
|
54
|
+
rerender({ viewport: 'sm', responsiveProp, defaultValue })
|
|
55
|
+
expect(result.current).toBe(defaultValue)
|
|
56
|
+
rerender({ viewport: 'md', responsiveProp, defaultValue })
|
|
57
|
+
expect(result.current).toBe(2)
|
|
58
|
+
rerender({ viewport: 'lg', responsiveProp, defaultValue })
|
|
59
|
+
expect(result.current).toBe(4)
|
|
60
|
+
rerender({ viewport: 'xl', responsiveProp, defaultValue })
|
|
61
|
+
expect(result.current).toBe(4)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('applies falsy values appropriately (replacing `undefined` with default value)', () => {
|
|
65
|
+
const responsiveProp = { sm: '', md: null, lg: false, xl: undefined }
|
|
66
|
+
const defaultValue = 0
|
|
67
|
+
const { rerender, result } = renderHook(
|
|
68
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
69
|
+
{
|
|
70
|
+
wrapper,
|
|
71
|
+
initialProps: { viewport: 'xs', responsiveProp, defaultValue }
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
expect(result.current).toBe(0)
|
|
75
|
+
rerender({ viewport: 'sm', responsiveProp, defaultValue })
|
|
76
|
+
expect(result.current).toBe('')
|
|
77
|
+
rerender({ viewport: 'md', responsiveProp, defaultValue })
|
|
78
|
+
expect(result.current).toBe(null)
|
|
79
|
+
rerender({ viewport: 'lg', responsiveProp, defaultValue })
|
|
80
|
+
expect(result.current).toBe(false)
|
|
81
|
+
rerender({ viewport: 'xl', responsiveProp, defaultValue })
|
|
82
|
+
expect(result.current).toBe(false)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('returns a non-object prop unchanged', () => {
|
|
86
|
+
const exactFn = () => {}
|
|
87
|
+
const defaultValue = 'some value'
|
|
88
|
+
const { rerender, result } = renderHook(
|
|
89
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
90
|
+
{
|
|
91
|
+
wrapper,
|
|
92
|
+
initialProps: { viewport: 'xs', responsiveProp: exactFn, defaultValue }
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
expect(result.current).toBe(exactFn)
|
|
96
|
+
rerender({ viewport: 'sm', responsiveProp: exactFn, defaultValue })
|
|
97
|
+
expect(result.current).toBe(exactFn)
|
|
98
|
+
rerender({ viewport: 'md', responsiveProp: exactFn, defaultValue })
|
|
99
|
+
expect(result.current).toBe(exactFn)
|
|
100
|
+
rerender({ viewport: 'lg', responsiveProp: exactFn, defaultValue })
|
|
101
|
+
expect(result.current).toBe(exactFn)
|
|
102
|
+
rerender({ viewport: 'xl', responsiveProp: exactFn, defaultValue })
|
|
103
|
+
expect(result.current).toBe(exactFn)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('returns a falsy value including explicit null unchanged, default value if undefined', () => {
|
|
107
|
+
const defaultValue = 'some value'
|
|
108
|
+
const { rerender, result } = renderHook(
|
|
109
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
110
|
+
{
|
|
111
|
+
wrapper,
|
|
112
|
+
initialProps: { viewport: 'xs', responsiveProp: null, defaultValue }
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
expect(result.current).toBe(null)
|
|
116
|
+
rerender({ viewport: 'sm', responsiveProp: false, defaultValue })
|
|
117
|
+
expect(result.current).toBe(false)
|
|
118
|
+
rerender({ viewport: 'md', responsiveProp: 0, defaultValue })
|
|
119
|
+
expect(result.current).toBe(0)
|
|
120
|
+
rerender({ viewport: 'lg', responsiveProp: '', defaultValue })
|
|
121
|
+
expect(result.current).toBe('')
|
|
122
|
+
rerender({ viewport: 'xl', responsiveProp: undefined, defaultValue })
|
|
123
|
+
expect(result.current).toBe(defaultValue)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('returns a non-responsive object unchanged, rather than the default value', () => {
|
|
127
|
+
const nonresponsiveProp = { a: 1, b: 2 }
|
|
128
|
+
const defaultValue = 'some value'
|
|
129
|
+
const { rerender, result } = renderHook(
|
|
130
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
131
|
+
{
|
|
132
|
+
wrapper,
|
|
133
|
+
initialProps: { viewport: 'xs', responsiveProp: nonresponsiveProp, defaultValue }
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
expect(result.current).toBe(nonresponsiveProp)
|
|
137
|
+
rerender({ viewport: 'sm', responsiveProp: nonresponsiveProp, defaultValue })
|
|
138
|
+
expect(result.current).toBe(nonresponsiveProp)
|
|
139
|
+
rerender({ viewport: 'md', responsiveProp: nonresponsiveProp, defaultValue })
|
|
140
|
+
expect(result.current).toBe(nonresponsiveProp)
|
|
141
|
+
rerender({ viewport: 'lg', responsiveProp: nonresponsiveProp, defaultValue })
|
|
142
|
+
expect(result.current).toBe(nonresponsiveProp)
|
|
143
|
+
rerender({ viewport: 'xl', responsiveProp: nonresponsiveProp, defaultValue })
|
|
144
|
+
expect(result.current).toBe(nonresponsiveProp)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('returns the smallest viewport property if current viewport value is missing', () => {
|
|
148
|
+
const responsiveProp = { xl: 5, lg: 4, md: 3 }
|
|
149
|
+
const defaultValue = 'some value'
|
|
150
|
+
const { result } = renderHook(
|
|
151
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
152
|
+
{
|
|
153
|
+
initialProps: { responsiveProp, defaultValue }
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
expect(result.current).toBe(3)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('treats a responsive prop with extra non-responsive properties as responsive', () => {
|
|
160
|
+
const responsiveProp = { md: 3, lg: 4, options: { a: 1, b: 2 } }
|
|
161
|
+
const defaultValue = 'some value'
|
|
162
|
+
const { rerender, result } = renderHook(
|
|
163
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
164
|
+
{
|
|
165
|
+
wrapper,
|
|
166
|
+
initialProps: { viewport: 'xs', responsiveProp, defaultValue }
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
expect(result.current).toBe(defaultValue)
|
|
170
|
+
rerender({ viewport: 'sm', responsiveProp, defaultValue })
|
|
171
|
+
expect(result.current).toBe(defaultValue)
|
|
172
|
+
rerender({ viewport: 'md', responsiveProp, defaultValue })
|
|
173
|
+
expect(result.current).toBe(3)
|
|
174
|
+
rerender({ viewport: 'lg', responsiveProp, defaultValue })
|
|
175
|
+
expect(result.current).toBe(4)
|
|
176
|
+
rerender({ viewport: 'xl', responsiveProp, defaultValue })
|
|
177
|
+
expect(result.current).toBe(4)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('treats an object with only explicitly-undefined viewport properties as responsive', () => {
|
|
181
|
+
// For example, if some logic generates the values for each viewport of a responsive prop,
|
|
182
|
+
// and in a certain case, they happen to all come out as `undefined`.
|
|
183
|
+
const responsiveProp = { sm: undefined, lg: undefined, options: { a: 1, b: 2 } }
|
|
184
|
+
const defaultValue = 'some value'
|
|
185
|
+
const { rerender, result } = renderHook(
|
|
186
|
+
(args) => useResponsiveProp(args.responsiveProp, args.defaultValue),
|
|
187
|
+
{
|
|
188
|
+
wrapper,
|
|
189
|
+
initialProps: { viewport: 'xs', responsiveProp, defaultValue }
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
expect(result.current).toBe(defaultValue)
|
|
193
|
+
rerender({ viewport: 'sm', responsiveProp, defaultValue })
|
|
194
|
+
expect(result.current).toBe(defaultValue)
|
|
195
|
+
rerender({ viewport: 'md', responsiveProp, defaultValue })
|
|
196
|
+
expect(result.current).toBe(defaultValue)
|
|
197
|
+
rerender({ viewport: 'lg', responsiveProp, defaultValue })
|
|
198
|
+
expect(result.current).toBe(defaultValue)
|
|
199
|
+
rerender({ viewport: 'xl', responsiveProp, defaultValue })
|
|
200
|
+
expect(result.current).toBe(defaultValue)
|
|
201
|
+
})
|
|
202
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { renderHook } from '@testing-library/react-hooks'
|
|
3
|
-
import { useSpacingScale } from '../../src/utils
|
|
3
|
+
import { useSpacingScale } from '../../src/utils'
|
|
4
4
|
import Theme from '../../__fixtures__/Theme'
|
|
5
5
|
import Viewport from '../../__fixtures__/Viewport'
|
|
6
6
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks'
|
|
2
|
+
import useUniqueId from '../../lib/utils/useUniqueId'
|
|
3
|
+
|
|
4
|
+
describe('useUniqueId hook', () => {
|
|
5
|
+
it('returns the same id for each re-render, but a different id for a new instance', () => {
|
|
6
|
+
const { result, rerender } = renderHook(() => useUniqueId('prefix'))
|
|
7
|
+
|
|
8
|
+
expect(result.current).toBe('prefix-1')
|
|
9
|
+
|
|
10
|
+
rerender()
|
|
11
|
+
|
|
12
|
+
expect(result.current).toBe('prefix-1')
|
|
13
|
+
|
|
14
|
+
// has to be done within the same test case, as we can't ensure order in which tests are being run in parallel
|
|
15
|
+
const { result: result2, rerender: rerender2 } = renderHook(() => useUniqueId('prefix'))
|
|
16
|
+
|
|
17
|
+
expect(result.current).toBe('prefix-1')
|
|
18
|
+
expect(result2.current).toBe('prefix-2')
|
|
19
|
+
|
|
20
|
+
// ensure that instances' rerendering doesn't affect one another
|
|
21
|
+
rerender2()
|
|
22
|
+
|
|
23
|
+
expect(result.current).toBe('prefix-1')
|
|
24
|
+
expect(result2.current).toBe('prefix-2')
|
|
25
|
+
|
|
26
|
+
rerender()
|
|
27
|
+
|
|
28
|
+
expect(result.current).toBe('prefix-1')
|
|
29
|
+
expect(result2.current).toBe('prefix-2')
|
|
30
|
+
})
|
|
31
|
+
})
|
package/jest.config.js
CHANGED
|
@@ -13,11 +13,17 @@ module.exports = {
|
|
|
13
13
|
// __dirname here tells babel to look in components-base for babel root when running from monorepo root
|
|
14
14
|
'\\.(js|jsx)$': ['babel-jest', { cwd: __dirname }]
|
|
15
15
|
},
|
|
16
|
-
setupFilesAfterEnv: [
|
|
16
|
+
setupFilesAfterEnv: [
|
|
17
|
+
'@testing-library/jest-native/extend-expect',
|
|
18
|
+
// Fail tests that use console.error or console.warn
|
|
19
|
+
'<rootDir>/../../jest-no-console'
|
|
20
|
+
],
|
|
17
21
|
moduleNameMapper: {
|
|
18
22
|
'.+\\.(otf|svg|png|jpg)$': 'identity-obj-proxy'
|
|
19
23
|
},
|
|
20
|
-
transformIgnorePatterns:
|
|
24
|
+
transformIgnorePatterns: [
|
|
25
|
+
'node_modules/(?!(jest-)?react-native|@react-native-community|@react-native-picker)'
|
|
26
|
+
],
|
|
21
27
|
// Count everything in src when calculating coverage
|
|
22
28
|
collectCoverageFrom: ['src/**/*.{js,jsx}']
|
|
23
29
|
}
|
package/lib/Box/Box.js
CHANGED
|
@@ -102,6 +102,7 @@ const Box = ({
|
|
|
102
102
|
variant,
|
|
103
103
|
tokens,
|
|
104
104
|
scroll,
|
|
105
|
+
testID,
|
|
105
106
|
...rest
|
|
106
107
|
}) => {
|
|
107
108
|
const a11y = a11yProps.select(rest);
|
|
@@ -117,11 +118,14 @@ const Box = ({
|
|
|
117
118
|
if (scroll) {
|
|
118
119
|
const scrollProps = typeof scroll === 'object' ? scroll : {};
|
|
119
120
|
scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle];
|
|
120
|
-
return /*#__PURE__*/React.createElement(ScrollView, Object.assign({}, scrollProps, a11y
|
|
121
|
+
return /*#__PURE__*/React.createElement(ScrollView, Object.assign({}, scrollProps, a11y, {
|
|
122
|
+
testID: testID
|
|
123
|
+
}), children);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
return /*#__PURE__*/React.createElement(View, Object.assign({}, a11y, {
|
|
124
|
-
style: styles
|
|
127
|
+
style: styles,
|
|
128
|
+
testID: testID
|
|
125
129
|
}), children);
|
|
126
130
|
};
|
|
127
131
|
|
|
@@ -136,6 +140,7 @@ Box.propTypes = {
|
|
|
136
140
|
scroll: PropTypes.oneOfType([PropTypes.bool, ScrollView.propTypes ? PropTypes.shape(ScrollView.propTypes) : PropTypes.object]),
|
|
137
141
|
variant: variantProp.propType,
|
|
138
142
|
tokens: getTokensPropType('Box'),
|
|
143
|
+
testID: PropTypes.string,
|
|
139
144
|
children: PropTypes.node.isRequired
|
|
140
145
|
};
|
|
141
146
|
export default Box;
|
package/lib/Button/Button.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import ButtonBase from './ButtonBase';
|
|
3
3
|
import buttonPropTypes from './propTypes';
|
|
4
|
+
import { useThemeTokensCallback } from '../ThemeProvider';
|
|
4
5
|
import { a11yProps } from '../utils/propTypes';
|
|
5
6
|
|
|
6
7
|
const Button = ({
|
|
7
8
|
accessibilityRole = 'button',
|
|
9
|
+
tokens,
|
|
10
|
+
variant,
|
|
8
11
|
...props
|
|
9
|
-
}) =>
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
+
}) => {
|
|
13
|
+
const getTokens = useThemeTokensCallback('Button', tokens, variant);
|
|
14
|
+
return /*#__PURE__*/React.createElement(ButtonBase, Object.assign({}, props, {
|
|
15
|
+
tokens: getTokens,
|
|
16
|
+
accessibilityRole: accessibilityRole
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
12
19
|
|
|
13
20
|
Button.propTypes = { ...a11yProps.types,
|
|
14
21
|
...buttonPropTypes
|
package/lib/Button/ButtonBase.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Pressable, Text, View, StyleSheet, Platform } from 'react-native';
|
|
3
|
-
import { useThemeTokensCallback } from '../ThemeProvider';
|
|
4
3
|
import { applyTextStyles, applyShadowToken } from '../ThemeProvider/utils';
|
|
5
4
|
import buttonPropTypes from './propTypes';
|
|
6
|
-
import { a11yProps, hrefAttrsProp, linkProps } from '../utils
|
|
7
|
-
|
|
8
|
-
const getCursorStyle = (inactive, accessibilityRole) => {
|
|
9
|
-
if (inactive) return 'not-allowed'; // These roles should result in cursor: pointer but don't in current RNW releases
|
|
10
|
-
|
|
11
|
-
if (['checkbox', 'radio', 'switch'].includes(accessibilityRole)) return 'pointer'; // For everything else, let React Native Web figure it out internally
|
|
12
|
-
|
|
13
|
-
return undefined;
|
|
14
|
-
};
|
|
5
|
+
import { a11yProps, getCursorStyle, hrefAttrsProp, linkProps, resolvePressableState, resolvePressableTokens } from '../utils';
|
|
15
6
|
|
|
16
7
|
const getOuterBorderOffset = ({
|
|
17
8
|
outerBorderGap = 0,
|
|
@@ -19,6 +10,7 @@ const getOuterBorderOffset = ({
|
|
|
19
10
|
}) => outerBorderGap + outerBorderWidth;
|
|
20
11
|
|
|
21
12
|
const selectOuterContainerStyles = ({
|
|
13
|
+
alignSelf,
|
|
22
14
|
opacity,
|
|
23
15
|
outerBorderColor,
|
|
24
16
|
outerBorderWidth,
|
|
@@ -26,6 +18,7 @@ const selectOuterContainerStyles = ({
|
|
|
26
18
|
outerBorderRadius = 0,
|
|
27
19
|
outerBackgroundColor
|
|
28
20
|
}) => ({
|
|
21
|
+
alignSelf,
|
|
29
22
|
padding: outerBorderGap,
|
|
30
23
|
borderWidth: outerBorderWidth,
|
|
31
24
|
borderColor: outerBorderColor,
|
|
@@ -37,13 +30,7 @@ const selectOuterContainerStyles = ({
|
|
|
37
30
|
const selectOuterWidthStyles = ({
|
|
38
31
|
outerBorderGap,
|
|
39
32
|
outerBorderWidth,
|
|
40
|
-
width
|
|
41
|
-
// TODO: make margin the responsibility of a parent
|
|
42
|
-
// https://github.com/telus/universal-design-system/issues/525
|
|
43
|
-
marginTop = 0,
|
|
44
|
-
marginBottom = 0,
|
|
45
|
-
marginLeft = 0,
|
|
46
|
-
marginRight = 0
|
|
33
|
+
width
|
|
47
34
|
}) => {
|
|
48
35
|
// The inner container's bounding box is the bounding box of the button overall
|
|
49
36
|
// so this many device pixels will sit outside of the overall bounding box
|
|
@@ -52,22 +39,16 @@ const selectOuterWidthStyles = ({
|
|
|
52
39
|
outerBorderWidth
|
|
53
40
|
});
|
|
54
41
|
const widthStyles = {
|
|
55
|
-
|
|
56
|
-
marginBottom: marginBottom - outerBorderOffset,
|
|
57
|
-
marginLeft: marginLeft - outerBorderOffset,
|
|
58
|
-
marginRight: marginRight - outerBorderOffset
|
|
42
|
+
margin: 0 - outerBorderOffset
|
|
59
43
|
};
|
|
60
44
|
|
|
61
45
|
if (!width) {
|
|
62
46
|
return { ...widthStyles,
|
|
63
47
|
// Wrap content, stopping a flex parent's default align-items: stretch stretching focus ring beyond content
|
|
64
48
|
...Platform.select({
|
|
49
|
+
// width: fit-content isn't supported on Firefox; can't cascade props like CSS `width: fit-content; width: --moz-fit-content;`
|
|
65
50
|
web: {
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
// No fit-content or inline-block in RN. TODO: we might need to provide a prop to allow flex-end or center
|
|
69
|
-
native: {
|
|
70
|
-
alignSelf: 'flex-start'
|
|
51
|
+
display: 'inline-flex'
|
|
71
52
|
}
|
|
72
53
|
})
|
|
73
54
|
};
|
|
@@ -104,7 +85,8 @@ const selectInnerContainerStyles = ({
|
|
|
104
85
|
paddingTop,
|
|
105
86
|
paddingBottom,
|
|
106
87
|
shadow,
|
|
107
|
-
borderWidth
|
|
88
|
+
borderWidth,
|
|
89
|
+
minWidth
|
|
108
90
|
}) => {
|
|
109
91
|
// Subtract border width from padding so overall button width/height doesn't
|
|
110
92
|
// jump around if the border width changes (avoiding NaN and negative padding)
|
|
@@ -116,6 +98,7 @@ const selectInnerContainerStyles = ({
|
|
|
116
98
|
paddingTop: offsetBorder(paddingTop),
|
|
117
99
|
paddingBottom: offsetBorder(paddingBottom),
|
|
118
100
|
backgroundColor,
|
|
101
|
+
minWidth,
|
|
119
102
|
...applyShadowToken(shadow)
|
|
120
103
|
};
|
|
121
104
|
};
|
|
@@ -148,76 +131,83 @@ const selectTextStyles = ({
|
|
|
148
131
|
|
|
149
132
|
const selectWebOnlyStyles = (inactive, themeTokens, {
|
|
150
133
|
accessibilityRole
|
|
151
|
-
}) =>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
134
|
+
}) => {
|
|
135
|
+
return Platform.select({
|
|
136
|
+
web: {
|
|
137
|
+
// if it would overflow the container, wraps instead
|
|
138
|
+
maxWidth: `calc(100% + ${getOuterBorderOffset(themeTokens) * 2}px)`,
|
|
139
|
+
outline: 'none',
|
|
140
|
+
// removes the default browser :focus outline
|
|
141
|
+
...getCursorStyle(inactive, accessibilityRole)
|
|
142
|
+
},
|
|
143
|
+
default: {}
|
|
144
|
+
});
|
|
145
|
+
}; // TODO: see if this can be made into a generalised utility, ideally when
|
|
146
|
+
// there is a stable, generic, generalised approach to within-component text
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
const resolveChildren = (children, {
|
|
150
|
+
textStyles,
|
|
151
|
+
state
|
|
152
|
+
}) => {
|
|
153
|
+
switch (typeof children) {
|
|
154
|
+
case 'function':
|
|
155
|
+
return children(state);
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
case 'string':
|
|
158
|
+
return /*#__PURE__*/React.createElement(Text, {
|
|
159
|
+
style: textStyles
|
|
160
|
+
}, children);
|
|
161
|
+
|
|
162
|
+
default:
|
|
163
|
+
return children;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
158
166
|
|
|
159
167
|
const ButtonBase = ({
|
|
160
168
|
href,
|
|
161
169
|
hrefAttrs,
|
|
162
170
|
children,
|
|
163
|
-
variant,
|
|
164
171
|
onPress,
|
|
165
|
-
tokens,
|
|
172
|
+
tokens = {},
|
|
166
173
|
disabled = false,
|
|
167
174
|
// alias for inactive
|
|
168
175
|
inactive = disabled,
|
|
169
176
|
selected = false,
|
|
170
177
|
...rest
|
|
171
178
|
}) => {
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const getButtonState = ({
|
|
175
|
-
pressed,
|
|
176
|
-
focused,
|
|
177
|
-
hovered
|
|
178
|
-
}) => ({
|
|
179
|
-
pressed,
|
|
180
|
-
focus: focused,
|
|
181
|
-
hover: hovered,
|
|
179
|
+
const extraButtonState = {
|
|
182
180
|
inactive,
|
|
183
181
|
selected
|
|
184
|
-
}
|
|
182
|
+
};
|
|
185
183
|
|
|
186
|
-
const
|
|
184
|
+
const resolveTokens = pressableState => resolvePressableTokens(tokens, pressableState, extraButtonState);
|
|
187
185
|
|
|
188
186
|
const a11y = a11yProps.select(rest);
|
|
189
187
|
|
|
190
188
|
const getPressableStyle = pressableState => {
|
|
191
|
-
const themeTokens =
|
|
192
|
-
return [staticStyles.
|
|
189
|
+
const themeTokens = resolveTokens(pressableState);
|
|
190
|
+
return [staticStyles.row, selectWebOnlyStyles(inactive, themeTokens, a11y), selectOuterContainerStyles(themeTokens), selectOuterWidthStyles(themeTokens)];
|
|
193
191
|
};
|
|
194
192
|
|
|
195
|
-
const handlePress = linkProps.handleHref({
|
|
196
|
-
href,
|
|
197
|
-
onPress
|
|
198
|
-
});
|
|
199
193
|
return /*#__PURE__*/React.createElement(Pressable, Object.assign({
|
|
200
|
-
|
|
194
|
+
href: href,
|
|
195
|
+
onPress: linkProps.handleHref({
|
|
196
|
+
href,
|
|
197
|
+
onPress
|
|
198
|
+
}),
|
|
201
199
|
style: getPressableStyle,
|
|
202
|
-
disabled: inactive
|
|
203
|
-
href: href
|
|
200
|
+
disabled: inactive
|
|
204
201
|
}, hrefAttrsProp.spread(hrefAttrs), a11y), pressableState => {
|
|
205
|
-
const themeTokens =
|
|
202
|
+
const themeTokens = resolveTokens(pressableState);
|
|
206
203
|
const containerStyles = selectInnerContainerStyles(themeTokens);
|
|
207
204
|
const borderStyles = selectBorderStyles(themeTokens);
|
|
208
|
-
const textStyles =
|
|
209
|
-
...Platform.select({
|
|
210
|
-
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
211
|
-
web: {
|
|
212
|
-
transition: 'color 200ms'
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
}; // If the container has a width set, fill it instead of sizing from content.
|
|
205
|
+
const textStyles = [selectTextStyles(themeTokens), staticStyles.text]; // If the container has a width set, fill it instead of sizing from content.
|
|
216
206
|
// If in future we support text alignments other than center, add here.
|
|
217
207
|
|
|
218
|
-
const stretchStyles =
|
|
208
|
+
const stretchStyles = themeTokens.width ? staticStyles.stretch : staticStyles.align;
|
|
219
209
|
return /*#__PURE__*/React.createElement(View, {
|
|
220
|
-
style: [containerStyles, borderStyles, stretchStyles, Platform.select({
|
|
210
|
+
style: [containerStyles, borderStyles, stretchStyles, staticStyles.row, Platform.select({
|
|
221
211
|
web: {
|
|
222
212
|
maxWidth: '100%',
|
|
223
213
|
// ensure overflowing content wraps
|
|
@@ -225,11 +215,12 @@ const ButtonBase = ({
|
|
|
225
215
|
transition: 'background-color 200ms, border-color 200ms'
|
|
226
216
|
}
|
|
227
217
|
})]
|
|
228
|
-
},
|
|
218
|
+
}, resolveChildren(children, {
|
|
219
|
+
state: { ...resolvePressableState(pressableState, extraButtonState),
|
|
220
|
+
textStyles
|
|
221
|
+
},
|
|
229
222
|
textStyles
|
|
230
|
-
})
|
|
231
|
-
style: textStyles
|
|
232
|
-
}, children));
|
|
223
|
+
}));
|
|
233
224
|
});
|
|
234
225
|
};
|
|
235
226
|
|
|
@@ -238,11 +229,24 @@ ButtonBase.propTypes = { ...a11yProps.types,
|
|
|
238
229
|
...linkProps.types
|
|
239
230
|
};
|
|
240
231
|
const staticStyles = StyleSheet.create({
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
232
|
+
row: {
|
|
233
|
+
// Apply all button alignment horizontally; no vertical stacking within a button
|
|
234
|
+
flexDirection: 'row'
|
|
235
|
+
},
|
|
236
|
+
text: {
|
|
237
|
+
flexGrow: 1,
|
|
238
|
+
// On native but not web, flexShrink here wraps text prematurely
|
|
239
|
+
...Platform.select({
|
|
240
|
+
// TODO: https://github.com/telus/universal-design-system/issues/487
|
|
241
|
+
web: {
|
|
242
|
+
transition: 'color 200ms'
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
},
|
|
246
|
+
align: {
|
|
247
|
+
alignItems: 'center'
|
|
244
248
|
},
|
|
245
|
-
|
|
249
|
+
stretch: {
|
|
246
250
|
flex: 1,
|
|
247
251
|
alignItems: 'center',
|
|
248
252
|
justifyContent: 'center'
|