Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 143 additions & 3 deletions apps/www/src/content/docs/components/tabs/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
export const preview = {
type: 'code',
code: `
<Flex direction="row" gap="large" style={{ width: "100%", fontSize: "12px" }}>
<Flex direction="row" gap="large" style={{ width: "100%", fontSize: "12px", padding: "0 16px" }}>
<Tabs defaultValue="tab-one">
<Tabs.List>
<Tabs.Tab value="tab-one" leadingIcon={<Info />}>Hoisting</Tabs.Tab>
<Tabs.Tab value="tab-two">Hosting</Tabs.Tab>
<Tabs.Tab value="tab-three" leadingIcon={<Info />}>Editor</Tabs.Tab>
<Tabs.Tab value="tab-four">Billing</Tabs.Tab>
<Tabs.Tab value="tab-five">SEO</Tabs.Tab>
</Tabs.List>
Expand Down Expand Up @@ -77,3 +75,145 @@ export const disabledDemo = {
</Tabs>
</div>`
};

export const standaloneVariantDemo = {
type: 'code',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" variant="standalone">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
};

export const plainVariantDemo = {
type: 'code',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" variant="plain">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
};

export const variantsDemo = {
type: 'code',
tabs: [
{
name: 'Segmented (default)',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
},
{
name: 'Standalone',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" variant="standalone">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
},
{
name: 'Plain',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" variant="plain">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
}
]
};

export const sizesDemo = {
type: 'code',
tabs: [
{
name: 'Small',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" size="small">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
},
{
name: 'Medium',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" size="medium">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
},
{
name: 'Large',
code: `
<div style={{ width: "400px" }}>
<Tabs defaultValue="tab1" size="large">
<Tabs.List>
<Tabs.Tab value="tab1">Account</Tabs.Tab>
<Tabs.Tab value="tab2" disabled>Password</Tabs.Tab>
<Tabs.Tab value="tab3">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">Account settings</Tabs.Content>
<Tabs.Content value="tab2">Password settings</Tabs.Content>
<Tabs.Content value="tab3">Other settings</Tabs.Content>
</Tabs>
</div>`
}
]
};
17 changes: 16 additions & 1 deletion apps/www/src/content/docs/components/tabs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ description: A set of layered sections of content that display one panel at a ti
source: packages/raystack/components/tabs
---

import { preview, basicDemo, iconsDemo, disabledDemo } from "./demo.ts";
import {
preview,
basicDemo,
iconsDemo,
disabledDemo,
variantsDemo,
sizesDemo
} from "./demo.ts";

<Demo data={preview} />

Expand Down Expand Up @@ -63,6 +70,14 @@ Renders the content panel for a tab.

<Demo data={disabledDemo} />

### Variants

<Demo data={variantsDemo} />

### Sizes

<Demo data={sizesDemo} />

## Accessibility

- Follows the [WAI-ARIA Tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/)
Expand Down
6 changes: 6 additions & 0 deletions apps/www/src/content/docs/components/tabs/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export interface TabsRootProps {
/** The orientation of the tabs. */
orientation?: 'horizontal' | 'vertical';

/** Visual variant for how tabs are rendered. */
variant?: 'segmented' | 'standalone' | 'plain';

/** Size variant applied to all tab triggers. */
size?: 'small' | 'medium' | 'large';

/** Additional CSS class names. */
className?: string;
}
Expand Down
84 changes: 84 additions & 0 deletions packages/raystack/components/tabs/__tests__/tabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,90 @@ describe('Tabs', () => {
});
});

describe('Variants', () => {
it('applies standalone variant class', () => {
render(
<Tabs defaultValue='tab1' variant='standalone'>
<Tabs.List>
<Tabs.Tab value='tab1'>{TAB_1_TEXT}</Tabs.Tab>
</Tabs.List>
<Tabs.Content value='tab1'>{CONTENT_1_TEXT}</Tabs.Content>
</Tabs>
);

const tablist = screen.getByRole('tablist');
const root = tablist.closest(`.${styles.root}`);
expect(root).not.toBeNull();
expect(root).toHaveClass(styles['variant-standalone']);
});

it('defaults to segmented variant class', () => {
render(
<Tabs defaultValue='tab1'>
<Tabs.List>
<Tabs.Tab value='tab1'>{TAB_1_TEXT}</Tabs.Tab>
</Tabs.List>
<Tabs.Content value='tab1'>{CONTENT_1_TEXT}</Tabs.Content>
</Tabs>
);

const tablist = screen.getByRole('tablist');
const root = tablist.closest(`.${styles.root}`);
expect(root).not.toBeNull();
expect(root).toHaveClass(styles['variant-segmented']);
});

it('applies plain variant class', () => {
render(
<Tabs defaultValue='tab1' variant='plain'>
<Tabs.List>
<Tabs.Tab value='tab1'>{TAB_1_TEXT}</Tabs.Tab>
</Tabs.List>
<Tabs.Content value='tab1'>{CONTENT_1_TEXT}</Tabs.Content>
</Tabs>
);

const tablist = screen.getByRole('tablist');
const root = tablist.closest(`.${styles.root}`);
expect(root).not.toBeNull();
expect(root).toHaveClass(styles['variant-plain']);
});
});

describe('Sizes', () => {
it('defaults to large size class', () => {
render(
<Tabs defaultValue='tab1'>
<Tabs.List>
<Tabs.Tab value='tab1'>{TAB_1_TEXT}</Tabs.Tab>
</Tabs.List>
<Tabs.Content value='tab1'>{CONTENT_1_TEXT}</Tabs.Content>
</Tabs>
);

const tablist = screen.getByRole('tablist');
const root = tablist.closest(`.${styles.root}`);
expect(root).not.toBeNull();
expect(root).toHaveClass(styles['size-large']);
});

it('applies small size class', () => {
render(
<Tabs defaultValue='tab1' size='small'>
<Tabs.List>
<Tabs.Tab value='tab1'>{TAB_1_TEXT}</Tabs.Tab>
</Tabs.List>
<Tabs.Content value='tab1'>{CONTENT_1_TEXT}</Tabs.Content>
</Tabs>
);

const tablist = screen.getByRole('tablist');
const root = tablist.closest(`.${styles.root}`);
expect(root).not.toBeNull();
expect(root).toHaveClass(styles['size-small']);
});
});

describe('Data Attributes', () => {
it('has aria-selected on active trigger', () => {
render(<BasicTabs />);
Expand Down
Loading
Loading