diff --git a/packages/orbit-components/src/Accordion/Accordion.stories.tsx b/packages/orbit-components/src/Accordion/Accordion.stories.tsx
index d80fc8ed8b..53ee4db952 100644
--- a/packages/orbit-components/src/Accordion/Accordion.stories.tsx
+++ b/packages/orbit-components/src/Accordion/Accordion.stories.tsx
@@ -264,3 +264,58 @@ export const LoadingAccordion: Story = {
),
};
+
+export const MobileFirstInteraction: Story = {
+ render: function Render() {
+ const [expandedSection, setExpandedSection] = React.useState("");
+
+ return (
+
+ Traditional button-based expansion:
+ setExpandedSection(String(id))}
+ >
+
+ Click the button to expand
+ Uses traditional button-based expansion
+
+ }
+ >
+ This section uses the traditional button-based expansion.
+
+
+
+ Mobile-first tile click expansion:
+ setExpandedSection(String(id))}
+ >
+
+ Click anywhere on the header to expand
+ Uses mobile-first tile click interaction
+
+ }
+ >
+
+ This section uses the mobile-first expandOnTileClick interaction.
+
+
+ The entire header area is clickable for better mobile usability.
+
+
+ Keyboard users can still use Enter or Space to expand/collapse.
+
+
+
+
+ );
+ },
+};
diff --git a/packages/orbit-components/src/Accordion/AccordionSection/components/SectionHeader.tsx b/packages/orbit-components/src/Accordion/AccordionSection/components/SectionHeader.tsx
index 3adcf20f77..726c7c7a2f 100644
--- a/packages/orbit-components/src/Accordion/AccordionSection/components/SectionHeader.tsx
+++ b/packages/orbit-components/src/Accordion/AccordionSection/components/SectionHeader.tsx
@@ -8,6 +8,7 @@ interface Props extends Common.Globals {
readonly children: React.ReactNode;
readonly expanded: boolean;
readonly expandable: boolean;
+ readonly expandOnTileClick?: boolean;
readonly onExpand?: Common.Callback;
readonly actions?: React.ReactNode;
}
@@ -16,25 +17,73 @@ const AccordionSectionHeader = ({
children,
actions,
expanded,
+ expandOnTileClick,
onExpand,
expandable,
dataTest,
-}: Props) => (
-
-
{children}
- {!expanded && expandable && (
-
- {actions || (
-
- )}
-
- )}
-
-);
+}: Props) => {
+ const isInteractive = expandOnTileClick && !expanded && expandable;
+
+ const handleClick = React.useCallback(
+ (e: React.MouseEvent) => {
+ e.stopPropagation();
+ onExpand?.();
+ },
+ [onExpand],
+ );
+
+ const handleKeyDown = React.useCallback(
+ (e: React.KeyboardEvent) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ onExpand?.();
+ }
+ },
+ [onExpand],
+ );
+
+ const handleButtonClick = React.useCallback(
+ (e: React.SyntheticEvent) => {
+ e.stopPropagation();
+ onExpand?.();
+ },
+ [onExpand],
+ );
+
+ const content = (
+ <>
+ {children}
+ {!expanded && expandable && (
+
+ {actions || (
+
+ )}
+
+ )}
+ >
+ );
+
+ return (
+
+ {content}
+
+ );
+};
export default AccordionSectionHeader;
diff --git a/packages/orbit-components/src/Accordion/AccordionSection/index.tsx b/packages/orbit-components/src/Accordion/AccordionSection/index.tsx
index 4685ee5ca4..d5f60d55c4 100644
--- a/packages/orbit-components/src/Accordion/AccordionSection/index.tsx
+++ b/packages/orbit-components/src/Accordion/AccordionSection/index.tsx
@@ -19,6 +19,7 @@ const AccordionSection = ({
actions,
dataTest,
expandable = true,
+ expandOnTileClick = false,
}: Props) => {
const { expanded, onExpand, loading } = useAccordion();
@@ -40,6 +41,7 @@ const AccordionSection = ({
expanded={Boolean(isExpanded)}
onExpand={onExpand}
expandable={expandable}
+ expandOnTileClick={expandOnTileClick}
dataTest={dataTest}
>
{header}
diff --git a/packages/orbit-components/src/Accordion/AccordionSection/types.d.ts b/packages/orbit-components/src/Accordion/AccordionSection/types.d.ts
index 747c2c0213..8c8fd619f3 100644
--- a/packages/orbit-components/src/Accordion/AccordionSection/types.d.ts
+++ b/packages/orbit-components/src/Accordion/AccordionSection/types.d.ts
@@ -10,6 +10,7 @@ export interface Props extends Common.Globals {
readonly actions?: React.ReactNode;
readonly expanded?: boolean;
readonly expandable?: boolean;
+ readonly expandOnTileClick?: boolean; // Mobile-first interaction
readonly onExpand?: Common.Callback;
readonly header?: React.ReactNode;
readonly footer?: React.ReactNode;
diff --git a/packages/orbit-components/src/Accordion/__tests__/index.test.tsx b/packages/orbit-components/src/Accordion/__tests__/index.test.tsx
index 3508112c6c..44e1d4cc1b 100644
--- a/packages/orbit-components/src/Accordion/__tests__/index.test.tsx
+++ b/packages/orbit-components/src/Accordion/__tests__/index.test.tsx
@@ -1,9 +1,9 @@
import React from "react";
-import { render, screen } from "../../test-utils";
+import { render, screen, fireEvent, waitFor } from "../../test-utils";
import Accordion, { AccordionSection } from "..";
-describe(`Accordion`, () => {
+describe("Accordion", () => {
const expandedSection = "0X1";
const dataTest = "Accordion";
const id = "accordionId";
@@ -35,7 +35,7 @@ describe(`Accordion`, () => {
expect(screen.getByTestId(`${sectionDataTest}Loading`)).toBeInTheDocument();
});
- describe(`AccordionSection`, () => {
+ describe("AccordionSection", () => {
it("should render passed components", () => {
render(
{
expect(screen.getByTestId(`${sectionDataTest}Content`)).toBeInTheDocument();
expect(screen.getByTestId(`${sectionDataTest}Footer`)).toBeInTheDocument();
});
+
+ describe("expandOnTileClick functionality", () => {
+ const sectionId = "test";
+
+ beforeEach(() => {
+ onExpand.mockClear();
+ });
+
+ it("should expand when header is clicked and expandOnTileClick is true", async () => {
+ const clickTestId = `${sectionDataTest}-click`;
+ const expandHandler = onExpand;
+ render(
+
+
+ ,
+ );
+
+ const header = screen.getByTestId(`${clickTestId}Header`);
+ expect(header).toHaveClass("cursor-pointer");
+
+ await waitFor(() => {
+ fireEvent.click(header);
+ });
+
+ expect(expandHandler).toHaveBeenCalledWith(sectionId);
+ });
+
+ it("should not expand when header is clicked and expandOnTileClick is false", async () => {
+ const noClickTestId = `${sectionDataTest}-no-click`;
+ const expandHandler = onExpand;
+ render(
+
+
+ ,
+ );
+
+ const header = screen.getByTestId(`${noClickTestId}Header`);
+
+ await waitFor(() => {
+ fireEvent.click(header);
+ });
+
+ expect(expandHandler).not.toHaveBeenCalled();
+ });
+
+ it("should maintain button click functionality with expandOnTileClick", async () => {
+ const buttonTestId = `${sectionDataTest}-button`;
+ const expandHandler = onExpand;
+ render(
+
+
+ ,
+ );
+
+ const button = screen.getByRole("button", { name: "Open" });
+
+ await waitFor(() => {
+ fireEvent.click(button);
+ });
+
+ expect(expandHandler).toHaveBeenCalledWith(sectionId);
+ });
+ });
});
});