Skip to content

Commit 22b379c

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

15 files changed

+111
-14
lines changed

docs/src/pages/components/RecursiveList.svx

+8-1
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" />
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

-1
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

-1
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

+1-1
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 src/utils/toHierarchy.d.ts

+2
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 src/utils/toHierarchy.js

+2
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;
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 tests/RecursiveList/RecursiveList.test.svelte

+2-6
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

+47
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+
});

tests/TreeView/TreeView.hierarchy.test.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { Button, TreeView } from "carbon-components-svelte";
3-
import { toHierarchy } from "../../src/TreeView/treeview";
3+
import { toHierarchy } from "../../src/utils/toHierarchy";
44
import type { TreeNodeId } from "carbon-components-svelte/TreeView/TreeView.svelte";
55
import Analytics from "carbon-icons-svelte/lib/Analytics.svelte";
66

tests/TreeView/to-hierarchy.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { toHierarchy } from "../../src/TreeView/treeview";
1+
import { toHierarchy } from "../../src/utils/toHierarchy";
22

33
describe("toHierarchy", () => {
44
test("should create a flat hierarchy when no items have parents", () => {

types/TreeView/index.d.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export { default as TreeView } from "./TreeView.svelte";
2-
export { toHierarchy } from "./treeview";

types/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ export { default as TooltipFooter } from "./Tooltip/TooltipFooter.svelte";
143143
export { default as TooltipDefinition } from "./TooltipDefinition/TooltipDefinition.svelte";
144144
export { default as TooltipIcon } from "./TooltipIcon/TooltipIcon.svelte";
145145
export { default as TreeView } from "./TreeView/TreeView.svelte";
146-
export { default as toHierarchy } from "./TreeView/treeview";
147146
export { default as Truncate } from "./Truncate/Truncate.svelte";
148147
export { default as truncate } from "./Truncate/truncate";
149148
export { default as Header } from "./UIShell/Header.svelte";
@@ -167,3 +166,4 @@ export { default as SkipToContent } from "./UIShell/SkipToContent.svelte";
167166
export { default as HeaderGlobalAction } from "./UIShell/HeaderGlobalAction.svelte";
168167
export { default as HeaderSearch } from "./UIShell/HeaderSearch.svelte";
169168
export { default as UnorderedList } from "./UnorderedList/UnorderedList.svelte";
169+
export { default as toHierarchy } from "./utils/toHierarchy";

types/TreeView/treeview.d.ts types/utils/toHierarchy.d.ts

+2
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;

0 commit comments

Comments
 (0)