Skip to content

Commit

Permalink
[web] Load openapi.json from repo #1216 (#1254)
Browse files Browse the repository at this point in the history
* load openapi.json file in web

* load openapi.json file in web

* load openapi.json file in web

* web - fixed coverage

* web - removed comments

* web - prevent code tok be highlighted twice
  • Loading branch information
janavlachova authored Sep 14, 2024
1 parent f2da398 commit 08ae816
Show file tree
Hide file tree
Showing 24 changed files with 2,573 additions and 5,291 deletions.
65 changes: 65 additions & 0 deletions agdb_web/components/common/code-block/code-block.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding: 0.5rem 1rem;
background-color: var(--grey-color-6);
font-size: 0.75rem;
}
.wrapper {
position: relative;
background-color: var(--grey-color-7);
color: var(--grey-color-1);
margin: 0;
overflow-x: auto;
> pre {
padding: 0;
> code {
font-size: 0.8em;
padding: 1rem !important;
}
}

&:hover .copyButton {
opacity: 1;
}
::selection {
background-color: var(--blue-0) !important;
color: var(--white) !important;
}
}

.codeBlock {
margin-top: 1.5rem;
border-radius: 0.75rem;
overflow: hidden;
}

.copyButton {
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
background-color: var(--grey-color-6);
color: var(--grey-color-3);
cursor: pointer;
transition:
color 0.2s,
opacity 0.2s;
position: absolute;
top: 0.5rem;
right: 0.5rem;
opacity: 0;
padding: 0.375rem;
border: 1px solid var(--grey-color-5);
svg {
width: 1rem;
height: 1rem;
}
&:hover {
color: var(--grey-color-1);
}
}
48 changes: 48 additions & 0 deletions agdb_web/components/common/code-block/code-block.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { cleanup, render, screen } from "@testing-library/react";
import { CodeBlock } from "./code-block";

const writeText = vi.fn();

Object.assign(navigator, {
clipboard: {
writeText,
},
});
vi.mock("@/hooks/i18n", () => ({
useI18n: () => ({
t: (key: string) => {
if (key === "button.copy-code") return "Copy code";
return "";
},
}),
}));
describe("CodeBlock", () => {
beforeEach(() => {
vi.clearAllMocks();
cleanup();
});
it("should render the code block with correct code and language", () => {
const code = `{
"name": "John Doe",
"age": 30,
"email": "
}`;
render(<CodeBlock code={code} language="json" />);
const text = screen.getByText('"John Doe"');
expect(text).toBeDefined();
const copyButton = screen.getByTitle("Copy code");
expect(copyButton).toBeDefined();
});
it("should copy the code to clipboard", () => {
const code = `{
"name": "John Doe",
"age": 30,
"email": "
}`;
const component = render(<CodeBlock code={code} language="json" />);
const copyButton = component.queryByTestId("copy-code");
copyButton?.click();
expect(writeText).toHaveBeenCalledWith(code);
});
});
53 changes: 53 additions & 0 deletions agdb_web/components/common/code-block/code-block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FC, useRef, useEffect } from "react";
import styles from "./code-block.module.scss";
import { CopyIcon } from "nextra/icons";
import { useI18n } from "@/hooks/i18n";
import { useHighlight } from "./hooks";

export interface CodeBlockProps {
code: string;
language: "json" | "javascript" | "typescript" | "rust" | "python" | "php";
header?: string;
copy?: boolean;
}

export const CodeBlock: FC<CodeBlockProps> = ({
code,
language,
header,
copy = true,
}) => {
const { t } = useI18n();

const { highlight, setLanguage } = useHighlight();

setLanguage(language);
const codeRef = useRef(null);

useEffect(() => {
codeRef.current && highlight(codeRef.current);
}, [code, highlight]);

return (
<div className={styles.codeBlock}>
{header && <div className={styles.header}>{header}</div>}
<div className={styles.wrapper}>
<pre>
<code ref={codeRef} className={language}>
{code}
</code>
</pre>
{copy && (
<button
className={styles.copyButton}
onClick={() => navigator.clipboard.writeText(code)}
title={t("button.copy-code")}
data-testid="copy-code"
>
<CopyIcon />
</button>
)}
</div>
</div>
);
};
107 changes: 107 additions & 0 deletions agdb_web/components/common/code-block/hooks.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { describe, it, expect, vi } from "vitest";
import { cleanup, renderHook } from "@testing-library/react";
import { useHighlight } from "./hooks";
import { render } from "@testing-library/react";
import { beforeEach } from "node:test";

const { languagePackMock } = vi.hoisted(() => ({
languagePackMock: vi.fn(),
highlightModuleMock: vi.fn(),
}));

vi.mock("highlight.js/lib/core");

vi.mock("highlight.js/lib/languages/json", () => languagePackMock);
vi.mock("highlight.js/lib/languages/rust", () => languagePackMock);
vi.mock("highlight.js/lib/languages/python", () => languagePackMock);
vi.mock("highlight.js/lib/languages/php", () => languagePackMock);
vi.mock("highlight.js/lib/languages/javascript", () => languagePackMock);
vi.mock("highlight.js/lib/languages/typescript", () => languagePackMock);

describe("useHighlight", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetAllMocks();
vi.resetModules();
});
describe("highlight", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetModules();
});
it("should highlight the code", async () => {
const highlightElementMock = vi.fn();
const hljs = await import("highlight.js/lib/core");
hljs.default.highlightElement = highlightElementMock;

const code = (
<div>{`{
"name": "John Doe",
"age": 30,
"email": "
}`}</div>
);
const { container } = render(code);
const { result } = renderHook(() => useHighlight());
result.current.highlight(container.firstChild as HTMLElement);
expect(highlightElementMock).toHaveBeenCalledWith(
container.firstChild,
);
});

it("should not highlight the code again", async () => {
const highlightElementMock = vi.fn();
const hljs = await import("highlight.js/lib/core");
hljs.default.highlightElement = highlightElementMock;

const code = (
<div>{`{
"name": "John Doe",
"age": 30,
"email": "
}`}</div>
);
const { container } = render(code);
if (container.firstChild)
(container.firstChild as HTMLElement).dataset.highlighted =
"true";
const { result } = renderHook(() => useHighlight());
result.current.highlight(container.firstChild as HTMLElement);
expect(highlightElementMock).not.toHaveBeenCalled();
});
});

describe("setLanguage", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.resetAllMocks();
vi.resetModules();
cleanup();
});

it.each([
["json"],
["rust"],
["python"],
["php"],
["javascript"],
["typescript"],
])("should set the language - %s", async (language) => {
const registerLanguageMock = vi.fn();
const hljs = await import("highlight.js/lib/core");
hljs.default.registerLanguage = registerLanguageMock;
const { result } = renderHook(() => useHighlight());
result.current.setLanguage(language);
expect(registerLanguageMock).toHaveBeenCalledOnce();
});

it("should set the language - empty", async () => {
const registerLanguageMock = vi.fn();
const hljs = await import("highlight.js/lib/core");
hljs.default.registerLanguage = registerLanguageMock;
const { result } = renderHook(() => useHighlight());
result.current.setLanguage("");
expect(registerLanguageMock).not.toHaveBeenCalled();
});
});
});
54 changes: 54 additions & 0 deletions agdb_web/components/common/code-block/hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import hljs from "highlight.js/lib/core";

export const useHighlight = () => {
const highlight = (code: HTMLElement) => {
if (!code.dataset.highlighted) {
hljs.highlightElement(code);
}
};

const setLanguage = (language: string) => {
switch (language) {
case "json":
hljs.registerLanguage(
"json",
require("highlight.js/lib/languages/json"),
);
break;
case "rust":
hljs.registerLanguage(
"rust",
require("highlight.js/lib/languages/rust"),
);
break;
case "python":
hljs.registerLanguage(
"python",
require("highlight.js/lib/languages/python"),
);
break;
case "php":
hljs.registerLanguage(
"php",
require("highlight.js/lib/languages/php"),
);
break;
case "javascript":
hljs.registerLanguage(
"javascript",
require("highlight.js/lib/languages/javascript"),
);
break;
case "typescript":
hljs.registerLanguage(
"typescript",
require("highlight.js/lib/languages/typescript"),
);
break;
default:
break;
}
};

return { highlight, setLanguage };
};
3 changes: 3 additions & 0 deletions agdb_web/components/common/code-block/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { CodeBlock } from "./code-block";

export default CodeBlock;
3 changes: 3 additions & 0 deletions agdb_web/components/common/link-item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LinkItem } from "./link-item";

export default LinkItem;
21 changes: 21 additions & 0 deletions agdb_web/components/common/link-item/link-item.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, expect, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { LinkItem } from "./link-item";

vi.mock("@/hooks/i18n", () => ({
useI18n: () => ({
t: (key: string) => {
if (key === "url.about") return "/about";
if (key === "link.about") return "About";
return "";
},
}),
}));

describe("LinkItem", () => {
it("should render the link item with correct link and text", () => {
render(<LinkItem i18nKey="about" />);
const link = screen.getByText("About");
expect(link.getAttribute("href")).toBe("/about");
});
});
File renamed without changes.
2 changes: 1 addition & 1 deletion agdb_web/components/layout/footer/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styles from "./footer.module.scss";
import { LinkItem } from "@/components/common/link-item";
import LinkItem from "@/components/common/link-item";

export const Footer = () => {
return (
Expand Down
2 changes: 1 addition & 1 deletion agdb_web/components/pages/intro/intro.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
background-color 0.3s ease;
&:hover {
background-color: var(--grey-color-1);
color: var(--grey-color-6);
color: var(--grey-color-8);
}
}
}
Expand Down
Loading

0 comments on commit 08ae816

Please sign in to comment.