diff --git a/package.json b/package.json index fd108c0579..d3e4cea931 100644 --- a/package.json +++ b/package.json @@ -306,7 +306,7 @@ "concurrently": "^5.2.0", "css-element-queries": "^1.2.3", "css-loader": "^3.5.3", - "deepdash-es": "^5.3.5", + "deepdash": "^5.3.5", "dompurify": "^2.0.11", "electron": "^9.4.0", "electron-builder": "^22.7.0", diff --git a/src/renderer/components/layout/page-layout.tsx b/src/renderer/components/layout/page-layout.tsx index 345f881449..31d785b36a 100644 --- a/src/renderer/components/layout/page-layout.tsx +++ b/src/renderer/components/layout/page-layout.tsx @@ -80,9 +80,9 @@ export class PageLayout extends React.Component {
{ showNavigation && ( -
+
+ )}
{children} diff --git a/src/renderer/components/scroll-spy/__tests__/scroll-spy.test.tsx b/src/renderer/components/scroll-spy/__tests__/scroll-spy.test.tsx new file mode 100644 index 0000000000..341f786355 --- /dev/null +++ b/src/renderer/components/scroll-spy/__tests__/scroll-spy.test.tsx @@ -0,0 +1,186 @@ +import React from "react"; +import "@testing-library/jest-dom/extend-expect"; +import { render, waitFor } from "@testing-library/react"; +import { ScrollSpy } from "../scroll-spy"; +import { RecursiveTreeView } from "../../tree-view"; + +const observe = jest.fn(); + +Object.defineProperty(window, "IntersectionObserver", { + writable: true, + value: jest.fn().mockImplementation(() => ({ + observe, + unobserve: jest.fn(), + })), +}); + +describe("", () => { + it("renders w/o errors", () => { + const { container } = render( ( +
+
+

Application

+
+
+ )}/>); + + expect(container).toBeInstanceOf(HTMLElement); + }); + + it("calls intersection observer", () => { + render( ( +
+
+

Application

+
+
+ )}/>); + + expect(observe).toHaveBeenCalled(); + }); + + it("renders navigation component", async () => { + const { queryByTestId } = render( ( +
+ +
+

Application

+
+
+ )}/>); + + await waitFor(() => { + expect(queryByTestId("TreeView")).toBeInTheDocument(); + }); + }); + + it("throws if no sections founded", () => { + // Prevent writing to stderr during this render. + const err = console.error; + + console.error = jest.fn(); + + expect(() => render( ( +
+ Content +
+ )}/>)).toThrow(); + + // Restore writing to stderr. + console.error = err; + }); +}); + + +describe(" navigation inside ", () => { + it("contains links to all sections", async () => { + const { queryByTitle } = render( ( +
+ +
+

Application

+
+

Appearance

+
+
+

Theme

+
description
+
+
+
+ )}/>); + + await waitFor(() => { + expect(queryByTitle("Application")).toBeInTheDocument(); + expect(queryByTitle("Appearance")).toBeInTheDocument(); + expect(queryByTitle("Theme")).toBeInTheDocument(); + }); + }); + + it("not showing links to sections without id", async () => { + const { queryByTitle } = render( ( +
+ +
+

Application

+
+

Kubectl

+
+
+

Appearance

+
+
+
+ )}/>); + + await waitFor(() => { + expect(queryByTitle("Application")).toBeInTheDocument(); + expect(queryByTitle("Appearance")).toBeInTheDocument(); + expect(queryByTitle("Kubectl")).not.toBeInTheDocument(); + }); + }); + + it("expands parent sections", async () => { + const { queryByTitle } = render( ( +
+ +
+

Application

+
+

Appearance

+
+
+

Theme

+
description
+
+
+
+

Kubernetes

+
+

Kubectl

+
+
+
+ )}/>); + + await waitFor(() => { + expect(queryByTitle("Application")).toHaveAttribute("aria-expanded"); + expect(queryByTitle("Kubernetes")).toHaveAttribute("aria-expanded"); + }); + + // console.log(prettyDOM()); + }); + + it("skips sections without headings", async () => { + const { queryByTitle } = render( ( +
+ +
+

Application

+
+

Appearance

+
+
+

Theme

+
+
+
+ )}/>); + + await waitFor(() => { + expect(queryByTitle("Application")).toBeInTheDocument(); + expect(queryByTitle("appearance")).not.toBeInTheDocument(); + expect(queryByTitle("Appearance")).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/renderer/components/scroll-spy/scroll-spy.tsx b/src/renderer/components/scroll-spy/scroll-spy.tsx index 6edfc13647..c93231d7d8 100644 --- a/src/renderer/components/scroll-spy/scroll-spy.tsx +++ b/src/renderer/components/scroll-spy/scroll-spy.tsx @@ -31,7 +31,7 @@ export function ScrollSpy(props: Props) { sections.forEach(section => { const id = section.getAttribute("id"); const parentId = section.parentElement.id; - 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; if (!name || !id) { diff --git a/src/renderer/components/tree-view/tree-view.tsx b/src/renderer/components/tree-view/tree-view.tsx index be77092b86..783662a9f9 100644 --- a/src/renderer/components/tree-view/tree-view.tsx +++ b/src/renderer/components/tree-view/tree-view.tsx @@ -5,7 +5,11 @@ import { Icon } from "../icon"; import TreeView from "@material-ui/lab/TreeView"; import TreeItem from "@material-ui/lab/TreeItem"; import { cssNames } from "../../utils"; -import findDeep from "deepdash-es/findDeep"; + +import _ from "lodash"; +import getDeepDash from "deepdash"; + +const deepDash = getDeepDash(_); export interface NavigationTree { id: string; @@ -26,7 +30,7 @@ const scrollToItem = (id: string) => { }; const getSelectedNode = (data: NavigationTree[]) => { - return findDeep(data, (value, key) => key === "selected" && value === true)?.parent; + return deepDash.findDeep(data, (value, key) => key === "selected" && value === true)?.parent; }; export function RecursiveTreeView({ data }: Props) { @@ -63,6 +67,7 @@ export function RecursiveTreeView({ data }: Props) { label={node.name} onLabelClick={(event) => onLabelClick(event, node.id)} className={cssNames({selected: node.selected})} + title={node.name} > {Array.isArray(node.children) ? node.children.map((node) => renderTree([node])) : null} @@ -84,6 +89,7 @@ export function RecursiveTreeView({ data }: Props) { return (