Skip to content

Commit

Permalink
feat(in-page-navigation): add vertical (#3537)
Browse files Browse the repository at this point in the history
* feat(in-page-navigation): add vertical

* fix: lint error

* fix: overflow story
  • Loading branch information
TheSisb authored Oct 13, 2023
1 parent c58b3bf commit 92c16ec
Show file tree
Hide file tree
Showing 8 changed files with 346 additions and 92 deletions.
6 changes: 6 additions & 0 deletions .changeset/slow-humans-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@twilio-paste/in-page-navigation": minor
"@twilio-paste/core": minor
---

[In Page Navigation] Add new `orientation` property with a vertical option.
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,8 @@ import * as React from "react";
import { InPageNavigation, InPageNavigationItem } from "../src";

describe("InPageNavigation", () => {
it("should render a nav with correct aria-label", () => {
const { getByRole } = render(
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
<InPageNavigationItem href="#">page 2</InPageNavigationItem>
</InPageNavigation>,
);

expect(getByRole("navigation")).toHaveAttribute("aria-label", "my-nav");
});

it("should render a list with list items and links", () => {
const { getAllByRole } = render(
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
<InPageNavigationItem href="#">page 2</InPageNavigationItem>
</InPageNavigation>,
);

expect(getAllByRole("list")).toHaveLength(1);
expect(getAllByRole("listitem")).toHaveLength(2);
expect(getAllByRole("link")).toHaveLength(2);
});

it("should use the currentPage prop to apply aria-current", () => {
const { getByText } = render(
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
<InPageNavigationItem currentPage href="#">
page 2
</InPageNavigationItem>
</InPageNavigation>,
);

expect(getByText("page 2")).toHaveAttribute("aria-current", "page");
});

it("should pass props given to InPageNavigationItem onto its <a> child", () => {
const { getByText } = render(
it("should render semantically correct with aria properly", () => {
const { getByRole, getAllByRole, getByText } = render(
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem data-test-id="page-1" href="#">
page 1
Expand All @@ -54,40 +17,17 @@ describe("InPageNavigation", () => {
</InPageNavigation>,
);

expect(getByRole("navigation")).toHaveAttribute("aria-label", "my-nav");
expect(getAllByRole("list")).toHaveLength(1);
expect(getAllByRole("listitem")).toHaveLength(2);
expect(getAllByRole("link")).toHaveLength(2);
expect(getByText("page 2")).toHaveAttribute("aria-current", "page");
expect(getByText("page 1")).toHaveAttribute("data-test-id", "page-1");
});
});

describe("Customization", () => {
it("should set a default element name", () => {
const { getByRole } = render(
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
</InPageNavigation>,
);

expect(getByRole("navigation")).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION");
expect(getByRole("list")).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEMS");
expect(getByRole("listitem")).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM");
expect(getByRole("link")).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM_ANCHOR");
});

it("should set a custom element name when provided", () => {
const { getByRole } = render(
<InPageNavigation element="MY_IN_PAGE_NAVIGATION" aria-label="my-nav">
<InPageNavigationItem element="MY_IN_PAGE_NAVIGATION_ITEM" href="#">
page 1
</InPageNavigationItem>
</InPageNavigation>,
);

expect(getByRole("navigation")).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION");
expect(getByRole("list")).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEMS");
expect(getByRole("listitem")).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM");
expect(getByRole("link")).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM_ANCHOR");
});

it("should add custom styles to default element names", () => {
it("should add custom styles to the default element name", () => {
const { getByRole } = render(
<CustomizationProvider
baseTheme="default"
Expand All @@ -102,13 +42,24 @@ describe("Customization", () => {
<InPageNavigation aria-label="my-nav">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
</InPageNavigation>
,
</CustomizationProvider>,
);

expect(getByRole("navigation")).toHaveStyleRule("font-weight", "400");
expect(getByRole("list")).toHaveStyleRule("padding", "0.75rem");
expect(getByRole("listitem")).toHaveStyleRule("margin", "0.75rem");
expect(getByRole("link")).toHaveStyleRule("font-size", "1rem");
const nav = getByRole("navigation");
const list = getByRole("list");
const listitem = getByRole("listitem");
const link = getByRole("link");

expect(nav).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION");
expect(list).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEMS");
expect(listitem).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM");
expect(link).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM_ANCHOR");

expect(nav).toHaveStyleRule("font-weight", "400");
expect(list).toHaveStyleRule("padding", "0.75rem");
expect(listitem).toHaveStyleRule("margin", "0.75rem");
expect(link).toHaveStyleRule("font-size", "1rem");
});

it("should add custom styles to custom element names", () => {
Expand All @@ -131,9 +82,19 @@ describe("Customization", () => {
</CustomizationProvider>,
);

expect(getByRole("navigation")).toHaveStyleRule("font-weight", "400");
expect(getByRole("list")).toHaveStyleRule("padding", "0.75rem");
expect(getByRole("listitem")).toHaveStyleRule("margin", "0.75rem");
expect(getByRole("link")).toHaveStyleRule("font-size", "1rem");
const nav = getByRole("navigation");
const list = getByRole("list");
const listitem = getByRole("listitem");
const link = getByRole("link");

expect(nav).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION");
expect(list).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEMS");
expect(listitem).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM");
expect(link).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM_ANCHOR");

expect(nav).toHaveStyleRule("font-weight", "400");
expect(list).toHaveStyleRule("padding", "0.75rem");
expect(listitem).toHaveStyleRule("margin", "0.75rem");
expect(link).toHaveStyleRule("font-size", "1rem");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { render } from "@testing-library/react";
import { CustomizationProvider } from "@twilio-paste/customization";
import * as React from "react";

import { InPageNavigation, InPageNavigationItem } from "../src";

describe("InPageNavigation", () => {
it("should render semantically correct with aria properly", () => {
const { getByRole, getAllByRole, getByText } = render(
<InPageNavigation aria-label="my-nav" orientation="vertical">
<InPageNavigationItem data-test-id="page-1" href="#">
page 1
</InPageNavigationItem>
<InPageNavigationItem currentPage href="#">
page 2
</InPageNavigationItem>
</InPageNavigation>,
);

expect(getByRole("navigation")).toHaveAttribute("aria-label", "my-nav");
expect(getAllByRole("list")).toHaveLength(1);
expect(getAllByRole("listitem")).toHaveLength(2);
expect(getAllByRole("link")).toHaveLength(2);
expect(getByText("page 2")).toHaveAttribute("aria-current", "page");
expect(getByText("page 1")).toHaveAttribute("data-test-id", "page-1");
});
});

describe("Customization", () => {
it("should set a default element name", () => {
const { getByRole } = render(
<CustomizationProvider
baseTheme="default"
theme={TestTheme}
elements={{
IN_PAGE_NAVIGATION: { fontWeight: "fontWeightLight" },
IN_PAGE_NAVIGATION_ITEMS: { padding: "space40" },
IN_PAGE_NAVIGATION_ITEM: { margin: "space40" },
IN_PAGE_NAVIGATION_ITEM_ANCHOR: { fontSize: "fontSize40" },
}}
>
<InPageNavigation aria-label="my-nav" orientation="vertical">
<InPageNavigationItem href="#">page 1</InPageNavigationItem>
</InPageNavigation>
</CustomizationProvider>,
);

const nav = getByRole("navigation");
const list = getByRole("list");
const listitem = getByRole("listitem");
const link = getByRole("link");

expect(nav).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION");
expect(list).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEMS");
expect(listitem).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM");
expect(link).toHaveAttribute("data-paste-element", "IN_PAGE_NAVIGATION_ITEM_ANCHOR");

expect(nav).toHaveStyleRule("font-weight", "400");
expect(list).toHaveStyleRule("padding", "0.75rem");
expect(listitem).toHaveStyleRule("margin", "0.75rem");
expect(link).toHaveStyleRule("font-size", "1rem");
});

it("should add custom styles to custom element names", () => {
const { getByRole } = render(
<CustomizationProvider
baseTheme="default"
theme={TestTheme}
elements={{
MY_IN_PAGE_NAVIGATION: { fontWeight: "fontWeightLight" },
MY_IN_PAGE_NAVIGATION_ITEMS: { padding: "space40" },
MY_IN_PAGE_NAVIGATION_ITEM: { margin: "space40" },
MY_IN_PAGE_NAVIGATION_ITEM_ANCHOR: { fontSize: "fontSize40" },
}}
>
<InPageNavigation element="MY_IN_PAGE_NAVIGATION" aria-label="my-nav" orientation="vertical">
<InPageNavigationItem element="MY_IN_PAGE_NAVIGATION_ITEM" href="#">
page 1
</InPageNavigationItem>
</InPageNavigation>
</CustomizationProvider>,
);

const nav = getByRole("navigation");
const list = getByRole("list");
const listitem = getByRole("listitem");
const link = getByRole("link");

expect(nav).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION");
expect(list).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEMS");
expect(listitem).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM");
expect(link).toHaveAttribute("data-paste-element", "MY_IN_PAGE_NAVIGATION_ITEM_ANCHOR");

expect(nav).toHaveStyleRule("font-weight", "400");
expect(list).toHaveStyleRule("padding", "0.75rem");
expect(listitem).toHaveStyleRule("margin", "0.75rem");
expect(link).toHaveStyleRule("font-size", "1rem");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,67 @@ import type { BoxProps } from "@twilio-paste/box";
import * as React from "react";

import { InPageNavigationContext } from "./InPageNavigationContext";
import type { Variants } from "./types";
import type { Orientation, Variants } from "./types";

export interface InPageNavigationProps extends Omit<React.ComponentPropsWithRef<"div">, "children"> {
children?: React.ReactNode;
element?: BoxProps["element"];
marginBottom?: "space0";
"aria-label": string;
variant?: Variants;
orientation?: Orientation;
}

const InPageNavigation = React.forwardRef<HTMLDivElement, InPageNavigationProps>(
({ element = "IN_PAGE_NAVIGATION", variant = "default", marginBottom, children, ...props }, ref) => {
(
{
element = "IN_PAGE_NAVIGATION",
variant = "default",
orientation = "horizontal",
marginBottom,
children,
...props
},
ref,
) => {
const isFullWidth = variant === "fullWidth" || variant === "inverse_fullWidth";

if (orientation === "vertical") {
return (
<InPageNavigationContext.Provider value={{ variant, orientation }}>
<Box {...safelySpreadBoxProps(props)} as="nav" ref={ref} element={element}>
<Box
as="ul"
listStyleType="none"
element={`${element}_ITEMS`}
display="flex"
flexDirection="column"
margin="space0"
padding="space0"
minWidth="size20"
maxWidth="size40"
rowGap="space20"
>
{children}
</Box>
</Box>
</InPageNavigationContext.Provider>
);
}

return (
<InPageNavigationContext.Provider value={{ variant }}>
<InPageNavigationContext.Provider value={{ variant, orientation }}>
<Box {...safelySpreadBoxProps(props)} as="nav" ref={ref} element={element}>
<Box
as="ul"
listStyleType="none"
element={`${element}_ITEMS`}
display="flex"
justifyContent={isFullWidth ? "space-evenly" : "flex-start"}
columnGap={!isFullWidth ? "space80" : "space0"}
padding="space0"
margin="space0"
marginBottom={marginBottom || "space60"}
padding="space0"
columnGap={!isFullWidth ? "space80" : "space0"}
>
{children}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import type { Variants } from "./types";

interface InPageNavigationContextValue {
variant?: Variants;
orientation?: "horizontal" | "vertical";
}

const InPageNavigationContext = React.createContext<InPageNavigationContextValue>({
variant: "default",
orientation: "horizontal",
});

export { InPageNavigationContext };
Loading

0 comments on commit 92c16ec

Please sign in to comment.