Skip to content

Commit

Permalink
Merge branch 'master' of github.com:pacocoursey/next-themes
Browse files Browse the repository at this point in the history
  • Loading branch information
pacocoursey committed Mar 30, 2021
2 parents 43d6260 + d41a339 commit 17b9d77
Show file tree
Hide file tree
Showing 5 changed files with 3,342 additions and 46 deletions.
12 changes: 12 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,17 @@
"loose": true
}
]
],
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
"@babel/preset-typescript",
"@babel/preset-react"
]
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# next-themes ![next-themes minzip package size](https://img.shields.io/bundlephobia/minzip/next-themes)
# next-themes ![next-themes minzip package size](https://img.shields.io/bundlephobia/minzip/next-themes) ![Version](https://img.shields.io/npm/v/next-themes.svg?colorB=green)

An abstraction for themes in your Next.js app.

Expand Down
200 changes: 200 additions & 0 deletions __tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import {act, render, screen} from "@testing-library/react";
import {ThemeProvider, useTheme} from "../index";
import React, {useEffect} from "react";

let localStorageMock: { [key: string]: string } = {}

// HelperComponent to render the theme inside a paragraph-tag and setting a theme via the forceSetTheme prop
const HelperComponent = ({forceSetTheme}: { forceSetTheme?: string }) => {
const {setTheme, theme, forcedTheme} = useTheme()

useEffect(() => {
if (forceSetTheme) {
setTheme(forceSetTheme)
}
}, [forceSetTheme])

return <>
<p data-testid="theme">{theme}</p>
<p data-testid="forcedTheme">{forcedTheme}</p>
</>;
}

beforeAll(() => {
// Create a mock of the window.matchMedia function
// Based on: https://stackoverflow.com/questions/39830580/jest-test-fails-typeerror-window-matchmedia-is-not-a-function
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // Deprecated
removeListener: jest.fn(), // Deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
})

// Create mocks of localStorage getItem and setItem functions
global.Storage.prototype.getItem = jest.fn((key: string) => localStorageMock[key])
global.Storage.prototype.setItem = jest.fn((key: string, value: string) => {
localStorageMock[key] = value;
})
})

beforeEach(() => {
// Clear the localStorage-mock
localStorageMock = {}
})

describe('defaultTheme test-suite', () => {

test('should return system when no default-theme is set', () => {
render(
<ThemeProvider>
<HelperComponent/>
</ThemeProvider>
)

expect(screen.getByTestId('theme').textContent).toBe('system')
})

test('should return light when no default-theme is set and enableSystem=false', () => {
render(
<ThemeProvider enableSystem={false}>
<HelperComponent/>
</ThemeProvider>
)

expect(screen.getByTestId('theme').textContent).toBe('light')
})

test('should return light when light is set as default-theme', () => {
render(
<ThemeProvider defaultTheme="light">
<HelperComponent/>
</ThemeProvider>
)

expect(screen.getByTestId('theme').textContent).toBe('light')
})

test('should return dark when dark is set as default-theme', () => {
render(
<ThemeProvider defaultTheme="dark">
<HelperComponent/>
</ThemeProvider>
)

expect(screen.getByTestId('theme').textContent).toBe('dark')
})
});

describe('custom storageKey test-suite', () => {

test('should save to localStorage with \'theme\' key when using default settings', () => {
act(() => {
render(<ThemeProvider>
<HelperComponent forceSetTheme="light"/>
</ThemeProvider>)
})

expect(global.Storage.prototype.getItem).toHaveBeenCalledWith('theme')
expect(global.Storage.prototype.setItem).toHaveBeenCalledWith('theme', 'light')
})

test('should save to localStorage with \'custom\' when setting prop \'storageKey\' to \'customKey\'', () => {
act(() => {
render(<ThemeProvider storageKey="customKey">
<HelperComponent forceSetTheme="light"/>
</ThemeProvider>)
})

expect(global.Storage.prototype.getItem).toHaveBeenCalledWith('customKey')
expect(global.Storage.prototype.setItem).toHaveBeenCalledWith('customKey', 'light')
})

})

describe('custom attribute test-suite', () => {

test('should use data-theme attribute when using default', () => {
act(() => {
render(<ThemeProvider>
<HelperComponent forceSetTheme="light"/>
</ThemeProvider>)
})

expect(document.documentElement.getAttribute("data-theme")).toBe("light")
})

test('should use class attribute (CSS-class) when attribute="class"', () => {
act(() => {
render(<ThemeProvider attribute="class">
<HelperComponent forceSetTheme="light"/>
</ThemeProvider>)
})

expect(document.documentElement.classList.contains("light")).toBeTruthy()
});

test('should use "data-example"-attribute when attribute="data-example"', () => {
act(() => {
render(<ThemeProvider attribute="data-example">
<HelperComponent forceSetTheme="light"/>
</ThemeProvider>)
})

expect(document.documentElement.getAttribute('data-example')).toBe('light')
})

})

describe('custom value-mapping test-suite', () => {

test('should use custom value mapping when using value={{pink:"my-pink-theme"}}', () => {
localStorageMock["theme"] = "pink"

act(() => {
render(<ThemeProvider themes={['pink', 'light', 'dark', 'system']} value={{pink: "my-pink-theme"}}>
<HelperComponent forceSetTheme="pink"/>
</ThemeProvider>)
})

expect(document.documentElement.getAttribute('data-theme')).toBe('my-pink-theme')
expect(global.Storage.prototype.setItem).toHaveBeenCalledWith('theme', 'pink')
})

})

describe('forcedTheme test-suite', () => {

test('should render saved theme when no forcedTheme is set', () => {
localStorageMock['theme'] = 'dark';

render(
<ThemeProvider>
<HelperComponent/>
</ThemeProvider>
)

expect(screen.getByTestId('theme').textContent).toBe('dark')
expect(screen.getByTestId('forcedTheme').textContent).toBe('')
})

test('should render light theme when forcedTheme is set a', () => {
localStorageMock['theme'] = 'dark'

act(() => {
render(<ThemeProvider forcedTheme="light">
<HelperComponent/>
</ThemeProvider>)
})

expect(screen.getByTestId('theme').textContent).toBe('dark')
expect(screen.getByTestId('forcedTheme').textContent).toBe('light')
})

})
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
],
"scripts": {
"prepublish": "yarn build",
"build": "microbundle --jsx React.createElement --compress --no-sourcemap"
"build": "microbundle --jsx React.createElement --compress --no-sourcemap",
"test": "jest"
},
"dependencies": {},
"peerDependencies": {
Expand All @@ -20,10 +21,24 @@
"react-dom": "*"
},
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.13.0",
"@testing-library/react": "^11.2.5",
"@types/jest": "^26.0.21",
"@types/next": "^9.0.0",
"@types/react": "^16.9.53",
"babel-jest": "^26.6.3",
"jest": "^26.6.3",
"microbundle": "^0.12.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"typescript": "^4.0.3"
},
"repository": {
"type": "git",
"url": "https://github.com/pacocoursey/next-themes.git"
}
}
Loading

0 comments on commit 17b9d77

Please sign in to comment.