diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 16e5af7fa..a55414819 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -20,6 +20,9 @@ "minutes": "m", "seconds": "s" }, + "auth": { + "signout": "Sign out" + }, "widget": { "missing_type": "Missing Widget Type: {{type}}", "api_error": "API Error", diff --git a/src/components/toggles/signout.jsx b/src/components/toggles/signout.jsx new file mode 100644 index 000000000..2a0ac09fc --- /dev/null +++ b/src/components/toggles/signout.jsx @@ -0,0 +1,21 @@ +import { signOut, useSession } from "next-auth/react"; +import { useTranslation } from "next-i18next/pages"; +import { MdLogout } from "react-icons/md"; + +export default function SignOut() { + const { t } = useTranslation(); + const { status } = useSession(); + + if (status !== "authenticated") { + return null; + } + + return ( +
+ +
+ ); +} diff --git a/src/components/toggles/signout.test.jsx b/src/components/toggles/signout.test.jsx new file mode 100644 index 000000000..2635cea83 --- /dev/null +++ b/src/components/toggles/signout.test.jsx @@ -0,0 +1,37 @@ +// @vitest-environment jsdom + +import { fireEvent, render } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { signOut, useSession } = vi.hoisted(() => ({ + signOut: vi.fn(), + useSession: vi.fn(), +})); + +vi.mock("next-auth/react", () => ({ signOut, useSession })); +vi.mock("next-i18next/pages", () => ({ useTranslation: () => ({ t: (key) => key }) })); + +import SignOut from "./signout"; + +describe("components/toggles/signout", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it.each(["unauthenticated", "loading"])("renders nothing when session status is %s", (status) => { + useSession.mockReturnValue({ status }); + + const { container } = render(); + + expect(container).toBeEmptyDOMElement(); + }); + + it("renders and signs out when authenticated", () => { + useSession.mockReturnValue({ status: "authenticated" }); + + const { getByRole } = render(); + fireEvent.click(getByRole("button")); + + expect(signOut).toHaveBeenCalledWith({ callbackUrl: "/" }); + }); +}); diff --git a/src/pages/index.jsx b/src/pages/index.jsx index a51313e07..02adabe16 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -35,6 +35,10 @@ const ColorToggle = dynamic(() => import("components/toggles/color"), { ssr: false, }); +const SignOut = dynamic(() => import("components/toggles/signout"), { + ssr: false, +}); + const Version = dynamic(() => import("components/version"), { ssr: false, }); @@ -502,6 +506,7 @@ function Home({ initialSettings }) {
{!settings?.color && } + {!settings.theme && }