From 57a9e0ce45ea37ae939feced8c6704b16b688297 Mon Sep 17 00:00:00 2001 From: Huw Wilkins Date: Fri, 17 May 2024 10:31:50 +1000 Subject: [PATCH] feat: add application layout Panel component --- src/components/Panel/Panel.stories.tsx | 62 +++++++ src/components/Panel/Panel.test.tsx | 78 ++++++++ src/components/Panel/Panel.tsx | 244 +++++++++++++++++++++++++ src/components/Panel/index.ts | 5 + src/index.ts | 2 + src/types/index.ts | 8 + src/utils.ts | 7 + 7 files changed, 406 insertions(+) create mode 100644 src/components/Panel/Panel.stories.tsx create mode 100644 src/components/Panel/Panel.test.tsx create mode 100644 src/components/Panel/Panel.tsx create mode 100644 src/components/Panel/index.ts diff --git a/src/components/Panel/Panel.stories.tsx b/src/components/Panel/Panel.stories.tsx new file mode 100644 index 000000000..6c81efdbc --- /dev/null +++ b/src/components/Panel/Panel.stories.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import Panel from "./Panel"; +import Button from "components/Button"; +import Icon from "components/Icon"; + +const meta: Meta = { + component: Panel, + tags: ["autodocs"], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Panel content", + title: "Panel", + }, +}; + +export const Header: Story = { + args: { + controls: ( + + ), + title: "Panel title", + titleComponent: "h1", + }, +}; + +/** + * The logo may be provided as attributes to use the standard logo. If this is + * not sufficient the a `ReactNode` can be passed to the `logo` prop instead. + */ +export const Logo: Story = { + args: { + logo: { + icon: "https://assets.ubuntu.com/v1/7144ec6d-logo-jaas-icon.svg", + name: "https://assets.ubuntu.com/v1/a85f7947-juju_black-text-only.svg", + nameAlt: "Juju", + }, + }, +}; + +/** + * If the default header does not meet your needs then a `ReactNode` can be + * passed to the `header` prop to replace the header. + */ +export const CustomHeader: Story = { + args: { + header: ( +
+ This header replaces the entire header area +
+ ), + }, +}; diff --git a/src/components/Panel/Panel.test.tsx b/src/components/Panel/Panel.test.tsx new file mode 100644 index 000000000..c7cbd5312 --- /dev/null +++ b/src/components/Panel/Panel.test.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { render, screen, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import type { ButtonHTMLAttributes } from "react"; + +import Panel from "./Panel"; + +it("displays a title", () => { + const title = "Test Panel"; + render(); + expect(screen.getByText(title)).toHaveClass("p-panel__title"); +}); + +it("displays a logo", () => { + render( + + ); + const link = screen.getByRole("link", { name: "Icon SVG Name SVG" }); + expect(link).toHaveAttribute("href", "http://example.com"); + expect(within(link).getByRole("img", { name: "Icon SVG" })).toHaveAttribute( + "src", + "icon.svg" + ); + expect(within(link).getByRole("img", { name: "Name SVG" })).toHaveAttribute( + "src", + "name.svg" + ); +}); + +it("logo handles different components", () => { + const Link = ({ ...props }: ButtonHTMLAttributes) => ( +