1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Expand and collapse tree nodes

Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
Alex Andreev 2023-02-01 13:29:22 +03:00
parent e6ab47f1eb
commit 0ecf00ed03
3 changed files with 247 additions and 4 deletions

View File

@ -1,5 +1,211 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NamespaceTreeView /> once the subscribe resolves collapses item by clicking minus button 1`] = `
<body>
<div>
<div
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-levels-deep-1"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer Component-iconContainer-22"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label Component-label-24 MuiTypography-body1"
>
levels-deep
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group Component-group-23 MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-a-1"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer Component-iconContainer-22"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root close"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M17.485 17.512q-.281.281-.682.281t-.696-.268l-4.12-4.147-4.12 4.147q-.294.268-.696.268t-.682-.281-.281-.682.294-.669l4.12-4.147-4.12-4.147q-.294-.268-.294-.669t.281-.682.682-.281.696 .268l4.12 4.147 4.12-4.147q.294-.268.696-.268t.682.281 .281.669-.294.682l-4.12 4.147 4.12 4.147q.294.268 .294.669t-.281.682zM22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label Component-label-24 MuiTypography-body1"
>
level-deep-child-a
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group Component-group-23 MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
/>
</div>
</ul>
</li>
<li
aria-expanded="false"
class="MuiTreeItem-root"
data-testid="namespace-level-deep-child-b-1"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer Component-iconContainer-22"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 12.977h-4.923v4.896q0 .401-.281.682t-.682.281v0q-.375 0-.669-.281t-.294-.682v-4.896h-4.923q-.401 0-.682-.294t-.281-.669v0q0-.401.281-.682t.682-.281h4.923v-4.896q0-.401.294-.682t.669-.281v0q.401 0 .682.281t.281.682v4.896h4.923q.401 0 .682.281t.281.682v0q0 .375-.281.669t-.682.294z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label Component-label-24 MuiTypography-body1"
>
level-deep-child-b
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group Component-group-23"
role="group"
style="min-height: 0px; height: 0px; transition-duration: 300ms;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-subchild-a-1"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer Component-iconContainer-22"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root close"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M17.485 17.512q-.281.281-.682.281t-.696-.268l-4.12-4.147-4.12 4.147q-.294.268-.696.268t-.682-.281-.281-.682.294-.669l4.12-4.147-4.12-4.147q-.294-.268-.294-.669t.281-.682.682-.281.696 .268l4.12 4.147 4.12-4.147q.294-.268.696-.268t.682.281 .281.669-.294.682l-4.12 4.147 4.12 4.147q.294.268 .294.669t-.281.682zM22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label Component-label-24 MuiTypography-body1"
>
level-deep-subchild-a
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group Component-group-23 MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
/>
</div>
</ul>
</li>
</div>
</div>
</ul>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> once the subscribe resolves renders 2 levels deep 1`] = `
<body>
<div>
@ -32,6 +238,7 @@ exports[`<NamespaceTreeView /> once the subscribe resolves renders 2 levels deep
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
@ -119,6 +326,7 @@ exports[`<NamespaceTreeView /> once the subscribe resolves renders 2 levels deep
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
@ -237,6 +445,7 @@ exports[`<NamespaceTreeView /> once the subscribe resolves renders namespace wit
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
@ -395,6 +604,7 @@ exports[`<NamespaceTreeView /> once the subscribe resolves renders namespace wit
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"

View File

@ -1,6 +1,7 @@
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { DiContainer } from "@ogre-tools/injectable";
import { fireEvent } from "@testing-library/react";
import React from "react";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
@ -219,11 +220,21 @@ describe("<NamespaceTreeView />", () => {
expect(result.baseElement).toMatchSnapshot();
});
it("expands children items", () => {
it("expands children items by default", () => {
const result = render(<NamespaceTreeView root={levelsDeep} />);
const deepest = result.getByTestId("namespace-level-deep-child-b-1");
expect(deepest).toHaveAttribute("aria-expanded", "true");
});
it("collapses item by clicking minus button", () => {
const result = render(<NamespaceTreeView root={levelsDeep} />);
const levelB = result.getByTestId("namespace-level-deep-child-b-1");
const minusButton = levelB.querySelector("[data-testid='minus-square']");
fireEvent.click(minusButton!);
expect(result.baseElement).toMatchSnapshot();
});
});
});

View File

@ -20,9 +20,15 @@ interface Dependencies {
getDetailsUrl: GetDetailsUrl;
}
function isNamespaceControlledByHNC(namespace: Namespace) {
const hierarchicalNamesaceControllerLabel = "hnc.x-k8s.io/included-namespace=true";
return namespace.getLabels().find(label => label === hierarchicalNamesaceControllerLabel);
}
function NonInjectableNamespaceTreeView({ root, namespaceStore, getDetailsUrl }: Dependencies & NamespaceTreeViewProps) {
const hierarchicalNamespaces = namespaceStore.getByLabel(["hnc.x-k8s.io/included-namespace=true"]);
const expandedItems = hierarchicalNamespaces.map(ns => `namespace-${ns.getId()}`);
const [expandedItems, setExpandedItems] = React.useState<string[]>(hierarchicalNamespaces.map(ns => `namespace-${ns.getId()}`));
function renderChildren(parent: Namespace) {
const children = hierarchicalNamespaces.filter(ns =>
@ -34,6 +40,10 @@ function NonInjectableNamespaceTreeView({ root, namespaceStore, getDetailsUrl }:
key={`namespace-${child.getId()}`}
nodeId={`namespace-${child.getId()}`}
data-testid={`namespace-${child.getId()}`}
onIconClick={(evt) =>{
toggleNode(`namespace-${child.getId()}`)
evt.stopPropagation();
}}
label={(
<>
<Link key={child.getId()} to={getDetailsUrl(child.selfLink)}>
@ -49,7 +59,15 @@ function NonInjectableNamespaceTreeView({ root, namespaceStore, getDetailsUrl }:
));
}
if (!root.getLabels().find(label => label === "hnc.x-k8s.io/included-namespace=true")) {
function toggleNode(id: string) {
if (expandedItems.includes(id)) {
setExpandedItems(expandedItems.filter(item => item !== id));
} else {
setExpandedItems([...expandedItems, id]);
}
}
if (!isNamespaceControlledByHNC(root)) {
return null;
}
@ -67,6 +85,10 @@ function NonInjectableNamespaceTreeView({ root, namespaceStore, getDetailsUrl }:
nodeId={`namespace-${root.getId()}`}
label={root.getName()}
data-testid={`namespace-${root.getId()}`}
onIconClick={(evt) => {
toggleNode(`namespace-${root.getId()}`);
evt.stopPropagation();
}}
>
{renderChildren(root)}
</StyledTreeItem>
@ -77,7 +99,7 @@ function NonInjectableNamespaceTreeView({ root, namespaceStore, getDetailsUrl }:
function MinusSquare() {
return (
<SvgIcon style={{ width: 14, height: 14 }}>
<SvgIcon style={{ width: 14, height: 14 }} data-testid="minus-square">
<path d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z" />
</SvgIcon>
);