Skip to content

Commit

Permalink
Add TabBar
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding committed Oct 4, 2024
1 parent 4d535c5 commit 9717fd2
Show file tree
Hide file tree
Showing 30 changed files with 511 additions and 374 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
"webpack": "5.94.0",
"recursive-readdir": "2.2.3",
"@storybook/test@npm:8.2.4": "patch:@storybook/test@npm%3A8.2.4#~/.yarn/patches/@storybook-test-npm-8.2.4-0a53c854b7.patch",
"@joshwooding/vite-plugin-react-docgen-typescript": "0.4.0"
"@joshwooding/vite-plugin-react-docgen-typescript": "0.4.1"
},
"browserslist": {
"production": [
Expand Down
44 changes: 24 additions & 20 deletions packages/core/stories/badge/badge.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
ThumbsDownIcon,
ThumbsUpIcon,
} from "@salt-ds/icons";
import { TabListNext, TabNext, TabsNext } from "@salt-ds/lab";
import { TabBar, TabListNext, TabNext, TabsNext } from "@salt-ds/lab";
import type { Meta, StoryFn } from "@storybook/react";

export default {
Expand Down Expand Up @@ -70,18 +70,20 @@ export const String: StoryFn = () => {
export const InlineBadge: StoryFn = () => {
return (
<TabsNext defaultValue="Home">
<TabListNext
style={{
minWidth: 350,
}}
>
<TabNext value="Home">Home</TabNext>
<TabNext value="Transactions" aria-label="Transations - 30 updates">
Transactions
<Badge value={30} />
</TabNext>
<TabNext value="Loans">Loans</TabNext>
</TabListNext>
<TabBar padding separator>
<TabListNext
style={{
minWidth: 350,
}}
>
<TabNext value="Home">Home</TabNext>
<TabNext value="Transactions" aria-label="Transations - 30 updates">
Transactions
<Badge value={30} />
</TabNext>
<TabNext value="Loans">Loans</TabNext>
</TabListNext>
</TabBar>
</TabsNext>
);
};
Expand Down Expand Up @@ -121,13 +123,15 @@ export const DotBadge: StoryFn<typeof Badge> = () => {
export const InlineDotBadge: StoryFn<typeof Badge> = () => {
return (
<TabsNext defaultValue="Home">
<TabListNext variant="inline" style={{ minWidth: 350 }}>
<TabNext value="Home">Home</TabNext>
<TabNext value="Transactions" aria-label="Transactions - New">
Transactions <Badge />
</TabNext>
<TabNext value="Loans">Loans</TabNext>
</TabListNext>
<TabBar>
<TabListNext appearance="transparent" style={{ minWidth: 350 }}>
<TabNext value="Home">Home</TabNext>
<TabNext value="Transactions" aria-label="Transactions - New">
Transactions <Badge />
</TabNext>
<TabNext value="Loans">Loans</TabNext>
</TabListNext>
</TabBar>
</TabsNext>
);
};
44 changes: 22 additions & 22 deletions packages/lab/src/__tests__/__e2e__/tabs-next/TabsNext.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as tabsStories from "@stories/tabs-next/tabs-next.stories";
import { composeStories } from "@storybook/react";

const {
Main,
Bordered,
DisabledTabs,
Overflow,
AddTabs,
Expand All @@ -13,13 +13,13 @@ const {

describe("Given a Tabstrip", () => {
it("should render with tablist and tab roles", () => {
cy.mount(<Main />);
cy.mount(<Bordered />);
cy.findByRole("tablist").should("be.visible");
cy.findAllByRole("tab").should("have.length", 5);
});

it("should support keyboard navigation and wrap", () => {
cy.mount(<Main />);
cy.mount(<Bordered />);
cy.realPress("Tab");
cy.findByRole("tab", { name: "Home" }).should("have.focus");
cy.realPress("ArrowRight");
Expand All @@ -36,7 +36,7 @@ describe("Given a Tabstrip", () => {

it("should support selection with a mouse", () => {
const changeSpy = cy.stub().as("changeSpy");
cy.mount(<Main onChange={changeSpy} />);
cy.mount(<Bordered onChange={changeSpy} />);
cy.findByRole("tab", { name: "Home" }).should(
"have.attr",
"aria-selected",
Expand All @@ -58,7 +58,7 @@ describe("Given a Tabstrip", () => {

it("should support selection with the keyboard", () => {
const changeSpy = cy.stub().as("changeSpy");
cy.mount(<Main onChange={changeSpy} />);
cy.mount(<Bordered onChange={changeSpy} />);
cy.realPress("Tab");
cy.findByRole("tab", { name: "Home" }).should("have.focus");
cy.findByRole("tab", { name: "Home" }).should(
Expand Down Expand Up @@ -116,17 +116,17 @@ describe("Given a Tabstrip", () => {
it("should overflow into a menu when there is not enough space to show all tabs", () => {
cy.mount(<Overflow />);
cy.findAllByRole("tab").should("have.length", 17);
cy.findAllByRole("tab").filter(":visible").should("have.length", 3);
cy.findAllByRole("tab").filter(":not(:visible)").should("have.length", 14);
cy.findAllByRole("tab").filter(":visible").should("have.length", 4);
cy.findAllByRole("tab").filter(":not(:visible)").should("have.length", 13);
cy.get("[data-overflowbutton]").should("be.visible");
});

it("should allow keyboard navigation in the menu", () => {
cy.mount(<Overflow />);
cy.get("[data-overflowbutton]").realClick();
cy.findByRole("tab", { name: "Checks" }).should("be.focused");
cy.realPress("ArrowDown");
cy.findByRole("tab", { name: "Liquidity" }).should("be.focused");
cy.realPress("ArrowDown");
cy.findByRole("tab", { name: "Reports" }).should("be.focused");
cy.realPress("End");
cy.findByRole("tab", { name: "Screens" }).should("be.focused");
cy.realPress("Escape");
Expand Down Expand Up @@ -155,15 +155,15 @@ describe("Given a Tabstrip", () => {
"aria-selected",
"true",
);
cy.findByRole("button", { name: "Add Tab" }).realClick();
cy.findByRole("button", { name: "Add tab" }).realClick();
cy.findAllByRole("tab").should("have.length", 4);
cy.findByRole("tab", { name: "Home" }).should(
"have.attr",
"aria-selected",
"true",
);
cy.findByRole("tab", { name: "New tab" }).should("be.visible");
cy.findByRole("button", { name: "Add Tab" }).should("be.focused");
cy.findByRole("button", { name: "Add tab" }).should("be.focused");
});

it("should support adding tabs with confirmation", () => {
Expand All @@ -174,10 +174,10 @@ describe("Given a Tabstrip", () => {
"aria-selected",
"true",
);
cy.findByRole("button", { name: "Add Tab" }).realClick();
cy.findByRole("button", { name: "Add tab" }).realClick();

cy.findByRole("dialog").should("be.visible");
cy.findByLabelText("New Tab name").realClick();
cy.findByLabelText("New tab name").realClick();
cy.realType("New tab");
cy.findByRole("button", { name: "Confirm" }).realClick();

Expand All @@ -188,7 +188,7 @@ describe("Given a Tabstrip", () => {
"aria-selected",
"true",
);
cy.findByRole("button", { name: "Add Tab" }).should("be.focused");
cy.findByRole("button", { name: "Add tab" }).should("be.focused");
});

it("should support closing tabs with a mouse", () => {
Expand Down Expand Up @@ -217,7 +217,7 @@ describe("Given a Tabstrip", () => {
"aria-selected",
"true",
);
cy.findByRole("tab", { name: "Checks" }).should("be.focused");
cy.findByRole("tab", { name: "Transactions" }).should("be.focused");

cy.findByRole("button", { name: "Close tab Home" }).realClick();
cy.findAllByRole("tab").should("have.length", 2);
Expand Down Expand Up @@ -264,23 +264,23 @@ describe("Given a Tabstrip", () => {
cy.findByRole("tab", { name: "Transactions" }).should("be.focused");
});

it("should support close with confirmation", () => {
it("should support closing with confirmation", () => {
cy.mount(<CloseWithConfirmation />);
cy.findAllByRole("tab").should("have.length", 5);
cy.findAllByRole("tab").should("have.length", 3);

cy.findAllByRole("button", { name: "Close tab Home" }).realClick();
cy.findByRole("dialog").should("be.visible");

cy.findByRole("button", { name: "Cancel" }).realClick();
cy.findByRole("dialog").should("not.be.visible");
cy.findByRole("button", { name: "No" }).realClick();
cy.findByRole("dialog").should("not.to.exist");
cy.findByRole("button", { name: "Close tab Home" }).should("be.focused");

cy.findAllByRole("button", { name: "Close tab Home" }).realClick();
cy.findByRole("dialog").should("be.visible");

cy.findByRole("button", { name: "Confirm" }).realClick();
cy.findByRole("dialog").should("not.be.visible");
cy.findAllByRole("tab").should("have.length", 4);
cy.findByRole("button", { name: "Yes" }).realClick();
cy.findByRole("dialog").should("not.to.exist");
cy.findAllByRole("tab").should("have.length", 2);
cy.findByRole("tab", { name: "Transactions" }).should(
"have.attr",
"aria-selected",
Expand Down
21 changes: 21 additions & 0 deletions packages/lab/src/tabs-next/TabBar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.saltTabBar {
display: flex;
align-items: center;
flex-direction: row;
gap: var(--salt-spacing-100);
position: relative;
box-sizing: border-box;
}

.saltTabBar-separator::before {
content: "";
position: absolute;
inset: auto 0 0 0;
height: var(--salt-size-border);
border-bottom: var(--salt-size-border) var(--salt-separable-borderStyle) var(--salt-separable-secondary-borderColor);
}

.saltTabBar-padding {
padding-left: var(--salt-spacing-300);
padding-right: var(--salt-spacing-300);
}
44 changes: 44 additions & 0 deletions packages/lab/src/tabs-next/TabBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import { type ComponentPropsWithRef, forwardRef } from "react";

import { makePrefixer } from "@salt-ds/core";
import { clsx } from "clsx";
import tabBarCss from "./TabBar.css";

export interface TabBarProps extends ComponentPropsWithRef<"div"> {
separator?: boolean;
padding?: boolean;
}

const withBaseName = makePrefixer("saltTabBar");

export const TabBar = forwardRef<HTMLDivElement, TabBarProps>(
function TabBar(props, ref) {
const { className, children, separator, padding, ...rest } = props;

const targetWindow = useWindow();
useComponentCssInjection({
testId: "salt-tab-bar",
css: tabBarCss,
window: targetWindow,
});

return (
<div
className={clsx(
withBaseName(),
{
[withBaseName("separator")]: separator,
[withBaseName("padding")]: padding,
},
className,
)}
{...rest}
ref={ref}
>
{children}
</div>
);
},
);
15 changes: 0 additions & 15 deletions packages/lab/src/tabs-next/TabListNext.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
align-items: center;
position: relative;
background: transparent;
width: 100%;
min-height: calc(var(--salt-size-base) + var(--salt-spacing-100));
gap: var(--salt-spacing-100);
}
Expand All @@ -19,20 +18,6 @@
justify-content: flex-end;
}

.saltTabListNext-main {
padding-left: var(--salt-spacing-300);
padding-right: var(--salt-spacing-300);
box-sizing: border-box;
}

.saltTabListNext-main::before {
content: "";
position: absolute;
inset: auto 0 0 0;
height: var(--salt-size-border);
border-bottom: var(--salt-size-border) var(--salt-separable-borderStyle) var(--salt-separable-secondary-borderColor);
}

.saltTabListNext-activeColorPrimary {
--saltTabListNext-activeColor: var(--salt-container-primary-background);
}
Expand Down
Loading

0 comments on commit 9717fd2

Please sign in to comment.