Skip to content

Commit

Permalink
πŸŒ‘ persist darkness (#349)
Browse files Browse the repository at this point in the history
Co-authored-by: Brad Garropy <[email protected]>
  • Loading branch information
bradgarropy and bgarropy-atlassian authored Feb 24, 2023
1 parent 90bfc60 commit 3f0a985
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 35 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bradgarropy.com",
"version": "6.0.0",
"version": "6.1.0",
"description": "🏠 my home on the web",
"keywords": [
"javascript",
Expand Down
Empty file removed public/.gitkeep
Empty file.
9 changes: 9 additions & 0 deletions public/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const localTheme = window.localStorage.getItem("theme")

if (localTheme === "light") {
document.documentElement.classList.remove("dark")
}

if (localTheme === "dark") {
document.documentElement.classList.add("dark")
}
3 changes: 2 additions & 1 deletion src/components/ColorTheme/ColorTheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const ColorTheme: FC = () => {
onClick={onClick}
aria-label={label}
>
{theme === "light" ? <Moon /> : <Sun />}
{theme === "light" ? <Moon /> : null}
{theme === "dark" ? <Sun /> : null}
</button>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/context/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type AppProviderProps = {

const AppProvider: FC<AppProviderProps> = ({children}) => {
const [open, setOpen] = useState(false)
const [theme, setTheme] = useTheme()
const {theme, setTheme} = useTheme()

const context = {
open,
Expand Down
80 changes: 59 additions & 21 deletions src/hooks/useTheme/useTheme.test.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,80 @@
import {act, renderHook} from "@testing-library/react"
import {useTheme} from "hooks"

test("returns default theme", () => {
const mockGetItem = jest.fn()
const mockSetItem = jest.fn()

beforeEach(() => {
global.Storage.prototype.getItem = mockGetItem
global.Storage.prototype.setItem = mockSetItem
})

afterEach(() => {
mockGetItem.mockReset()
mockGetItem.mockReset()
})

test("defaults to light theme", () => {
mockGetItem.mockReturnValue(null)

const {result} = renderHook(() => useTheme())
const [theme] = result.current
expect(theme).toEqual("light")

expect(result.current.theme).toEqual("light")
expect(mockSetItem).toHaveBeenCalledTimes(1)
expect(mockSetItem).toHaveBeenCalledWith("theme", "light")
expect(document.documentElement.classList.contains("dark")).toBeFalsy()
})

test("returns light theme", () => {
const {result} = renderHook(() => useTheme("light"))
const [theme] = result.current
expect(theme).toEqual("light")
test("uses localstorage light theme", () => {
mockGetItem.mockReturnValue("light")

const {result} = renderHook(() => useTheme())

expect(result.current.theme).toEqual("light")
expect(mockSetItem).toHaveBeenCalledTimes(1)
expect(mockSetItem).toHaveBeenCalledWith("theme", "light")
expect(document.documentElement.classList.contains("dark")).toBeFalsy()
})

test("returns dark theme", () => {
const {result} = renderHook(() => useTheme("dark"))
const [theme] = result.current
expect(theme).toEqual("dark")
test("uses localstorage dark theme", () => {
mockGetItem.mockReturnValue("dark")

const {result} = renderHook(() => useTheme())

expect(result.current.theme).toEqual("dark")
expect(mockSetItem).toHaveBeenCalledTimes(1)
expect(mockSetItem).toHaveBeenCalledWith("theme", "dark")
expect(document.documentElement.classList.contains("dark")).toBeTruthy()
})

test("sets dark theme", () => {
const {result} = renderHook(() => useTheme("light"))
expect(result.current[0]).toEqual("light")
test("switches to light theme", () => {
mockGetItem.mockReturnValue("dark")

const {result} = renderHook(() => useTheme())
expect(result.current.theme).toEqual("dark")

act(() => {
result.current[1]("dark")
result.current.setTheme("light")
})

expect(result.current[0]).toEqual("dark")
expect(result.current.theme).toEqual("light")
expect(mockSetItem).toHaveBeenCalledTimes(2)
expect(mockSetItem).toHaveBeenLastCalledWith("theme", "light")
expect(document.documentElement.classList.contains("dark")).toBeFalsy()
})

test("sets light theme", () => {
const {result} = renderHook(() => useTheme("dark"))
expect(result.current[0]).toEqual("dark")
test("switches to dark theme", () => {
mockGetItem.mockReturnValue("light")

const {result} = renderHook(() => useTheme())
expect(result.current.theme).toEqual("light")

act(() => {
result.current[1]("light")
result.current.setTheme("dark")
})

expect(result.current[0]).toEqual("light")
expect(result.current.theme).toEqual("dark")
expect(mockSetItem).toHaveBeenCalledTimes(2)
expect(mockSetItem).toHaveBeenLastCalledWith("theme", "dark")
expect(document.documentElement.classList.contains("dark")).toBeTruthy()
})
24 changes: 15 additions & 9 deletions src/hooks/useTheme/useTheme.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import {Dispatch, SetStateAction, useEffect, useRef, useState} from "react"
import {Dispatch, SetStateAction, useEffect, useState} from "react"

type Theme = "light" | "dark"

const useTheme = (
defaultTheme: Theme = "light",
): [Theme, Dispatch<SetStateAction<Theme>>] => {
const initialRender = useRef(true)
const [theme, setTheme] = useState<Theme>(defaultTheme)
const useTheme = (): {
theme: Theme
setTheme: Dispatch<SetStateAction<Theme>>
} => {
const [theme, setTheme] = useState<Theme>()

useEffect(() => {
if (initialRender.current) {
initialRender.current = false
const localTheme = window.localStorage.getItem("theme") as Theme
setTheme(localTheme ?? "light")
}, [])

useEffect(() => {
if (!theme) {
return
}

if (theme === "dark") {
window.localStorage.setItem("theme", "dark")
document.documentElement.classList.add("dark")
} else {
window.localStorage.setItem("theme", "light")
document.documentElement.classList.remove("dark")
}
}, [theme])

return [theme, setTheme]
return {theme, setTheme}
}

export default useTheme
2 changes: 2 additions & 0 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Document, {
Main,
NextScript,
} from "next/document"
import Script from "next/script"

class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
Expand Down Expand Up @@ -35,6 +36,7 @@ class MyDocument extends Document {
<body className="bg-white transition duration-300 dark:bg-black">
<Main />
<NextScript />
<Script src="/theme.js" strategy="beforeInteractive" />
</body>
</Html>
)
Expand Down

1 comment on commit 3f0a985

@vercel
Copy link

@vercel vercel bot commented on 3f0a985 Feb 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.