From 6a2e3da5988cc3584c8188b9be8ea6adc423dc81 Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Fri, 2 Oct 2026 16:37:04 +0200 Subject: [PATCH] fix(chain-of-thought): share one Collapsible root for header and content ChainOfThoughtHeader and ChainOfThoughtContent each rendered their own Collapsible root, so the trigger's aria-controls named the content id of the header's root, which never renders, and the content had an id no control referenced. ChainOfThought now renders the Collapsible root, as Reasoning does, and the header and content use its trigger and content. --- .changeset/chain-of-thought-aria-controls.md | 5 ++ .../__tests__/chain-of-thought.test.tsx | 20 ++++++ packages/elements/src/chain-of-thought.tsx | 69 ++++++++++--------- 3 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 .changeset/chain-of-thought-aria-controls.md diff --git a/.changeset/chain-of-thought-aria-controls.md b/.changeset/chain-of-thought-aria-controls.md new file mode 100644 index 00000000..3b98aaa3 --- /dev/null +++ b/.changeset/chain-of-thought-aria-controls.md @@ -0,0 +1,5 @@ +--- +"ai-elements": patch +--- + +Fix ChainOfThought header aria-controls pointing at an element that is never rendered diff --git a/packages/elements/__tests__/chain-of-thought.test.tsx b/packages/elements/__tests__/chain-of-thought.test.tsx index a6e17eb4..9edc569a 100644 --- a/packages/elements/__tests__/chain-of-thought.test.tsx +++ b/packages/elements/__tests__/chain-of-thought.test.tsx @@ -68,6 +68,26 @@ describe("chainOfThought", () => { expect(onOpenChange).toHaveBeenCalledWith(true); }); + + it("points the header's aria-controls at the content", async () => { + const user = userEvent.setup(); + + render( + + + Steps + + ); + + const trigger = screen.getByRole("button"); + await user.click(trigger); + + expect(trigger).toHaveAttribute("aria-expanded", "true"); + expect(trigger).toHaveAttribute( + "aria-controls", + screen.getByText("Steps").id + ); + }); }); describe("chainOfThoughtHeader", () => { diff --git a/packages/elements/src/chain-of-thought.tsx b/packages/elements/src/chain-of-thought.tsx index f1b26601..42887718 100644 --- a/packages/elements/src/chain-of-thought.tsx +++ b/packages/elements/src/chain-of-thought.tsx @@ -60,9 +60,14 @@ export const ChainOfThought = memo( return ( -
+ {children} -
+
); } @@ -74,29 +79,27 @@ export type ChainOfThoughtHeaderProps = ComponentProps< export const ChainOfThoughtHeader = memo( ({ className, children, ...props }: ChainOfThoughtHeaderProps) => { - const { isOpen, setIsOpen } = useChainOfThought(); + const { isOpen } = useChainOfThought(); return ( - - + + + {children ?? "Chain of Thought"} + + - - - {children ?? "Chain of Thought"} - - - - + /> + ); } ); @@ -179,21 +182,19 @@ export type ChainOfThoughtContentProps = ComponentProps< export const ChainOfThoughtContent = memo( ({ className, children, ...props }: ChainOfThoughtContentProps) => { - const { isOpen } = useChainOfThought(); + useChainOfThought(); return ( - - - {children} - - + + {children} + ); } );