Skip to content

Commit 22b379c

Browse files
committed
Make toHierarchy even more generic (reusable with RecursiveList
1 parent 2d51ea9 commit 22b379c

File tree

15 files changed

+111
-14
lines changed

15 files changed

+111
-14
lines changed

docs/src/pages/components/RecursiveList.svx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,11 @@ Set `type` to `"ordered"` to use the ordered list variant.
3737

3838
Set `type` to `"ordered-native"` to use the native styles for an ordered list.
3939

40-
<FileSource src="/framed/RecursiveList/RecursiveListOrderedNative" />
40+
<FileSource src="/framed/RecursiveList/RecursiveListOrderedNative" />
41+
42+
## Flat data structure
43+
44+
If working with a flat data structure, use the `toHierarchy` utility
45+
to convert a flat data structure into a hierarchical array accepted by the `nodes` prop.
46+
47+
<FileSource src="/framed/RecursiveList/RecursiveListFlatArray" />
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
import { RecursiveList, toHierarchy } from "carbon-components-svelte";
3+
4+
const nodesFlat = [
5+
{ id: 1, text: "Item 1" },
6+
{ id: 2, text: "Item 1a", pid: 1 },
7+
{ id: 3, html: "<h5>HTML content</h5>", pid: 2 },
8+
{ id: 4, text: "Item 2" },
9+
{ id: 5, href: "https://svelte.dev/", pid: 4 },
10+
{
11+
id: 6,
12+
href: "https://svelte.dev/",
13+
text: "Link with custom text",
14+
pid: 4,
15+
},
16+
{ id: 7, text: "Item 3" },
17+
];
18+
</script>
19+
20+
<RecursiveList nodes={toHierarchy(nodesFlat, (node) => node.pid)} />

src/TreeView/index.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export { default as TreeView } from "./TreeView.svelte";
2-
export { toHierarchy } from "./treeview";

src/TreeView/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export { default as TreeView } from "./TreeView.svelte";
2-
export { toHierarchy } from "./treeview";

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ export { Tooltip, TooltipFooter } from "./Tooltip";
127127
export { TooltipDefinition } from "./TooltipDefinition";
128128
export { TooltipIcon } from "./TooltipIcon";
129129
export { TreeView } from "./TreeView";
130-
export { toHierarchy } from "./TreeView/treeview";
131130
export { Truncate } from "./Truncate";
132131
export { default as truncate } from "./Truncate/truncate";
133132
export {
@@ -153,3 +152,4 @@ export {
153152
HeaderSearch,
154153
} from "./UIShell";
155154
export { UnorderedList } from "./UnorderedList";
155+
export { toHierarchy } from "./utils/toHierarchy";

src/TreeView/treeview.d.ts renamed to src/utils/toHierarchy.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ export function toHierarchy<
1717
*/
1818
getParentId: (node: T) => T[K] | null,
1919
): (T & { nodes?: (T & { nodes?: T[] })[] })[];
20+
21+
export default toHierarchy;

src/TreeView/treeview.js renamed to src/utils/toHierarchy.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ export function toHierarchy(flatArray, getParentId) {
4747

4848
return tree;
4949
}
50+
51+
export default toHierarchy;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script lang="ts">
2+
import { RecursiveList } from "carbon-components-svelte";
3+
import toHierarchy from "../../src/utils/toHierarchy";
4+
5+
let nodes = toHierarchy(
6+
[
7+
{ id: 1, text: "Item 1" },
8+
{ id: 2, text: "Item 1a", pid: 1 },
9+
{ id: 3, html: "<h5>HTML content</h5>", pid: 2 },
10+
{ id: 4, text: "Item 2" },
11+
{ id: 5, href: "https://svelte.dev/", pid: 4 },
12+
{
13+
id: 6,
14+
href: "https://svelte.dev/",
15+
text: "Link with custom text",
16+
pid: 4,
17+
},
18+
{ id: 7, text: "Item 3" },
19+
],
20+
(node) => node.pid,
21+
);
22+
</script>
23+
24+
<RecursiveList type="ordered" {nodes} />

tests/RecursiveList.test.svelte renamed to tests/RecursiveList/RecursiveList.test.svelte

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,14 @@
1414
{
1515
text: "Item 2",
1616
nodes: [
17-
{
18-
href: "https://svelte.dev/",
19-
},
17+
{ href: "https://svelte.dev/" },
2018
{
2119
href: "https://svelte.dev/",
2220
text: "Link with custom text",
2321
},
2422
],
2523
},
26-
{
27-
text: "Item 3",
28-
},
24+
{ text: "Item 3" },
2925
];
3026
</script>
3127

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { render, screen } from "@testing-library/svelte";
2+
import RecursiveListHierarchyTest from "./RecursiveList.hierarchy.test.svelte";
3+
import RecursiveListTest from "./RecursiveList.test.svelte";
4+
5+
const testCases = [
6+
{ name: "RecursiveList", component: RecursiveListTest },
7+
{ name: "RecursiveList hierarchy", component: RecursiveListHierarchyTest },
8+
];
9+
10+
describe.each(testCases)("$name", ({ component }) => {
11+
it("renders all top-level items", () => {
12+
render(component);
13+
14+
expect(screen.getByText("Item 1")).toBeInTheDocument();
15+
expect(screen.getByText("Item 2")).toBeInTheDocument();
16+
expect(screen.getByText("Item 3")).toBeInTheDocument();
17+
18+
expect(screen.getAllByRole("list")).toHaveLength(4);
19+
20+
// Nested items
21+
expect(screen.getByText("Item 1a")).toBeInTheDocument();
22+
});
23+
24+
it("renders HTML content", () => {
25+
render(component);
26+
27+
const htmlContent = screen.getByText("HTML content");
28+
expect(htmlContent.tagName).toBe("H5");
29+
});
30+
31+
it("renders links correctly", () => {
32+
render(component);
33+
34+
const links = screen.getAllByRole("link");
35+
expect(links).toHaveLength(2);
36+
37+
// Link with custom text
38+
const customLink = screen.getByText("Link with custom text");
39+
expect(customLink).toHaveAttribute("href", "https://svelte.dev/");
40+
41+
// Plain link
42+
const plainLink = links.find(
43+
(link) => link.textContent === "https://svelte.dev/",
44+
);
45+
expect(plainLink).toHaveAttribute("href", "https://svelte.dev/");
46+
});
47+
});

0 commit comments

Comments
 (0)