diff --git a/.changeset/light-olives-grab.md b/.changeset/light-olives-grab.md
new file mode 100644
index 000000000..fc28acf17
--- /dev/null
+++ b/.changeset/light-olives-grab.md
@@ -0,0 +1,5 @@
+---
+"@hopper-ui/components": patch
+---
+
+Added the Accordion component.
diff --git a/apps/docs/components/card/card.css b/apps/docs/components/card/card.css
index fb866a277..a5f1ca301 100644
--- a/apps/docs/components/card/card.css
+++ b/apps/docs/components/card/card.css
@@ -12,6 +12,7 @@
background: linear-gradient(90deg, var(--background) 0.625rem, transparent 1%) 50%,
linear-gradient(var(--background) 0.625rem, transparent 1%) 50%,
var(--dot-background);
+ background-position: 0 0;
background-size: 0.75rem 0.75rem;
color: var(--color);
}
diff --git a/apps/docs/content/components/navigation/Accordion.mdx b/apps/docs/content/components/navigation/Accordion.mdx
new file mode 100644
index 000000000..e13da42ee
--- /dev/null
+++ b/apps/docs/content/components/navigation/Accordion.mdx
@@ -0,0 +1,102 @@
+---
+title: Accordion
+description: An Accordion is a grouping of related disclosures. It supports both single and multiple expanded items.
+category: "navigation"
+links:
+ source: https://github.com/gsoft-inc/wl-hopper/blob/main/packages/components/src/Accordion/src/Accordion.tsx
+---
+
+
+
+
+ ## Guidelines
+
+ TODO: If we have some guidelines about this component's usage
+
+ ### Accessibility ?
+
+ TODO: If we have some guidelines about this component and accessibility
+
+
+## Anatomy
+
+
+ TODO: We have anatomy screenshots from the Figma, we could most likely use them here
+
+ ### Concepts
+
+ TODO: links to related concepts
+
+
+### Composed Components
+
+An `Accordion` uses the following components.
+
+
+
+## Usage
+
+### Disabled
+
+An accordion can be disabled.
+
+
+
+### Variants
+
+An accordion has multiple variants.
+
+**Standalone** - Used when the accordion is not inside a container.
+
+
+
+**Inline** - Used when placing a accordion inside a container.
+
+
+
+### Expanded
+
+By default, only one disclosure will be expanded at a time. Use `allowsMultipleExpanded` prop to expand multiple disclosures.
+
+
+
+### Icon
+
+An accordion heading can contain an icon.
+
+
+
+### Description
+
+An accordion heading can contain a description.
+
+
+
+### Controlled
+
+An accordion can handle its opened panels in controlled mode.
+
+
+
+
+ ## Advanced customization
+
+ ### Contexts
+ TODO: Example of context + content about the context
+
+ ### Custom Children
+
+ TODO: Example of passing custom children to the components to fake a slot
+
+ ### Custom Component
+
+ TODO: Example of creating a custom version of this component
+
+
+## Props
+
+
+
+## Migration Notes
+
+
diff --git a/apps/docs/content/components/navigation/Disclosure.mdx b/apps/docs/content/components/navigation/Disclosure.mdx
index e85743024..39e4aa325 100644
--- a/apps/docs/content/components/navigation/Disclosure.mdx
+++ b/apps/docs/content/components/navigation/Disclosure.mdx
@@ -33,7 +33,7 @@ links:
A `Disclosure` uses the following components:
-
+
## Usage
diff --git a/apps/docs/examples/Preview.ts b/apps/docs/examples/Preview.ts
index 403776f49..055b1f324 100644
--- a/apps/docs/examples/Preview.ts
+++ b/apps/docs/examples/Preview.ts
@@ -854,6 +854,30 @@ export const Previews: Record = {
"layout/docs/stack/alignY": {
component: lazy(() => import("@/../../packages/components/src/layout/docs/stack/alignY.tsx"))
},
+ "Accordion/docs/preview": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/preview.tsx"))
+ },
+ "Accordion/docs/disabled": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/disabled.tsx"))
+ },
+ "Accordion/docs/standalone": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/standalone.tsx"))
+ },
+ "Accordion/docs/inline": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/inline.tsx"))
+ },
+ "Accordion/docs/multiple-selection": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/multiple-selection.tsx"))
+ },
+ "Accordion/docs/icon": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/icon.tsx"))
+ },
+ "Accordion/docs/description": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/description.tsx"))
+ },
+ "Accordion/docs/controlled": {
+ component: lazy(() => import("@/../../packages/components/src/Accordion/docs/controlled.tsx"))
+ },
"Disclosure/docs/preview": {
component: lazy(() => import("@/../../packages/components/src/Disclosure/docs/preview.tsx"))
},
diff --git a/apps/docs/examples/overview/Accordion.svg b/apps/docs/examples/overview/Accordion.svg
new file mode 100644
index 000000000..4a050909e
--- /dev/null
+++ b/apps/docs/examples/overview/Accordion.svg
@@ -0,0 +1 @@
+
diff --git a/apps/docs/examples/overview/index.ts b/apps/docs/examples/overview/index.ts
index cee951509..82cf6e54b 100644
--- a/apps/docs/examples/overview/index.ts
+++ b/apps/docs/examples/overview/index.ts
@@ -1,5 +1,6 @@
import type { FunctionComponent, SVGProps } from "react";
+import Accordion from "./Accordion.svg";
import Avatar from "./Avatar.svg";
import Badge from "./Badge.svg";
import Button from "./Button.svg";
@@ -48,6 +49,7 @@ interface OverviewComponentsType {
}
export const OverviewComponents: OverviewComponentsType = {
+ Accordion,
Avatar,
Badge,
Button,
diff --git a/packages/components/src/Accordion/docs/controlled.tsx b/packages/components/src/Accordion/docs/controlled.tsx
new file mode 100644
index 000000000..9f68af615
--- /dev/null
+++ b/packages/components/src/Accordion/docs/controlled.tsx
@@ -0,0 +1,39 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div, Span } from "@hopper-ui/components";
+import { useState } from "react";
+
+export default function Example() {
+ const [expandedKeys, setExpandedKeys] = useState>(new Set());
+
+ const handleExpandedChange = (keys: Set) => {
+ setExpandedKeys(keys);
+ };
+
+ return (
+
+
+ {expandedKeys.size > 0 ? `${Array.from(expandedKeys).join(", ")} is opened.` : "No sections are opened."}
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/description.tsx b/packages/components/src/Accordion/docs/description.tsx
new file mode 100644
index 000000000..7eaa0e02b
--- /dev/null
+++ b/packages/components/src/Accordion/docs/description.tsx
@@ -0,0 +1,38 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div, Inline, Text } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+
+
+ Workleap Officevibe
+ Engagement survey and feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+ Workleap Pingboard
+ Interactive org chart and directory
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+ Workleap Performance
+ Performance review management and tracking
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
+
diff --git a/packages/components/src/Accordion/docs/disabled.tsx b/packages/components/src/Accordion/docs/disabled.tsx
new file mode 100644
index 000000000..125683010
--- /dev/null
+++ b/packages/components/src/Accordion/docs/disabled.tsx
@@ -0,0 +1,22 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/icon.tsx b/packages/components/src/Accordion/docs/icon.tsx
new file mode 100644
index 000000000..4fda2424e
--- /dev/null
+++ b/packages/components/src/Accordion/docs/icon.tsx
@@ -0,0 +1,32 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div, Text } from "@hopper-ui/components";
+import { PinSolidIcon, SparklesIcon, SproutIcon } from "@hopper-ui/icons";
+
+export default function Example() {
+ return (
+
+
+
+
+
+ Workleap Officevibe
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+ Workleap Pingboard
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+ Workleap Performance
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/inline.tsx b/packages/components/src/Accordion/docs/inline.tsx
new file mode 100644
index 000000000..497bb64e8
--- /dev/null
+++ b/packages/components/src/Accordion/docs/inline.tsx
@@ -0,0 +1,22 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/migration-notes.md b/packages/components/src/Accordion/docs/migration-notes.md
new file mode 100644
index 000000000..66f03b6d1
--- /dev/null
+++ b/packages/components/src/Accordion/docs/migration-notes.md
@@ -0,0 +1,7 @@
+Coming from Orbiter, you should be aware of the following changes:
+
+- `expansionMode="multiple"` has been replaced with `allowsMultipleExpanded`.
+- `borderless` and `bordered` variants are no more. `inline` and `standalone` are the new variants. There is no direct match; the new variants are context-based, depending on whether an accordion is contained or not.
+- `autofocus` is removed. It did not make sense to have.
+- The `disclosure` component is used instead of `Item`.
+- `disabled` is renamed to `isDisabled` on the item/disclosure.
diff --git a/packages/components/src/Accordion/docs/multiple-selection.tsx b/packages/components/src/Accordion/docs/multiple-selection.tsx
new file mode 100644
index 000000000..1949c0972
--- /dev/null
+++ b/packages/components/src/Accordion/docs/multiple-selection.tsx
@@ -0,0 +1,22 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/preview.tsx b/packages/components/src/Accordion/docs/preview.tsx
new file mode 100644
index 000000000..0eb0113e1
--- /dev/null
+++ b/packages/components/src/Accordion/docs/preview.tsx
@@ -0,0 +1,22 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ );
+}
diff --git a/packages/components/src/Accordion/docs/standalone.tsx b/packages/components/src/Accordion/docs/standalone.tsx
new file mode 100644
index 000000000..c1c39ffea
--- /dev/null
+++ b/packages/components/src/Accordion/docs/standalone.tsx
@@ -0,0 +1,22 @@
+import { Accordion, Disclosure, DisclosureHeader, DisclosurePanel, Div } from "@hopper-ui/components";
+
+export default function Example() {
+ return (
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
Multiple Expanded
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
Description
+
+
+
+
+ Workleap Officevibe
+ Engagement and Feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+ Workleap Pingboard
+ Org Chart
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+ Workleap Performance
+ Performance Management
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
Icon
+
+
+
+
+
+ Workleap Officevibe
+ Engagement and Feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+
+ Workleap Pingboard
+ Org Chart
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+
+ Workleap Performance
+ Performance Management
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
Style
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
Zoom
+
+
+
+
+
+ Workleap Officevibe
+ Engagement and Feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+
+ Workleap Pingboard
+ Org Chart
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+
+ Workleap Performance
+ Performance Management
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+
+
+
+
+ Workleap Officevibe
+ Engagement and Feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+
+ Workleap Pingboard
+ Org Chart
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+
+ Workleap Performance
+ Performance Management
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+
+ ),
+ args: {
+ defaultExpandedKeys: ["officevibe"]
+ }
+} satisfies Story;
+
+export const InlineVariant = {
+ ...Default,
+ args: {
+ defaultExpandedKeys: ["officevibe"],
+ variant: "inline"
+ }
+} satisfies Story;
+
+const StateTemplate = (args: Partial) => (
+
+
+
+
+
+ Workleap Officevibe
+ Engagement and Feedback
+
+
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+
+
+
+ Workleap Pingboard
+ Org Chart
+
+
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+
+
+
+ Workleap Performance
+ Performance Management
+
+
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+);
+
+export const DefaultStates = {
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ const triggers = canvas.getAllByRole("button");
+
+ triggers.forEach(trigger => {
+ if (trigger.getAttribute("disabled") !== "") {
+ const accordionElem = trigger.closest(".hop-Accordion");
+
+ if (accordionElem?.getAttribute("data-chromatic-force-focus")) {
+ trigger?.setAttribute("data-focus-visible", "true");
+ accordionElem?.removeAttribute("data-chromatic-force-focus");
+ }
+
+ if (accordionElem?.getAttribute("data-chromatic-force-press")) {
+ trigger?.setAttribute("data-pressed", "true");
+ accordionElem?.removeAttribute("data-chromatic-force-press");
+ }
+
+ if (accordionElem?.getAttribute("data-chromatic-force-hover")) {
+ trigger.setAttribute("data-hovered", "true");
+ accordionElem?.removeAttribute("data-chromatic-force-hover");
+ }
+ }
+ });
+ },
+ render: args => (
+
+
Default
+
+
Disabled
+
+
Focus Visible
+
+
Hovered
+
+
Pressed
+
+
Focus Visible & Disabled
+
+
+ ),
+ args: {
+ defaultExpandedKeys: ["officevibe"]
+ }
+} satisfies Story;
+
+export const InlineStates = {
+ ...DefaultStates,
+ args: {
+ defaultExpandedKeys: ["officevibe"],
+ variant: "inline"
+ }
+} satisfies Story;
\ No newline at end of file
diff --git a/packages/components/src/Accordion/tests/jest/Accordion.ssr.test.tsx b/packages/components/src/Accordion/tests/jest/Accordion.ssr.test.tsx
new file mode 100644
index 000000000..3adac5a33
--- /dev/null
+++ b/packages/components/src/Accordion/tests/jest/Accordion.ssr.test.tsx
@@ -0,0 +1,31 @@
+/**
+ * @jest-environment node
+ */
+import { renderToString } from "react-dom/server";
+
+import { Disclosure, DisclosureHeader, DisclosurePanel } from "../../../Disclosure/index.ts";
+import { Accordion } from "../../src/Accordion.tsx";
+
+describe("Accordion", () => {
+ it("should render on the server", () => {
+ const renderOnServer = () =>
+ renderToString(
+
+
+ Workleap Officevibe
+ Help employees speak up and make sure they feel heard. Continuous and real-time surveys offer feedback to celebrate every win, recognize commitment, and uncover challenges.
+
+
+ Workleap Pingboard
+ Make teamwork work. Use your org chart to create lasting connections across your distributed and hybrid teams to make collaboration easier.
+
+
+ Workleap Performance
+ Drive impact by simplifying how your leaders and you manage team performance throughout the year.
+
+
+ );
+
+ expect(renderOnServer).not.toThrow();
+ });
+});
diff --git a/packages/components/src/Accordion/tests/jest/Accordion.test.tsx b/packages/components/src/Accordion/tests/jest/Accordion.test.tsx
new file mode 100644
index 000000000..307f71b14
--- /dev/null
+++ b/packages/components/src/Accordion/tests/jest/Accordion.test.tsx
@@ -0,0 +1,156 @@
+import { render, screen } from "@hopper-ui/test-utils";
+import { createRef } from "react";
+
+import { Disclosure, DisclosureHeader, DisclosurePanel } from "../../../Disclosure/index.ts";
+import { Accordion } from "../../src/Accordion.tsx";
+import { AccordionContext } from "../../src/AccordionContext.ts";
+
+describe("Accordion", () => {
+ it("should render with default class", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveClass("hop-Accordion");
+ });
+
+ it("should support custom class", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveClass("hop-Accordion");
+ expect(element).toHaveClass("test");
+ });
+
+ it("should support custom style", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveStyle({ marginTop: "var(--hop-space-stack-sm)", marginBottom: "13px" });
+ });
+
+ it("should support DOM props", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveAttribute("data-foo", "bar");
+ });
+
+ it("should support slots", () => {
+ render(
+
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveClass("test");
+ });
+
+ it("should support refs", () => {
+ const ref = createRef();
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ expect(ref.current).not.toBeNull();
+ expect(ref.current instanceof HTMLDivElement).toBeTruthy();
+ });
+
+ it("should render a class for standalone variant by default", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveClass("hop-Accordion--standalone");
+ });
+
+ it("should render a class for inline variant", () => {
+ render(
+
+
+
+ Disclosure Header
+
+
+ Disclosure Panel
+
+
+
+ );
+
+ const element = screen.getByTestId("accordion");
+ expect(element).toHaveClass("hop-Accordion--inline");
+ });
+});
diff --git a/packages/components/src/Disclosure/src/Disclosure.module.css b/packages/components/src/Disclosure/src/Disclosure.module.css
index 8096f7727..f97954f57 100644
--- a/packages/components/src/Disclosure/src/Disclosure.module.css
+++ b/packages/components/src/Disclosure/src/Disclosure.module.css
@@ -18,9 +18,9 @@
/* Inline */
--hop-Disclosure-inline-border-radius: 0;
--hop-Disclosure-inline-box-shadow: none;
- --hop-Disclosure-inline-panel-border-size: 0;
- --hop-Disclosure-inline-panel-border-size-expanded: 0;
- --hop-Disclosure-inline-panel-border-color: transparent;
+ --hop-Disclosure-inline-panel-border-size: 0 0 var(--hop-space-10) 0;
+ --hop-Disclosure-inline-panel-border-size-expanded: 0 0 var(--hop-space-10) 0;
+ --hop-Disclosure-inline-panel-border-color: var(--hop-neutral-border-weak);
/* Disabled */
--hop-Disclosure-box-shadow-disabled: none;
@@ -30,6 +30,7 @@
--panel-background-color: var(--hop-Disclosure-panel-background-color);
--panel-padding: var(--hop-Disclosure-panel-padding);
--panel-border-size: var(--hop-Disclosure-standalone-panel-border-size);
+ --panel-border-color: var(--hop-Disclosure-standalone-panel-border-color);
box-sizing: border-box;
border-radius: var(--border-radius);
@@ -54,22 +55,18 @@
.hop-Disclosure:has(.hop-Disclosure__header)[data-expanded] {
--panel-border-size: var(--hop-Disclosure-standalone-panel-border-size-expanded);
- --panel-border-color: var(--hop-Disclosure-standalone-panel-border-color);
--panel-padding: var(--hop-Disclosure-with-header-panel-padding);
}
-.hop-Disclosure--inline {
- --panel-border-size: var(--hop-Disclosure-inline-panel-border-size);
-}
-
.hop-Disclosure--inline:has(.hop-Disclosure__header) {
--border-radius: var(--hop-Disclosure-inline-border-radius);
--box-shadow: var(--hop-Disclosure-inline-box-shadow);
+ --panel-border-size: var(--hop-Disclosure-inline-panel-border-size);
+ --panel-border-color: var(--hop-Disclosure-inline-panel-border-color);
}
.hop-Disclosure--inline:has(.hop-Disclosure__header)[data-expanded] {
--panel-border-size: var(--hop-Disclosure-inline-panel-border-size-expanded);
- --panel-border-color: var(--hop-Disclosure-inline-panel-border-color);
}
.hop-Disclosure[data-disabled] {
diff --git a/packages/components/src/Disclosure/src/Disclosure.tsx b/packages/components/src/Disclosure/src/Disclosure.tsx
index 4034b5634..81ad55c05 100644
--- a/packages/components/src/Disclosure/src/Disclosure.tsx
+++ b/packages/components/src/Disclosure/src/Disclosure.tsx
@@ -1,6 +1,7 @@
import { useStyledSystem, type StyledComponentProps } from "@hopper-ui/styled-system";
+import clsx from "clsx";
import { forwardRef, type ForwardedRef } from "react";
-import { composeRenderProps, Disclosure as RACDisclosure, useContextProps, type DisclosureProps as RACDisclosureProps } from "react-aria-components";
+import { composeRenderProps, Disclosure as RACDisclosure, useContextProps, useSlottedContext, type DisclosureProps as RACDisclosureProps } from "react-aria-components";
import { ToggleArrowContext } from "../../ToggleArrow/index.ts";
import { composeClassnameRenderProps, cssModule, SlotProvider } from "../../utils/index.ts";
@@ -28,6 +29,9 @@ function Disclosure(props: DisclosureProps, ref: ForwardedRef) {
...otherProps
} = ownProps;
+ const disclosureHeaderCtx = useSlottedContext(DisclosureHeaderContext);
+ const disclosurePanelCtx = useSlottedContext(DisclosurePanelContext);
+
const classNames = composeClassnameRenderProps(
className,
GlobalDisclosureCssSelector,
@@ -64,10 +68,10 @@ function Disclosure(props: DisclosureProps, ref: ForwardedRef) {
variant: variant
}],
[DisclosureHeaderContext, {
- className: styles["hop-Disclosure__header"]
+ className: clsx(disclosureHeaderCtx?.className, styles["hop-Disclosure__header"])
}],
[DisclosurePanelContext, {
- className: styles["hop-Disclosure__panel"]
+ className: clsx(disclosurePanelCtx?.className, styles["hop-Disclosure__panel"])
}],
[ToggleArrowContext, {
isExpanded: disclosureRenderProps.isExpanded
diff --git a/packages/components/src/Disclosure/src/DisclosureHeader.module.css b/packages/components/src/Disclosure/src/DisclosureHeader.module.css
index 3263d2098..e5dfdf973 100644
--- a/packages/components/src/Disclosure/src/DisclosureHeader.module.css
+++ b/packages/components/src/Disclosure/src/DisclosureHeader.module.css
@@ -21,8 +21,8 @@
/* Inline */
--hop-DisclosureHeader-inline-outline-size: 0;
--hop-DisclosureHeader-inline-outline-color: transparent;
- --hop-DisclosureHeader-inline-border-size: 0 0 var(--hop-space-10) 0;
- --hop-DisclosureHeader-inline-border-color: var(--hop-neutral-border-weak);
+ --hop-DisclosureHeader-inline-border-size: 0;
+ --hop-DisclosureHeader-inline-border-color: transparent;
--hop-DisclosureHeader-inline-border-radius: 0;
/* Expanded */
@@ -55,6 +55,7 @@
--hop-DisclosureHeader-standalone-outline-color-focused: var(--hop-primary-border-focus);
--hop-DisclosureHeader-inline-outline-size-focused: var(--hop-space-20);
--hop-DisclosureHeader-inline-outline-color-focused: var(--hop-primary-border-focus);
+ --hop-DisclosureHeader-outline-offset-focused: calc(-1 * var(--hop-space-20));
/* Disabled */
--hop-DisclosureHeader-background-color-disabled: var(--hop-neutral-surface-disabled);
@@ -72,6 +73,7 @@
--description-color: var(--hop-DisclosureHeader-description-color);
--outline-size: var(--hop-DisclosureHeader-standalone-outline-size);
--outline-color: var(--hop-DisclosureHeader-standalone-outline-color);
+ --outline-offset: var(--hop-DisclosureHeader-outline-offset);
--border-radius: var(--hop-DisclosureHeader-standalone-border-radius);
--transition-info: var(--hop-easing-duration-2) var(--hop-easing-productive);
--border-radius-transition-info: var(--hop-DisclosureHeader-border-radius-transition);
@@ -107,7 +109,7 @@
border-width: var(--border-size);
border-radius: var(--border-radius);
outline: var(--outline-size) solid var(--outline-color);
- outline-offset: var(--hop-DisclosureHeader-outline-offset);
+ outline-offset: var(--outline-offset);
transition: var(--transition);
}
@@ -147,6 +149,7 @@
--icon-color: var(--hop-DisclosureHeader-icon-color-focused);
--outline-size: var(--hop-DisclosureHeader-standalone-outline-size-focused);
--outline-color: var(--hop-DisclosureHeader-standalone-outline-color-focused);
+ --outline-offset: var(--hop-DisclosureHeader-outline-offset-focused);
}
.hop-DisclosureHeader__button--inline {
@@ -164,6 +167,7 @@
.hop-DisclosureHeader__button--inline[data-focus-visible] {
--outline-size: var(--hop-DisclosureHeader-inline-outline-size-focused);
--outline-color: var(--hop-DisclosureHeader-inline-outline-color-focused);
+ --outline-offset: var(--hop-DisclosureHeader-outline-offset-focused);
}
.hop-DisclosureHeader__button[data-disabled] {
diff --git a/packages/components/src/Disclosure/tests/chromatic/Disclosure.stories.tsx b/packages/components/src/Disclosure/tests/chromatic/Disclosure.stories.tsx
index cd12fe992..538b8204c 100644
--- a/packages/components/src/Disclosure/tests/chromatic/Disclosure.stories.tsx
+++ b/packages/components/src/Disclosure/tests/chromatic/Disclosure.stories.tsx
@@ -64,8 +64,15 @@ export const Default = {
- We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
- Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
+ We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
+ Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
+
+
+
+
+
+
+
+ Shipping, Delivery Times, and Easy Returns Policy Overview
+ Explore our comprehensive shipping options, estimated delivery times for various regions, and our simple, customer-friendly returns process to make sure you feel comfortable with every purchase.
+
+
+
+ We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
+ Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
+
+
+
+
+
+
+ Shipping, Delivery Times, and Easy Returns Policy Overview
+ Explore our comprehensive shipping options, estimated delivery times for various regions, and our simple, customer-friendly returns process to make sure you feel comfortable with every purchase.
+
+
+
+ We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
+ Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
+
+
),
args: {
@@ -92,17 +126,6 @@ export const InlineVariant = {
}
} satisfies Story;
-export const CustomHeader = {
- render: args => (
-
-
-
- Disclosure Panel
-
-
- )
-} satisfies Story;
-
const StateTemplate = (args: Partial) => (
@@ -172,40 +195,4 @@ export const InlineStates = {
variant: "inline",
defaultExpanded: true
}
-} satisfies Story;
-
-export const Zoom = {
- render: args => (
-
-
-
-
-
- Shipping, Delivery Times, and Easy Returns Policy Overview
- Explore our comprehensive shipping options, estimated delivery times for various regions, and our simple, customer-friendly returns process to make sure you feel comfortable with every purchase.
-
-
-
- We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
- Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
-
-
-
-
-
-
- Shipping, Delivery Times, and Easy Returns Policy Overview
- Explore our comprehensive shipping options, estimated delivery times for various regions, and our simple, customer-friendly returns process to make sure you feel comfortable with every purchase.
-
-
-
- We offer free standard shipping on all orders over $50. Orders are typically processed within 1-2 business days, and delivery times vary based on your location. Expedited shipping options are available for an additional fee.
- Returns are easy and hassle-free. You have 30 days from the date of delivery to return items for a full refund. Items must be in their original condition and packaging. For further assistance, please contact our support team.
-
-
-
- ),
- args: {
- defaultExpanded: true
- }
} satisfies Story;
\ No newline at end of file
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index 204f3cb68..59f6c65d7 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -1,3 +1,4 @@
+export * from "./Accordion/index.ts";
export * from "./Avatar/index.ts";
export * from "./Badge/index.ts";
export * from "./buttons/index.ts";