Skip to content

Commit fa11db2

Browse files
committed
fix(accordion): render expanded panels under their headers
1 parent 46559e8 commit fa11db2

File tree

4 files changed

+39
-30
lines changed

4 files changed

+39
-30
lines changed

docs/widgets/accordion.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Displays stacked sections that can be expanded or collapsed.
44

5+
Expanded content renders directly under its own header in item order.
6+
57
## Usage
68

79
```ts

docs/widgets/catalog.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@
375375
"name": "accordion",
376376
"requiredProps": ["id", "items"],
377377
"commonProps": ["expanded", "onChange"],
378-
"example": "ui.accordion({ id: 'acc', items })"
378+
"example": "ui.accordion({ id: 'acc', items, expanded, onChange })"
379379
},
380380
{
381381
"name": "breadcrumb",

packages/core/src/widgets/__tests__/accordion.test.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ import {
88
resolveAccordionExpanded,
99
toggleAccordionExpanded,
1010
} from "../accordion.js";
11+
import type { VNode } from "../types.js";
1112
import { ui } from "../ui.js";
1213

14+
function childSummary(child: VNode): string {
15+
if (child.kind === "text") return child.text;
16+
if (child.kind === "button") return child.props.label;
17+
return child.kind;
18+
}
19+
1320
const baseProps = {
1421
id: "acc-main",
1522
items: [
@@ -105,18 +112,24 @@ describe("accordion vnode construction", () => {
105112
const children = buildAccordionChildren(baseProps);
106113
assert.equal(children[0]?.kind, "focusZone");
107114
if (children[0]?.kind !== "focusZone") return;
108-
assert.equal(children[0].props.id, getAccordionHeadersZoneId(baseProps.id));
109-
assert.equal(children[0].children.length, 3);
115+
const zone = children[0];
116+
assert.equal(zone.props.id, getAccordionHeadersZoneId(baseProps.id));
117+
assert.equal(zone.children.length, 4);
118+
assert.deepEqual(zone.children.map(childSummary), [
119+
"> Intro",
120+
"v API",
121+
"api-content",
122+
"> FAQ",
123+
]);
110124
});
111125

112-
test("buildAccordionChildren appends expanded panel content", () => {
126+
test("buildAccordionChildren renders expanded panel directly after its header", () => {
113127
const children = buildAccordionChildren({ ...baseProps, expanded: ["faq"] });
114-
assert.equal(children.length, 2);
115-
const content = children[1];
116-
assert.equal(content?.kind, "text");
117-
if (content?.kind === "text") {
118-
assert.equal(content.text, "faq-content");
119-
}
128+
assert.equal(children.length, 1);
129+
const zone = children[0];
130+
assert.equal(zone?.kind, "focusZone");
131+
if (zone?.kind !== "focusZone") return;
132+
assert.deepEqual(zone.children.map(childSummary), ["> Intro", "> API", "v FAQ", "faq-content"]);
120133
});
121134

122135
test("buildAccordionChildren in single mode keeps first expanded panel", () => {
@@ -125,18 +138,17 @@ describe("accordion vnode construction", () => {
125138
expanded: ["faq", "intro"],
126139
allowMultiple: false,
127140
});
128-
assert.equal(children.length, 2);
129-
const content = children[1];
130-
assert.equal(content?.kind, "text");
131-
if (content?.kind === "text") {
132-
assert.equal(content.text, "faq-content");
133-
}
141+
assert.equal(children.length, 1);
142+
const zone = children[0];
143+
assert.equal(zone?.kind, "focusZone");
144+
if (zone?.kind !== "focusZone") return;
145+
assert.deepEqual(zone.children.map(childSummary), ["> Intro", "> API", "v FAQ", "faq-content"]);
134146
});
135147

136148
test("createAccordionVNode emits accordion kind", () => {
137149
const vnode = createAccordionVNode(baseProps);
138150
assert.equal(vnode.kind, "accordion");
139-
assert.equal(vnode.children.length, 2);
151+
assert.equal(vnode.children.length, 1);
140152
});
141153

142154
test("ui.accordion returns a layout-transparent composite wrapper vnode", () => {

packages/core/src/widgets/accordion.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
9494
const expanded = resolveAccordionExpanded(props.expanded, itemKeys, allowMultiple);
9595
const expandedSet = new Set(expanded);
9696

97-
const headerButtons: VNode[] = [];
97+
const zoneChildren: VNode[] = [];
9898
for (let i = 0; i < props.items.length; i++) {
9999
const item = props.items[i];
100100
if (!item) continue;
101101
const isExpanded = expandedSet.has(item.key);
102-
headerButtons.push({
102+
zoneChildren.push({
103103
kind: "button",
104104
props: {
105105
id: getAccordionTriggerId(props.id, i, item.key),
@@ -110,9 +110,12 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
110110
},
111111
},
112112
});
113+
if (isExpanded) {
114+
zoneChildren.push(item.content);
115+
}
113116
}
114117

115-
const children: VNode[] = [
118+
return Object.freeze([
116119
{
117120
kind: "focusZone",
118121
props: {
@@ -122,17 +125,9 @@ export function buildAccordionChildren(props: AccordionProps): readonly VNode[]
122125
columns: 1,
123126
wrapAround: false,
124127
},
125-
children: Object.freeze(headerButtons),
128+
children: Object.freeze(zoneChildren),
126129
},
127-
];
128-
129-
for (const item of props.items) {
130-
if (!item) continue;
131-
if (!expandedSet.has(item.key)) continue;
132-
children.push(item.content);
133-
}
134-
135-
return Object.freeze(children);
130+
]);
136131
}
137132

138133
export function createAccordionVNode(props: AccordionProps): VNode {

0 commit comments

Comments
 (0)