Skip to content

Commit

Permalink
Merge pull request #28 from react18-tools/fix-FOUC
Browse files Browse the repository at this point in the history
Fix fouc
  • Loading branch information
mayank1513 committed Jun 13, 2024
2 parents b47fee5 + d1f6181 commit b4edc9a
Show file tree
Hide file tree
Showing 29 changed files with 228 additions and 187 deletions.
8 changes: 8 additions & 0 deletions examples/app-router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# app-router

## 0.0.22

### Patch Changes

- Updated dependencies
- [email protected]
- [email protected]

## 0.0.21

### Patch Changes
Expand Down
1 change: 1 addition & 0 deletions examples/app-router/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NextJsSSGThemeSwitcher } from "nextjs-themes/server";
import { Inter } from "next/font/google";
import { SharedRootLayout, darkThemes, lightThemes } from "shared-ui";
import Link from "next/link";
import "nextjs-themes/src/styles.css";

const inter = Inter({ subsets: ["latin"] });
const forcedPages: ForcedPage[] = [
Expand Down
2 changes: 1 addition & 1 deletion examples/app-router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "app-router",
"version": "0.0.21",
"version": "0.0.22",
"private": true,
"scripts": {
"dev": "next dev --port 3002",
Expand Down
8 changes: 8 additions & 0 deletions examples/pages-router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# nextjs-pages-router

## 1.0.17

### Patch Changes

- Updated dependencies
- [email protected]
- [email protected]

## 1.0.16

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/pages-router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pages-router",
"version": "1.0.16",
"version": "1.0.17",
"private": true,
"scripts": {
"dev": "next dev --port 3003",
Expand Down
8 changes: 8 additions & 0 deletions examples/simple-multi-theme/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# simple-multi-theme

## 1.0.17

### Patch Changes

- Updated dependencies
- [email protected]
- [email protected]

## 1.0.16

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/simple-multi-theme/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "simple-multi-theme",
"version": "1.0.16",
"version": "1.0.17",
"private": true,
"scripts": {
"dev": "next dev --port 3001",
Expand Down
7 changes: 7 additions & 0 deletions examples/tailwind/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# tailwind

## 0.1.11

### Patch Changes

- Updated dependencies
- [email protected]

## 0.1.10

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/tailwind/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tailwind",
"version": "0.1.10",
"version": "0.1.11",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
8 changes: 8 additions & 0 deletions examples/vite/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# vite-example

## 0.0.22

### Patch Changes

- Updated dependencies
- [email protected]
- [email protected]

## 0.0.21

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion examples/vite/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vite-example",
"private": true,
"version": "0.0.21",
"version": "0.0.22",
"type": "module",
"scripts": {
"dev": "vite --port 3001",
Expand Down
6 changes: 6 additions & 0 deletions lib/nextjs-themes/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# nextjs-themes

## 3.1.2

### Patch Changes

- Fix FOUC

## 3.1.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion lib/nextjs-themes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "nextjs-themes",
"author": "Mayank Kumar Chaudhari <https://mayank-chaudhari.vercel.app>",
"private": false,
"version": "3.1.1",
"version": "3.1.2",
"description": "Unleash the Power of React Server Components! Use multiple themes on your site with confidence, without losing any advantages of React Server Components.",
"main": "./index.ts",
"types": "./index.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { act, cleanup, fireEvent, render, renderHook, screen } from "@testing-li
import { ColorSwitch } from "./color-switch";
import { useTheme } from "../../hooks";
import { afterEach, describe, test } from "vitest";
import { DARK, LIGHT, SYSTEM } from "../../constants";

describe("color-switch", () => {
afterEach(cleanup);
Expand All @@ -12,12 +13,12 @@ describe("color-switch", () => {
render(<ColorSwitch />);
const element = screen.getByTestId("color-switch");
act(() => fireEvent.click(element));
expect(hook.result.current.colorSchemePref).toBe("dark");
expect(hook.result.current.colorSchemePref).toBe(DARK);
act(() => fireEvent.click(element));
expect(hook.result.current.colorSchemePref).toBe("light");
expect(hook.result.current.colorSchemePref).toBe(LIGHT);
act(() => fireEvent.click(element));
expect(hook.result.current.colorSchemePref).toBe("system");
expect(hook.result.current.colorSchemePref).toBe(SYSTEM);
act(() => fireEvent.click(element));
expect(hook.result.current.colorSchemePref).toBe("dark");
expect(hook.result.current.colorSchemePref).toBe(DARK);
});
});
17 changes: 9 additions & 8 deletions lib/nextjs-themes/src/client/color-switch/color-switch.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from "react";
import { useTheme } from "../../hooks";
import { DARK, LIGHT, SYSTEM } from "../../constants";

export interface ColorSwitchProps {
/** Diameter of the color switch */
Expand All @@ -22,19 +23,19 @@ export interface ColorSwitchProps {
* <ColorSwitch size={20} skipSystem />
* ```
*/
export function ColorSwitch({ size = 25, skipSystem }: ColorSwitchProps) {
export const ColorSwitch = ({ size = 25, skipSystem }: ColorSwitchProps) => {
const { colorSchemePref, setColorSchemePref } = useTheme();
const toggleColorScheme = () => {
switch (colorSchemePref) {
case "":
case "system":
setColorSchemePref("dark");
case SYSTEM:
setColorSchemePref(DARK);
break;
case "dark":
setColorSchemePref("light");
case DARK:
setColorSchemePref(LIGHT);
break;
case "light":
setColorSchemePref(skipSystem ? "dark" : "system");
case LIGHT:
setColorSchemePref(skipSystem ? DARK : SYSTEM);
}
};
return (
Expand All @@ -47,4 +48,4 @@ export function ColorSwitch({ size = 25, skipSystem }: ColorSwitchProps) {
style={{ "--size": `${size}px` }}
/>
);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { act, cleanup, render, renderHook } from "@testing-library/react";
import { afterEach, describe, test } from "vitest";
import { useTheme } from "../../hooks";
import { ForceColorScheme } from "./force-color-scheme";
import { DARK, LIGHT } from "../../constants";

describe.concurrent("force-color-scheme", () => {
afterEach(cleanup);
/** Test only the things that this component is responsible for - chanding state*/
test("Force theme with force color scheme", async ({ expect }) => {
const { result } = renderHook(() => useTheme());
act(() => result.current.setForcedColorScheme("light"));
const { unmount } = await act(() => render(<ForceColorScheme colorScheme="dark" />));
expect(result.current.forcedColorScheme).toBe("dark");
act(() => result.current.setForcedColorScheme(LIGHT));
const { unmount } = await act(() => render(<ForceColorScheme colorScheme={DARK} />));
expect(result.current.forcedColorScheme).toBe(DARK);
act(() => unmount());
expect(result.current.forcedColorScheme).toBe(undefined);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";
import * as React from "react";
import { useEffect } from "react";
import type { ColorSchemeType } from "../../constants";
import { useTheme } from "../../hooks";

export function ForceColorScheme(props: { colorScheme: ColorSchemeType }) {
/** Force color scheme on a page */
export const ForceColorScheme = (props: { colorScheme: ColorSchemeType }) => {
const { setForcedColorScheme } = useTheme();
useEffect(() => {
setForcedColorScheme(props.colorScheme);
Expand All @@ -13,4 +13,4 @@ export function ForceColorScheme(props: { colorScheme: ColorSchemeType }) {
};
}, [props.colorScheme]);
return null;
}
};
6 changes: 3 additions & 3 deletions lib/nextjs-themes/src/client/force-theme/force-theme.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";
import * as React from "react";
import { useEffect } from "react";
import { useTheme } from "../../hooks";

export function ForceTheme(props: { theme: string }) {
/** Force theme on a page */
export const ForceTheme = (props: { theme: string }) => {
const { setForcedTheme } = useTheme();
useEffect(() => {
setForcedTheme(props.theme);
Expand All @@ -12,4 +12,4 @@ export function ForceTheme(props: { theme: string }) {
};
}, [props.theme]);
return null;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { RenderHookResult, act, cleanup, fireEvent, render, renderHook } from "@
import { afterEach, beforeEach, describe, test } from "vitest";
import { useTheme } from "../../hooks";
import { ThemeSwitcher } from "./theme-switcher";
import { encodeState, getResolvedColorScheme, getResolvedTheme } from "../../utils";
import useRGS, { SetterArgType } from "r18gs";
import { DEFAULT_ID, ThemeStoreType, initialState } from "../../constants";
import { DARK, DEFAULT_ID, LIGHT, ThemeStoreType, initialState } from "../../constants";
import { MEDIA } from "../../utils";

/** get dom attribute */
const getResolvedTheme = () => {
const theme = document.documentElement.getAttribute("data-theme");
return theme;
};

/**
* -> concurrency is not feasible because of global store conflicts
Expand Down Expand Up @@ -35,7 +41,7 @@ describe("theme-switcher", () => {
test("Test defaultDark theme", async ({ expect }) => {
const darkTheme = "dark1";
/** simulate system dark mode */
act(() => rgsHook.result.current[1](state => ({ ...state, systemColorScheme: "dark" })));
act(() => rgsHook.result.current[1](state => ({ ...state, systemColorScheme: DARK })));
/** simulate changing darkTheme by another component by user */
await new Promise(res => setTimeout(res, 350));
const { result } = renderHook(() => useTheme());
Expand All @@ -56,13 +62,13 @@ describe("theme-switcher", () => {

test("test color scheme preference", async ({ expect }) => {
const { result } = renderHook(() => useTheme());
act(() => result.current.setColorSchemePref("light"));
act(() => result.current.setColorSchemePref(LIGHT));
act(() => result.current.setLightTheme("yellow"));
act(() => result.current.setDarkTheme("dark-blue"));
act(() => result.current.setTheme("blue"));
await new Promise(res => setTimeout(res, 250));
expect(getResolvedTheme()).toBe("yellow");
act(() => result.current.setColorSchemePref("dark"));
act(() => result.current.setColorSchemePref(DARK));
/** note we do not require to await second time -- ?? what is user is setting theme from multuple useEffects? */
expect(getResolvedTheme()).toBe("dark-blue");
});
Expand All @@ -89,7 +95,6 @@ describe("theme-switcher", () => {
expect(getResolvedTheme()).toBe("");
act(() => result.current.setForcedTheme(undefined));
expect(getResolvedTheme()).toBe("black");
expect(getResolvedColorScheme()).toBe("dark");
});

test("Storage event", async ({ expect }) => {
Expand All @@ -98,7 +103,10 @@ describe("theme-switcher", () => {
await act(() =>
fireEvent(
window,
new StorageEvent("storage", { key: DEFAULT_ID, newValue: encodeState({ ...initialState, theme: MY_THEME }) }),
new StorageEvent("storage", {
key: DEFAULT_ID,
newValue: JSON.stringify({ ...initialState, theme: MY_THEME }),
}),
),
);
expect(hook.result.current.theme).toBe(MY_THEME);
Expand All @@ -125,6 +133,16 @@ describe("theme-switcher with props", () => {
test("forced colorScheme prop", async ({ expect }) => {
// global state is continuing from previous testss
await act(() => render(<ThemeSwitcher forcedColorScheme="light" />));
expect(getResolvedColorScheme()).toBe("light");
expect(getResolvedTheme()).toBe("");
});

test("media change event", async ({ expect }) => {
await act(() => render(<ThemeSwitcher />));
await act(() => {
// globalThis.window.media = LIGHT as ResolvedScheme;
// @ts-expect-error -- ok
matchMedia(MEDIA).onchange?.();
});
expect(getResolvedTheme()).toBe(DARK);
});
});
Loading

0 comments on commit b4edc9a

Please sign in to comment.