mirror of
https://github.com/kremalicious/portfolio.git
synced 2024-12-22 17:23:22 +01:00
remove AppProvider, replace with hooks
This commit is contained in:
parent
d9fa36608e
commit
796dfc64fa
@ -74,7 +74,7 @@ If a visitor has set the theme manually that selection is remembered in `localSt
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
|
||||
- [`src/store/provider.jsx`](src/store/provider.jsx)
|
||||
- [`src/hooks/use-dark-mode.jsx`](src/hooks/use-dark-mode.jsx)
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
|
@ -1,6 +1,3 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import AppProvider from './src/store/AppProvider'
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
|
||||
// Global styles
|
||||
@ -11,15 +8,6 @@ if (typeof window !== 'undefined' && !window.IntersectionObserver) {
|
||||
import('intersection-observer')
|
||||
}
|
||||
|
||||
// React Context in Browser
|
||||
export const wrapRootElement = ({ element }) => {
|
||||
return <AppProvider>{element}</AppProvider>
|
||||
}
|
||||
|
||||
wrapRootElement.propTypes = {
|
||||
element: PropTypes.any
|
||||
}
|
||||
|
||||
// Layout with Page Transitions
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
||||
|
||||
|
@ -1,13 +1,4 @@
|
||||
import React from 'react'
|
||||
import { renderToString } from 'react-dom/server'
|
||||
import AppProvider from './src/store/AppProvider'
|
||||
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
|
||||
|
||||
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
|
||||
// React Context in SSR/build
|
||||
const ConnectedBody = () => <AppProvider>{bodyComponent}</AppProvider>
|
||||
replaceBodyHTMLString(renderToString(<ConnectedBody />))
|
||||
}
|
||||
|
||||
// Layout with Page Transitions
|
||||
export const wrapPageElement = wrapPageElementWithLayout
|
||||
|
@ -4,13 +4,13 @@
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
border-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 3px 5px rgba($brand-main, 0.15),
|
||||
0 5px 16px rgba($brand-main, 0.15);
|
||||
|
||||
@media (min-width: $projectImageMaxWidth) {
|
||||
max-width: $projectImageMaxWidth;
|
||||
border-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.dark) & {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useContext } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Helmet from 'react-helmet'
|
||||
import posed from 'react-pose'
|
||||
import Context from '../../store/createContext'
|
||||
import useDarkMode from '../../hooks/use-dark-mode'
|
||||
import { fadeIn } from '../atoms/Transitions'
|
||||
|
||||
import { ReactComponent as Day } from '../../images/day.svg'
|
||||
@ -40,7 +40,7 @@ ThemeToggleInput.propTypes = {
|
||||
}
|
||||
|
||||
export default function ThemeSwitch() {
|
||||
const { darkMode, toggleDark } = useContext(Context)
|
||||
const { darkMode, toggleDark } = useDarkMode()
|
||||
const themeColor = darkMode ? '#1d2224' : '#e7eef4'
|
||||
|
||||
return (
|
||||
|
@ -1,53 +1,23 @@
|
||||
import React from 'react'
|
||||
import { render, fireEvent, cleanup } from '@testing-library/react'
|
||||
import Context from '../../store/createContext'
|
||||
import { render, fireEvent, cleanup, wait } from '@testing-library/react'
|
||||
import ThemeSwitch from './ThemeSwitch'
|
||||
|
||||
describe('ThemeSwitch', () => {
|
||||
afterEach(cleanup)
|
||||
const toggleDark = jest.fn()
|
||||
|
||||
it('renders correctly', () => {
|
||||
const { container } = render(
|
||||
<Context.Provider
|
||||
value={{ darkMode: false, toggleDark: () => toggleDark }}
|
||||
>
|
||||
<ThemeSwitch />
|
||||
</Context.Provider>
|
||||
)
|
||||
|
||||
const switchContainer = container.querySelector('aside')
|
||||
|
||||
expect(switchContainer).toBeInTheDocument()
|
||||
it('renders correctly', async () => {
|
||||
const { container } = render(<ThemeSwitch />)
|
||||
await wait(() => container.querySelector('aside'))
|
||||
expect(container.querySelector('aside')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('switches when it is dark', () => {
|
||||
const { container } = render(
|
||||
<Context.Provider
|
||||
value={{ darkMode: true, toggleDark: () => toggleDark }}
|
||||
>
|
||||
<ThemeSwitch />
|
||||
</Context.Provider>
|
||||
)
|
||||
|
||||
const toggle = container.querySelector('input')
|
||||
expect(toggle).toHaveAttribute('checked')
|
||||
})
|
||||
|
||||
it('checkbox can be changed', () => {
|
||||
const { container } = render(
|
||||
<Context.Provider
|
||||
value={{ darkMode: false, toggleDark: () => toggleDark }}
|
||||
>
|
||||
<ThemeSwitch />
|
||||
</Context.Provider>
|
||||
)
|
||||
it('checkbox can be changed', async () => {
|
||||
const { container } = render(<ThemeSwitch />)
|
||||
|
||||
const toggle = container.querySelector('input')
|
||||
const label = container.querySelector('label')
|
||||
expect(toggle.checked).toBeFalsy()
|
||||
fireEvent.click(label)
|
||||
fireEvent.change(toggle, { target: { checked: true } })
|
||||
expect(toggle.checked).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
@ -1,34 +1,27 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import { render, cleanup, wait } from '@testing-library/react'
|
||||
import { useStaticQuery } from 'gatsby'
|
||||
import Header from './Header'
|
||||
import Context from '../../store/createContext'
|
||||
import data from '../../../jest/__fixtures__/meta.json'
|
||||
|
||||
describe('Header', () => {
|
||||
beforeEach(() => {
|
||||
useStaticQuery.mockImplementation(() => {
|
||||
return {
|
||||
...data
|
||||
}
|
||||
return { ...data }
|
||||
})
|
||||
})
|
||||
|
||||
it('renders correctly', () => {
|
||||
const { container } = render(
|
||||
<Context.Provider value={{ darkMode: false, toggleDark: () => null }}>
|
||||
<Header />
|
||||
</Context.Provider>
|
||||
)
|
||||
afterEach(cleanup)
|
||||
|
||||
it('renders correctly', async () => {
|
||||
const { container } = render(<Header />)
|
||||
await wait(() => container.firstChild)
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('Availability can be hidden', () => {
|
||||
const { container } = render(
|
||||
<Context.Provider value={{ darkMode: false, toggleDark: () => null }}>
|
||||
<Header minimal={true} />
|
||||
</Context.Provider>
|
||||
)
|
||||
it('Availability can be hidden', async () => {
|
||||
const { container } = render(<Header minimal={true} />)
|
||||
await wait(() => container.querySelector('.availability'))
|
||||
expect(container.querySelector('.availability')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
32
src/hooks/use-dark-mode.js
Normal file
32
src/hooks/use-dark-mode.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { getLocationTimes } from '../utils/getLocationTimes'
|
||||
import { getCountry } from '../utils/getCountry'
|
||||
|
||||
export default function useDarkMode() {
|
||||
const store = typeof localStorage === 'undefined' ? null : localStorage
|
||||
const darkLocalStorage = store && store.getItem('darkMode')
|
||||
const [darkMode, setDarkMode] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
darkLocalStorage
|
||||
? darkLocalStorage === 'true'
|
||||
? setDarkMode(true)
|
||||
: setDarkMode(false)
|
||||
: checkTimes()
|
||||
|
||||
async function checkTimes() {
|
||||
const geolocation = await getCountry()
|
||||
const { sunset, sunrise } = getLocationTimes(geolocation)
|
||||
const now = new Date().getHours()
|
||||
const weWantItDarkTimes = now >= sunset || now <= sunrise
|
||||
weWantItDarkTimes && setDarkMode(true)
|
||||
}
|
||||
}, [darkLocalStorage])
|
||||
|
||||
function toggleDark() {
|
||||
setDarkMode(!darkMode)
|
||||
store && store.setItem('darkMode', !darkMode)
|
||||
}
|
||||
|
||||
return { darkMode, toggleDark }
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render } from '@testing-library/react'
|
||||
import { useStaticQuery } from 'gatsby'
|
||||
import { useMeta } from './use-meta'
|
||||
import data from '../../jest/__fixtures__/meta.json'
|
||||
|
||||
beforeEach(() => {
|
||||
useStaticQuery.mockImplementationOnce(() => ({ ...data }))
|
||||
})
|
||||
|
||||
describe('useMeta', () => {
|
||||
it('renders correctly', () => {
|
||||
const { social } = useMeta()
|
||||
const { container } = render(<div>{social.Twitter}</div>)
|
||||
|
||||
expect(container.textContent).toBe('https://twitter.com/kremalicious')
|
||||
})
|
||||
})
|
@ -1,74 +0,0 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Context from './createContext'
|
||||
import { getLocationTimes } from '../utils/getLocationTimes'
|
||||
import { getCountry } from '../utils/getCountry'
|
||||
|
||||
export default class AppProvider extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
darkMode: false,
|
||||
toggleDark: () => this.toggleDark(),
|
||||
geolocation: null
|
||||
}
|
||||
|
||||
store = typeof localStorage === 'undefined' ? null : localStorage
|
||||
|
||||
mounted = false
|
||||
|
||||
async componentDidMount() {
|
||||
this.mounted = true
|
||||
|
||||
const geolocation = await getCountry()
|
||||
this.setState({ geolocation })
|
||||
this.checkDark()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted = false
|
||||
}
|
||||
|
||||
setDark(darkMode) {
|
||||
this.mounted && this.setState({ darkMode })
|
||||
}
|
||||
|
||||
darkLocalStorageMode(darkLocalStorage) {
|
||||
darkLocalStorage === 'true' ? this.setDark(true) : this.setDark(false)
|
||||
}
|
||||
|
||||
//
|
||||
// All the checks to see if we should go dark or light
|
||||
//
|
||||
async checkTimes() {
|
||||
const { geolocation, darkMode } = this.state
|
||||
const { sunset, sunrise } = getLocationTimes(geolocation)
|
||||
const now = new Date().getHours()
|
||||
const weWantItDarkTimes = now >= sunset || now <= sunrise
|
||||
|
||||
!darkMode && weWantItDarkTimes ? this.setDark(true) : this.setDark(false)
|
||||
}
|
||||
|
||||
async checkDark() {
|
||||
const darkLocalStorage = await this.store.getItem('darkMode')
|
||||
|
||||
darkLocalStorage
|
||||
? this.darkLocalStorageMode(darkLocalStorage)
|
||||
: this.checkTimes()
|
||||
}
|
||||
|
||||
toggleDark = () => {
|
||||
this.setState({ darkMode: !this.state.darkMode })
|
||||
this.store && this.store.setItem('darkMode', !this.state.darkMode)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Context.Provider value={this.state}>
|
||||
{this.props.children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
import { render, fireEvent } from '@testing-library/react'
|
||||
import { LocalStorageMock } from '@react-mock/localstorage'
|
||||
import AppProvider from './AppProvider.jsx'
|
||||
import Context from './createContext.jsx'
|
||||
|
||||
describe('AppProvider', () => {
|
||||
it('renders correctly', () => {
|
||||
const { container } = render(<AppProvider>Hello</AppProvider>)
|
||||
|
||||
expect(container.firstChild.textContent).toBe('Hello')
|
||||
})
|
||||
|
||||
it('renders with dark detected in localStorage', () => {
|
||||
const { getByTestId } = render(
|
||||
<LocalStorageMock items={{ dark: 'true' }}>
|
||||
<AppProvider>
|
||||
<Context.Consumer>
|
||||
{state => (
|
||||
<button data-testid="toggle" onClick={() => state.toggleDark()}>
|
||||
Toggle
|
||||
</button>
|
||||
)}
|
||||
</Context.Consumer>
|
||||
</AppProvider>
|
||||
</LocalStorageMock>
|
||||
)
|
||||
fireEvent.click(getByTestId('toggle'))
|
||||
})
|
||||
|
||||
it('renders with light detected in localStorage', () => {
|
||||
render(
|
||||
<LocalStorageMock items={{ dark: 'false' }}>
|
||||
<AppProvider>Hello</AppProvider>
|
||||
</LocalStorageMock>
|
||||
)
|
||||
})
|
||||
})
|
@ -1,5 +0,0 @@
|
||||
import { createContext } from 'react'
|
||||
|
||||
const Context = createContext()
|
||||
|
||||
export default Context
|
Loading…
Reference in New Issue
Block a user