2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
import Head from "next/head";
import { createContext, useContext, useState, useEffect } from "react";
import type { ReactNode } from "react";
import { APP_NAME } from "@calcom/lib/constants";
type MetaType = {
title: string;
description: string;
backButton?: boolean;
CTA?: ReactNode;
borderInShellHeader?: boolean;
};
const initialMeta: MetaType = {
title: "",
description: "",
backButton: false,
CTA: null,
borderInShellHeader: true,
};
const MetaContext = createContext({
meta: initialMeta,
// eslint-disable-next-line @typescript-eslint/no-empty-function
setMeta: (_newMeta: Partial<MetaType>) => {},
});
export function useMeta() {
return useContext(MetaContext);
}
export function MetaProvider({ children }: { children: ReactNode }) {
const [value, setValue] = useState(initialMeta);
const setMeta = (newMeta: Partial<MetaType>) => {
setValue((v) => ({ ...v, ...newMeta }));
};
return <MetaContext.Provider value={{ meta: value, setMeta }}>{children}</MetaContext.Provider>;
}
/**
* The purpose of this component is to simplify title and description handling.
* Similarly to `next/head`'s `Head` component this allow us to update the metadata for a page
* from any children, also exposes the metadata via the `useMeta` hook in case we need them
* elsewhere (ie. on a Heading, Title, Subtitle, etc.)
* @example <Meta title="Password" description="Manage settings for your account passwords" />
*/
export default function Meta({ title, description, backButton, CTA, borderInShellHeader }: MetaType) {
const { setMeta, meta } = useMeta();
/* @TODO: maybe find a way to have this data on first render to prevent flicker */
useEffect(() => {
if (meta.title !== title || meta.description !== description || meta.CTA !== CTA) {
setMeta({ title, description, backButton, CTA, borderInShellHeader });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [title, description, backButton, CTA]);
const title_ = `${title} | ${APP_NAME}`;
return (
<Head>
<title>{title_}</title>
<meta name="description" content={description} />
</Head>
);
}

View File

@@ -0,0 +1 @@
export { default as Meta, MetaProvider, useMeta } from "./Meta";

View File

@@ -0,0 +1,54 @@
import { render } from "@testing-library/react";
import { vi } from "vitest";
import Meta, { useMeta } from "./Meta";
vi.mock("./Meta", async () => {
const actualMeta = (await vi.importActual("./Meta")) as object;
const MockMeta = ({ title, description }: { title: string; description: string }) => (
<div>
<h1>{title}</h1>
<p>{description}</p>
</div>
);
type MetaProviderProps = {
children: React.ReactNode;
};
return {
...actualMeta,
default: MockMeta,
useMeta: vi.fn(),
MetaProvider: ({ children }: MetaProviderProps) => children,
};
});
describe("Meta Component", () => {
test("Should render with default metadata", () => {
(useMeta as jest.Mock).mockReturnValue({
meta: { title: "", description: "", backButton: false, CTA: null },
});
const { getByText } = render(<Meta title="Page Title" description="Page Description" />);
expect(getByText("Page Title")).toBeInTheDocument();
expect(getByText("Page Description")).toBeInTheDocument();
});
test("Should update metadata when props change", () => {
(useMeta as jest.Mock).mockReturnValue({
meta: { title: "", description: "", backButton: false, CTA: null },
});
const { rerender, getByText } = render(<Meta title="Page Title" description="Page Description" />);
expect(getByText("Page Title")).toBeInTheDocument();
expect(getByText("Page Description")).toBeInTheDocument();
rerender(<Meta title="New Title" description="New Description" />);
expect(getByText("New Title")).toBeInTheDocument();
expect(getByText("New Description")).toBeInTheDocument();
});
});