mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fix scroll-spy.test.tsx
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
fa9683a170
commit
49faa2f9b7
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import "@testing-library/jest-dom/extend-expect";
|
import "@testing-library/jest-dom/extend-expect";
|
||||||
import { render, waitFor } from "@testing-library/react";
|
import { render, screen, waitFor } from "@testing-library/react";
|
||||||
import { ScrollSpy } from "../scroll-spy";
|
import { ScrollSpy } from "../scroll-spy";
|
||||||
import { RecursiveTreeView } from "../../tree-view";
|
import { RecursiveTreeView } from "../../tree-view";
|
||||||
|
|
||||||
@ -23,7 +23,6 @@ describe("<ScrollSpy/>", () => {
|
|||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
const { container } = render((
|
const { container } = render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={() => (
|
render={() => (
|
||||||
<div>
|
<div>
|
||||||
<section id="application">
|
<section id="application">
|
||||||
@ -40,7 +39,6 @@ describe("<ScrollSpy/>", () => {
|
|||||||
it("calls intersection observer", () => {
|
it("calls intersection observer", () => {
|
||||||
render((
|
render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={() => (
|
render={() => (
|
||||||
<div>
|
<div>
|
||||||
<section id="application">
|
<section id="application">
|
||||||
@ -55,9 +53,8 @@ describe("<ScrollSpy/>", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renders dataTree component", async () => {
|
it("renders dataTree component", async () => {
|
||||||
const { queryByTestId } = render((
|
render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={dataTree => (
|
render={dataTree => (
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<nav>
|
||||||
@ -71,9 +68,7 @@ describe("<ScrollSpy/>", () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
await waitFor(() => {
|
expect(await screen.findByTestId("TreeView")).toBeInTheDocument();
|
||||||
expect(queryByTestId("TreeView")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws if no sections founded", () => {
|
it("throws if no sections founded", () => {
|
||||||
@ -84,7 +79,6 @@ describe("<ScrollSpy/>", () => {
|
|||||||
|
|
||||||
expect(() => render((
|
expect(() => render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={() => (
|
render={() => (
|
||||||
<div>
|
<div>
|
||||||
Content
|
Content
|
||||||
@ -101,9 +95,8 @@ describe("<ScrollSpy/>", () => {
|
|||||||
|
|
||||||
describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
||||||
it("contains links to all sections", async () => {
|
it("contains links to all sections", async () => {
|
||||||
const { queryByTitle } = render((
|
render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={dataTree => (
|
render={dataTree => (
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<nav>
|
||||||
@ -124,17 +117,14 @@ describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
await waitFor(() => {
|
expect(await screen.findByTitle("Application")).toBeInTheDocument();
|
||||||
expect(queryByTitle("Application")).toBeInTheDocument();
|
expect(await screen.findByTitle("Appearance")).toBeInTheDocument();
|
||||||
expect(queryByTitle("Appearance")).toBeInTheDocument();
|
expect(await screen.findByTitle("Theme")).toBeInTheDocument();
|
||||||
expect(queryByTitle("Theme")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("not showing links to sections without id", async () => {
|
it("not showing links to sections without id", async () => {
|
||||||
const { queryByTitle } = render((
|
const { queryByTitle } = render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={dataTree => (
|
render={dataTree => (
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<nav>
|
||||||
@ -154,17 +144,17 @@ describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
expect(await screen.findByTitle("Application")).toBeInTheDocument();
|
||||||
|
expect(await screen.findByTitle("Appearance")).toBeInTheDocument();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(queryByTitle("Application")).toBeInTheDocument();
|
|
||||||
expect(queryByTitle("Appearance")).toBeInTheDocument();
|
|
||||||
expect(queryByTitle("Kubectl")).not.toBeInTheDocument();
|
expect(queryByTitle("Kubectl")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("expands parent sections", async () => {
|
it("expands parent sections", async () => {
|
||||||
const { queryByTitle } = render((
|
render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={dataTree => (
|
render={dataTree => (
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<nav>
|
||||||
@ -191,16 +181,13 @@ describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
await waitFor(() => {
|
expect(await screen.findByTitle("Application")).toHaveAttribute("aria-expanded");
|
||||||
expect(queryByTitle("Application")).toHaveAttribute("aria-expanded");
|
expect(await screen.findByTitle("Kubernetes")).toHaveAttribute("aria-expanded");
|
||||||
expect(queryByTitle("Kubernetes")).toHaveAttribute("aria-expanded");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips sections without headings", async () => {
|
it("skips sections without headings", async () => {
|
||||||
const { queryByTitle } = render((
|
render((
|
||||||
<ScrollSpy
|
<ScrollSpy
|
||||||
htmlFor=""
|
|
||||||
render={dataTree => (
|
render={dataTree => (
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<nav>
|
||||||
@ -220,10 +207,11 @@ describe("<TreeView/> dataTree inside <ScrollSpy/>", () => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
expect(await screen.findByTitle("Application")).toBeInTheDocument();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(queryByTitle("Application")).toBeInTheDocument();
|
expect(screen.queryByTitle("appearance")).not.toBeInTheDocument();
|
||||||
expect(queryByTitle("appearance")).not.toBeInTheDocument();
|
expect(screen.queryByTitle("Appearance")).not.toBeInTheDocument();
|
||||||
expect(queryByTitle("Appearance")).not.toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import type { NavigationTree } from "../tree-view";
|
|||||||
|
|
||||||
export interface ScrollSpyProps extends React.DOMAttributes<HTMLElement> {
|
export interface ScrollSpyProps extends React.DOMAttributes<HTMLElement> {
|
||||||
render: (data: NavigationTree[]) => JSX.Element;
|
render: (data: NavigationTree[]) => JSX.Element;
|
||||||
htmlFor: string; // Id of the element to put observers on
|
htmlFor?: string; // Id of the element to put observers on
|
||||||
rootMargin?: string; // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
|
rootMargin?: string; // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#creating_an_intersection_observer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,12 +20,14 @@ export const ScrollSpy = observer(({ render, htmlFor, rootMargin = "0px 0px -100
|
|||||||
const [tree, setTree] = useState<NavigationTree[]>([]);
|
const [tree, setTree] = useState<NavigationTree[]>([]);
|
||||||
const [activeElementId, setActiveElementId] = useState("");
|
const [activeElementId, setActiveElementId] = useState("");
|
||||||
|
|
||||||
const setSections = () => {
|
const setSections = (): NodeListOf<HTMLElement> => {
|
||||||
sections.current = parent.current?.querySelectorAll("section");
|
sections.current = parent.current?.querySelectorAll("section");
|
||||||
|
|
||||||
if (!sections.current?.length) {
|
if (!sections.current?.length) {
|
||||||
throw new Error("No <section/> tag founded! Content should be placed inside <section></section> elements to activate navigation.");
|
throw new Error("No <section/> tag founded! Content should be placed inside <section></section> elements to activate navigation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sections.current;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSectionsParentElement = () => {
|
const getSectionsParentElement = () => {
|
||||||
@ -33,14 +35,14 @@ export const ScrollSpy = observer(({ render, htmlFor, rootMargin = "0px 0px -100
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateNavigation = () => {
|
const updateNavigation = () => {
|
||||||
const parentSection = getSectionsParentElement();
|
setTree(getNavigation(getSectionsParentElement()));
|
||||||
|
|
||||||
if (parentSection) {
|
|
||||||
setTree(getNavigation(parentSection));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNavigation = (element: Element) => {
|
const getNavigation = (element: Element | null | undefined): NavigationTree[] => {
|
||||||
|
if (!element) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const sections = element.querySelectorAll(":scope > section"); // Searching only direct children of an element. Impossible without :scope
|
const sections = element.querySelectorAll(":scope > section"); // Searching only direct children of an element. Impossible without :scope
|
||||||
const children: NavigationTree[] = [];
|
const children: NavigationTree[] = [];
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ export const ScrollSpy = observer(({ render, htmlFor, rootMargin = "0px 0px -100
|
|||||||
const name = section.querySelector("h1, h2, h3, h4, h5, h6")?.textContent;
|
const name = section.querySelector("h1, h2, h3, h4, h5, h6")?.textContent;
|
||||||
const selected = id === activeElementId;
|
const selected = id === activeElementId;
|
||||||
|
|
||||||
if (!name || !id || !parentId) {
|
if (!name || !id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,13 +76,13 @@ export const ScrollSpy = observer(({ render, htmlFor, rootMargin = "0px 0px -100
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const observeSections = () => {
|
const observeSections = (list: NodeListOf<HTMLElement>) => {
|
||||||
const options: IntersectionObserverInit = {
|
const options: IntersectionObserverInit = {
|
||||||
root: document.getElementById(htmlFor) || getSectionsParentElement(),
|
root: (htmlFor && document.getElementById(htmlFor)) || getSectionsParentElement(),
|
||||||
rootMargin,
|
rootMargin,
|
||||||
};
|
};
|
||||||
|
|
||||||
sections.current?.forEach((section) => {
|
list.forEach((section) => {
|
||||||
const observer = new IntersectionObserver(handleIntersect, options);
|
const observer = new IntersectionObserver(handleIntersect, options);
|
||||||
const target = section.querySelector("section") || section;
|
const target = section.querySelector("section") || section;
|
||||||
|
|
||||||
@ -89,9 +91,10 @@ export const ScrollSpy = observer(({ render, htmlFor, rootMargin = "0px 0px -100
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSections();
|
const list = setSections();
|
||||||
observeSections();
|
|
||||||
}, []);
|
observeSections(list);
|
||||||
|
}, [parent.current]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateNavigation();
|
updateNavigation();
|
||||||
|
|||||||
@ -18,7 +18,7 @@ const deepDash = getDeepDash(_);
|
|||||||
|
|
||||||
export interface NavigationTree {
|
export interface NavigationTree {
|
||||||
id: string;
|
id: string;
|
||||||
parentId: string;
|
parentId?: string;
|
||||||
name: string;
|
name: string;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
children?: NavigationTree[];
|
children?: NavigationTree[];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user