@workday/canvas-kit-docs 5.3.17 → 5.4.0-next.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.
- package/dist/commonjs/lib/specs.js +429 -19
- package/dist/es6/lib/specs.js +429 -19
- package/dist/mdx/6.0-MIGRATION-GUIDE.mdx +567 -0
- package/dist/mdx/COMPOUND_COMPONENTS.mdx +31 -30
- package/dist/mdx/TESTING.mdx +30 -6
- package/dist/mdx/changelog.stories.mdx +0 -1
- package/dist/mdx/labs-react/layout/Stack.mdx +7 -2
- package/dist/mdx/labs-react/search-form/SearchForm.mdx +62 -0
- package/dist/mdx/labs-react/search-form/examples/Basic.tsx +61 -0
- package/dist/mdx/labs-react/search-form/examples/CustomTheme.tsx +72 -0
- package/dist/mdx/labs-react/search-form/examples/Grow.tsx +62 -0
- package/dist/mdx/labs-react/search-form/examples/PropTables.splitProps.tsx +4 -0
- package/dist/mdx/labs-react/search-form/examples/RTL.tsx +70 -0
- package/dist/mdx/labs-react/search-form/examples/Theming.tsx +64 -0
- package/dist/mdx/preview-react/_examples/SelectWithFormik.mdx +8 -0
- package/dist/mdx/preview-react/_examples/TextInputWithFormik.mdx +8 -0
- package/dist/mdx/preview-react/_examples/examples/SelectWithFormik.tsx +47 -0
- package/dist/mdx/preview-react/_examples/examples/TextInputWithFormik.tsx +105 -0
- package/dist/mdx/preview-react/form-field/FormField.mdx +39 -0
- package/dist/mdx/preview-react/form-field/examples/Custom.tsx +57 -0
- package/dist/mdx/preview-react/form-field/examples/Select.tsx +50 -0
- package/dist/mdx/preview-react/side-panel/examples/AlwaysOpen.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/Basic.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/ExternalControl.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/RightOrigin.tsx +1 -1
- package/dist/mdx/preview-react/side-panel/examples/Variant.tsx +1 -1
- package/dist/mdx/preview-react/text-area/TextArea.mdx +122 -0
- package/dist/mdx/preview-react/text-area/examples/Alert.tsx +36 -0
- package/dist/mdx/preview-react/text-area/examples/Basic.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Disabled.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Error.tsx +40 -0
- package/dist/mdx/preview-react/text-area/examples/Grow.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/HiddenLabel.tsx +20 -0
- package/dist/mdx/preview-react/text-area/examples/LabelPositionHorizontal.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/LabelPositionVertical.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/Placeholder.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/RefForwarding.tsx +28 -0
- package/dist/mdx/preview-react/text-area/examples/Required.tsx +17 -0
- package/dist/mdx/preview-react/text-area/examples/ResizeConstraints.tsx +22 -0
- package/dist/mdx/preview-react/text-input/TextInput.mdx +138 -0
- package/dist/mdx/preview-react/text-input/examples/Alert.tsx +35 -0
- package/dist/mdx/preview-react/text-input/examples/Basic.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Disabled.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Error.tsx +40 -0
- package/dist/mdx/preview-react/text-input/examples/Grow.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/HiddenLabel.tsx +20 -0
- package/dist/mdx/preview-react/text-input/examples/LabelPositionHorizontal.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/LabelPositionVertical.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Password.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/Placeholder.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/RefForwarding.tsx +28 -0
- package/dist/mdx/preview-react/text-input/examples/Required.tsx +17 -0
- package/dist/mdx/preview-react/text-input/examples/ThemedAlert.tsx +46 -0
- package/dist/mdx/preview-react/text-input/examples/ThemedError.tsx +35 -0
- package/dist/mdx/react/_examples/CookieBanner.mdx +8 -0
- package/dist/mdx/react/_examples/GlobalHeader.mdx +12 -0
- package/dist/mdx/react/_examples/PageHeader.mdx +8 -0
- package/dist/mdx/react/_examples/examples/CookieBanner.tsx +97 -0
- package/dist/mdx/react/_examples/examples/GlobalHeader.tsx +66 -0
- package/dist/mdx/react/_examples/examples/PageHeader.tsx +63 -0
- package/dist/mdx/react/button/button/Button.mdx +26 -2
- package/dist/mdx/react/button/button/examples/Primary.tsx +10 -1
- package/dist/mdx/react/button/button/examples/PrimaryInverse.tsx +14 -0
- package/dist/mdx/react/button/button/examples/Secondary.tsx +10 -1
- package/dist/mdx/react/button/button/examples/SecondaryInverse.tsx +14 -0
- package/dist/mdx/react/button/button/examples/Tertiary.tsx +13 -1
- package/dist/mdx/react/button/button/examples/TertiaryInverse.tsx +14 -0
- package/dist/mdx/react/popup/examples/FocusRedirect.tsx +2 -1
- package/dist/mdx/react/tabs/Tabs.mdx +31 -5
- package/dist/mdx/react/tabs/examples/DisabledTab.tsx +1 -1
- package/dist/mdx/react/tabs/examples/DynamicTabs.tsx +82 -38
- package/dist/mdx/react/tabs/examples/HoistedModel.tsx +4 -4
- package/dist/mdx/react/tabs/examples/Icons.tsx +36 -0
- package/dist/mdx/react/tabs/examples/{NamedKeys.tsx → NamedTabs.tsx} +0 -0
- package/dist/mdx/react/tabs/examples/OverflowTabs.tsx +58 -0
- package/dist/mdx/react/tabs/examples/SinglePanel.tsx +1 -1
- package/dist/mdx/react/text-area/TextArea.mdx +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {createComponent, styled, StyledType} from '@workday/canvas-kit-react/common';
|
|
4
|
+
import {colors, commonColors, depth, type, space} from '@workday/canvas-kit-react/tokens';
|
|
5
|
+
import {PrimaryButton, TertiaryButton} from '@workday/canvas-kit-react/button';
|
|
6
|
+
|
|
7
|
+
const CookieBannerItem = createComponent('div')({
|
|
8
|
+
displayName: 'CookieBanner.Item',
|
|
9
|
+
Component: ({isRow, ...elProps}: ItemProps, ref) => (
|
|
10
|
+
<BannerItem ref={ref} isRow={isRow} {...elProps} />
|
|
11
|
+
),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const CookieBanner = createComponent('div')({
|
|
15
|
+
displayName: 'CookieBanner',
|
|
16
|
+
Component: (props: BannerProps, ref, Element) => <Banner ref={ref} as={Element} {...props} />,
|
|
17
|
+
subComponents: {Item: CookieBannerItem},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default () => {
|
|
21
|
+
const DefaultNotice = `We use cookies to ensure that we give you the best experience on our website.
|
|
22
|
+
If you continue without changing your settings, we'll assume that you are willing to receive cookies.`;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ExampleContainer>
|
|
26
|
+
<CookieBanner isClosed={false}>
|
|
27
|
+
<CookieBanner.Item>{DefaultNotice}</CookieBanner.Item>
|
|
28
|
+
<CookieBanner.Item isRow>
|
|
29
|
+
<TertiaryButton>Settings</TertiaryButton>
|
|
30
|
+
<PrimaryButton>Continue</PrimaryButton>
|
|
31
|
+
</CookieBanner.Item>
|
|
32
|
+
</CookieBanner>
|
|
33
|
+
</ExampleContainer>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface BannerProps {
|
|
38
|
+
isClosed?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ItemProps {
|
|
42
|
+
isRow?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ExampleContainer = styled('div')({
|
|
46
|
+
minHeight: 84,
|
|
47
|
+
margin: space.xs,
|
|
48
|
+
position: 'relative',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const Banner = styled('div')<BannerProps & StyledType>(
|
|
52
|
+
type.levels.subtext.medium,
|
|
53
|
+
{
|
|
54
|
+
backgroundColor: commonColors.background,
|
|
55
|
+
borderTop: `1px solid ${colors.soap400}`,
|
|
56
|
+
display: 'flex',
|
|
57
|
+
...depth[1],
|
|
58
|
+
padding: space.m,
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
justifyContent: 'space-between',
|
|
61
|
+
position: 'absolute',
|
|
62
|
+
bottom: 0,
|
|
63
|
+
left: 0,
|
|
64
|
+
right: 0,
|
|
65
|
+
zIndex: 99,
|
|
66
|
+
transition: 'transform 0.2s ease-out',
|
|
67
|
+
'@media (max-width: 450px)': {
|
|
68
|
+
flexDirection: 'column',
|
|
69
|
+
alignItems: 'stretch',
|
|
70
|
+
textAlign: 'center',
|
|
71
|
+
padding: `${space.s} 0`,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
({isClosed}) => isClosed && {transform: 'translateY(100%)'}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const BannerItem = styled('div')<ItemProps & StyledType>(
|
|
78
|
+
{
|
|
79
|
+
marginLeft: space.s,
|
|
80
|
+
marginRight: space.s,
|
|
81
|
+
'@media (max-width: 450px)': {
|
|
82
|
+
'&:not(:first-of-type)': {
|
|
83
|
+
marginTop: space.s,
|
|
84
|
+
'> *': {
|
|
85
|
+
flex: 1,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
({isRow}) =>
|
|
91
|
+
isRow && {
|
|
92
|
+
display: 'flex',
|
|
93
|
+
'> *': {
|
|
94
|
+
marginLeft: space.s,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {styled, createComponent, dubLogoBlue} from '@workday/canvas-kit-react/common';
|
|
3
|
+
import {colors, depth, space, type} from '@workday/canvas-kit-react/tokens';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
notificationsIcon,
|
|
7
|
+
inboxIcon,
|
|
8
|
+
justifyIcon,
|
|
9
|
+
assistantIcon,
|
|
10
|
+
} from '@workday/canvas-system-icons-web';
|
|
11
|
+
|
|
12
|
+
import {IconButton, Hyperlink} from '@workday/canvas-kit-react/button';
|
|
13
|
+
import {Avatar} from '@workday/canvas-kit-react/avatar';
|
|
14
|
+
import {SearchForm, HStack, HStackProps, StackSpacing} from '@workday/canvas-kit-labs-react';
|
|
15
|
+
|
|
16
|
+
interface HeaderItemProps extends Omit<HStackProps, 'spacing'> {
|
|
17
|
+
spacing?: StackSpacing;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default () => (
|
|
21
|
+
<GlobalHeader>
|
|
22
|
+
<GlobalHeader.Item>
|
|
23
|
+
<IconButton aria-label="menu" icon={justifyIcon} />
|
|
24
|
+
<Hyperlink>
|
|
25
|
+
<WorkdayLogo dangerouslySetInnerHTML={{__html: dubLogoBlue}} />
|
|
26
|
+
</Hyperlink>
|
|
27
|
+
</GlobalHeader.Item>
|
|
28
|
+
<GlobalHeader.Item margin="auto" width="100%" maxWidth={`calc(${space.xxxl} * 6)`}>
|
|
29
|
+
<SearchForm onSubmit={() => 1} />
|
|
30
|
+
</GlobalHeader.Item>
|
|
31
|
+
<GlobalHeader.Item>
|
|
32
|
+
<IconButton aria-label="messages" icon={assistantIcon} />
|
|
33
|
+
<IconButton aria-label="notifications" icon={notificationsIcon} />
|
|
34
|
+
<IconButton aria-label="inbox" icon={inboxIcon} />
|
|
35
|
+
<Avatar size={Avatar.Size.m} variant={Avatar.Variant.Light} />
|
|
36
|
+
</GlobalHeader.Item>
|
|
37
|
+
</GlobalHeader>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const GlobalHeaderItem = createComponent('div')({
|
|
41
|
+
displayName: 'GlobalHeader.Item',
|
|
42
|
+
Component: ({spacing = 's', ...props}: HeaderItemProps, ref) => (
|
|
43
|
+
<HStack spacing={spacing} alignItems="center" marginX={space.xs} ref={ref} {...props} />
|
|
44
|
+
),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const GlobalHeader = createComponent('header')({
|
|
48
|
+
displayName: 'GlobalHeader',
|
|
49
|
+
Component: (props, ref, Element) => <HeaderWrapper ref={ref} as={Element} {...props} />,
|
|
50
|
+
subComponents: {Item: GlobalHeaderItem},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const HeaderWrapper = styled('header')({
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
justifyContent: 'space-between',
|
|
57
|
+
boxSizing: 'border-box',
|
|
58
|
+
...type.levels.subtext.large,
|
|
59
|
+
WebkitFontSmoothing: 'antialiased',
|
|
60
|
+
MozOsxFontSmoothing: 'grayscale',
|
|
61
|
+
backgroundColor: colors.frenchVanilla100,
|
|
62
|
+
...depth[1],
|
|
63
|
+
padding: space.xxs,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const WorkdayLogo = styled('span')({lineHeight: 0});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {styled, createComponent} from '@workday/canvas-kit-react/common';
|
|
3
|
+
|
|
4
|
+
import {colors, gradients, space, type} from '@workday/canvas-kit-react/tokens';
|
|
5
|
+
|
|
6
|
+
import {HStack, HStackProps, StackSpacing} from '@workday/canvas-kit-labs-react';
|
|
7
|
+
import {IconButton} from '@workday/canvas-kit-react/button';
|
|
8
|
+
import {justifyIcon, notificationsIcon} from '@workday/canvas-system-icons-web';
|
|
9
|
+
|
|
10
|
+
interface HeaderItemProps extends Omit<HStackProps, 'spacing'> {
|
|
11
|
+
spacing?: StackSpacing;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default () => (
|
|
15
|
+
<PageHeader>
|
|
16
|
+
<PageHeader.Title>Page Header</PageHeader.Title>
|
|
17
|
+
<PageHeader.Item>
|
|
18
|
+
<IconButton aria-label="notifications" icon={notificationsIcon} variant="inverse" />
|
|
19
|
+
<IconButton aria-label="menu" icon={justifyIcon} variant="inverse" />
|
|
20
|
+
</PageHeader.Item>
|
|
21
|
+
</PageHeader>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const PageHeaderItem = createComponent('div')({
|
|
25
|
+
displayName: 'PageHeader.Item',
|
|
26
|
+
Component: ({spacing = 'xxs', ...props}: HeaderItemProps, ref, Element) => (
|
|
27
|
+
<HStack spacing={spacing} ref={ref} as={Element} {...props} />
|
|
28
|
+
),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const PageHeaderTitle = createComponent('h2')({
|
|
32
|
+
displayName: 'PageHeader.Title',
|
|
33
|
+
Component: ({children, ...props}, ref, Element) => (
|
|
34
|
+
<Title ref={ref} as={Element} {...props}>
|
|
35
|
+
{children}
|
|
36
|
+
</Title>
|
|
37
|
+
),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const PageHeader = createComponent('header')({
|
|
41
|
+
displayName: 'PageHeader',
|
|
42
|
+
Component: (props, ref, Element) => <Header ref={ref} as={Element} {...props} />,
|
|
43
|
+
subComponents: {Item: PageHeaderItem, Title: PageHeaderTitle},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const Header = styled('header')({
|
|
47
|
+
padding: `${space.xs} ${space.xl}`,
|
|
48
|
+
backgroundImage: gradients.blueberry,
|
|
49
|
+
color: colors.frenchVanilla100,
|
|
50
|
+
WebkitFontSmoothing: 'antialiased',
|
|
51
|
+
MozOsxFontSmoothing: 'grayscale',
|
|
52
|
+
display: 'flex',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
justifyContent: 'space-between',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const Title = styled('h2')({
|
|
58
|
+
...type.levels.heading.medium,
|
|
59
|
+
color: colors.frenchVanilla100,
|
|
60
|
+
padding: `${space.xs} 0`,
|
|
61
|
+
margin: 0,
|
|
62
|
+
whiteSpace: 'nowrap',
|
|
63
|
+
});
|
|
@@ -7,8 +7,11 @@ import {
|
|
|
7
7
|
import {Specifications} from '@workday/canvas-kit-docs';
|
|
8
8
|
|
|
9
9
|
import Primary from './examples/Primary';
|
|
10
|
+
import PrimaryInverse from './examples/PrimaryInverse';
|
|
10
11
|
import Secondary from './examples/Secondary';
|
|
12
|
+
import SecondaryInverse from './examples/SecondaryInverse';
|
|
11
13
|
import Tertiary from './examples/Tertiary';
|
|
14
|
+
import TertiaryInverse from './examples/TertiaryInverse';
|
|
12
15
|
import Delete from './examples/Delete';
|
|
13
16
|
|
|
14
17
|
|
|
@@ -35,8 +38,17 @@ Primary Buttons are high emphasis. Use once per screen to draw attention to the
|
|
|
35
38
|
action. Multiple primary buttons make it confusing for the user to understand what action they
|
|
36
39
|
should take. Not all screens require a Primary Button.
|
|
37
40
|
|
|
41
|
+
Primary Buttons have four sizes: `extraSmall`, `small`, `medium`, and `large`. Icons are supported
|
|
42
|
+
for every size and can be positioned to the `left` or `right` with the `iconPosition` prop.
|
|
43
|
+
|
|
38
44
|
<ExampleCodeBlock code={Primary} />
|
|
39
45
|
|
|
46
|
+
Primary Buttons also have an `inverse` variant. While it looks similar to the default Secondary
|
|
47
|
+
Button, the default outline as well as the hover and focus states are different. Use this variant
|
|
48
|
+
when you need to place a Primary Button on a dark or colorful background such as `blueberry400`.
|
|
49
|
+
|
|
50
|
+
<ExampleCodeBlock code={PrimaryInverse} />
|
|
51
|
+
|
|
40
52
|
#### Props
|
|
41
53
|
|
|
42
54
|
Undocumented props are spread to the underlying `<button>` element.
|
|
@@ -49,10 +61,16 @@ Secondary Buttons have a medium level of emphasis. Use them for non-critical act
|
|
|
49
61
|
Buttons can be used on most pages without restrictions and work well for multiple actions of equal
|
|
50
62
|
weight. They can be used in conjunction with a Primary Button or independently.
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
Secondary Buttons have four sizes: `extraSmall`, `small`, `medium`, and `large`. Icons are supported
|
|
65
|
+
for every size and can be positioned to the `left` or `right` with the `iconPosition` prop.
|
|
53
66
|
|
|
54
67
|
<ExampleCodeBlock code={Secondary} />
|
|
55
68
|
|
|
69
|
+
Secondary Buttons also have an `inverse` variant. Use this when you need to place a Secondary Button
|
|
70
|
+
on a dark or colorful background such as `blueberry400`.
|
|
71
|
+
|
|
72
|
+
<ExampleCodeBlock code={SecondaryInverse} />
|
|
73
|
+
|
|
56
74
|
#### Props
|
|
57
75
|
|
|
58
76
|
Undocumented props are spread to the underlying `<button>` element.
|
|
@@ -66,10 +84,16 @@ the user may not often be looking to do. Tertiary Buttons have lower prominence
|
|
|
66
84
|
not visible until it is interacted with. Use Tertiary Buttons for supplemental actions such as “View
|
|
67
85
|
More”, “Read More” or “Select a File”. Tertiary Buttons are frequently used on Cards.
|
|
68
86
|
|
|
69
|
-
|
|
87
|
+
Tertiary Buttons have three sizes: `extraSmall`, `small`, and `medium`. Icons are supported for
|
|
88
|
+
every size and can be positioned to the `left` or `right` with the `iconPosition` prop.
|
|
70
89
|
|
|
71
90
|
<ExampleCodeBlock code={Tertiary} />
|
|
72
91
|
|
|
92
|
+
Tertiary Buttons also have an `inverse` variant. Use this when you need to place a Tertiary Button
|
|
93
|
+
on a dark or colorful background such as `blueberry400`.
|
|
94
|
+
|
|
95
|
+
<ExampleCodeBlock code={TertiaryInverse} />
|
|
96
|
+
|
|
73
97
|
#### Props
|
|
74
98
|
|
|
75
99
|
Undocumented props are spread to the underlying `<button>` element.
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
4
6
|
|
|
5
|
-
export default () =>
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" padding="s">
|
|
9
|
+
<PrimaryButton>Primary</PrimaryButton>
|
|
10
|
+
<PrimaryButton icon={plusIcon} iconPosition="right">
|
|
11
|
+
Primary
|
|
12
|
+
</PrimaryButton>
|
|
13
|
+
</HStack>
|
|
14
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {PrimaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
6
|
+
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" backgroundColor="blueberry400" padding="s">
|
|
9
|
+
<PrimaryButton variant="inverse">Primary</PrimaryButton>
|
|
10
|
+
<PrimaryButton icon={plusIcon} iconPosition="right" variant="inverse">
|
|
11
|
+
Primary
|
|
12
|
+
</PrimaryButton>
|
|
13
|
+
</HStack>
|
|
14
|
+
);
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import {SecondaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
4
6
|
|
|
5
|
-
export default () =>
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" padding="s">
|
|
9
|
+
<SecondaryButton>Secondary</SecondaryButton>
|
|
10
|
+
<SecondaryButton icon={plusIcon} iconPosition="right">
|
|
11
|
+
Secondary
|
|
12
|
+
</SecondaryButton>
|
|
13
|
+
</HStack>
|
|
14
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {SecondaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
6
|
+
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" backgroundColor="blueberry400" padding="s">
|
|
9
|
+
<SecondaryButton variant="inverse">Secondary</SecondaryButton>
|
|
10
|
+
<SecondaryButton icon={plusIcon} iconPosition="right" variant="inverse">
|
|
11
|
+
Secondary
|
|
12
|
+
</SecondaryButton>
|
|
13
|
+
</HStack>
|
|
14
|
+
);
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import {TertiaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
4
6
|
|
|
5
|
-
export default () =>
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" padding="s">
|
|
9
|
+
<TertiaryButton>Tertiary</TertiaryButton>
|
|
10
|
+
<TertiaryButton icon={plusIcon} iconPosition="right">
|
|
11
|
+
Tertiary
|
|
12
|
+
</TertiaryButton>
|
|
13
|
+
<TertiaryButton icon={plusIcon} iconPosition="right" disabled>
|
|
14
|
+
Tertiary
|
|
15
|
+
</TertiaryButton>
|
|
16
|
+
</HStack>
|
|
17
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {TertiaryButton} from '@workday/canvas-kit-react/button';
|
|
4
|
+
import {HStack} from '@workday/canvas-kit-labs-react/layout';
|
|
5
|
+
import {plusIcon} from '@workday/canvas-system-icons-web';
|
|
6
|
+
|
|
7
|
+
export default () => (
|
|
8
|
+
<HStack spacing="s" backgroundColor="blueberry400" padding="s">
|
|
9
|
+
<TertiaryButton variant="inverse">Tertiary</TertiaryButton>
|
|
10
|
+
<TertiaryButton icon={plusIcon} iconPosition="right" variant="inverse">
|
|
11
|
+
Tertiary
|
|
12
|
+
</TertiaryButton>
|
|
13
|
+
</HStack>
|
|
14
|
+
);
|
|
@@ -49,7 +49,8 @@ export default () => {
|
|
|
49
49
|
<Popup.CloseButton as={DeleteButton} onClick={handleDelete}>
|
|
50
50
|
Delete
|
|
51
51
|
</Popup.CloseButton>
|
|
52
|
-
|
|
52
|
+
{/* Disabled elements should not be focusable and focus should move to the next focusable element */}
|
|
53
|
+
<Popup.CloseButton disabled>Cancel</Popup.CloseButton>
|
|
53
54
|
</HStack>
|
|
54
55
|
</Popup.Card>
|
|
55
56
|
</Popup.Popper>
|
|
@@ -2,9 +2,11 @@ import {Tabs} from '@workday/canvas-kit-react/tabs';
|
|
|
2
2
|
import {Specifications} from '@workday/canvas-kit-docs';
|
|
3
3
|
|
|
4
4
|
import Basic from './examples/Basic';
|
|
5
|
-
import
|
|
5
|
+
import NamedTabs from './examples/NamedTabs';
|
|
6
6
|
import RightToLeft from './examples/RightToLeft';
|
|
7
|
+
import OverflowTabs from './examples/OverflowTabs';
|
|
7
8
|
import DisabledTab from './examples/DisabledTab';
|
|
9
|
+
import Icons from './examples/Icons';
|
|
8
10
|
import SinglePanel from './examples/SinglePanel';
|
|
9
11
|
import AlternativeTabStop from './examples/AlternativeTabStop';
|
|
10
12
|
import HoistedModel from './examples/HoistedModel';
|
|
@@ -38,10 +40,26 @@ yarn add @workday/canvas-kit-react
|
|
|
38
40
|
in a variety of ways: `Tabs.List`, `Tabs.Item` and `Tabs.Panel`. It follows the
|
|
39
41
|
[W3 Tabs specification](https://www.w3.org/TR/wai-aria-practices/#tabpanel).
|
|
40
42
|
|
|
41
|
-
In this example, we set up a basic `Tabs` component with five tabs.
|
|
43
|
+
In this example, we set up a basic `Tabs` component with five tabs. This example uses a static API
|
|
44
|
+
that does not support overflow.
|
|
42
45
|
|
|
43
46
|
<ExampleCodeBlock code={Basic} />
|
|
44
47
|
|
|
48
|
+
### Overflow Tabs
|
|
49
|
+
|
|
50
|
+
Tabs is a responsive component based on the width of its container. If the rendered tabs exceed the
|
|
51
|
+
width of the `Tabs.List`, an overflow menu will be rendered. This only works against the dynamic API
|
|
52
|
+
where you give the `TabsModel` an array of items to be rendered. The dynamic API handles the React
|
|
53
|
+
`key` for you based on the item's identifier. The dynamic API requires either an `id` on each item
|
|
54
|
+
object or a `getId` function that returns an identifier based on the item. The below example uses an
|
|
55
|
+
`id` property on each item.
|
|
56
|
+
|
|
57
|
+
The dynamic API takes in any object, but since nothing is known about your object, a
|
|
58
|
+
[render prop](https://reactjs.org/docs/render-props.html) is necessary to instruct a list how it
|
|
59
|
+
should render.
|
|
60
|
+
|
|
61
|
+
<ExampleCodeBlock code={OverflowTabs} />
|
|
62
|
+
|
|
45
63
|
### Hoisted Model
|
|
46
64
|
|
|
47
65
|
By default, `Tabs` will create and use its own [model](#model) internally. Alternatively, you may
|
|
@@ -55,13 +73,13 @@ trigger an event to change the active tab.
|
|
|
55
73
|
|
|
56
74
|
<ExampleCodeBlock code={HoistedModel} />
|
|
57
75
|
|
|
58
|
-
### Named
|
|
76
|
+
### Named Tabs
|
|
59
77
|
|
|
60
78
|
`Tabs.Item` and `Tabs.Panel` both take an optional `name` attribute that is used for the
|
|
61
|
-
`onActivate` callback. This example is identical to the Basic Example, but with named
|
|
79
|
+
`onActivate` callback. This example is identical to the Basic Example, but with named tabs for the
|
|
62
80
|
`Tabs.Item` and `Tabs.Panel` subcomponents.
|
|
63
81
|
|
|
64
|
-
<ExampleCodeBlock code={
|
|
82
|
+
<ExampleCodeBlock code={NamedTabs} />
|
|
65
83
|
|
|
66
84
|
### Right-to-Left (RTL)
|
|
67
85
|
|
|
@@ -75,6 +93,14 @@ Set the `disabled` prop of a `Tabs.Item` to `true` to disable it.
|
|
|
75
93
|
|
|
76
94
|
<ExampleCodeBlock code={DisabledTab} />
|
|
77
95
|
|
|
96
|
+
### Tab Icons
|
|
97
|
+
|
|
98
|
+
Tabs can have icons. You must set `hasIcon` on the `Tabs.Item` and use the `Icon` and `Text`
|
|
99
|
+
subcomponent. The `hasIcon` changes the `Tabs.Item` to change where overflow detection is from the
|
|
100
|
+
`Tabs.Item` element to the `Tabs.Item.Text` element.
|
|
101
|
+
|
|
102
|
+
<ExampleCodeBlock code={Icons} />
|
|
103
|
+
|
|
78
104
|
### Alternative Tab Stop
|
|
79
105
|
|
|
80
106
|
By default, tab panels are focusable for accessibility. If the contents of a tab panel have a
|
|
@@ -8,7 +8,7 @@ export default () => {
|
|
|
8
8
|
<Tabs>
|
|
9
9
|
<Tabs.List>
|
|
10
10
|
<Tabs.Item>First Tab</Tabs.Item>
|
|
11
|
-
<Tabs.Item disabled>Disabled Tab</Tabs.Item>
|
|
11
|
+
<Tabs.Item aria-disabled>Disabled Tab</Tabs.Item>
|
|
12
12
|
<Tabs.Item>Third Tab</Tabs.Item>
|
|
13
13
|
</Tabs.List>
|
|
14
14
|
<div style={{marginTop: space.m}}>
|
|
@@ -1,60 +1,104 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import {space} from '@workday/canvas-kit-react/tokens';
|
|
3
3
|
|
|
4
|
-
import {Tabs, useTabsModel} from '@workday/canvas-kit-react/tabs';
|
|
4
|
+
import {Tabs, useTabsModel, TabsModel} from '@workday/canvas-kit-react/tabs';
|
|
5
|
+
import {SelectionModel} from '../../lib/selection';
|
|
6
|
+
|
|
7
|
+
type Tab = {
|
|
8
|
+
tab: string;
|
|
9
|
+
id: string;
|
|
10
|
+
};
|
|
5
11
|
|
|
6
12
|
export default () => {
|
|
7
|
-
const [tabs, setTabs] = React.useState([
|
|
8
|
-
{tab: 'Tab 1',
|
|
9
|
-
{tab: 'Tab 2',
|
|
10
|
-
{tab: 'Tab 3',
|
|
13
|
+
const [tabs, setTabs] = React.useState<Tab[]>([
|
|
14
|
+
{tab: 'Tab 1', id: 'tab-1'},
|
|
15
|
+
{tab: 'Tab 2', id: 'tab-2'},
|
|
16
|
+
{tab: 'Tab 3', id: 'tab-3'},
|
|
17
|
+
{tab: 'Add Tab', id: 'add'},
|
|
11
18
|
]);
|
|
12
|
-
const addedRef = React.useRef(tabs.length);
|
|
19
|
+
const addedRef = React.useRef(tabs.length - 1);
|
|
13
20
|
const model = useTabsModel({
|
|
14
|
-
|
|
21
|
+
items: tabs,
|
|
22
|
+
shouldSelect: ({data}) => data.id !== 'add',
|
|
15
23
|
});
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
// A ref of the model for the render functions to work around the caching done to lists
|
|
26
|
+
const modelRef = React.useRef(model);
|
|
27
|
+
modelRef.current = model;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Helper function that should be called when an item is programmatically removed. The following
|
|
31
|
+
* side effects depend on state of the model:
|
|
32
|
+
* * **Item is focused**: Focus will be moved to next item in the list
|
|
33
|
+
* * **Item is selected**: Selection will be moved to the next item in the list
|
|
34
|
+
* @param id The id of the item that will be removed
|
|
35
|
+
*/
|
|
36
|
+
const removeItem = <T extends unknown>(id: string, model: SelectionModel<T>) => {
|
|
37
|
+
const index = model.state.items.findIndex(item => model.getId(item) === model.state.cursorId);
|
|
38
|
+
console.log('index', index, id, model.state.cursorId, model.state.items);
|
|
39
|
+
const nextIndex = index === model.state.items.length - 1 ? index - 1 : index + 1;
|
|
40
|
+
const nextId = model.getId(model.state.items[nextIndex]);
|
|
41
|
+
if (model.state.selectedIds[0] === id) {
|
|
42
|
+
// We're removing the currently selected item. Select next item
|
|
43
|
+
model.events.select({id: nextId});
|
|
44
|
+
}
|
|
45
|
+
if (model.state.cursorId === id) {
|
|
46
|
+
// We're removing the currently focused item. Focus next item
|
|
47
|
+
model.events.goTo({id: nextId});
|
|
48
|
+
|
|
49
|
+
// wait for stabilization of state
|
|
50
|
+
requestAnimationFrame(() => {
|
|
51
|
+
document.querySelector<HTMLElement>(`#${model.state.id}-${nextId}`)?.focus();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const onKeyDown = (id: string) => (e: React.KeyboardEvent<HTMLElement>) => {
|
|
57
|
+
if (e.key === 'Delete' && id !== 'add') {
|
|
58
|
+
setTabs(tabs.filter(item => item.id !== id));
|
|
59
|
+
const model = modelRef.current;
|
|
60
|
+
removeItem(id, model);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const onClick = (id: string) => (e: React.MouseEvent) => {
|
|
65
|
+
if (id === 'add') {
|
|
66
|
+
addedRef.current += 1;
|
|
67
|
+
setTabs(tabs => {
|
|
68
|
+
const newTabs = tabs.slice(0, tabs.length - 1);
|
|
69
|
+
const addTab = tabs.slice(-1);
|
|
70
|
+
return newTabs.concat(
|
|
71
|
+
{tab: `Tab ${addedRef.current}`, id: `tab-${addedRef.current}`},
|
|
72
|
+
addTab
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
model.events.goTo({id: 'add'});
|
|
20
76
|
}
|
|
21
77
|
};
|
|
22
78
|
|
|
23
79
|
return (
|
|
24
80
|
<Tabs model={model}>
|
|
25
|
-
<Tabs.List>
|
|
26
|
-
{
|
|
27
|
-
<Tabs.Item
|
|
28
|
-
key={item.name}
|
|
29
|
-
name={item.name}
|
|
30
|
-
index={index}
|
|
31
|
-
onKeyDown={e => onKeyDown(e, item.name)}
|
|
32
|
-
>
|
|
81
|
+
<Tabs.List overflowButton={<Tabs.OverflowButton>More</Tabs.OverflowButton>}>
|
|
82
|
+
{(item: Tab) => (
|
|
83
|
+
<Tabs.Item name={item.id} onKeyDown={onKeyDown(item.id)} onClick={onClick(item.id)}>
|
|
33
84
|
{item.tab}
|
|
34
85
|
</Tabs.Item>
|
|
35
|
-
)
|
|
36
|
-
<Tabs.Item
|
|
37
|
-
key={'last'}
|
|
38
|
-
index={tabs.length}
|
|
39
|
-
name={'last'}
|
|
40
|
-
onClick={() => {
|
|
41
|
-
addedRef.current += 1;
|
|
42
|
-
setTabs(tabs =>
|
|
43
|
-
tabs.concat({tab: `Tab ${addedRef.current}`, name: `tab-${addedRef.current}`})
|
|
44
|
-
);
|
|
45
|
-
model.events.goTo({id: 'last'});
|
|
46
|
-
}}
|
|
47
|
-
>
|
|
48
|
-
Add Tab
|
|
49
|
-
</Tabs.Item>
|
|
86
|
+
)}
|
|
50
87
|
</Tabs.List>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
<Tabs.
|
|
88
|
+
<Tabs.Menu.Popper>
|
|
89
|
+
<Tabs.Menu.Card maxWidth={300} maxHeight={200}>
|
|
90
|
+
<Tabs.Menu.List>
|
|
91
|
+
{(item: Tab) => <Tabs.Menu.Item name={item.id}>{item.tab}</Tabs.Menu.Item>}
|
|
92
|
+
</Tabs.Menu.List>
|
|
93
|
+
</Tabs.Menu.Card>
|
|
94
|
+
</Tabs.Menu.Popper>
|
|
95
|
+
<Tabs.Panels>
|
|
96
|
+
{(item: Tab) => (
|
|
97
|
+
<Tabs.Panel marginTop="m" name={item.id}>
|
|
54
98
|
Contents of {item.tab}
|
|
55
99
|
</Tabs.Panel>
|
|
56
|
-
)
|
|
57
|
-
</
|
|
100
|
+
)}
|
|
101
|
+
</Tabs.Panels>
|
|
58
102
|
</Tabs>
|
|
59
103
|
);
|
|
60
104
|
};
|
|
@@ -6,8 +6,8 @@ import {Tabs, useTabsModel} from '@workday/canvas-kit-react/tabs';
|
|
|
6
6
|
|
|
7
7
|
export default () => {
|
|
8
8
|
const model = useTabsModel({
|
|
9
|
-
|
|
10
|
-
console.log('
|
|
9
|
+
onSelect({data, prevState}) {
|
|
10
|
+
console.log('Selected Tab', data, prevState);
|
|
11
11
|
},
|
|
12
12
|
});
|
|
13
13
|
|
|
@@ -27,10 +27,10 @@ export default () => {
|
|
|
27
27
|
</Tabs>
|
|
28
28
|
<SecondaryButton
|
|
29
29
|
onClick={() => {
|
|
30
|
-
model.events.
|
|
30
|
+
model.events.select({id: 'third'});
|
|
31
31
|
}}
|
|
32
32
|
>
|
|
33
|
-
|
|
33
|
+
Select Third Tab
|
|
34
34
|
</SecondaryButton>
|
|
35
35
|
</>
|
|
36
36
|
);
|