Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] load openapi.json schema on click #1257 #1259

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions agdb_web/components/common/code-block/code-block.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
padding: 0.5rem 1rem;
background-color: var(--grey-color-6);
font-size: 0.75rem;

button {
background-color: var(--grey-color-5);
color: var(--grey-color-2);
border: 1px solid var(--grey-color-4);
border-radius: 0.375rem;
padding: 0.1rem 0.5rem;
cursor: pointer;
transition:
background-color 0.2s,
color 0.2s;
&:hover {
background-color: var(--grey-color-5);
color: var(--grey-color-1);
}
}
}
.wrapper {
position: relative;
Expand Down Expand Up @@ -63,3 +79,17 @@
color: var(--grey-color-1);
}
}

.buttonWrapper {
display: flex;
justify-content: center;
align-items: center;
margin: 2rem 0;
button {
font-size: 0.9rem;
transition: opacity 0.2s;
&:hover {
opacity: 0.8;
}
}
}
45 changes: 41 additions & 4 deletions agdb_web/components/common/code-block/code-block.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { cleanup, render, screen } from "@testing-library/react";
import { cleanup, fireEvent, render } from "@testing-library/react";
import { CodeBlock } from "./code-block";

const writeText = vi.fn();
Expand Down Expand Up @@ -28,11 +28,15 @@ describe("CodeBlock", () => {
"age": 30,
"email": "
}`;
render(<CodeBlock code={code} language="json" />);
const text = screen.getByText('"John Doe"');
const component = render(
<CodeBlock code={code} language="json" header="header" />,
);
const text = component.getByText('"John Doe"');
expect(text).toBeDefined();
const copyButton = screen.getByTitle("Copy code");
const copyButton = component.getByTitle("Copy code");
expect(copyButton).toBeDefined();
const header = component.getByText("header");
expect(header).toBeDefined();
});
it("should copy the code to clipboard", () => {
const code = `{
Expand All @@ -45,4 +49,37 @@ describe("CodeBlock", () => {
copyButton?.click();
expect(writeText).toHaveBeenCalledWith(code);
});

it("should show and hide the code block", () => {
const code = `{
"name": "John Doe",
"age": 30,
"email": "
}`;

const codeMock: {
current: string | undefined;
} = {
current: undefined,
};

const component = render(
<CodeBlock
header="header"
code={codeMock.current}
language="json"
onLoad={() => {
codeMock.current = code;
}}
/>,
);
const showButton = component.getByText("Show code");
fireEvent.click(showButton);

const hideButton = component.getByText("Hide code");
expect(hideButton).toBeDefined();
fireEvent.click(hideButton);
const showButtonAfterHide = component.getByText("Show code");
expect(showButtonAfterHide).toBeDefined();
});
});
60 changes: 51 additions & 9 deletions agdb_web/components/common/code-block/code-block.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,85 @@
import { FC, useRef, useEffect } from "react";
import { FC, useRef, useEffect, useState } 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;
code?: string;
language: "json" | "javascript" | "typescript" | "rust" | "python" | "php";
header?: string;
copy?: boolean;
showButtonText?: string;
hideButtonText?: string;
onLoad?: () => void;
}

export const CodeBlock: FC<CodeBlockProps> = ({
code,
language,
header,
copy = true,
showButtonText = "Show code",
hideButtonText = "Hide code",
onLoad,
}) => {
const { t } = useI18n();

const { highlight, setLanguage } = useHighlight();

const [hidden, setHidden] = useState(code ? false : true);

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

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

const handleShowClick = () => {
setHidden(false);
!code && onLoad && onLoad();
};

return (
<div className={styles.codeBlock}>
{header && <div className={styles.header}>{header}</div>}
{header && (
<div className={styles.header}>
<span>{header}</span>
{onLoad && !hidden && (
<button
type="button"
data-testid="hide-code"
onClick={() => setHidden(true)}
>
{hideButtonText}
</button>
)}
</div>
)}
<div className={styles.wrapper}>
<pre>
<code ref={codeRef} className={language}>
{code}
</code>
</pre>
{copy && (
{code && !hidden ? (
<pre>
<code ref={codeRef} className={language}>
{code}
</code>
</pre>
) : (
onLoad && (
<div className={styles.buttonWrapper}>
<button
type="button"
className={styles.showButton}
onClick={handleShowClick}
data-testid="show-code"
>
{showButtonText}
</button>
</div>
)
)}

{code && copy && !hidden && (
<button
className={styles.copyButton}
onClick={() => navigator.clipboard.writeText(code)}
Expand Down
40 changes: 22 additions & 18 deletions agdb_web/components/pages/openapi/openapi.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { expect, describe, it, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { fireEvent, render, screen } from "@testing-library/react";

import { OpenApi } from "./openapi";
import { beforeEach } from "node:test";

const mocks = vi.hoisted(() => {
return {
openapiFile: {
openapi: "3.0.0",
info: {
title: "AGDB API",
version: "1.0.0",
},
paths: {},
},
};
});
vi.mock("../../../../agdb_server/openapi.json", () => ({
default: mocks.openapiFile,
}));
const jsonMock = {
openapi: "3.0.0",
info: {
title: "AGDB API",
version: "1.0.0",
},
paths: {},
};

vi.mock("next/router", () => ({
useRouter: () => ({
pathname: "/",
Expand All @@ -29,9 +23,19 @@ describe("openapi", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("should render the openapi", () => {

it("should render the openapi code on click", async () => {
const file = await import("../../../../agdb_server/openapi.json");

//@ts-ignore
file.default = jsonMock;

render(<OpenApi />);
expect(screen.getByText("openapi.json")).toBeDefined();
expect(screen.getByText('"AGDB API"')).toBeDefined();

const showButton = screen.getByText("Show code");
fireEvent.click(showButton);

expect(screen.getByText("Hide code")).toBeDefined();
});
});
19 changes: 15 additions & 4 deletions agdb_web/components/pages/openapi/openapi.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import React from "react";
import openapiFile from "../../../../agdb_server/openapi.json";
import { useCallback, useState } from "react";
import CodeBlock from "@/components/common/code-block";

export const OpenApi = () => {
const openapiJson = JSON.stringify(openapiFile, null, 2);
const [openapiFile, setOpenapiFile] = useState<string>();
const handleLoadCode = useCallback(() => {
!openapiFile &&
import("../../../../agdb_server/openapi.json").then((data) => {
const openapiString = JSON.stringify(data.default, null, 2);
setOpenapiFile(openapiString);
});
}, [openapiFile]);
return (
<CodeBlock code={openapiJson} language="json" header="openapi.json" />
<CodeBlock
code={openapiFile}
language="json"
header="openapi.json"
onLoad={handleLoadCode}
/>
);
};