Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/widgets/accordion.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Displays stacked sections that can be expanded or collapsed.

Expanded content renders directly under its own header in item order.

## Usage

```ts
Expand Down
2 changes: 1 addition & 1 deletion docs/widgets/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@
"name": "accordion",
"requiredProps": ["id", "items"],
"commonProps": ["expanded", "onChange"],
"example": "ui.accordion({ id: 'acc', items })"
"example": "ui.accordion({ id: 'acc', items, expanded, onChange })"
},
{
"name": "breadcrumb",
Expand Down
44 changes: 28 additions & 16 deletions packages/core/src/widgets/__tests__/accordion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ import {
resolveAccordionExpanded,
toggleAccordionExpanded,
} from "../accordion.js";
import type { VNode } from "../types.js";
import { ui } from "../ui.js";

function childSummary(child: VNode): string {
if (child.kind === "text") return child.text;
if (child.kind === "button") return child.props.label;
return child.kind;
}

const baseProps = {
id: "acc-main",
items: [
Expand Down Expand Up @@ -105,18 +112,24 @@ describe("accordion vnode construction", () => {
const children = buildAccordionChildren(baseProps);
assert.equal(children[0]?.kind, "focusZone");
if (children[0]?.kind !== "focusZone") return;
assert.equal(children[0].props.id, getAccordionHeadersZoneId(baseProps.id));
assert.equal(children[0].children.length, 3);
const zone = children[0];
assert.equal(zone.props.id, getAccordionHeadersZoneId(baseProps.id));
assert.equal(zone.children.length, 4);
assert.deepEqual(zone.children.map(childSummary), [
"> Intro",
"v API",
"api-content",
"> FAQ",
]);
Comment on lines +118 to +123
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix Biome formatter violation.

The pipeline indicates Biome would reformat this multi-line array to a single line. Apply the formatter's expected style to pass CI.

Suggested fix
-    assert.deepEqual(zone.children.map(childSummary), [
-      "> Intro",
-      "v API",
-      "api-content",
-      "> FAQ",
-    ]);
+    assert.deepEqual(zone.children.map(childSummary), ["> Intro", "v API", "api-content", "> FAQ"]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert.deepEqual(zone.children.map(childSummary), [
"> Intro",
"v API",
"api-content",
"> FAQ",
]);
assert.deepEqual(zone.children.map(childSummary), ["> Intro", "v API", "api-content", "> FAQ"]);
🧰 Tools
🪛 GitHub Actions: ci

[error] 118-123: Biome check failed (formatter). Formatter would have reformatted assert.deepEqual(zone.children.map(childSummary), [...]) to a single-line array: assert.deepEqual(zone.children.map(childSummary), [">·Intro", "v·API", "api-content", ">·FAQ"]).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/widgets/__tests__/accordion.test.ts` around lines 118 -
123, The multi-line array passed to assert.deepEqual in the test using
zone.children.map(childSummary) violates Biome formatting; reformat the expected
array into the single-line style Biome enforces so the call to
assert.deepEqual(zone.children.map(childSummary), [...]) becomes a single-line
array literal (keeping the same string elements "> Intro", "v API",
"api-content", "> FAQ") to satisfy the formatter.

});

test("buildAccordionChildren appends expanded panel content", () => {
test("buildAccordionChildren renders expanded panel directly after its header", () => {
const children = buildAccordionChildren({ ...baseProps, expanded: ["faq"] });
assert.equal(children.length, 2);
const content = children[1];
assert.equal(content?.kind, "text");
if (content?.kind === "text") {
assert.equal(content.text, "faq-content");
}
assert.equal(children.length, 1);
const zone = children[0];
assert.equal(zone?.kind, "focusZone");
if (zone?.kind !== "focusZone") return;
assert.deepEqual(zone.children.map(childSummary), ["> Intro", "> API", "v FAQ", "faq-content"]);
});

test("buildAccordionChildren in single mode keeps first expanded panel", () => {
Expand All @@ -125,18 +138,17 @@ describe("accordion vnode construction", () => {
expanded: ["faq", "intro"],
allowMultiple: false,
});
assert.equal(children.length, 2);
const content = children[1];
assert.equal(content?.kind, "text");
if (content?.kind === "text") {
assert.equal(content.text, "faq-content");
}
assert.equal(children.length, 1);
const zone = children[0];
assert.equal(zone?.kind, "focusZone");
if (zone?.kind !== "focusZone") return;
assert.deepEqual(zone.children.map(childSummary), ["> Intro", "> API", "v FAQ", "faq-content"]);
});

test("createAccordionVNode emits accordion kind", () => {
const vnode = createAccordionVNode(baseProps);
assert.equal(vnode.kind, "accordion");
assert.equal(vnode.children.length, 2);
assert.equal(vnode.children.length, 1);
});

test("ui.accordion returns a layout-transparent composite wrapper vnode", () => {
Expand Down
21 changes: 8 additions & 13 deletions packages/core/src/widgets/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
const expanded = resolveAccordionExpanded(props.expanded, itemKeys, allowMultiple);
const expandedSet = new Set(expanded);

const headerButtons: VNode[] = [];
const zoneChildren: VNode[] = [];
for (let i = 0; i < props.items.length; i++) {
const item = props.items[i];
if (!item) continue;
const isExpanded = expandedSet.has(item.key);
headerButtons.push({
zoneChildren.push({
kind: "button",
props: {
id: getAccordionTriggerId(props.id, i, item.key),
Expand All @@ -110,9 +110,12 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
},
},
});
if (isExpanded) {
zoneChildren.push(item.content);
}
}

const children: VNode[] = [
return Object.freeze([
{
kind: "focusZone",
props: {
Expand All @@ -122,17 +125,9 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
columns: 1,
wrapAround: false,
},
children: Object.freeze(headerButtons),
children: Object.freeze(zoneChildren),
},
];

for (const item of props.items) {
if (!item) continue;
if (!expandedSet.has(item.key)) continue;
children.push(item.content);
}

return Object.freeze(children);
]);
}

export function createAccordionVNode(props: AccordionProps): VNode {
Expand Down
Loading