1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-23 01:29:41 +01:00

Merge pull request #134 from kremalicious/feature/code

New open source section
This commit is contained in:
Matthias Kretschmann 2019-05-27 00:20:46 +02:00 committed by GitHub
commit 41c151971c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 486 additions and 75 deletions

View File

@ -1,7 +1,6 @@
dist: xenial
language: node_js
node_js:
- '11'
node_js: node
cache:
directories:

View File

@ -17,6 +17,7 @@
- [🎉 Features](#-features)
- [⛵️ Lighthouse score](#-lighthouse-score)
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
- [🐱 GitHub repositories](#-github-repositories)
- [💅 Theme switcher](#-theme-switcher)
- [🏆 SEO component](#-seo-component)
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
@ -52,6 +53,18 @@ Gatsby automatically creates pages from each item in that file utilizing the [`P
- [`content/projects.yml`](content/projects.yml)
- [`src/templates/Project.jsx`](src/templates/Project.jsx)
### 🐱 GitHub repositories
The open source section at the bottom of the front page shows selected GitHub repositories, sourced from GitHub.
On build time, all my public repositories are fetched from GitHub, then filtered against the ones defined in `content/repos.yml`, sorted by the last push date, and provided via the page context of the front page.
If you want to know how, have a look at the respective components:
- [`gatsby-node.js`](gatsby-node.js)
- [`content/repos.yml`](content/repos.yml)
- [`src/components/molecules/Repository.jsx`](src/components/molecules/Repository.jsx)
### 💅 Theme switcher
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's local sunset and sunrise times. Uses Cloudflare's geo location HTTP header functionality.

View File

@ -1,34 +1,34 @@
title: Matthias Kretschmann
tagline: Designer & Developer
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
url: https://matthiaskretschmann.com
email: m@kretschmann.io
avatar: ../src/images/avatar.jpg
img: ../src/images/twitter-card.png
- title: Matthias Kretschmann
tagline: Designer & Developer
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
url: https://matthiaskretschmann.com
email: m@kretschmann.io
avatar: ../src/images/avatar.jpg
img: ../src/images/twitter-card.png
social:
Email: mailto:m@kretschmann.io
Blog: https://kremalicious.com
Twitter: https://twitter.com/kremalicious
GitHub: https://github.com/kremalicious
Dribbble: https://dribbble.com/kremalicious
social:
Email: mailto:m@kretschmann.io
Blog: https://kremalicious.com
Twitter: https://twitter.com/kremalicious
GitHub: https://github.com/kremalicious
Dribbble: https://dribbble.com/kremalicious
availability:
status: false
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Lets talk</a>!'
unavailable: Not available for new projects.
availability:
status: false
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Lets talk</a>!'
unavailable: Not available for new projects.
# Footer actions
gpg: https://kretschmann.io/pub.gpg
addressbook: /matthias-kretschmann.vcf
# Footer actions
gpg: https://kretschmann.io/pub.gpg
addressbook: /matthias-kretschmann.vcf
typekitID: dtg3zui
typekitID: dtg3zui
# Analytics tools
matomoUrl: https://analytics.kremalicious.com
matomoSite: 2
# Analytics tools
matomoUrl: https://analytics.kremalicious.com
matomoSite: 2
allowedHosts:
- matthiaskretschmann.com
- beta.matthiaskretschmann.com
- localhost
allowedHosts:
- matthiaskretschmann.com
- beta.matthiaskretschmann.com
- localhost

11
content/repos.yml Normal file
View File

@ -0,0 +1,11 @@
- user: kremalicious
repos:
- portfolio
- blog
- blowfish
- gatsby-plugin-matomo
- gatsby-redirect-from
- hyper-mac-pro
- appstorebadges
- kbdfun
- wp-icons-template

View File

@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs')
const yaml = require('js-yaml')
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8'))
const { title, description, url, matomoSite, matomoUrl } = meta
const { title, description, url, matomoSite, matomoUrl } = meta[0]
module.exports = {
siteMetadata: {

View File

@ -1,7 +1,15 @@
/* eslint-disable no-console */
const path = require('path')
const remark = require('remark')
const markdown = require('remark-parse')
const html = require('remark-html')
const axios = require('axios')
const fs = require('fs')
const yaml = require('js-yaml')
const reposYaml = yaml.load(fs.readFileSync('./content/repos.yml', 'utf8'))
const { performance } = require('perf_hooks')
const chalk = require('chalk')
function truncate(n, useWordBoundary) {
if (this.length <= n) {
@ -15,6 +23,59 @@ function truncate(n, useWordBoundary) {
)
}
//
// Get GitHub repos
//
async function getGithubRepos(data) {
const allRepos = await axios.get(
`https://api.github.com/users/${data.user}/repos?per_page=100`
)
const repos = allRepos.data
// filter by what's defined in content/repos.yml
.filter(({ name }) => data.repos.includes(name))
// sort by pushed to, newest first
.sort((a, b) => b.pushed_at.localeCompare(a.pushed_at))
return repos
}
//
// Get GitHub repos once and store for later build stages
//
let repos
exports.onPreBootstrap = async () => {
const t0 = performance.now()
try {
repos = await getGithubRepos(reposYaml[0])
const t1 = performance.now()
const ms = t1 - t0
const s = ((ms / 1000) % 60).toFixed(3)
console.log(
chalk.green('success ') + `getGithubRepos: ${repos.length} repos - ${s} s`
)
} catch (error) {
throw Error(error.message)
}
}
//
// Add repos to front page's context
//
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path === '/')
createPage({
...page,
context: {
...page.context,
repos
}
})
}
exports.onCreateNode = ({ node, actions }) => {
const { createNodeField } = actions

View File

@ -1,5 +1,5 @@
{
"contentYaml": {
"metaYaml": {
"title": "Matthias Kretschmann",
"tagline": "Designer & Developer",
"description": "Portfolio of web &amp; ui designer/developer hybrid Matthias Kretschmann.",

View File

@ -0,0 +1,102 @@
[
{
"id": 133283555,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzMyODM1NTU=",
"name": "portfolio",
"full_name": "kremalicious/portfolio",
"private": false,
"owner": {
"login": "kremalicious",
"id": 90316,
"node_id": "MDQ6VXNlcjkwMzE2",
"avatar_url": "https://avatars1.githubusercontent.com/u/90316?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kremalicious",
"html_url": "https://github.com/kremalicious",
"followers_url": "https://api.github.com/users/kremalicious/followers",
"following_url": "https://api.github.com/users/kremalicious/following{/other_user}",
"gists_url": "https://api.github.com/users/kremalicious/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kremalicious/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kremalicious/subscriptions",
"organizations_url": "https://api.github.com/users/kremalicious/orgs",
"repos_url": "https://api.github.com/users/kremalicious/repos",
"events_url": "https://api.github.com/users/kremalicious/events{/privacy}",
"received_events_url": "https://api.github.com/users/kremalicious/received_events",
"type": "User",
"site_admin": false
},
"html_url": "https://github.com/kremalicious/portfolio",
"description": "👔 Portfolio thingy, built with Gatsby",
"fork": false,
"url": "https://api.github.com/repos/kremalicious/portfolio",
"forks_url": "https://api.github.com/repos/kremalicious/portfolio/forks",
"keys_url": "https://api.github.com/repos/kremalicious/portfolio/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/kremalicious/portfolio/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/kremalicious/portfolio/teams",
"hooks_url": "https://api.github.com/repos/kremalicious/portfolio/hooks",
"issue_events_url": "https://api.github.com/repos/kremalicious/portfolio/issues/events{/number}",
"events_url": "https://api.github.com/repos/kremalicious/portfolio/events",
"assignees_url": "https://api.github.com/repos/kremalicious/portfolio/assignees{/user}",
"branches_url": "https://api.github.com/repos/kremalicious/portfolio/branches{/branch}",
"tags_url": "https://api.github.com/repos/kremalicious/portfolio/tags",
"blobs_url": "https://api.github.com/repos/kremalicious/portfolio/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/kremalicious/portfolio/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/kremalicious/portfolio/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/kremalicious/portfolio/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/kremalicious/portfolio/statuses/{sha}",
"languages_url": "https://api.github.com/repos/kremalicious/portfolio/languages",
"stargazers_url": "https://api.github.com/repos/kremalicious/portfolio/stargazers",
"contributors_url": "https://api.github.com/repos/kremalicious/portfolio/contributors",
"subscribers_url": "https://api.github.com/repos/kremalicious/portfolio/subscribers",
"subscription_url": "https://api.github.com/repos/kremalicious/portfolio/subscription",
"commits_url": "https://api.github.com/repos/kremalicious/portfolio/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/kremalicious/portfolio/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/kremalicious/portfolio/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/kremalicious/portfolio/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/kremalicious/portfolio/contents/{+path}",
"compare_url": "https://api.github.com/repos/kremalicious/portfolio/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/kremalicious/portfolio/merges",
"archive_url": "https://api.github.com/repos/kremalicious/portfolio/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/kremalicious/portfolio/downloads",
"issues_url": "https://api.github.com/repos/kremalicious/portfolio/issues{/number}",
"pulls_url": "https://api.github.com/repos/kremalicious/portfolio/pulls{/number}",
"milestones_url": "https://api.github.com/repos/kremalicious/portfolio/milestones{/number}",
"notifications_url": "https://api.github.com/repos/kremalicious/portfolio/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/kremalicious/portfolio/labels{/name}",
"releases_url": "https://api.github.com/repos/kremalicious/portfolio/releases{/id}",
"deployments_url": "https://api.github.com/repos/kremalicious/portfolio/deployments",
"created_at": "2018-05-13T23:53:31Z",
"updated_at": "2019-05-26T19:34:49Z",
"pushed_at": "2019-05-26T19:35:10Z",
"git_url": "git://github.com/kremalicious/portfolio.git",
"ssh_url": "git@github.com:kremalicious/portfolio.git",
"clone_url": "https://github.com/kremalicious/portfolio.git",
"svn_url": "https://github.com/kremalicious/portfolio",
"homepage": "https://matthiaskretschmann.com",
"size": 135135,
"stargazers_count": 55,
"watchers_count": 55,
"language": "JavaScript",
"has_issues": true,
"has_projects": false,
"has_downloads": true,
"has_wiki": false,
"has_pages": false,
"forks_count": 5,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 1,
"license": {
"key": "mit",
"name": "MIT License",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit",
"node_id": "MDc6TGljZW5zZTEz"
},
"forks": 5,
"open_issues": 1,
"watchers": 55,
"default_branch": "master"
}
]

View File

@ -23,6 +23,7 @@
"new": "babel-node ./scripts/new.js"
},
"dependencies": {
"axios": "^0.18.0",
"classnames": "^2.2.6",
"file-saver": "^2.0.1",
"gatsby": "^2.7.1",
@ -63,6 +64,7 @@
"babel-eslint": "^10.0.1",
"babel-jest": "^24.7.1",
"babel-preset-gatsby": "^0.1.11",
"chalk": "^2.4.2",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
"eslint-loader": "^2.1.2",

View File

@ -16,7 +16,7 @@ import styles from './Layout.module.scss'
const query = graphql`
query {
contentYaml {
metaYaml {
allowedHosts
}
}
@ -41,7 +41,7 @@ export default class Layout extends PureComponent {
<StaticQuery
query={query}
render={data => {
const { allowedHosts } = data.contentYaml
const { allowedHosts } = data.metaYaml
return (
<>

View File

@ -5,7 +5,7 @@
width: 100%;
color: $brand-cyan;
text-align: center;
border-radius: .25rem;
border-radius: $border-radius;
padding: $spacer / 4 $spacer / 2;
transition-property: all;
background: rgba(#fff, .15);

View File

@ -10,6 +10,7 @@ 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'
import { ReactComponent as Star } from '../../images/star.svg'
const LinkIcon = ({ title, type, ...props }) => {
let typeOrTitle = type ? type : title
@ -39,6 +40,8 @@ const LinkIcon = ({ title, type, ...props }) => {
return <Blog {...props} />
case 'Twitter':
return <Twitter {...props} />
case 'star':
return <Star {...props} />
default:
return null
}

View File

@ -5,7 +5,7 @@ import { StaticQuery, graphql } from 'gatsby'
const query = graphql`
query {
contentYaml {
metaYaml {
title
tagline
description
@ -75,7 +75,7 @@ export default class SEO extends PureComponent {
query={query}
render={data => {
const { project } = this.props
const meta = data.contentYaml
const meta = data.metaYaml
const title = (project && project.title) || null
const description =
(project && project.fields.excerpt) || meta.description

View File

@ -19,7 +19,7 @@ const TypekitScript = typekitID => (
const query = graphql`
query {
contentYaml {
metaYaml {
typekitID
}
}
@ -29,7 +29,7 @@ const Typekit = () => (
<StaticQuery
query={query}
render={data => {
const { typekitID } = data.contentYaml
const { typekitID } = data.metaYaml
return (
typekitID && (

View File

@ -5,7 +5,7 @@ import vCard from 'vcf'
const query = graphql`
query {
contentYaml {
metaYaml {
title
tagline
description
@ -37,7 +37,7 @@ export default class Vcard extends PureComponent {
<StaticQuery
query={query}
render={data => {
const meta = data.contentYaml
const meta = data.metaYaml
const handleAddressbookClick = e => {
e.preventDefault()

View File

@ -24,14 +24,14 @@ describe('Vcard', () => {
})
it('combined vCard download process finishes', async () => {
await init(data.contentYaml)
await init(data.metaYaml)
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
})
it('vCard can be constructed', async () => {
const vcard = await constructVcard(
'data:image/jpeg;base64,00',
data.contentYaml
data.metaYaml
)
expect(vcard).toBeDefined()
})

View File

@ -1,7 +1,7 @@
@import 'variables';
.availability {
border-radius: .25rem;
border-radius: $border-radius;
color: $text-color-light;
z-index: 2;
padding: $spacer / 2;

View File

@ -14,7 +14,7 @@ describe('Availability', () => {
it('renders correctly when status: true', () => {
useStaticQuery.mockImplementationOnce(() => {
return {
contentYaml: {
metaYaml: {
availability: {
status: true,
available: 'I am available.',
@ -32,7 +32,7 @@ describe('Availability', () => {
it('renders correctly when status: false', () => {
useStaticQuery.mockImplementationOnce(() => {
return {
contentYaml: {
metaYaml: {
availability: {
status: false,
available: 'I am available.',

View File

@ -9,7 +9,7 @@ import styles from './LogoUnit.module.scss'
const query = graphql`
query {
contentYaml {
metaYaml {
title
tagline
}
@ -35,7 +35,7 @@ export default class LogoUnit extends PureComponent {
<StaticQuery
query={query}
render={data => {
const { title, tagline } = data.contentYaml
const { title, tagline } = data.metaYaml
return (
<div className={this.wrapClasses}>

View File

@ -10,7 +10,7 @@ beforeEach(() => {
describe('LogoUnit', () => {
it('renders correctly from data file values', () => {
const { title, tagline } = data.contentYaml
const { title, tagline } = data.metaYaml
const { container, getByTestId } = render(<LogoUnit />)
expect(container.firstChild).toBeInTheDocument()

View File

@ -10,7 +10,7 @@ beforeEach(() => {
describe('Networks', () => {
it('renders correctly from data file values', () => {
const { social } = data.contentYaml
const { social } = data.metaYaml
const { container, getByTestId } = render(<Networks />)
expect(container.firstChild).toBeInTheDocument()

View File

@ -9,7 +9,7 @@
@media (min-width: $projectImageMaxWidth) {
max-width: $projectImageMaxWidth;
border-radius: .25rem;
border-radius: $border-radius;
overflow: hidden;
}

View File

@ -15,7 +15,7 @@
padding: $spacer / 4;
text-align: center;
background: rgba(#fff, .15);
border-radius: .25rem;
border-radius: $border-radius;
border: .05rem solid transparent;
color: $brand-grey-light;
font-size: $font-size-small;

View File

@ -0,0 +1,45 @@
import React from 'react'
import PropTypes from 'prop-types'
import LinkIcon from '../atoms/LinkIcon'
import styles from './Repository.module.scss'
const Repository = ({ repo }) => {
const { name, description, html_url, homepage, stargazers_count } = repo
// for blog & portfolio and if there's no homepage, use github url
// else use homepage field
const repoLink =
name === 'blog' || name === 'portfolio' || !homepage ? html_url : homepage
return (
<div className={styles.repo}>
<h1 className={styles.repoTitle}>
<a href={repoLink}>{name}</a>
</h1>
<p>{description}</p>
<p className={styles.meta}>
{name === 'portfolio' || name === 'blog'
? null
: homepage && (
<a href={homepage}>
<LinkIcon title="website" /> Learn more
</a>
)}
<a href={html_url}>
<LinkIcon title="github" /> GitHub
</a>
<a href={`${html_url}/stargazers`}>
<LinkIcon title="star" /> {stargazers_count}
</a>
</p>
</div>
)
}
Repository.propTypes = {
repo: PropTypes.object.isRequired
}
export default Repository

View File

@ -0,0 +1,59 @@
@import 'variables';
.repo {
padding: $spacer;
border-radius: $border-radius;
background: rgba(#fff, .15);
box-shadow: 0 3px 5px rgba($brand-main, .1),
0 5px 16px rgba($brand-main, .1);
display: flex;
flex-wrap: wrap;
align-items: flex-start;
:global(.dark) & {
background: darken($body-background-color--dark, 1%);
box-shadow: 0 3px 5px rgba(darken($brand-main, 20%), .1),
0 5px 16px rgba(darken($brand-main, 20%), .1);
}
> * {
width: 100%;
}
p {
font-size: $font-size-small;
}
p:last-child {
margin: 0;
}
}
.repoTitle {
font-size: $font-size-h4;
margin-bottom: $spacer / 2;
}
.meta {
font-size: $font-size-small;
align-self: flex-end;
display: flex;
justify-content: space-between;
a {
display: inline-block;
color: $brand-grey-light;
font-variant-numeric: lining-nums;
&:hover,
&:focus {
color: $brand-cyan;
}
}
svg {
fill: currentColor;
width: $font-size-mini;
height: $font-size-mini;
}
}

View File

@ -0,0 +1,40 @@
import React from 'react'
import { render } from 'react-testing-library'
import Repository from './Repository'
import repos from '../../../jest/__fixtures__/repos.json'
describe('Repository', () => {
it('renders correctly', () => {
const { container } = render(<Repository repo={repos[0]} />)
expect(container.firstChild).toBeInTheDocument()
})
it('uses html_url as main link for portfolio & blog', () => {
const repo1 = {
name: 'portfolio',
html_url: 'html_url'
}
const { container } = render(<Repository repo={repo1} />)
expect(container.querySelector('h1 > a').getAttribute('href')).toBe(
repo1.html_url
)
})
it('renders homepage link when provided', () => {
const repo = {
name: 'Hello',
homepage: 'hello'
}
const { container } = render(<Repository repo={repo} />)
expect(container.querySelectorAll('p:last-child a').length).toBe(3)
})
it('renders no link without homepage', () => {
const repo = { name: 'Hello' }
const { container } = render(<Repository repo={repo} />)
expect(container.querySelectorAll('p:last-child a').length).toBe(2)
})
})

View File

@ -14,7 +14,7 @@ const query = graphql`
bugs
}
contentYaml {
metaYaml {
title
url
gpg
@ -68,7 +68,7 @@ export default class Footer extends PureComponent {
query={query}
render={data => {
const pkg = data.portfolioJson
const meta = data.contentYaml
const meta = data.metaYaml
return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
}}

View File

@ -10,7 +10,7 @@ import styles from './Header.module.scss'
const query = graphql`
query {
contentYaml {
metaYaml {
availability {
status
}
@ -30,7 +30,7 @@ export default class Header extends PureComponent {
<StaticQuery
query={query}
render={data => {
const meta = data.contentYaml
const meta = data.metaYaml
let headerClasses = classNames([styles.header], {
[styles.minimal]: minimal

View File

@ -0,0 +1,24 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Repository from '../molecules/Repository'
import styles from './Repositories.module.scss'
export default class Repositories extends PureComponent {
static propTypes = {
repos: PropTypes.array.isRequired
}
render() {
return (
<section className={styles.section}>
<h1 className={styles.sectionTitle}>Open Source Projects</h1>
<div className={styles.repos}>
{this.props.repos.map(repo => (
<Repository key={repo.name} repo={repo} />
))}
</div>
</section>
)
}
}

View File

@ -0,0 +1,29 @@
@import 'variables';
.section {
max-width: calc(#{$projectImageMaxWidth} - #{$spacer * 2});
margin: $spacer * 3 auto 0 auto;
padding-left: $spacer;
padding-right: $spacer;
}
.sectionTitle {
font-size: $font-size-h3;
margin-bottom: $spacer * 2;
text-align: center;
}
.repos {
display: grid;
grid-template-columns: 1fr;
grid-gap: $spacer * 2;
@media (min-width: $screen-sm) {
grid-template-columns: 1fr 1fr;
grid-gap: $spacer * 2;
}
@media (min-width: $screen-md) {
grid-gap: $spacer * 3;
}
}

View File

@ -0,0 +1,11 @@
import React from 'react'
import { render } from 'react-testing-library'
import Repositories from './Repositories'
import repos from '../../../jest/__fixtures__/repos.json'
describe('Repositories', () => {
it('renders correctly', () => {
const { container } = render(<Repositories repos={repos} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -2,7 +2,7 @@ import { useStaticQuery, graphql } from 'gatsby'
const query = graphql`
query Meta {
contentYaml {
metaYaml {
title
tagline
description
@ -31,6 +31,6 @@ const query = graphql`
`
export const useMeta = () => {
const { contentYaml } = useStaticQuery(query)
return contentYaml
const { metaYaml } = useStaticQuery(query)
return metaYaml
}

3
src/images/star.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<polygon points="22.136 19.625 32 12 20 12 16 0 12 12 0 12 9.875 19.594 6 32 16.016 24.32 26.008 32"/>
</svg>

After

Width:  |  Height:  |  Size: 196 B

View File

@ -16,8 +16,16 @@ describe('Home', () => {
...projectImageFiles
}
const pageContext = {
repos: [
{
name: 'Hello'
}
]
}
it('renders correctly from data file values', () => {
const { container } = render(<Home data={data} />)
const { container } = render(<Home data={data} pageContext={pageContext} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -5,6 +5,7 @@ import SEO from '../components/atoms/SEO'
import ProjectImage from '../components/molecules/ProjectImage'
import { ReactComponent as Images } from '../images/images.svg'
import styles from './index.module.scss'
import Repositories from '../components/organisms/Repositories'
function getImageCount(images, slug) {
let array = []
@ -19,12 +20,12 @@ function getImageCount(images, slug) {
export default class Home extends PureComponent {
static propTypes = {
data: PropTypes.object,
location: PropTypes.object
data: PropTypes.object.isRequired,
pageContext: PropTypes.object.isRequired
}
render() {
const { data } = this.props
const { data, pageContext } = this.props
const projects = data.allProjectsYaml.edges
const images = data.projectImageFiles.edges
@ -56,6 +57,8 @@ export default class Home extends PureComponent {
)
})}
</div>
<Repositories repos={pageContext.repos} />
</>
)
}

View File

@ -5,22 +5,23 @@ import { getLocationTimes } from '../utils/getLocationTimes'
import { getCountry } from '../utils/getCountry'
export default class AppProvider extends PureComponent {
static propTypes = {
children: PropTypes.any.isRequired
}
state = {
dark: false,
toggleDark: () => this.toggleDark(),
geolocation: null
}
static propTypes = {
children: PropTypes.any.isRequired
}
store = typeof localStorage === 'undefined' ? null : localStorage
mounted = false
async componentDidMount() {
this.mounted = true
const geolocation = await getCountry()
this.setState({ geolocation })
this.checkDark()
@ -30,16 +31,12 @@ export default class AppProvider extends PureComponent {
this.mounted = false
}
setDark() {
this.mounted && this.setState({ dark: true })
}
setLight() {
this.mounted && this.setState({ dark: false })
setDark(dark) {
this.mounted && this.setState({ dark })
}
darkLocalStorageMode(darkLocalStorage) {
darkLocalStorage === 'true' ? this.setDark() : this.setLight()
darkLocalStorage === 'true' ? this.setDark(true) : this.setDark(false)
}
//
@ -51,7 +48,7 @@ export default class AppProvider extends PureComponent {
const now = new Date().getHours()
const weWantItDarkTimes = now >= sunset || now <= sunrise
!dark && weWantItDarkTimes ? this.setDark() : this.setLight()
!dark && weWantItDarkTimes ? this.setDark(true) : this.setDark(false)
}
async checkDark() {

View File

@ -67,6 +67,7 @@ $color-headings--dark: $brand-main-light;
/////////////////////////////////////
$spacer: ($font-size-base * $line-height);
$border-radius: .25rem;
// Responsive breakpoints
/////////////////////////////////////