From 3e341140e6b7ea37ab049c2f0882ab4b1f80e5ff Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Sun, 14 Apr 2019 20:14:40 +0200 Subject: [PATCH 1/2] remove icon duplication --- src/components/atoms/LinkIcon.jsx | 9 +++++++ src/components/atoms/LinkIcon.test.jsx | 9 +++++++ src/components/molecules/Networks.jsx | 33 ++------------------------ 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/components/atoms/LinkIcon.jsx b/src/components/atoms/LinkIcon.jsx index af1c407..9acfad8 100644 --- a/src/components/atoms/LinkIcon.jsx +++ b/src/components/atoms/LinkIcon.jsx @@ -7,6 +7,9 @@ import { ReactComponent as Info } from '../../images/info.svg' import { ReactComponent as Styleguide } from '../../images/styleguide.svg' import { ReactComponent as GitHub } from '../../images/github.svg' import { ReactComponent as Dribbble } from '../../images/dribbble.svg' +import { ReactComponent as Email } from '../../images/email.svg' +import { ReactComponent as Blog } from '../../images/blog.svg' +import { ReactComponent as Twitter } from '../../images/twitter.svg' const LinkIcon = ({ title, type, ...props }) => { let typeOrTitle = type ? type : title @@ -30,6 +33,12 @@ const LinkIcon = ({ title, type, ...props }) => { case 'styleguide': case 'Styleguide': return + case 'Email': + return + case 'Blog': + return + case 'Twitter': + return default: return null } diff --git a/src/components/atoms/LinkIcon.test.jsx b/src/components/atoms/LinkIcon.test.jsx index 5d33c62..1971160 100644 --- a/src/components/atoms/LinkIcon.test.jsx +++ b/src/components/atoms/LinkIcon.test.jsx @@ -24,6 +24,15 @@ describe('LinkIcon', () => { rerender() expect(container.firstChild.nodeName).toBe('svg') + + rerender() + expect(container.firstChild.nodeName).toBe('svg') + + rerender() + expect(container.firstChild.nodeName).toBe('svg') + + rerender() + expect(container.firstChild.nodeName).toBe('svg') }) it('does not render with unknown type', () => { diff --git a/src/components/molecules/Networks.jsx b/src/components/molecules/Networks.jsx index f08325e..cfa6332 100644 --- a/src/components/molecules/Networks.jsx +++ b/src/components/molecules/Networks.jsx @@ -4,13 +4,7 @@ import { StaticQuery, graphql } from 'gatsby' import posed from 'react-pose' import classNames from 'classnames' import { moveInTop } from '../atoms/Transitions' - -import { ReactComponent as Email } from '../../images/email.svg' -import { ReactComponent as Blog } from '../../images/blog.svg' -import { ReactComponent as Twitter } from '../../images/twitter.svg' -import { ReactComponent as GitHub } from '../../images/github.svg' -import { ReactComponent as Dribbble } from '../../images/dribbble.svg' - +import LinkIcon from '../atoms/LinkIcon' import icons from '../atoms/Icons.module.scss' import styles from './Networks.module.scss' @@ -28,29 +22,6 @@ const query = graphql` } ` -class NetworkIcon extends PureComponent { - static propTypes = { - title: PropTypes.string - } - - render() { - switch (this.props.title) { - case 'Email': - return - case 'Blog': - return - case 'Twitter': - return - case 'GitHub': - return - case 'Dribbble': - return - default: - return null - } - } -} - export default class Networks extends PureComponent { static propTypes = { minimal: PropTypes.bool, @@ -86,7 +57,7 @@ export default class Networks extends PureComponent { href={meta.social[key]} key={i} > - + {key} ))} From 683ca4f71064048678e06914582c6885169425ed Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 15 Apr 2019 00:25:54 +0200 Subject: [PATCH 2/2] more tests, hook for metadata --- src/components/atoms/Button.test.jsx | 10 +- src/components/atoms/HostnameCheck.test.jsx | 1 + src/components/molecules/Availability.jsx | 59 +++++------- .../molecules/Availability.test.jsx | 56 +++++++++++ src/components/molecules/LogoUnit.jsx | 20 ++-- src/components/molecules/LogoUnit.test.jsx | 27 ++++++ src/components/molecules/Networks.jsx | 94 ++++++++----------- src/components/molecules/Networks.module.scss | 2 +- src/components/molecules/Networks.test.jsx | 39 ++++++++ src/components/molecules/ThemeSwitch.jsx | 6 +- src/hooks/use-meta.js | 36 +++++++ src/hooks/use-meta.test.js | 20 ++++ 12 files changed, 260 insertions(+), 110 deletions(-) create mode 100644 src/components/molecules/Availability.test.jsx create mode 100644 src/components/molecules/LogoUnit.test.jsx create mode 100644 src/components/molecules/Networks.test.jsx create mode 100644 src/hooks/use-meta.js create mode 100644 src/hooks/use-meta.test.js diff --git a/src/components/atoms/Button.test.jsx b/src/components/atoms/Button.test.jsx index c9ead8e..483dc12 100644 --- a/src/components/atoms/Button.test.jsx +++ b/src/components/atoms/Button.test.jsx @@ -5,15 +5,17 @@ import Button from './Button' describe('Button', () => { it('renders correctly', () => { - const { getByText } = render() + const { container } = render() - expect(getByText('Hello').nodeName).toBe('A') + expect(container.firstChild.nodeName).toBe('A') + expect(container.firstChild).toBeInTheDocument() }) it('renders children', () => { const children = Hello World - const { getByText } = render() + const { container } = render() - expect(getByText('Hello World')).toBeDefined() + expect(container.firstChild.nodeName).toBe('A') + expect(container.firstChild).toBeInTheDocument() }) }) diff --git a/src/components/atoms/HostnameCheck.test.jsx b/src/components/atoms/HostnameCheck.test.jsx index 3b51cba..4764907 100644 --- a/src/components/atoms/HostnameCheck.test.jsx +++ b/src/components/atoms/HostnameCheck.test.jsx @@ -12,6 +12,7 @@ describe('HostnameCheck', () => { const allowedHosts = ['hello.com'] const { container } = render() expect(container.firstChild).toHaveTextContent('do a remix') + expect(container.firstChild).toBeInTheDocument() }) it('does not render if on correct hostname', () => { diff --git a/src/components/molecules/Availability.jsx b/src/components/molecules/Availability.jsx index f0cc23a..a7671c5 100644 --- a/src/components/molecules/Availability.jsx +++ b/src/components/molecules/Availability.jsx @@ -1,48 +1,31 @@ -import React, { PureComponent } from 'react' +import React from 'react' import PropTypes from 'prop-types' -import { StaticQuery, graphql } from 'gatsby' import posed from 'react-pose' import { fadeIn } from '../atoms/Transitions' +import { useMeta } from '../../hooks/use-meta' import styles from './Availability.module.scss' -const query = graphql` - query { - contentYaml { - availability { - status - available - unavailable - } - } - } -` - const Animation = posed.aside(fadeIn) -export default class Availability extends PureComponent { - static propTypes = { hide: PropTypes.bool } +const Availability = ({ hide }) => { + const { availability } = useMeta() + const { status, available, unavailable } = availability + const className = status + ? `${styles.availability} ${styles.available}` + : `${styles.availability}` + const html = status ? available : unavailable - render() { - return ( - { - const { availability } = data.contentYaml - const { status, available, unavailable } = availability - const className = status - ? `${styles.availability} ${styles.available}` - : `${styles.availability}` - const html = status ? available : unavailable - - return ( - !this.props.hide && ( - -

- - ) - ) - }} - /> + return ( + !hide && ( + +

+ ) - } + ) } + +Availability.propTypes = { + hide: PropTypes.bool +} + +export default Availability diff --git a/src/components/molecules/Availability.test.jsx b/src/components/molecules/Availability.test.jsx new file mode 100644 index 0000000..b552111 --- /dev/null +++ b/src/components/molecules/Availability.test.jsx @@ -0,0 +1,56 @@ +import React from 'react' +import { render } from 'react-testing-library' +import Availability from './Availability' +import { useStaticQuery } from 'gatsby' +import data from '../../../jest/__fixtures__/meta.json' + +describe('Availability', () => { + it('renders correctly from data file values', () => { + useStaticQuery.mockImplementation(() => { + return { ...data } + }) + const { container } = render() + expect(container.firstChild).toBeInTheDocument() + }) + + it('renders correctly when status: true', () => { + useStaticQuery.mockImplementationOnce(() => { + return { + contentYaml: { + availability: { + status: true, + available: 'I am available.', + unavailable: 'Not available.' + } + } + } + }) + + const { container } = render() + expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild).toHaveTextContent('I am available.') + }) + + it('renders correctly when status: false', () => { + useStaticQuery.mockImplementationOnce(() => { + return { + contentYaml: { + availability: { + status: false, + available: 'I am available.', + unavailable: 'Not available.' + } + } + } + }) + + const { container } = render() + expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild).toHaveTextContent('Not available.') + }) + + it('can be hidden', () => { + const { container } = render() + expect(container.firstChild).not.toBeInTheDocument() + }) +}) diff --git a/src/components/molecules/LogoUnit.jsx b/src/components/molecules/LogoUnit.jsx index 252d3b2..1f8c67b 100644 --- a/src/components/molecules/LogoUnit.jsx +++ b/src/components/molecules/LogoUnit.jsx @@ -22,14 +22,15 @@ export default class LogoUnit extends PureComponent { } Animation = posed.div(moveInBottom) + + wrapClasses = classNames([styles.logounit], { + [styles.minimal]: this.props.minimal + }) + nameClasses = classNames('p-name', [styles.title]) descriptionClasses = classNames('p-job-title', [styles.description]) render() { - let wrapClasses = classNames([styles.logounit], { - [styles.minimal]: this.props.minimal - }) - return ( +

-

{title.toLowerCase()}

-

+

+ {title.toLowerCase()} +

+

{tagline.toLowerCase()}

diff --git a/src/components/molecules/LogoUnit.test.jsx b/src/components/molecules/LogoUnit.test.jsx new file mode 100644 index 0000000..190f710 --- /dev/null +++ b/src/components/molecules/LogoUnit.test.jsx @@ -0,0 +1,27 @@ +import React from 'react' +import { render } from 'react-testing-library' +import { StaticQuery } from 'gatsby' +import LogoUnit from './LogoUnit' +import data from '../../../jest/__fixtures__/meta.json' + +beforeEach(() => { + StaticQuery.mockImplementationOnce(({ render }) => render({ ...data })) +}) + +describe('LogoUnit', () => { + it('renders correctly from data file values', () => { + const { title, tagline } = data.contentYaml + const { container, getByTestId } = render() + + expect(container.firstChild).toBeInTheDocument() + expect(getByTestId('logo-title')).toHaveTextContent(title.toLowerCase()) + expect(getByTestId('logo-tagline')).toHaveTextContent(tagline.toLowerCase()) + }) + + it('renders in minimal variant', () => { + const { container } = render() + + expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild.className).toMatch(/logounit minimal/) + }) +}) diff --git a/src/components/molecules/Networks.jsx b/src/components/molecules/Networks.jsx index cfa6332..4d64703 100644 --- a/src/components/molecules/Networks.jsx +++ b/src/components/molecules/Networks.jsx @@ -1,71 +1,51 @@ -import React, { PureComponent } from 'react' +import React from 'react' import PropTypes from 'prop-types' -import { StaticQuery, graphql } from 'gatsby' import posed from 'react-pose' import classNames from 'classnames' import { moveInTop } from '../atoms/Transitions' import LinkIcon from '../atoms/LinkIcon' +import { useMeta } from '../../hooks/use-meta' import icons from '../atoms/Icons.module.scss' import styles from './Networks.module.scss' -const query = graphql` - query { - contentYaml { - social { - Email - Blog - Twitter - GitHub - Dribbble - } - } - } -` +const Animation = posed.aside(moveInTop) -export default class Networks extends PureComponent { - static propTypes = { - minimal: PropTypes.bool, - hide: PropTypes.bool - } - - Animation = posed.aside(moveInTop) - - linkClasses = key => - classNames({ - 'u-url': key !== 'Email', - 'u-email': key === 'Email', - [styles.link]: true - }) - - wrapClasses = classNames([styles.networks], { - [styles.minimal]: this.props.minimal +const linkClasses = key => + classNames({ + 'u-url': key !== 'Email', + 'u-email': key === 'Email', + [styles.link]: true }) - render() { - return ( - { - const meta = data.contentYaml +const Networks = ({ small, hide }) => { + const { social } = useMeta() - return ( - !this.props.hide && ( - - {Object.keys(meta.social).map((key, i) => ( - - - {key} - - ))} - - ) - ) - }} - /> + const wrapClasses = classNames([styles.networks], { + [styles.small]: small + }) + + return ( + !hide && ( + + {Object.keys(social).map((key, i) => ( + + + {key} + + ))} + ) - } + ) } + +Networks.propTypes = { + small: PropTypes.bool, + hide: PropTypes.bool +} + +export default Networks diff --git a/src/components/molecules/Networks.module.scss b/src/components/molecules/Networks.module.scss index 412a10f..6695269 100644 --- a/src/components/molecules/Networks.module.scss +++ b/src/components/molecules/Networks.module.scss @@ -48,7 +48,7 @@ } } -.minimal { +.small { .link { padding: $spacer / 4; margin-left: $spacer / 4; diff --git a/src/components/molecules/Networks.test.jsx b/src/components/molecules/Networks.test.jsx new file mode 100644 index 0000000..9ef5674 --- /dev/null +++ b/src/components/molecules/Networks.test.jsx @@ -0,0 +1,39 @@ +import React from 'react' +import { render } from 'react-testing-library' +import { useStaticQuery } from 'gatsby' +import Networks from './Networks' +import data from '../../../jest/__fixtures__/meta.json' + +beforeEach(() => { + useStaticQuery.mockImplementationOnce(() => { + return { ...data } + }) +}) + +describe('Networks', () => { + it('renders correctly from data file values', () => { + const { social } = data.contentYaml + const { container, getByTestId } = render() + + expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild.nodeName).toBe('ASIDE') + expect(getByTestId('network-email').href).toBe(social.Email) + expect(getByTestId('network-blog').href).toBe(social.Blog + '/') + expect(getByTestId('network-twitter').href).toBe(social.Twitter) + expect(getByTestId('network-github').href).toBe(social.GitHub) + expect(getByTestId('network-dribbble').href).toBe(social.Dribbble) + }) + + it('renders correctly in small variant', () => { + const { container } = render() + + expect(container.firstChild).toBeInTheDocument() + expect(container.firstChild.className).toMatch(/networks small/) + }) + + it('can be hidden', () => { + const { container } = render() + + expect(container.firstChild).not.toBeInTheDocument() + }) +}) diff --git a/src/components/molecules/ThemeSwitch.jsx b/src/components/molecules/ThemeSwitch.jsx index 1443ccb..8437f5e 100644 --- a/src/components/molecules/ThemeSwitch.jsx +++ b/src/components/molecules/ThemeSwitch.jsx @@ -1,4 +1,4 @@ -import React, { PureComponent, Fragment } from 'react' +import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import Helmet from 'react-helmet' import posed from 'react-pose' @@ -44,7 +44,7 @@ export default class ThemeSwitch extends PureComponent { return ( {({ dark, toggleDark }) => ( - + <> @@ -55,7 +55,7 @@ export default class ThemeSwitch extends PureComponent { - + )} ) diff --git a/src/hooks/use-meta.js b/src/hooks/use-meta.js new file mode 100644 index 0000000..47b0d9a --- /dev/null +++ b/src/hooks/use-meta.js @@ -0,0 +1,36 @@ +import { useStaticQuery, graphql } from 'gatsby' + +const query = graphql` + query Meta { + contentYaml { + title + tagline + description + url + email + social { + Email + Blog + Twitter + GitHub + Dribbble + } + availability { + status + available + unavailable + } + gpg + addressbook + typekitID + matomoUrl + matomoSite + allowedHosts + } + } +` + +export const useMeta = () => { + const { contentYaml } = useStaticQuery(query) + return contentYaml +} diff --git a/src/hooks/use-meta.test.js b/src/hooks/use-meta.test.js new file mode 100644 index 0000000..7ca0541 --- /dev/null +++ b/src/hooks/use-meta.test.js @@ -0,0 +1,20 @@ +import React from 'react' +import { render } from 'react-testing-library' +import { useStaticQuery } from 'gatsby' +import { useMeta } from './use-meta' +import data from '../../jest/__fixtures__/meta.json' + +beforeEach(() => { + useStaticQuery.mockImplementationOnce(() => { + return { ...data } + }) +}) + +describe('useMeta', () => { + it('renders correctly', () => { + const { social } = useMeta() + const { container } = render(
{social.Twitter}
) + + expect(container.textContent).toBe('https://twitter.com/kremalicious') + }) +})