@telus-uds/components-base 1.1.0 → 1.2.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.
- package/.ultra.cache.json +1 -1
- package/CHANGELOG.md +8 -0
- package/__fixtures__/Accessible.js +4 -2
- package/__fixtures__/Accessible.native.js +5 -2
- package/__fixtures__/testTheme.js +9 -0
- package/__tests__/HorizontalScroll/HorizontalScroll.test.jsx +1 -0
- package/__tests__/ToggleSwitch/ToggleSwitch.test.jsx +10 -0
- package/__tests__/ToggleSwitch/ToggleSwitchGroup.test.jsx +192 -0
- package/component-docs.json +614 -796
- package/lib/Button/ButtonBase.js +20 -6
- package/lib/Card/PressableCardBase.js +9 -3
- package/lib/Checkbox/Checkbox.js +0 -2
- package/lib/IconButton/IconButton.js +8 -3
- package/lib/Link/LinkBase.js +10 -3
- package/lib/Pagination/PageButton.js +3 -1
- package/lib/Pagination/Pagination.js +16 -4
- package/lib/Pagination/SideButton.js +3 -1
- package/lib/Radio/Radio.js +0 -2
- package/lib/Tabs/Tabs.js +12 -4
- package/lib/Tabs/TabsItem.js +12 -6
- package/lib/ToggleSwitch/ToggleSwitch.js +99 -37
- package/lib/ToggleSwitch/ToggleSwitchGroup.js +230 -0
- package/lib/ToggleSwitch/index.js +14 -4
- package/lib/index.js +13 -8
- package/lib/utils/index.js +10 -1
- package/lib/utils/propTypes.js +26 -1
- package/lib/utils/withLinkRouter.js +98 -0
- package/package.json +2 -2
- package/release-context.json +4 -4
- package/src/Button/ButtonBase.jsx +11 -4
- package/src/Card/PressableCardBase.jsx +6 -4
- package/src/Checkbox/Checkbox.jsx +0 -2
- package/src/IconButton/IconButton.jsx +6 -4
- package/src/Link/LinkBase.jsx +6 -4
- package/src/Pagination/PageButton.jsx +3 -2
- package/src/Pagination/Pagination.jsx +29 -2
- package/src/Pagination/SideButton.jsx +2 -2
- package/src/Radio/Radio.jsx +0 -2
- package/src/Tabs/Tabs.jsx +49 -22
- package/src/Tabs/TabsItem.jsx +11 -7
- package/src/ToggleSwitch/ToggleSwitch.jsx +92 -34
- package/src/ToggleSwitch/ToggleSwitchGroup.jsx +203 -0
- package/src/ToggleSwitch/index.js +2 -1
- package/src/index.js +1 -1
- package/src/utils/index.js +1 -0
- package/src/utils/propTypes.js +30 -0
- package/src/utils/withLinkRouter.jsx +68 -0
- package/stories/TextInput/TextArea.stories.jsx +1 -0
- package/stories/ToggleSwitch/ToggleSwitch.stories.jsx +5 -1
- package/stories/ToggleSwitch/ToggleSwitchGroup.stories.jsx +81 -0
package/src/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { default as StepTracker } from './StepTracker'
|
|
|
37
37
|
export { default as Tabs } from './Tabs'
|
|
38
38
|
export { default as Tags } from './Tags'
|
|
39
39
|
export * from './TextInput'
|
|
40
|
-
export
|
|
40
|
+
export * from './ToggleSwitch'
|
|
41
41
|
export { default as Tooltip } from './Tooltip'
|
|
42
42
|
export { default as TooltipButton } from './TooltipButton'
|
|
43
43
|
export { default as Typography } from './Typography'
|
package/src/utils/index.js
CHANGED
|
@@ -12,3 +12,4 @@ export { default as useSpacingScale } from './useSpacingScale'
|
|
|
12
12
|
export { default as useResponsiveProp } from './useResponsiveProp'
|
|
13
13
|
export * from './useResponsiveProp'
|
|
14
14
|
export { default as useUniqueId } from './useUniqueId'
|
|
15
|
+
export { default as withLinkRouter } from './withLinkRouter'
|
package/src/utils/propTypes.js
CHANGED
|
@@ -320,6 +320,36 @@ export const pressProps = {
|
|
|
320
320
|
selectHandlers: getPropSelector(pressHandlerPropTypes)
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
const clickHandlerMapping = {
|
|
324
|
+
onClick: 'onPress',
|
|
325
|
+
mouseDown: 'onPressIn',
|
|
326
|
+
mouseUp: 'onPressOut'
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export const clickProps = {
|
|
330
|
+
/**
|
|
331
|
+
* Web-oriented HTML click handlers that may be mapped to React Native press handlers
|
|
332
|
+
*/
|
|
333
|
+
types: Object.fromEntries(
|
|
334
|
+
Object.keys(clickHandlerMapping).map((mouseName) => [mouseName, PropTypes.func])
|
|
335
|
+
),
|
|
336
|
+
/**
|
|
337
|
+
* Takes a set of props and converts HTML mouse click oriented event handlers to closest
|
|
338
|
+
* equivalent React Native press event handler.
|
|
339
|
+
*
|
|
340
|
+
* Use this when a component that expects press-oriented props may need to support third-party
|
|
341
|
+
* web-oriented tooling that injects web-oriented event handlers directly. For example, for
|
|
342
|
+
* to support use with NextJS's 'next/link' component, which injects `onClick` prop into its child.
|
|
343
|
+
*/
|
|
344
|
+
toPressProps: (props) =>
|
|
345
|
+
Object.fromEntries(
|
|
346
|
+
Object.entries(props).map(([originalName, value]) => {
|
|
347
|
+
const translatedName = clickHandlerMapping[originalName]
|
|
348
|
+
return translatedName ? [translatedName, value] : [originalName, value]
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
323
353
|
const linkPropTypes = {
|
|
324
354
|
...pressPropTypes,
|
|
325
355
|
href: PropTypes.string,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
// Prototype-safe alternative to (linter-forbidden) someObject.hasOwnProperty()
|
|
5
|
+
const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop)
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Higher-order component that has no effect unless an additional prop `LinkRouter` is passed.
|
|
9
|
+
* This may be used to provide custom wrappers for integrations with third party libraries.
|
|
10
|
+
*
|
|
11
|
+
* If LinkRouter is passed, LinkRouter is rendered in place of the main component and is passed:
|
|
12
|
+
*
|
|
13
|
+
* - `linkRouterProps`: an optional object passed alongside LinkRouter, for props needed by the wrapper
|
|
14
|
+
* that are not valid props for the wrapped component.
|
|
15
|
+
* - `Component`: automatically provided, the original component to render inside the wrapper.
|
|
16
|
+
* - `ref`: forwarded `ref` passed down to `Component`
|
|
17
|
+
* - All other props passed to the outer component
|
|
18
|
+
*
|
|
19
|
+
* @example A LinkRouter component to be used with link-like components might look like:
|
|
20
|
+
*
|
|
21
|
+
* ```jsx
|
|
22
|
+
* const LinkLinkRouter = forwardRef(({ Component, linkRouterProps: { to, options }, href, ...rest }, ref) => {
|
|
23
|
+
* const { href: resolvedHref, onClick } = useSomeRouterHook({ to, href, options })
|
|
24
|
+
* return <Component href={resolvedHref} onPress={onClick} {...rest} />
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Any component that takes href and onPress props may then use this wrapper:
|
|
29
|
+
*
|
|
30
|
+
* ```jsx
|
|
31
|
+
* <Link href={href} LinkRouter={LinkLinkRouter} linkRouterProps={{ to, options }}>Some link</Link>
|
|
32
|
+
* <IconButton icon={SomeIcon} LinkRouter={LinkLinkRouter} linkRouterProps={{ to, options }} ref={iconRef} />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const withLinkRouter = (Component) => {
|
|
36
|
+
const wrappedComponent = forwardRef(({ LinkRouter, linkRouterProps, ...props }, ref) => {
|
|
37
|
+
if (!LinkRouter) return <Component {...props} ref={ref} />
|
|
38
|
+
return (
|
|
39
|
+
<LinkRouter linkRouterProps={linkRouterProps} Component={Component} ref={ref} {...props} />
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Ensure the returned component has appropriate outer properties set:
|
|
44
|
+
/* eslint-disable-next-line react/forbid-foreign-prop-types */
|
|
45
|
+
const { displayName, name, propTypes, ...otherProperties } = Component
|
|
46
|
+
|
|
47
|
+
// Apply unique component name as a displayName
|
|
48
|
+
wrappedComponent.displayName = Component.displayName || Component.name
|
|
49
|
+
|
|
50
|
+
// Apply proptypes including wrapper props - is safely { ...undefined, ...undefined } in prod
|
|
51
|
+
wrappedComponent.propTypes = { ...Component.propTypes, ...withLinkRouter.propTypes }
|
|
52
|
+
|
|
53
|
+
// Forward any other properties explicitly set e.g. Component.SubComponent
|
|
54
|
+
Object.keys(otherProperties).forEach((key) => {
|
|
55
|
+
// Skip internal React properties from wrappedComponent's forwardRef (render, $$typeof, etc)
|
|
56
|
+
if (hasOwnProperty(Component, key) && !hasOwnProperty(wrappedComponent, key)) {
|
|
57
|
+
wrappedComponent[key] = Component[key]
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
return wrappedComponent
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
withLinkRouter.propTypes = {
|
|
64
|
+
LinkRouter: PropTypes.elementType,
|
|
65
|
+
linkRouterProps: PropTypes.object
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default withLinkRouter
|
|
@@ -9,7 +9,11 @@ Default.storyName = 'ToggleSwitch'
|
|
|
9
9
|
|
|
10
10
|
export default {
|
|
11
11
|
title: 'ToggleSwitch',
|
|
12
|
-
component: ToggleSwitch
|
|
12
|
+
component: ToggleSwitch,
|
|
13
|
+
args: {
|
|
14
|
+
label: 'Enable data',
|
|
15
|
+
tooltip: 'Toggle this on to enable data'
|
|
16
|
+
}
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export const ToggleSwitchControlled = (args) => {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* eslint-disable react/no-multi-comp */
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
import { View } from 'react-native'
|
|
4
|
+
import { ToggleSwitchGroup, Typography } from '../../src'
|
|
5
|
+
import { Container } from '../supports'
|
|
6
|
+
|
|
7
|
+
const defaultArgs = {
|
|
8
|
+
items: [
|
|
9
|
+
{ label: 'First item', id: 'first' },
|
|
10
|
+
{ label: 'Second item', id: 'second' },
|
|
11
|
+
{ label: 'Third item', id: 'third' },
|
|
12
|
+
{ label: 'Fourth item', id: 'fourth' },
|
|
13
|
+
{ label: 'Fifth item', id: 'fifth' },
|
|
14
|
+
{ label: 'Sixth item', id: 'sixth' }
|
|
15
|
+
],
|
|
16
|
+
initialValues: ['second', 'fourth']
|
|
17
|
+
}
|
|
18
|
+
const defaultControlledArgs = {
|
|
19
|
+
...defaultArgs,
|
|
20
|
+
values: defaultArgs.initialValues,
|
|
21
|
+
initialValues: undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
title: 'ToggleSwitchGroup',
|
|
26
|
+
component: ToggleSwitchGroup,
|
|
27
|
+
args: {
|
|
28
|
+
...defaultArgs
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const UncontrolledTemplate = (args) => <ToggleSwitchGroup {...args} />
|
|
33
|
+
UncontrolledTemplate.propTypes = ToggleSwitchGroup.propTypes
|
|
34
|
+
|
|
35
|
+
const ControlledTemplate = ({ values, ...args }) => {
|
|
36
|
+
// Simulate saving and retrieving data from a server
|
|
37
|
+
const [isSaving, setIsSaving] = useState(false)
|
|
38
|
+
const [currentValues, setValues] = useState(values)
|
|
39
|
+
const handleChange = (newValue) => {
|
|
40
|
+
setIsSaving(true)
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
setValues(newValue)
|
|
43
|
+
setIsSaving(false)
|
|
44
|
+
}, 400)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const text = isSaving ? 'Saving...' : `Saved selected IDs: "${currentValues.join('", "')}".`
|
|
48
|
+
return (
|
|
49
|
+
<View>
|
|
50
|
+
<Container padding={4} margin={0}>
|
|
51
|
+
<Typography variant={{ size: 'small', colour: 'secondary' }}>{text}</Typography>
|
|
52
|
+
</Container>
|
|
53
|
+
<Container padding={4} margin={0}>
|
|
54
|
+
<ToggleSwitchGroup {...args} values={currentValues} onChange={handleChange} />
|
|
55
|
+
</Container>
|
|
56
|
+
</View>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
ControlledTemplate.propTypes = ToggleSwitchGroup.propTypes
|
|
61
|
+
|
|
62
|
+
export const Default = UncontrolledTemplate.bind({})
|
|
63
|
+
Default.storyName = 'ToggleSwitchGroup'
|
|
64
|
+
|
|
65
|
+
export const ToggleSwitchGroupMaxThree = UncontrolledTemplate.bind({})
|
|
66
|
+
ToggleSwitchGroupMaxThree.args = { maxValues: 3 }
|
|
67
|
+
|
|
68
|
+
export const ToggleSwitchGroupMaxUnlimited = UncontrolledTemplate.bind({})
|
|
69
|
+
ToggleSwitchGroupMaxUnlimited.args = { maxValues: null }
|
|
70
|
+
|
|
71
|
+
export const ToggleSwitchGroupControlled = ControlledTemplate.bind({})
|
|
72
|
+
ToggleSwitchGroupControlled.args = { ...defaultControlledArgs }
|
|
73
|
+
|
|
74
|
+
export const ToggleSwitchGroupControlledMaxThree = ControlledTemplate.bind({})
|
|
75
|
+
ToggleSwitchGroupControlledMaxThree.args = { ...defaultControlledArgs, maxValues: 3 }
|
|
76
|
+
|
|
77
|
+
export const ToggleSwitchGroupControlledMaxUnlimited = ControlledTemplate.bind({})
|
|
78
|
+
ToggleSwitchGroupControlledMaxUnlimited.args = { ...defaultControlledArgs, maxValues: null }
|
|
79
|
+
|
|
80
|
+
export const ToggleSwitchGroupInactive = UncontrolledTemplate.bind({})
|
|
81
|
+
ToggleSwitchGroupInactive.args = { inactive: true }
|