Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
15 changes: 15 additions & 0 deletions docs/src/pages/components/Tabs.svx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,21 @@ Use the `secondaryChildren` slot to customize the secondary label with custom ma
</svelte:fragment>
</Tabs>

## Container type with icons and secondary label

Container type tabs can use both `icon` and `secondaryLabel` for a primary label, icon, and optional secondary line (e.g. counts or status).

<Tabs type="container">
<Tab label="Calendar" icon={Calendar} secondaryLabel="(12 events)" />
<Tab label="Information" icon={Information} secondaryLabel="(3 new)" />
<Tab label="Settings" icon={Settings} secondaryLabel="(2 pending)" disabled />
<svelte:fragment slot="content">
<TabContent>Calendar content</TabContent>
<TabContent>Information content</TabContent>
<TabContent>Settings content</TabContent>
</svelte:fragment>
</Tabs>

## Skeleton (default)

Show a loading state with the default skeleton variant.
Expand Down
17 changes: 17 additions & 0 deletions tests/Tabs/TabIconSecondaryLabel.test.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
import { Tab, TabContent, Tabs } from "carbon-components-svelte";
import Calendar from "../../src/icons/Calendar.svelte";
import Information from "../../src/icons/Information.svelte";
import Settings from "../../src/icons/Settings.svelte";
</script>

<Tabs type="container">
<Tab label="Calendar" icon={Calendar} secondaryLabel="(12 events)" />
<Tab label="Information" icon={Information} secondaryLabel="(3 new)" />
<Tab label="Settings" icon={Settings} secondaryLabel="(2 pending)" disabled />
<svelte:fragment slot="content">
<TabContent>Calendar content</TabContent>
<TabContent>Information content</TabContent>
<TabContent>Settings content</TabContent>
</svelte:fragment>
</Tabs>
65 changes: 65 additions & 0 deletions tests/Tabs/Tabs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { user } from "../setup-tests";
import Tab from "./Tab.test.svelte";
import TabIcon from "./TabIcon.test.svelte";
import TabIconContainer from "./TabIconContainer.test.svelte";
import TabIconSecondaryLabel from "./TabIconSecondaryLabel.test.svelte";
import TabSecondaryLabel from "./TabSecondaryLabel.test.svelte";
import Tabs from "./Tabs.test.svelte";
import TabsDynamic from "./TabsDynamic.test.svelte";
Expand Down Expand Up @@ -519,6 +520,70 @@ describe("Container tabs with icon", () => {
});
});

describe("Container tabs with icons and secondary label", () => {
it("should render container type with icon and secondary label on each tab", () => {
const { container } = render(TabIconSecondaryLabel);

const tabsContainer = screen.getByRole("navigation");
expect(tabsContainer).toHaveClass("bx--tabs--container");

expect(screen.getByRole("tab", { name: /Calendar/ })).toBeInTheDocument();
expect(screen.getByText("(12 events)")).toBeInTheDocument();

expect(
screen.getByRole("tab", { name: /Information/ }),
).toBeInTheDocument();
expect(screen.getByText("(3 new)")).toBeInTheDocument();

expect(screen.getByRole("tab", { name: /Settings/ })).toBeInTheDocument();
expect(screen.getByText("(2 pending)")).toBeInTheDocument();

const iconWrappers = container.querySelectorAll(
".bx--tabs__nav-item--icon",
);
expect(iconWrappers).toHaveLength(3);
});

it("should have label wrapper and secondary label when tab has icon and secondaryLabel", () => {
const { container } = render(TabIconSecondaryLabel);

const navItems = container.querySelectorAll(".bx--tabs__nav-item");
const calendarTab = navItems[0];
const labelWrapper = calendarTab?.querySelector(
".bx--tabs__nav-item-label-wrapper",
);
const secondaryLabel = calendarTab?.querySelector(
".bx--tabs__nav-item-secondary-label",
);
const iconWrapper = calendarTab?.querySelector(".bx--tabs__nav-item--icon");

expect(labelWrapper).toBeInTheDocument();
expect(secondaryLabel).toBeInTheDocument();
expect(secondaryLabel).toHaveTextContent("(12 events)");
expect(iconWrapper).toBeInTheDocument();
});

it("should be clickable and show content when tab has icon and secondary label", async () => {
render(TabIconSecondaryLabel);

const informationTab = screen.getByRole("tab", { name: /Information/ });
await user.click(informationTab);

expect(informationTab).toHaveAttribute("aria-selected", "true");
expect(screen.getByText("Information content")).toBeVisible();
});

it("should not select disabled tab with icon and secondary label", async () => {
render(TabIconSecondaryLabel);

const settingsTab = screen.getByRole("tab", { name: /Settings/ });
expect(settingsTab).toHaveAttribute("aria-disabled", "true");
await user.click(settingsTab);

expect(settingsTab).toHaveAttribute("aria-selected", "false");
});
});

describe("Container tabs with secondary label", () => {
it("should render container with tall class when any tab has secondary label", () => {
render(TabSecondaryLabel);
Expand Down
Loading