-
Notifications
You must be signed in to change notification settings - Fork 10
Tabs: Styling #2545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tabs: Styling #2545
Changes from 13 commits
3c0903f
962db5d
d793e37
221bcae
c25efd9
e87cf98
5c18a5e
adea769
b99ea1e
88a99b2
73210cf
08039a9
e6cb053
533176d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
"@khanacademy/wonder-blocks-tabs": minor | ||
--- | ||
|
||
Tabs: | ||
|
||
- Add styling | ||
- Add support for `styles` prop | ||
- Add `animated` prop for enabling transition animations for the selected tab | ||
|
||
NavigationTabs: | ||
|
||
- Refactored current tab indicator logic to work with Tabs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import * as React from "react"; | ||
import {View} from "@khanacademy/wonder-blocks-core"; | ||
import {border, semanticColor, sizing} from "@khanacademy/wonder-blocks-tokens"; | ||
|
||
type Props = { | ||
children: React.ReactNode; | ||
}; | ||
|
||
/** | ||
* Component that renders a placeholder block. It is useful for visualizing | ||
* layouts. | ||
*/ | ||
export const Placeholder = (props: Props) => { | ||
const {children} = props; | ||
return ( | ||
<View | ||
style={{ | ||
backgroundColor: semanticColor.surface.secondary, | ||
padding: sizing.size_120, | ||
margin: sizing.size_010, | ||
border: `${border.width.thin}px dashed ${semanticColor.border.subtle}`, | ||
width: "100%", | ||
alignItems: "center", | ||
}} | ||
> | ||
{children} | ||
</View> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import type {Meta, StoryObj} from "@storybook/react"; | ||
import * as React from "react"; | ||
|
||
import {Tab} from "@khanacademy/wonder-blocks-tabs"; | ||
import {AllVariants} from "../components/all-variants"; | ||
import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon"; | ||
import {IconMappings} from "../wonder-blocks-icon/phosphor-icon.argtypes"; | ||
import {addStyle} from "@khanacademy/wonder-blocks-core"; | ||
import {sizing} from "@khanacademy/wonder-blocks-tokens"; | ||
import {rtlText} from "../components/text-for-testing"; | ||
|
||
const StyledDiv = addStyle("div"); | ||
|
||
const generateRows = (rtl: boolean = false) => [ | ||
{ | ||
name: "Default", | ||
props: { | ||
children: rtl ? rtlText : "Tab", | ||
}, | ||
}, | ||
{ | ||
name: "With Icons", | ||
props: { | ||
children: ( | ||
<StyledDiv | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
gap: sizing.size_040, | ||
}} | ||
> | ||
<PhosphorIcon icon={IconMappings.cookie} /> | ||
{rtl ? rtlText : "Tab"} | ||
<PhosphorIcon icon={IconMappings.iceCream} /> | ||
</StyledDiv> | ||
), | ||
ariaLabel: "Tab with icons", | ||
}, | ||
}, | ||
{ | ||
name: "Icon Only", | ||
props: { | ||
children: ( | ||
<PhosphorIcon icon={IconMappings.iceCream} size="medium" /> | ||
), | ||
ariaLabel: "Tab with icon", | ||
}, | ||
}, | ||
]; | ||
|
||
const rows = generateRows(); | ||
const rtlRows = generateRows(true); | ||
|
||
const columns = [ | ||
{ | ||
name: "Default", | ||
props: {}, | ||
}, | ||
{ | ||
name: "Selected", | ||
props: { | ||
selected: true, | ||
}, | ||
}, | ||
]; | ||
|
||
type Story = StoryObj<typeof Tab>; | ||
|
||
/** | ||
* The following stories are used to generate the pseudo states for the Switch | ||
* component. This is only used for visual testing in Chromatic. | ||
*/ | ||
const meta = { | ||
title: "Packages / Tabs / Tabs / Subcomponents /Tab - All Variants", | ||
component: Tab, | ||
render: (args) => ( | ||
<> | ||
<AllVariants rows={rows} columns={columns}> | ||
{(props) => ( | ||
<div role="tablist"> | ||
<Tab {...args} {...props} /> | ||
</div> | ||
)} | ||
</AllVariants> | ||
<div dir="rtl"> | ||
<AllVariants rows={rtlRows} columns={columns}> | ||
{(props) => ( | ||
<div role="tablist"> | ||
<Tab {...args} {...props} /> | ||
</div> | ||
)} | ||
</AllVariants> | ||
</div> | ||
</> | ||
), | ||
args: {}, | ||
tags: ["!autodocs"], | ||
} satisfies Meta<typeof Tab>; | ||
|
||
export default meta; | ||
|
||
export const Default: Story = {}; | ||
|
||
export const Hover: Story = { | ||
parameters: {pseudo: {hover: true}}, | ||
}; | ||
|
||
export const Focus: Story = { | ||
parameters: {pseudo: {focusVisible: true}}, | ||
}; | ||
|
||
export const HoverFocus: Story = { | ||
name: "Hover + Focus", | ||
parameters: {pseudo: {hover: true, focusVisible: true}}, | ||
}; | ||
|
||
export const Press: Story = { | ||
parameters: {pseudo: {hover: true, active: true}}, | ||
}; | ||
|
||
export const PressFocus: Story = { | ||
parameters: {pseudo: {focusVisible: true, active: true}}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
import type {Meta, StoryObj} from "@storybook/react"; | ||
import * as React from "react"; | ||
|
||
import {Tabs} from "@khanacademy/wonder-blocks-tabs"; | ||
import {AllVariants} from "../components/all-variants"; | ||
import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon"; | ||
import {IconMappings} from "../wonder-blocks-icon/phosphor-icon.argtypes"; | ||
import {addStyle, View} from "@khanacademy/wonder-blocks-core"; | ||
import {rtlText} from "../components/text-for-testing"; | ||
|
||
const StyledDiv = addStyle("div"); | ||
|
||
const generateRows = (rtl: boolean = false) => [ | ||
{ | ||
name: "Default", | ||
props: { | ||
tabs: [ | ||
{ | ||
label: rtl ? rtlText : "Tab 1", | ||
id: "tab-1", | ||
panel: rtl ? rtlText : "Tab 1 Contents", | ||
}, | ||
{ | ||
label: rtl ? rtlText : "Tab 2", | ||
id: "tab-2", | ||
panel: rtl ? rtlText : "Tab 2 Contents", | ||
}, | ||
{ | ||
label: rtl ? rtlText : "Tab 3", | ||
id: "tab-3", | ||
panel: rtl ? rtlText : "Tab 3 Contents", | ||
}, | ||
], | ||
selectedTabId: "tab-1", | ||
}, | ||
}, | ||
{ | ||
name: "With Icons", | ||
props: { | ||
tabs: [ | ||
{ | ||
label: ( | ||
<StyledDiv | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
// TODO: Update to use spacing tokens | ||
gap: "4px", | ||
}} | ||
> | ||
<PhosphorIcon icon={IconMappings.cookie} /> | ||
{rtl ? rtlText : "Tab 1"} | ||
<PhosphorIcon icon={IconMappings.iceCream} /> | ||
</StyledDiv> | ||
), | ||
id: "tab-1", | ||
panel: rtl ? rtlText : "Tab 1 Contents", | ||
}, | ||
{ | ||
label: ( | ||
<StyledDiv | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
// TODO: Update to use spacing tokens | ||
gap: "4px", | ||
}} | ||
> | ||
<PhosphorIcon icon={IconMappings.cookie} /> | ||
{rtl ? rtlText : "Tab 2"} | ||
<PhosphorIcon icon={IconMappings.iceCream} /> | ||
</StyledDiv> | ||
), | ||
id: "tab-2", | ||
panel: rtl ? rtlText : "Tab 2 Contents", | ||
}, | ||
{ | ||
label: ( | ||
<StyledDiv | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
// TODO: Update to use spacing tokens | ||
gap: "4px", | ||
}} | ||
> | ||
<PhosphorIcon icon={IconMappings.cookie} /> | ||
{rtl ? rtlText : "Tab 3"} | ||
<PhosphorIcon icon={IconMappings.iceCream} /> | ||
</StyledDiv> | ||
), | ||
id: "tab-3", | ||
panel: rtl ? rtlText : "Tab 3 Contents", | ||
}, | ||
], | ||
selectedTabId: "tab-1", | ||
}, | ||
}, | ||
{ | ||
name: "Icon Only", | ||
props: { | ||
tabs: [ | ||
{ | ||
label: ( | ||
<PhosphorIcon | ||
icon={IconMappings.iceCream} | ||
size="medium" | ||
/> | ||
), | ||
id: "tab-1", | ||
panel: rtl ? rtlText : "Tab 1 Contents", | ||
"aria-label": "Tab 1", | ||
}, | ||
{ | ||
label: ( | ||
<PhosphorIcon | ||
icon={IconMappings.cookie} | ||
size="medium" | ||
/> | ||
), | ||
id: "tab-2", | ||
panel: rtl ? rtlText : "Tab 2 Contents", | ||
"aria-label": "Tab 2", | ||
}, | ||
], | ||
selectedTabId: "tab-1", | ||
}, | ||
}, | ||
]; | ||
|
||
const rows = generateRows(); | ||
const rtlRows = generateRows(true); | ||
|
||
const columns = [ | ||
{ | ||
name: "Default", | ||
props: {}, | ||
}, | ||
]; | ||
|
||
type Story = StoryObj<typeof Tabs>; | ||
|
||
/** | ||
* The following stories are used to generate the pseudo states for the Switch | ||
* component. This is only used for visual testing in Chromatic. | ||
*/ | ||
const meta = { | ||
title: "Packages / Tabs / Tabs / Tabs - All Variants", | ||
component: Tabs, | ||
render: (args) => ( | ||
<> | ||
<AllVariants rows={rows} columns={columns}> | ||
{(props) => ( | ||
<View> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: This There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
<Tabs {...args} {...props} /> | ||
</View> | ||
)} | ||
</AllVariants> | ||
<div dir="rtl"> | ||
<AllVariants rows={rtlRows} columns={columns}> | ||
{(props) => ( | ||
<View> | ||
<Tabs {...args} {...props} /> | ||
</View> | ||
)} | ||
</AllVariants> | ||
</div> | ||
</> | ||
), | ||
args: {}, | ||
tags: ["!autodocs"], | ||
} satisfies Meta<typeof Tabs>; | ||
|
||
export default meta; | ||
|
||
export const Default: Story = {}; | ||
|
||
export const Hover: Story = { | ||
parameters: {pseudo: {hover: true}}, | ||
}; | ||
|
||
export const Focus: Story = { | ||
parameters: {pseudo: {focusVisible: true}}, | ||
}; | ||
|
||
export const HoverFocus: Story = { | ||
name: "Hover + Focus", | ||
parameters: {pseudo: {hover: true, focusVisible: true}}, | ||
}; | ||
|
||
export const Press: Story = { | ||
parameters: {pseudo: {hover: true, active: true}}, | ||
}; | ||
|
||
export const Zoom: Story = { | ||
render: (args) => ( | ||
<> | ||
<AllVariants rows={rows} columns={columns} layout="list"> | ||
{(props) => ( | ||
<View> | ||
<Tabs {...args} {...props} /> | ||
</View> | ||
)} | ||
</AllVariants> | ||
<div dir="rtl"> | ||
<AllVariants rows={rtlRows} columns={columns} layout="list"> | ||
{(props) => ( | ||
<View> | ||
<Tabs {...args} {...props} /> | ||
</View> | ||
)} | ||
</AllVariants> | ||
</div> | ||
</> | ||
), | ||
globals: { | ||
zoom: "400%", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is working as expected in Chromatic now! I need to follow up with the Chromatic support team still on the other flaky tests we were seeing before |
||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: I guess this is possible now that Marcy has landed the new spacing tokens :).