-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(site): add CollapsibleSection component and shared utilities #23990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| import type { Meta, StoryObj } from "@storybook/react-vite"; | ||
| import { expect, userEvent, within } from "storybook/test"; | ||
| import { AdminBadge } from "./AdminBadge"; | ||
| import { | ||
| CollapsibleSection, | ||
| CollapsibleSectionContent, | ||
| CollapsibleSectionDescription, | ||
| CollapsibleSectionHeader, | ||
| CollapsibleSectionTitle, | ||
| } from "./CollapsibleSection"; | ||
|
|
||
| const meta: Meta<typeof CollapsibleSection> = { | ||
| title: "pages/AgentsPage/CollapsibleSection", | ||
| component: CollapsibleSection, | ||
| decorators: [ | ||
| (Story) => ( | ||
| <div style={{ maxWidth: 600 }}> | ||
| <Story /> | ||
| </div> | ||
| ), | ||
| ], | ||
| }; | ||
| export default meta; | ||
| type Story = StoryObj<typeof CollapsibleSection>; | ||
|
|
||
| const Placeholder = () => ( | ||
| <p className="text-sm text-content-secondary">Placeholder content</p> | ||
| ); | ||
|
|
||
| export const DefaultOpen: Story = { | ||
| render: () => ( | ||
| <CollapsibleSection> | ||
| <CollapsibleSectionHeader> | ||
| <div className="flex items-center gap-2"> | ||
| <CollapsibleSectionTitle>Default spend limit</CollapsibleSectionTitle> | ||
| <AdminBadge /> | ||
| </div> | ||
| <CollapsibleSectionDescription> | ||
| The deployment-wide spending cap. | ||
| </CollapsibleSectionDescription> | ||
| </CollapsibleSectionHeader> | ||
| <CollapsibleSectionContent> | ||
| <Placeholder /> | ||
| </CollapsibleSectionContent> | ||
| </CollapsibleSection> | ||
| ), | ||
| play: async ({ canvasElement }) => { | ||
| const canvas = within(canvasElement); | ||
|
|
||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
|
|
||
| const header = canvas.getByRole("button", { | ||
| name: /Default spend limit/i, | ||
| }); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
| }, | ||
| }; | ||
|
|
||
| export const Collapsed: Story = { | ||
| render: () => ( | ||
| <CollapsibleSection defaultOpen={false}> | ||
| <CollapsibleSectionHeader> | ||
| <div className="flex items-center gap-2"> | ||
| <CollapsibleSectionTitle>Default spend limit</CollapsibleSectionTitle> | ||
| <AdminBadge /> | ||
| </div> | ||
| <CollapsibleSectionDescription> | ||
| The deployment-wide spending cap. | ||
| </CollapsibleSectionDescription> | ||
| </CollapsibleSectionHeader> | ||
| <CollapsibleSectionContent> | ||
| <Placeholder /> | ||
| </CollapsibleSectionContent> | ||
| </CollapsibleSection> | ||
| ), | ||
| play: async ({ canvasElement }) => { | ||
| const canvas = within(canvasElement); | ||
|
|
||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
|
|
||
| const header = canvas.getByRole("button", { | ||
| name: /Default spend limit/i, | ||
| }); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
| }, | ||
| }; | ||
|
|
||
| export const NoBadge: Story = { | ||
| render: () => ( | ||
| <CollapsibleSection> | ||
| <CollapsibleSectionHeader> | ||
| <CollapsibleSectionTitle>Group limits</CollapsibleSectionTitle> | ||
| <CollapsibleSectionDescription> | ||
| Override defaults for groups. | ||
| </CollapsibleSectionDescription> | ||
| </CollapsibleSectionHeader> | ||
| <CollapsibleSectionContent> | ||
| <Placeholder /> | ||
| </CollapsibleSectionContent> | ||
| </CollapsibleSection> | ||
| ), | ||
| }; | ||
|
|
||
| export const InlineVariant: Story = { | ||
| render: () => ( | ||
| <CollapsibleSection variant="inline" defaultOpen={false}> | ||
| <CollapsibleSectionHeader> | ||
| <CollapsibleSectionTitle as="h3">Cost Tracking</CollapsibleSectionTitle> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2 No story tests controlled mode ( Every story uses uncontrolled mode (either PRs 2-4 in the stack will consume controlled mode (force-open on validation error, "expand all" toggle). A consumer passing Fix: add a PS. The
|
||
| <CollapsibleSectionDescription> | ||
| Set per-token pricing so Coder can track costs and enforce spending | ||
| limits. | ||
| </CollapsibleSectionDescription> | ||
| </CollapsibleSectionHeader> | ||
| <CollapsibleSectionContent> | ||
| <Placeholder /> | ||
| </CollapsibleSectionContent> | ||
| </CollapsibleSection> | ||
| ), | ||
| play: async ({ canvasElement }) => { | ||
| const canvas = within(canvasElement); | ||
|
|
||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
|
|
||
| const header = canvas.getByRole("button", { | ||
| name: /Cost Tracking/i, | ||
| }); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
|
|
||
| await userEvent.click(header); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
| }, | ||
| }; | ||
|
|
||
| export const KeyboardToggle: Story = { | ||
| render: () => ( | ||
| <CollapsibleSection> | ||
| <CollapsibleSectionHeader> | ||
| <CollapsibleSectionTitle>Keyboard section</CollapsibleSectionTitle> | ||
| </CollapsibleSectionHeader> | ||
| <CollapsibleSectionContent> | ||
| <Placeholder /> | ||
| </CollapsibleSectionContent> | ||
| </CollapsibleSection> | ||
| ), | ||
| play: async ({ canvasElement }) => { | ||
| const canvas = within(canvasElement); | ||
|
|
||
| const header = canvas.getByRole("button", { | ||
| name: /Keyboard section/i, | ||
| }); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
|
|
||
| header.focus(); | ||
|
|
||
| await userEvent.keyboard("{Enter}"); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
|
|
||
| await userEvent.keyboard("{Enter}"); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
|
|
||
| await userEvent.keyboard(" "); | ||
| expect(header).toHaveAttribute("aria-expanded", "false"); | ||
| expect(canvas.queryByText("Placeholder content")).not.toBeInTheDocument(); | ||
|
|
||
| await userEvent.keyboard(" "); | ||
| expect(header).toHaveAttribute("aria-expanded", "true"); | ||
| expect(canvas.getByText("Placeholder content")).toBeInTheDocument(); | ||
| }, | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3 New stray
{" "}text node before<hr>. Same class of bug as resolved #comment-3045753889. The base code at this position had no whitespace node. The four original instances were removed, but this one was introduced. (Bisky P3, Gon P3, Nami P3, Mafuuu P3, Meruem P3, Razor P3, Ging P3)