Skip to content

Commit

Permalink
fix: theme loading issue (#84)
Browse files Browse the repository at this point in the history
* Load theme earlier to avoid fouc

* fix system preference

* Renaming variables
  • Loading branch information
Balastrong authored Dec 16, 2023
1 parent 5b4c066 commit 8fe26fd
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 44 deletions.
4 changes: 2 additions & 2 deletions cypress/e2e/darkmode.spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ describe("DarkMode test", () => {
beforeEach(() => {
cy.visit("http://localhost:3000");
cy.wait(3000);
cy.get('[data-testid="darkModeButton"]').click();
cy.get('[data-testid="themeSelectorButton"]').click();
});

it("should select light Mode", () => {
Expand All @@ -27,7 +27,7 @@ describe("DarkMode test", () => {
},
});
cy.wait(3000);
cy.get('[data-testid="darkModeButton"]').click();
cy.get('[data-testid="themeSelectorButton"]').click();
cy.get('[data-testid="system-mode-option"]').click();
cy.get("html").should("have.data", "theme", "custom-dark");
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MAIN_LOGIN_PROVIDER } from "@/pages/api/auth/[...nextauth]";
import { signIn, signOut, useSession } from "next-auth/react";
import Image from "next/image";
import Link from "next/link";
import { DarkModeDropdown } from "./DarkModeDropdown";
import { ThemeSelector } from "./ThemeSelector";

export const Header = () => {
const { data: session, status } = useSession();
Expand Down Expand Up @@ -60,7 +60,7 @@ export const Header = () => {
</ul>
</div>
<div className="navbar-end">
<DarkModeDropdown />
<ThemeSelector />
{status === "authenticated" ? (
<div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { describe, test, expect } from "vitest";
import { DarkModeDropdown } from "./DarkModeDropdown";
import { ThemeSelector } from "./ThemeSelector";

describe("DarkModeDropdown", () => {
describe("ThemeSelector", () => {
test("should change light Icon if click Dark mode", () => {
render(<DarkModeDropdown />);
render(<ThemeSelector />);

const button = screen.getByTestId("darkModeButton");
const button = screen.getByTestId("themeSelectorButton");
fireEvent.click(button);

const darkModeItem = screen.getByTestId("dark-mode-option");
fireEvent.click(darkModeItem);

const buttonEdited = screen.getByTestId("darkModeButton");
const buttonEdited = screen.getByTestId("themeSelectorButton");
const darkModeSvg = buttonEdited.firstElementChild as SVGElement;
const testidValue = darkModeSvg.dataset.testid;

Expand All @@ -25,14 +25,14 @@ describe("DarkModeDropdown", () => {
});

test("should change to System Preference", () => {
render(<DarkModeDropdown />);
render(<ThemeSelector />);

const button = screen.getByTestId("darkModeButton");
const button = screen.getByTestId("themeSelectorButton");
fireEvent.click(button);
const systemItem = screen.getByTestId("system-mode-option");
fireEvent.click(systemItem);

const buttonEdited = screen.getByTestId("darkModeButton");
const buttonEdited = screen.getByTestId("themeSelectorButton");
const systemSvg = buttonEdited.firstElementChild as SVGAElement;
const testidValue = systemSvg.dataset.testid;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
import { useEffect, useState } from "react";

type DarkModeOptions = "custom-dark" | "light" | "system";
type ThemeOptions = "custom-dark" | "light" | "system";

export function DarkModeDropdown() {
const [darkModeOption, setDarkModeOption] =
useState<DarkModeOptions>("light");
export function ThemeSelector() {
const [selectedTheme, setSelectedTheme] = useState<ThemeOptions | undefined>(
undefined
);

useEffect(() => {
if (
localStorage.theme === "light" ||
(!("theme" in localStorage) &&
!window.matchMedia("(prefers-color-scheme:dark)").matches)
) {
setDocumentElement("light");
} else {
setDocumentElement("custom-dark");
const theme = localStorage.getItem("theme") as ThemeOptions;
if (theme) {
setSelectedTheme(theme);
}
}, []);

function onClick($event: React.MouseEvent<HTMLLIElement>) {
$event.stopPropagation();
const li = $event.currentTarget as HTMLLIElement;
setDocumentElement(li.id as DarkModeOptions);
setDocumentElement(li.id as ThemeOptions);
}

const setDocumentElement = (theme: DarkModeOptions) => {
setDarkModeOption(theme);
theme === "system"
? setSystemPreferenceTheme()
: setLightOrDarkTheme(theme);
const setDocumentElement = (theme: ThemeOptions) => {
setSelectedTheme(theme);
theme === "system" ? setSystemPreferenceTheme() : setTheme(theme);
};

const buttonIcon = getButtonIconByOption(darkModeOption);
const buttonIcon = getButtonIconByOption(selectedTheme);

return (
<>
<div className="dropdown dropdown-bottom dropdown-end">
<label
tabIndex={0}
className="btn btn-circle btn-ghost m-1"
data-testid="darkModeButton"
data-testid="themeSelectorButton"
>
{buttonIcon}
</label>
Expand Down Expand Up @@ -119,30 +113,26 @@ const SystemPreference = () => (
</svg>
);

const getButtonIconByOption = (option: DarkModeOptions) => {
const getButtonIconByOption = (option: ThemeOptions | undefined) => {
switch (option) {
case "light": {
case "light":
return <LightMode />;
}
case "custom-dark": {

case "custom-dark":
return <DarkMode />;
}
case "system": {

default:
return <SystemPreference />;
}
}
};

const setSystemPreferenceTheme = () => {
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
setTheme(isDark ? "custom-dark" : "light");
localStorage.removeItem("theme");
document.documentElement.dataset.theme = isDark ? "custom-dark" : "light";
isDark
? document.documentElement.classList.add("dark")
: document.documentElement.classList.remove("dark");
};

const setLightOrDarkTheme = (theme: "custom-dark" | "light") => {
const setTheme = (theme: "custom-dark" | "light") => {
localStorage.theme = theme;
document.documentElement.dataset.theme = theme;
theme === "custom-dark"
Expand Down
16 changes: 16 additions & 0 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ export default function Document() {
<Html lang="en" data-theme="light">
<Head />
<body className="light-mode dark:dark-mode">
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
var theme = localStorage.getItem('theme');
if (theme === "custom-dark" || (!theme && window.matchMedia("(prefers-color-scheme:dark)").matches)) {
document.documentElement.dataset.theme = "custom-dark";
document.documentElement.classList.add("dark")
} else {
document.documentElement.dataset.theme = "light";
document.documentElement.classList.remove("dark");
}
})()
`,
}}
/>
<Main />
<NextScript />
</body>
Expand Down

0 comments on commit 8fe26fd

Please sign in to comment.