From 796dfc64fa300e40546fd9115ff307e43e84d572 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 11 Nov 2019 23:46:47 +0100 Subject: [PATCH] remove AppProvider, replace with hooks --- README.md | 2 +- gatsby-browser.js | 12 --- gatsby-ssr.js | 9 --- src/components/atoms/ProjectImage.module.scss | 4 +- src/components/molecules/ThemeSwitch.jsx | 6 +- src/components/molecules/ThemeSwitch.test.jsx | 44 ++--------- src/components/organisms/Header.test.jsx | 27 +++---- src/hooks/use-dark-mode.js | 32 ++++++++ src/hooks/use-meta.test.js | 18 ----- src/store/AppProvider.jsx | 74 ------------------- src/store/AppProvider.test.jsx | 38 ---------- src/store/createContext.jsx | 5 -- 12 files changed, 55 insertions(+), 216 deletions(-) create mode 100644 src/hooks/use-dark-mode.js delete mode 100644 src/hooks/use-meta.test.js delete mode 100644 src/store/AppProvider.jsx delete mode 100644 src/store/AppProvider.test.jsx delete mode 100644 src/store/createContext.jsx diff --git a/README.md b/README.md index 1c010f9..b215a71 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/gatsby-browser.js b/gatsby-browser.js index 176fbad..c57d5a7 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -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 {element} -} - -wrapRootElement.propTypes = { - element: PropTypes.any -} - // Layout with Page Transitions export const wrapPageElement = wrapPageElementWithLayout diff --git a/gatsby-ssr.js b/gatsby-ssr.js index 967eba3..9d4bc63 100644 --- a/gatsby-ssr.js +++ b/gatsby-ssr.js @@ -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 = () => {bodyComponent} - replaceBodyHTMLString(renderToString()) -} - // Layout with Page Transitions export const wrapPageElement = wrapPageElementWithLayout diff --git a/src/components/atoms/ProjectImage.module.scss b/src/components/atoms/ProjectImage.module.scss index 17bda44..f0a1fc1 100644 --- a/src/components/atoms/ProjectImage.module.scss +++ b/src/components/atoms/ProjectImage.module.scss @@ -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) & { diff --git a/src/components/molecules/ThemeSwitch.jsx b/src/components/molecules/ThemeSwitch.jsx index fed715d..f33e7f7 100644 --- a/src/components/molecules/ThemeSwitch.jsx +++ b/src/components/molecules/ThemeSwitch.jsx @@ -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 ( diff --git a/src/components/molecules/ThemeSwitch.test.jsx b/src/components/molecules/ThemeSwitch.test.jsx index aa7bd38..ffced97 100644 --- a/src/components/molecules/ThemeSwitch.test.jsx +++ b/src/components/molecules/ThemeSwitch.test.jsx @@ -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( - toggleDark }} - > - - - ) - - const switchContainer = container.querySelector('aside') - - expect(switchContainer).toBeInTheDocument() + it('renders correctly', async () => { + const { container } = render() + await wait(() => container.querySelector('aside')) + expect(container.querySelector('aside')).toBeInTheDocument() }) - it('switches when it is dark', () => { - const { container } = render( - toggleDark }} - > - - - ) - - const toggle = container.querySelector('input') - expect(toggle).toHaveAttribute('checked') - }) - - it('checkbox can be changed', () => { - const { container } = render( - toggleDark }} - > - - - ) + it('checkbox can be changed', async () => { + const { container } = render() 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() }) }) diff --git a/src/components/organisms/Header.test.jsx b/src/components/organisms/Header.test.jsx index 4a696a9..38c87a4 100644 --- a/src/components/organisms/Header.test.jsx +++ b/src/components/organisms/Header.test.jsx @@ -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( - null }}> -
- - ) + afterEach(cleanup) + + it('renders correctly', async () => { + const { container } = render(
) + await wait(() => container.firstChild) expect(container.firstChild).toBeInTheDocument() }) - it('Availability can be hidden', () => { - const { container } = render( - null }}> -
- - ) + it('Availability can be hidden', async () => { + const { container } = render(
) + await wait(() => container.querySelector('.availability')) expect(container.querySelector('.availability')).not.toBeInTheDocument() }) }) diff --git a/src/hooks/use-dark-mode.js b/src/hooks/use-dark-mode.js new file mode 100644 index 0000000..f54ccd2 --- /dev/null +++ b/src/hooks/use-dark-mode.js @@ -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 } +} diff --git a/src/hooks/use-meta.test.js b/src/hooks/use-meta.test.js deleted file mode 100644 index f43d1b6..0000000 --- a/src/hooks/use-meta.test.js +++ /dev/null @@ -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(
{social.Twitter}
) - - expect(container.textContent).toBe('https://twitter.com/kremalicious') - }) -}) diff --git a/src/store/AppProvider.jsx b/src/store/AppProvider.jsx deleted file mode 100644 index d792d57..0000000 --- a/src/store/AppProvider.jsx +++ /dev/null @@ -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 ( - - {this.props.children} - - ) - } -} diff --git a/src/store/AppProvider.test.jsx b/src/store/AppProvider.test.jsx deleted file mode 100644 index 33c9962..0000000 --- a/src/store/AppProvider.test.jsx +++ /dev/null @@ -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(Hello) - - expect(container.firstChild.textContent).toBe('Hello') - }) - - it('renders with dark detected in localStorage', () => { - const { getByTestId } = render( - - - - {state => ( - - )} - - - - ) - fireEvent.click(getByTestId('toggle')) - }) - - it('renders with light detected in localStorage', () => { - render( - - Hello - - ) - }) -}) diff --git a/src/store/createContext.jsx b/src/store/createContext.jsx deleted file mode 100644 index 2852075..0000000 --- a/src/store/createContext.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react' - -const Context = createContext() - -export default Context