@kaizen/components 1.64.14 → 1.65.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 (44) hide show
  1. package/dist/cjs/__future__/Tabs/Tabs.cjs +23 -0
  2. package/dist/cjs/__future__/Tabs/subcomponents/Tab/Tab.cjs +39 -0
  3. package/dist/cjs/__future__/Tabs/subcomponents/Tab/Tab.module.css.cjs +7 -0
  4. package/dist/cjs/__future__/Tabs/subcomponents/TabList/TabList.cjs +31 -0
  5. package/dist/cjs/__future__/Tabs/subcomponents/TabList/TabList.module.css.cjs +7 -0
  6. package/dist/cjs/__future__/Tabs/subcomponents/TabPanel/TabPanel.cjs +24 -0
  7. package/dist/cjs/future.cjs +8 -0
  8. package/dist/esm/__future__/Tabs/Tabs.mjs +15 -0
  9. package/dist/esm/__future__/Tabs/subcomponents/Tab/Tab.mjs +30 -0
  10. package/dist/esm/__future__/Tabs/subcomponents/Tab/Tab.module.css.mjs +5 -0
  11. package/dist/esm/__future__/Tabs/subcomponents/TabList/TabList.mjs +22 -0
  12. package/dist/esm/__future__/Tabs/subcomponents/TabList/TabList.module.css.mjs +5 -0
  13. package/dist/esm/__future__/Tabs/subcomponents/TabPanel/TabPanel.mjs +16 -0
  14. package/dist/esm/future.mjs +4 -0
  15. package/dist/styles.css +188 -71
  16. package/dist/types/Tabs/subcomponents/index.d.ts +0 -1
  17. package/dist/types/__future__/Tabs/Tabs.d.ts +11 -0
  18. package/dist/types/__future__/Tabs/index.d.ts +2 -0
  19. package/dist/types/__future__/Tabs/subcomponents/Tab/Tab.d.ts +12 -0
  20. package/dist/types/__future__/Tabs/subcomponents/Tab/index.d.ts +1 -0
  21. package/dist/types/__future__/Tabs/subcomponents/TabList/TabList.d.ts +17 -0
  22. package/dist/types/__future__/Tabs/subcomponents/TabList/index.d.ts +1 -0
  23. package/dist/types/__future__/Tabs/subcomponents/TabPanel/TabPanel.d.ts +6 -0
  24. package/dist/types/__future__/Tabs/subcomponents/TabPanel/index.d.ts +1 -0
  25. package/dist/types/__future__/Tabs/subcomponents/index.d.ts +3 -0
  26. package/dist/types/__future__/index.d.ts +1 -0
  27. package/package.json +2 -2
  28. package/src/Tabs/subcomponents/index.ts +0 -1
  29. package/src/__future__/Tabs/Tabs.tsx +18 -0
  30. package/src/__future__/Tabs/_docs/Tabs--api-specification.mdx +43 -0
  31. package/src/__future__/Tabs/_docs/Tabs--migration-guide.mdx +93 -0
  32. package/src/__future__/Tabs/_docs/Tabs.stories.tsx +74 -0
  33. package/src/__future__/Tabs/index.ts +2 -0
  34. package/src/__future__/Tabs/subcomponents/Tab/Tab.module.css +94 -0
  35. package/src/__future__/Tabs/subcomponents/Tab/Tab.tsx +58 -0
  36. package/src/__future__/Tabs/subcomponents/Tab/index.ts +1 -0
  37. package/src/__future__/Tabs/subcomponents/TabList/TabList.module.css +8 -0
  38. package/src/__future__/Tabs/subcomponents/TabList/TabList.tsx +45 -0
  39. package/src/__future__/Tabs/subcomponents/TabList/index.ts +1 -0
  40. package/src/__future__/Tabs/subcomponents/TabPanel/TabPanel.module.css +12 -0
  41. package/src/__future__/Tabs/subcomponents/TabPanel/TabPanel.tsx +20 -0
  42. package/src/__future__/Tabs/subcomponents/TabPanel/index.ts +1 -0
  43. package/src/__future__/Tabs/subcomponents/index.ts +3 -0
  44. package/src/__future__/index.ts +1 -0
@@ -0,0 +1,93 @@
1
+ import { Meta } from "@storybook/blocks"
2
+
3
+ <Meta title="Components/Tabs/Tabs (Future)/Migration Guide" />
4
+
5
+ # Future Tabs migration guide
6
+
7
+ A brief guide on how and why to migrate from Kaizen's current `Tabs` to the `future` release.
8
+
9
+ ## Why the change?
10
+
11
+ Current Tabs uses the Reach UI library under the hood, which is no longer actively maintained. This switches the library used internally to React Aria Components.
12
+
13
+ ## Component and API changes at a glance
14
+
15
+ The Reach UI and React Aria APIs are fairly similar so there's not too much to adjust.
16
+
17
+ The biggest adjustment is that you now need to provide an `id` for each `<Tab>` and match it with the one on `<TabPanel>`
18
+
19
+ Additionally:
20
+ - `<TabPanel>`s no longer needs to be wrapped in a `<TabPanels>` component
21
+ - `classNameOverride` changes to `className`
22
+ - `<Tabs defaultIndex={}>` changes to `<Tabs defaultSelectedKey={}>`
23
+ - `<Tabs index={}>` changes to `<Tabs selectedKey={}>`
24
+ - `<Tabs onChange={}>` changes to `<Tabs onSelectionChange={}>`
25
+ - `<Tab disabled>` changes to `<Tab isDisabled>`
26
+
27
+ ## Migration examples
28
+
29
+ ### Uncontrolled
30
+
31
+ #### Before
32
+
33
+ ```tsx
34
+ <Tabs defaultIndex={1}>
35
+ <TabList>
36
+ <Tab>Tab 1</Tab>
37
+ <Tab>Tab 2</Tab>
38
+ <Tab disabled>Disabled tab</Tab>
39
+ </TabList>
40
+ <TabPanels>
41
+ <TabPanel classNameOverride="p-4">Content 1</TabPanel>
42
+ <TabPanel>Content 2</TabPanel>
43
+ <TabPanel>Disabled content</TabPanel>
44
+ </TabPanels>
45
+ </Tabs>
46
+ ```
47
+
48
+ #### After
49
+
50
+ ```tsx
51
+ <Tabs defaultSelectedKey="two">
52
+ <TabList>
53
+ <Tab id="one">Tab 1</Tab>
54
+ <Tab id="two">Tab 2</Tab>
55
+ <Tab id="three" isDisabled>Disabled tab</Tab>
56
+ </TabList>
57
+ <TabPanel id="one" className="p-4">Content 1</TabPanel>
58
+ <TabPanel id="two">Content 2</TabPanel>
59
+ </Tabs>
60
+ ```
61
+
62
+ ### Controlled
63
+
64
+ #### Before
65
+
66
+ ```tsx
67
+ <Tabs onChange={setSelectedTab} defaultIndex={1}>
68
+ <TabList>
69
+ <Tab>Tab 1</Tab>
70
+ <Tab>Tab 2</Tab>
71
+ <Tab disabled>Disabled tab</Tab>
72
+ </TabList>
73
+ <TabPanels>
74
+ <TabPanel>Content 1</TabPanel>
75
+ <TabPanel>Content 2</TabPanel>
76
+ <TabPanel>Disabled content</TabPanel>
77
+ </TabPanels>
78
+ </Tabs>
79
+ ```
80
+
81
+ #### After
82
+
83
+ ```tsx
84
+ <Tabs onSelectionChange={setSelectedTab} selectedKey="two">
85
+ <TabList>
86
+ <Tab id="one">Tab 1</Tab>
87
+ <Tab id="two">Tab 2</Tab>
88
+ <Tab id="three" isDisabled>Disabled tab</Tab>
89
+ </TabList>
90
+ <TabPanel id="one">Content 1</TabPanel>
91
+ <TabPanel id="two">Content 2</TabPanel>
92
+ </Tabs>
93
+ ```
@@ -0,0 +1,74 @@
1
+ import React, { useState } from "react"
2
+ import { Meta, StoryObj } from "@storybook/react"
3
+ import { Text } from "~components/Text"
4
+ import { Button } from "~components/__actions__/v2"
5
+ import { Tab, TabList, TabPanel, Tabs, Key } from "../index"
6
+
7
+ const meta = {
8
+ title: "Components/Tabs/Tabs (Future)",
9
+ component: Tabs,
10
+ args: {
11
+ children: (
12
+ <>
13
+ <TabList aria-label="Tabs">
14
+ <Tab id="one">Tab 1</Tab>
15
+ <Tab id="two">Tab 2</Tab>
16
+ <Tab id="three" badge="3">
17
+ Tab 3
18
+ </Tab>
19
+ <Tab id="four" isDisabled>
20
+ Disabled Tab
21
+ </Tab>
22
+ </TabList>
23
+ <TabPanel id="one" className="p-24">
24
+ <Text variant="body">Content 1</Text>
25
+ </TabPanel>
26
+ <TabPanel id="two" className="p-24">
27
+ <Text variant="body">Content 2</Text>
28
+ </TabPanel>
29
+ <TabPanel id="three" className="p-24">
30
+ <Text variant="body">Content 3</Text>
31
+ </TabPanel>
32
+ </>
33
+ ),
34
+ },
35
+ } satisfies Meta<typeof Tabs>
36
+
37
+ export default meta
38
+
39
+ type Story = StoryObj<typeof meta>
40
+
41
+ export const Playground: Story = {
42
+ parameters: {
43
+ chromatic: { disable: false },
44
+ docs: {
45
+ canvas: {
46
+ sourceState: "shown",
47
+ },
48
+ },
49
+ },
50
+ args: {
51
+ defaultSelectedKey: "one",
52
+ // eslint-disable-next-line no-console
53
+ onSelectionChange: (key): void => console.log("Tab changed to ", key),
54
+ },
55
+ }
56
+
57
+ export const Controlled: Story = {
58
+ render: args => {
59
+ const [selectedKey, setSelectedKey] = useState<Key>(0)
60
+ return (
61
+ <>
62
+ <Tabs
63
+ {...args}
64
+ selectedKey={selectedKey}
65
+ onSelectionChange={setSelectedKey}
66
+ />
67
+ <Button
68
+ label="Switch to tab 2"
69
+ onClick={(): void => setSelectedKey("two")}
70
+ />
71
+ </>
72
+ )
73
+ },
74
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Tabs"
2
+ export * from "./subcomponents"
@@ -0,0 +1,94 @@
1
+ .tab {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ border: 2px solid transparent;
5
+ border-bottom: 0;
6
+ border-top-left-radius: var(--border-borderless-border-radius);
7
+ border-top-right-radius: var(--border-borderless-border-radius);
8
+ background: var(--color-white);
9
+ white-space: nowrap;
10
+ text-decoration: none;
11
+ padding: var(--spacing-md) var(--spacing-md);
12
+ margin: 0;
13
+ font-family: var(--typography-heading-4-font-family);
14
+ font-size: var(--typography-heading-4-font-size);
15
+ font-weight: var(--typography-heading-4-font-weight);
16
+ line-height: var(--typography-heading-4-line-height);
17
+ letter-spacing: var(--typography-heading-4-letter-spacing);
18
+ color: var(--color-purple-800);
19
+
20
+ &:focus {
21
+ outline: none;
22
+ }
23
+
24
+ &:focus-visible {
25
+ background: var(--color-blue-100);
26
+ color: var(--color-blue-500);
27
+ border-color: var(--color-blue-500);
28
+ }
29
+
30
+ &[data-disabled] {
31
+ opacity: 0.3;
32
+ }
33
+
34
+ &:not(:first-child) {
35
+ margin-inline-start: var(--spacing-xs);
36
+ }
37
+
38
+ &:not([data-disabled]):hover {
39
+ background: var(--color-blue-100);
40
+ color: var(--color-blue-500);
41
+ }
42
+ }
43
+
44
+ .tab[data-selected] {
45
+ position: relative;
46
+ color: var(--color-blue-500);
47
+
48
+ &::before {
49
+ content: "";
50
+ display: block;
51
+ border-top-left-radius: 5px;
52
+ border-top-right-radius: 5px;
53
+ background-color: currentcolor;
54
+ height: 5px;
55
+ width: 100%;
56
+ position: absolute;
57
+ left: 0;
58
+ right: 0;
59
+ bottom: 0;
60
+ }
61
+ }
62
+
63
+ .badge {
64
+ margin-inline-start: var(--spacing-sm);
65
+ display: inline-flex;
66
+ align-items: center;
67
+ }
68
+
69
+ @media (forced-colors: active) {
70
+ .tab {
71
+ border: 2px solid transparent;
72
+
73
+ &:focus-visible::after {
74
+ content: "";
75
+ position: absolute;
76
+ background: transparent;
77
+ border-radius: var(--border-focus-ring-border-radius);
78
+ border-width: var(--border-focus-ring-border-width);
79
+ border-style: var(--border-focus-ring-border-style);
80
+ border-color: transparent;
81
+ inset: -2px;
82
+ }
83
+ }
84
+
85
+ .tab[data-selected]::before {
86
+ /* High contrast also doesn't see the pseudo element created to show the active tab. */
87
+ content: "";
88
+ position: absolute;
89
+ left: 0;
90
+ right: 0;
91
+ bottom: 0;
92
+ border-bottom: 2px solid transparent;
93
+ }
94
+ }
@@ -0,0 +1,58 @@
1
+ import React from "react"
2
+ import classnames from "classnames"
3
+ import { Tab as RACTab, TabProps as RACTabProps } from "react-aria-components"
4
+ import { Badge } from "~components/Badge"
5
+ import styles from "./Tab.module.css"
6
+
7
+ export type TabProps = {
8
+ /**
9
+ * Adds a Kaizen Badge component to the tab.
10
+ * Comes with some logic baked in - changes variant based on active/focus/hover state.
11
+ */
12
+ badge?: string
13
+ } & Omit<
14
+ RACTabProps,
15
+ // omitting link functionality because it goes against WAI ARIA standards https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
16
+ | "href"
17
+ | "hrefLang"
18
+ | "target"
19
+ | "rel"
20
+ | "download"
21
+ | "ping"
22
+ | "referrerPolicy"
23
+ >
24
+
25
+ /**
26
+ * A tab button
27
+ */
28
+ export const Tab = (props: TabProps): JSX.Element => {
29
+ const { badge, children, className, ...restProps } = props
30
+
31
+ const tabProps = {
32
+ className: classnames(styles.tab, className),
33
+ ...restProps,
34
+ }
35
+
36
+ return (
37
+ <RACTab {...tabProps}>
38
+ {({ isSelected, isFocusVisible, isHovered }) => (
39
+ <>
40
+ {children}
41
+ {badge && (
42
+ <span className={styles.badge}>
43
+ <Badge
44
+ variant={
45
+ isSelected || isFocusVisible || isHovered
46
+ ? "active"
47
+ : "default"
48
+ }
49
+ >
50
+ {badge}
51
+ </Badge>
52
+ </span>
53
+ )}
54
+ </>
55
+ )}
56
+ </RACTab>
57
+ )
58
+ }
@@ -0,0 +1 @@
1
+ export * from "./Tab"
@@ -0,0 +1,8 @@
1
+ .tabList {
2
+ border-bottom: 1px solid rgba(var(--color-gray-600-rgb), 0.1);
3
+ padding: var(--spacing-xs) var(--spacing-md) 0;
4
+ }
5
+
6
+ .noPadding {
7
+ padding: 0;
8
+ }
@@ -0,0 +1,45 @@
1
+ import React, { ReactNode } from "react"
2
+ import classnames from "classnames"
3
+ import {
4
+ TabList as RACTabList,
5
+ TabListProps as RACTabListProps,
6
+ } from "react-aria-components"
7
+ import styles from "./TabList.module.css"
8
+
9
+ export type TabListProps = {
10
+ /**
11
+ * Accessible name for the set of tabs
12
+ */
13
+ "aria-label": string
14
+ /**
15
+ * Removes the built in padding
16
+ */
17
+ noPadding?: boolean
18
+ children: ReactNode
19
+ } & RACTabListProps<HTMLElement>
20
+
21
+ /**
22
+ * Wrapper for the tabs themselves
23
+ */
24
+ export const TabList = (props: TabListProps): JSX.Element => {
25
+ const {
26
+ "aria-label": ariaLabel,
27
+ noPadding = false,
28
+ children,
29
+ className,
30
+ ...restProps
31
+ } = props
32
+ return (
33
+ <RACTabList
34
+ aria-label={ariaLabel}
35
+ className={classnames(
36
+ styles.tabList,
37
+ className,
38
+ noPadding && styles.noPadding
39
+ )}
40
+ {...restProps}
41
+ >
42
+ {children}
43
+ </RACTabList>
44
+ )
45
+ }
@@ -0,0 +1 @@
1
+ export * from "./TabList"
@@ -0,0 +1,12 @@
1
+ .tabPanel {
2
+ border: 2px solid transparent;
3
+
4
+ &:focus {
5
+ outline: none;
6
+ }
7
+
8
+ &:focus-visible {
9
+ border-color: var(--color-blue-500);
10
+ border-radius: var(--border-focus-ring-border-radius);
11
+ }
12
+ }
@@ -0,0 +1,20 @@
1
+ import React from "react"
2
+ import {
3
+ TabPanel as RACTabPanel,
4
+ TabPanelProps as RACTabPanelProps,
5
+ } from "react-aria-components"
6
+ import styles from "./TabPanel.module.css"
7
+
8
+ export type TabPanelProps = RACTabPanelProps
9
+
10
+ /**
11
+ * Wrapper for the content that shows when tab is active
12
+ */
13
+ export const TabPanel = (props: TabPanelProps): JSX.Element => {
14
+ const { className, children, ...restProps } = props
15
+ return (
16
+ <RACTabPanel className={(styles.tabPanel, className)} {...restProps}>
17
+ {children}
18
+ </RACTabPanel>
19
+ )
20
+ }
@@ -0,0 +1 @@
1
+ export * from "./TabPanel"
@@ -0,0 +1,3 @@
1
+ export * from "./Tab/"
2
+ export * from "./TabList/"
3
+ export * from "./TabPanel/"
@@ -1,2 +1,3 @@
1
1
  export * from "./Select"
2
2
  export * from "./Tag"
3
+ export * from "./Tabs"