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

Merge pull request #175 from kremalicious/feature/photos-layout

new photos layout
This commit is contained in:
Matthias Kretschmann 2019-10-12 00:30:05 +02:00 committed by GitHub
commit d011dd5870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 312 additions and 275 deletions

View File

@ -19,6 +19,7 @@
"plugin:prettier/recommended", "plugin:prettier/recommended",
"plugin:react/recommended" "plugin:react/recommended"
], ],
"plugins": ["@typescript-eslint", "react"],
"rules": { "rules": {
"object-curly-spacing": ["error", "always"], "object-curly-spacing": ["error", "always"],
"react/prop-types": "off", "react/prop-types": "off",

View File

@ -1,13 +1,17 @@
module.exports = { module.exports = {
transform: { transform: {
'^.+\\.tsx?$': '<rootDir>/jest/jest-preprocess.js' '^.+\\.(tsx?|jsx?)$': 'ts-jest',
'^.+\\.jsx?$': '<rootDir>/jest/jest-preprocess.js'
}, },
testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx)$',
moduleNameMapper: { moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy', '.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy',
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/jest/__mocks__/file-mock.js', '<rootDir>/jest/__mocks__/file-mock.ts',
'\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.js' '\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.ts'
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'], testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'], transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'],
globals: { globals: {
@ -15,6 +19,6 @@ module.exports = {
}, },
testURL: 'http://localhost', testURL: 'http://localhost',
setupFiles: ['<rootDir>/jest/loadershim.js'], setupFiles: ['<rootDir>/jest/loadershim.js'],
setupFilesAfterEnv: ['<rootDir>/jest/setup-test-env.js'], setupFilesAfterEnv: ['<rootDir>/jest/setup-test-env.ts'],
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/@types/**/*'] collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/@types/**/*']
} }

View File

@ -1 +0,0 @@
module.exports = 'test-file-stub'

View File

@ -0,0 +1 @@
export default 'test-file-stub'

View File

@ -1,13 +1,13 @@
const React = require('react') import React from 'react'
const gatsby = jest.requireActual('gatsby') const gatsby = jest.requireActual('gatsby')
module.exports = { export default {
...gatsby, ...gatsby,
graphql: jest.fn(), graphql: jest.fn(),
Link: jest.fn().mockImplementation( Link: jest.fn().mockImplementation(
// these props are invalid for an `a` tag // these props are invalid for an `a` tag
({ ({
/* eslint-disable no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
activeClassName, activeClassName,
activeStyle, activeStyle,
getProps, getProps,
@ -15,7 +15,7 @@ module.exports = {
ref, ref,
replace, replace,
to, to,
/* eslint-enable no-unused-vars */ /* eslint-enable @typescript-eslint/no-unused-vars */
...rest ...rest
}) => }) =>
React.createElement('a', { React.createElement('a', {

View File

@ -1 +0,0 @@
module.exports = { ReactComponent: 'svg' }

View File

@ -0,0 +1,3 @@
const content = 'svg'
export const ReactComponent = content
export default content

View File

@ -1 +0,0 @@
require('@testing-library/jest-dom/extend-expect')

1
jest/setup-test-env.ts Normal file
View File

@ -0,0 +1 @@
import '@testing-library/jest-dom/extend-expect'

View File

@ -18,6 +18,7 @@
"lint:css": "stylelint 'src/**/*.{css,scss}'", "lint:css": "stylelint 'src/**/*.{css,scss}'",
"lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git,coverage}/**/*'", "lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git,coverage}/**/*'",
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,md,json,css,scss}'", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,md,json,css,scss}'",
"tsc": "tsc --noEmit",
"deploy": "./scripts/deploy.sh", "deploy": "./scripts/deploy.sh",
"new": "babel-node ./scripts/new.js" "new": "babel-node ./scripts/new.js"
}, },
@ -90,6 +91,7 @@
"@testing-library/react": "^9.2.0", "@testing-library/react": "^9.2.0",
"@types/classnames": "^2.2.9", "@types/classnames": "^2.2.9",
"@types/jest": "^24.0.18", "@types/jest": "^24.0.18",
"@types/lunr": "^2.3.2",
"@types/node": "^12.7.8", "@types/node": "^12.7.8",
"@types/react": "^16.9.4", "@types/react": "^16.9.4",
"@types/react-dom": "^16.9.1", "@types/react-dom": "^16.9.1",
@ -124,6 +126,7 @@
"stylelint-config-prettier": "^6.0.0", "stylelint-config-prettier": "^6.0.0",
"stylelint-config-standard": "^19.0.0", "stylelint-config-standard": "^19.0.0",
"stylelint-prettier": "^1.1.1", "stylelint-prettier": "^1.1.1",
"ts-jest": "^24.1.0",
"typescript": "^3.6.3", "typescript": "^3.6.3",
"why-did-you-update": "^1.0.6" "why-did-you-update": "^1.0.6"
}, },

View File

@ -1,23 +1,18 @@
interface CSSModule {
[className: string]: string
}
// type shims for CSS modules
declare module '*.module.scss' {
const cssModule: CSSModule
export = cssModule
}
declare module '*.module.css' { declare module '*.module.css' {
const cssModule: CSSModule const classes: { [key: string]: string }
export = cssModule export default classes
} }
/* eslint-disable-next-line @typescript-eslint/no-empty-interface */ declare module '*.module.scss' {
interface SvgrComponent const classes: { [key: string]: string }
extends React.StatelessComponent<React.SVGAttributes<SVGElement>> {} export default classes
}
declare module '*.svg' { declare module '*.svg' {
const value: SvgrComponent import * as React from 'react'
export default value export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement>
>
const src: string
export default src
} }

5
src/@types/node_modules.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module 'pigeon-maps'
declare module 'pigeon-marker'
declare module 'react-blockies'
declare module 'react-time'
declare module 'remark-react'

View File

@ -1 +0,0 @@
declare module 'pigeon-maps'

View File

@ -1 +0,0 @@
declare module 'pigeon-marker'

View File

@ -1 +0,0 @@
declare module 'react-blockies'

View File

@ -1 +0,0 @@
declare module 'react-time'

View File

@ -1 +0,0 @@
declare module 'remark-react'

View File

@ -23,12 +23,7 @@
} }
.postImage { .postImage {
@include breakoutviewport();
max-width: none;
display: block; display: block;
margin-top: $spacer * 1.5;
margin-bottom: $spacer * 1.5;
a & { a & {
position: relative; position: relative;

View File

@ -5,6 +5,7 @@ import Container from '../atoms/Container'
import PostTeaser from '../Post/PostTeaser' import PostTeaser from '../Post/PostTeaser'
import SearchResultsEmpty from './SearchResultsEmpty' import SearchResultsEmpty from './SearchResultsEmpty'
import styles from './SearchResults.module.scss' import styles from './SearchResults.module.scss'
import { GatsbyImageProps } from 'gatsby-image'
const query = graphql` const query = graphql`
query { query {
@ -29,6 +30,22 @@ const query = graphql`
} }
` `
interface Page {
slug: string
}
interface PostNode {
node: {
id: string
fields: { slug: string }
frontmatter: {
title: string
type: string
image: { childImageSharp: GatsbyImageProps }
}
}
}
export default function SearchResults({ export default function SearchResults({
searchQuery, searchQuery,
results, results,
@ -48,13 +65,13 @@ export default function SearchResults({
<Container> <Container>
{results.length > 0 ? ( {results.length > 0 ? (
<ul> <ul>
{results.map(page => {results.map((page: Page) =>
posts posts
.filter(post => post.node.fields.slug === page.slug) .filter((post: PostNode) => post.node.fields.slug === page.slug)
.map(({ node }: { node: any }) => ( .map((post: PostNode) => (
<PostTeaser <PostTeaser
key={page.slug} key={page.slug}
post={node} post={post.node}
toggleSearch={toggleSearch} toggleSearch={toggleSearch}
/> />
)) ))

View File

@ -1,45 +0,0 @@
import React from 'react'
import { render, fireEvent } from '@testing-library/react'
import Search from '.'
import { useStaticQuery } from 'gatsby'
describe('Search', () => {
beforeEach(() => {
useStaticQuery.mockImplementation(() => {
return {
allMarkdownRemark: {
edges: [
{
node: {
id: 'ddd',
frontmatter: {
title: 'Hello',
image: {
childImageSharp: 'hello'
}
},
fields: {
slug: '/hello/'
}
}
}
]
}
}
})
const portalRoot = document.createElement('div')
portalRoot.setAttribute('id', 'document')
document.body.appendChild(portalRoot)
})
it('can be opened', () => {
const { getByTitle, getByPlaceholderText } = render(<Search lng="en" />)
fireEvent.click(getByTitle('Search'))
fireEvent.change(getByPlaceholderText('Search everything'), {
target: { value: 'hello' }
})
fireEvent.click(getByTitle('Close search'))
})
})

View File

@ -1,12 +1,26 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import { CSSTransition } from 'react-transition-group' import { CSSTransition } from 'react-transition-group'
import lunr from 'lunr'
import SearchInput from './SearchInput' import SearchInput from './SearchInput'
import SearchButton from './SearchButton' import SearchButton from './SearchButton'
import SearchResults from './SearchResults' import SearchResults from './SearchResults'
import styles from './index.module.scss' import styles from './index.module.scss'
declare global {
interface Window {
__LUNR__: {
readonly [language: string]: {
readonly index: lunr.Index
readonly store: {
readonly [key: string]: any
}
}
}
}
}
function getSearchResults(query: string, lng: string) { function getSearchResults(query: string, lng: string) {
if (!query || !window.__LUNR__) return [] if (!query || !window.__LUNR__) return []
const lunrIndex = window.__LUNR__[lng] const lunrIndex = window.__LUNR__[lng]

View File

@ -21,7 +21,7 @@ export default function Alerts({
message message
}: { }: {
transactionHash: string | null transactionHash: string | null
message: { text: MessageChannel; status: string } | null message: { text?: MessageChannel; status?: string } | null
}) { }) {
const constructMessage = () => { const constructMessage = () => {
let messageOutput let messageOutput
@ -53,7 +53,7 @@ export default function Alerts({
return ( return (
<div <div
className={classes()} className={classes()}
dangerouslySetInnerHTML={{ __html: constructMessage() }} dangerouslySetInnerHTML={{ __html: `${constructMessage()}` }}
/> />
) )
} }

View File

@ -3,7 +3,7 @@ import { getFiat } from './utils'
import styles from './Conversion.module.scss' import styles from './Conversion.module.scss'
export default class Conversion extends PureComponent< export default class Conversion extends PureComponent<
{ amount: string }, { amount: number },
{ euro: string; dollar: string } { euro: string; dollar: string }
> { > {
state = { state = {

View File

@ -10,7 +10,7 @@ export default function InputGroup({
sendTransaction, sendTransaction,
selectedAccount selectedAccount
}: { }: {
amount: string amount: number
onAmountChange(target: any): void onAmountChange(target: any): void
sendTransaction(): void sendTransaction(): void
selectedAccount?: string | null selectedAccount?: string | null

View File

@ -1,5 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import Web3 from 'web3'
import InputGroup from './InputGroup' import InputGroup from './InputGroup'
import Alerts, { alertMessages } from './Alerts' import Alerts, { alertMessages } from './Alerts'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -9,26 +9,40 @@ const ONE_SECOND = 1000
const ONE_MINUTE = ONE_SECOND * 60 const ONE_MINUTE = ONE_SECOND * 60
const correctNetwork = 1 const correctNetwork = 1
export default class Web3Donation extends PureComponent { interface Web3DonationState {
netId: number
networkName: string
accounts: string[]
selectedAccount: string
amount: number
transactionHash: string
receipt: string
message: {
status?: string
text?: string
}
inTransaction: boolean
}
export default class Web3Donation extends PureComponent<
{ address: string },
Web3DonationState
> {
state = { state = {
netId: null, netId: 0,
networkName: null, networkName: '',
accounts: [], accounts: [''],
selectedAccount: null, selectedAccount: '',
amount: '0.01', amount: 0.01,
transactionHash: null, transactionHash: '',
receipt: null, receipt: '',
message: null, message: {},
inTransaction: false inTransaction: false
} }
static propTypes = { web3: Web3 = null
address: PropTypes.string interval: any = null
} networkInterval: any = null
web3 = null
interval = null
networkInterval = null
componentDidMount() { componentDidMount() {
this.initWeb3() this.initWeb3()
@ -47,10 +61,15 @@ export default class Web3Donation extends PureComponent {
this.web3 this.web3
? this.initAllTheTings() ? this.initAllTheTings()
: this.setState({ : this.setState({
message: { status: 'error', text: alertMessages().noWeb3 } message: {
status: 'error',
text: alertMessages().noWeb3
}
}) })
} catch (error) { } catch (error) {
this.setState({ message: { status: 'error', text: error } }) this.setState({
message: { status: 'error', text: error }
})
} }
} }
@ -106,7 +125,10 @@ export default class Web3Donation extends PureComponent {
}) })
} else { } else {
this.setState({ this.setState({
message: { status: 'error', text: alertMessages().noAccount } message: {
status: 'error',
text: alertMessages().noAccount
}
}) })
} }
} }
@ -132,16 +154,21 @@ export default class Web3Donation extends PureComponent {
}) })
}) })
.on('error', error => .on('error', error =>
this.setState({ message: { status: 'error', text: error } }) this.setState({
message: { status: 'error', text: error.message }
})
) )
.then(() => { .then(() => {
this.setState({ this.setState({
message: { status: 'success', text: alertMessages().success } message: {
status: 'success',
text: alertMessages().success
}
}) })
}) })
} }
onAmountChange = ({ target }) => { onAmountChange = ({ target }: { target: any }) => {
this.setState({ amount: target.value }) this.setState({ amount: target.value })
} }

View File

@ -1,8 +1,18 @@
import Web3 from 'web3' import Web3 from 'web3'
declare global {
interface Window {
ethereum: any
web3: Web3
}
interface Console {
[key: string]: any
}
}
export class Logger { export class Logger {
static dispatch(verb: any, ...args: any) { static dispatch(verb: any, ...args: any) {
// eslint-disable-next-line no-console
console[verb](...args) console[verb](...args)
} }

View File

@ -10,6 +10,7 @@ const exif = {
fstop: '7.2', fstop: '7.2',
shutterspeed: '200', shutterspeed: '200',
focalLength: '200', focalLength: '200',
lensModel: 'Hello',
exposure: '200', exposure: '200',
gps: { latitude: '52.4792516', longitude: '13.431609' } gps: { latitude: '52.4792516', longitude: '13.431609' }
} }

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { graphql, useStaticQuery } from 'gatsby' import { graphql, useStaticQuery } from 'gatsby'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import { useSiteMetadata } from '../../hooks/use-site-metadata' import { useSiteMetadata } from '../../hooks/use-site-metadata'
const query = graphql` const query = graphql`
@ -15,69 +15,55 @@ const query = graphql`
} }
` `
const createSchemaOrg = ( // const createSchemaOrg = (
blogURL: string, // blogURL: string,
title: string, // title: string,
postSEO: boolean, // postSEO: boolean,
postURL: string, // postURL: string,
image: string, // image: string,
description: string // description: string,
) => { // author?: string
const schemaOrgJSONLD = [ // ) => {
{ // const schemaOrgJSONLD: any = [
'@context': 'http://schema.org', // {
'@type': 'WebSite', // '@context': 'http://schema.org',
url: blogURL, // '@type': 'WebSite',
name: title // url: blogURL,
} // name: title
] // }
// ]
if (postSEO) { // if (postSEO) {
schemaOrgJSONLD.push( // schemaOrgJSONLD.push({
{ // '@context': 'http://schema.org',
'@context': 'http://schema.org', // '@type': 'BlogPosting',
'@type': 'BreadcrumbList', // author,
itemListElement: [ // publisher: author,
{ // url: postURL,
'@type': 'ListItem', // name: title,
position: 1, // headline: title,
item: { // image: {
'@id': postURL, // '@type': 'ImageObject',
name: title, // url: image
image // },
} // description
} // })
] // }
}, // return schemaOrgJSONLD
{ // }
'@context': 'http://schema.org',
'@type': 'BlogPosting',
url: blogURL,
name: title,
headline: title,
image: {
'@type': 'ImageObject',
url: image
},
description
}
)
}
return schemaOrgJSONLD
}
const MetaTags = ({ const MetaTags = ({
description, description,
image, image,
url, url,
schema, // schema,
postSEO, postSEO,
title title
}: { }: {
description: string description: string
image: string image: string
url: string url: string
schema: string // schema: string
postSEO: boolean postSEO: boolean
title: string title: string
}) => { }) => {
@ -96,7 +82,7 @@ const MetaTags = ({
<link rel="canonical" href={url} /> <link rel="canonical" href={url} />
{/* Schema.org tags */} {/* Schema.org tags */}
<script type="application/ld+json">{schema}</script> {/* <script type="application/ld+json">{schema}</script> */}
{/* OpenGraph tags */} {/* OpenGraph tags */}
<meta property="og:url" content={url} /> <meta property="og:url" content={url} />
@ -161,23 +147,22 @@ export default function SEO({
const blogURL = siteUrl const blogURL = siteUrl
const url = postSEO ? postURL : blogURL const url = postSEO ? postURL : blogURL
let schema = createSchemaOrg( // const schema = createSchemaOrg(
blogURL, // blogURL,
title, // title,
postSEO, // postSEO,
postURL, // postURL,
image, // image,
description // description,
) // author
// )
schema = JSON.stringify(schema)
return ( return (
<MetaTags <MetaTags
description={description} description={description}
image={image} image={image}
url={url} url={url}
schema={schema} // schema={(schema as unknown) as string}
postSEO={postSEO} postSEO={postSEO}
title={title} title={title}
/> />

View File

@ -7,24 +7,25 @@ export const fadeIn = {
} }
} }
export const moveInTop = { const transition = { type: 'spring' }
enter: {
const enter = {
y: 0, y: 0,
transition: { type: 'spring' } ...transition
}, }
export const moveInTop = {
...enter,
exit: { exit: {
y: '-2rem', y: '-2rem',
transition: { type: 'spring' } ...transition
} }
} }
export const moveInBottom = { export const moveInBottom = {
enter: { ...enter,
y: 0,
transition: { type: 'spring' }
},
exit: { exit: {
y: '2rem', y: '2rem',
transition: { type: 'spring' } ...transition
} }
} }

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import { useSiteMetadata } from '../../hooks/use-site-metadata' import { useSiteMetadata } from '../../hooks/use-site-metadata'
const TypekitScript = (typekitID: string) => ( const TypekitScript = (typekitID: string) => (

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import { Link } from 'gatsby' import { Link } from 'gatsby'
import Hamburger from '../atoms/Hamburger' import Hamburger from '../atoms/Hamburger'
import styles from './Menu.module.scss' import styles from './Menu.module.scss'

View File

@ -30,13 +30,7 @@ export default function Vcard() {
return ( return (
<div className={styles.vcard}> <div className={styles.vcard}>
<Img <Img className={styles.avatar} fixed={avatar} alt="avatar" />
className={styles.avatar}
fixed={avatar}
alt="avatar"
width="80"
height="80"
/>
<p className={styles.description}> <p className={styles.description}>
Blog of designer &amp; developer{' '} Blog of designer &amp; developer{' '}
<a className="fn" rel="author" href={uri}> <a className="fn" rel="author" href={uri}>

View File

@ -3,7 +3,6 @@ import { graphql, Link } from 'gatsby'
import PostImage from '../components/Post/PostImage' import PostImage from '../components/Post/PostImage'
import Page from '../templates/Page' import Page from '../templates/Page'
import styles from './goodies.module.scss' import styles from './goodies.module.scss'
import { FluidObject } from 'gatsby-image'
const page = { const page = {
frontmatter: { frontmatter: {
@ -13,22 +12,21 @@ const page = {
} }
} }
interface GoodieNode { interface Post {
id: string id: string
fields: { slug: string } fields: { slug: string }
frontmatter: { frontmatter: {
title: string title: string
image: { childImageSharp: { fluid: FluidObject } } image: { childImageSharp: any }
} }
} }
const GoodiesThumbs = ({ edges }: { edges: [{ node: GoodieNode }] }) => const GoodiesThumb = ({ post }: { post: Post }) => {
edges.map(({ node }: { node: GoodieNode }) => { const { title, image } = post.frontmatter
const { title, image } = node.frontmatter const { slug } = post.fields
const { slug } = node.fields
return ( return (
<article className={styles.goodie} key={node.id}> <article className={styles.goodie}>
{image && ( {image && (
<Link to={slug}> <Link to={slug}>
<h1 className={styles.title}>{title}</h1> <h1 className={styles.title}>{title}</h1>
@ -37,18 +35,20 @@ const GoodiesThumbs = ({ edges }: { edges: [{ node: GoodieNode }] }) =>
)} )}
</article> </article>
) )
}) }
export default function Goodies({ export default function Goodies({
data, data,
location location
}: { }: {
data: { goodies: { edges: [{ node: GoodieNode }] } } data: { goodies: { edges: [{ node: Post }] } }
location: Location location: Location
}) { }) {
return ( return (
<Page title={page.frontmatter.title} post={page} location={location}> <Page title={page.frontmatter.title} post={page} location={location}>
<GoodiesThumbs edges={data.goodies.edges} /> {data.goodies.edges.map(({ node }) => (
<GoodiesThumb key={node.id} post={node} />
))}
</Page> </Page>
) )
} }

View File

@ -2,25 +2,33 @@
@import 'mixins'; @import 'mixins';
.photos { .photos {
@include breakoutviewport; @include breakoutviewport--full;
display: flex; display: grid;
flex-wrap: wrap; grid-template-columns: 1fr 1fr;
justify-content: space-between; gap: $spacer;
padding-left: $spacer; padding-left: $spacer;
padding-right: $spacer; padding-right: $spacer;
@media (min-width: $screen-md) { @media (min-width: $screen-sm) {
padding-left: 0; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
padding-right: 0;
} }
} }
.photo { .photo {
flex: 0 0 48%;
margin-bottom: 4%;
a { a {
display: block; display: block;
} }
figure {
> div {
margin: 0;
}
}
figcaption {
font-size: $font-size-base;
padding-left: $spacer / 2;
padding-right: $spacer / 2;
}
} }

View File

@ -1,10 +1,8 @@
import React from 'react' import React from 'react'
import { graphql, Link } from 'gatsby' import { graphql, Link } from 'gatsby'
import Image from '../components/atoms/Image'
import Page from '../templates/Page' import Page from '../templates/Page'
import PostImage from '../components/Post/PostImage'
import styles from './photos.module.scss' import styles from './photos.module.scss'
import { FluidObject } from 'gatsby-image'
const page = { const page = {
frontmatter: { frontmatter: {
@ -13,39 +11,47 @@ const page = {
} }
} }
interface PhotoNode { interface Photo {
node: {
id: string id: string
fields: { slug: string } fields: { slug: string }
frontmatter: { frontmatter: {
title: string title: string
type: string type: string
image: { childImageSharp: { fluid: FluidObject } } image: { childImageSharp: any }
}
} }
} }
const PhotoThumbs = ({ edges }: { edges: PhotoNode[] }) => const PhotoThumb = ({ photo }: { photo: Photo }) => {
edges.map(({ node }) => { const { title, image } = photo.frontmatter
const { title, image } = node.frontmatter const { slug } = photo.fields
const { slug } = node.fields const { fluid } = image.childImageSharp
return ( return (
<article className={styles.photo} key={node.id}> <article className={styles.photo}>
{image && ( {image && (
<Link to={slug}> <Link to={slug}>
<Image fluid={image.childImageSharp.fluid} alt={title} /> <PostImage title={title} fluid={fluid} alt={title} />
</Link> </Link>
)} )}
</article> </article>
) )
}) }
interface PhotosData {
photos: {
edges: [
{
node: Photo
}
]
}
}
export default function Photos({ export default function Photos({
data, data,
location location
}: { }: {
data: { photos: { edges: PhotoNode[] } } data: PhotosData
location: Location location: Location
}) { }) {
return ( return (
@ -55,7 +61,9 @@ export default function Photos({
location={location} location={location}
section={styles.photos} section={styles.photos}
> >
<PhotoThumbs edges={data.photos.edges} /> {data.photos.edges.map(({ node }) => (
<PhotoThumb key={node.id} photo={node} />
))}
</Page> </Page>
) )
} }

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import SEO from '../components/atoms/SEO' import SEO from '../components/atoms/SEO'
import Layout from '../components/Layout' import Layout from '../components/Layout'
import styles from './Page.module.scss' import styles from './Page.module.scss'

View File

@ -6,7 +6,21 @@
padding-top: $spacer; padding-top: $spacer;
padding-bottom: $spacer * 3; padding-bottom: $spacer * 3;
> a {
display: block;
}
&:only-child { &:only-child {
padding-bottom: $spacer; padding-bottom: $spacer;
} }
} }
.postImageWrap {
@include breakoutviewport();
figure {
max-width: none;
margin-top: $spacer * 1.5;
margin-bottom: 0;
}
}

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import Helmet from 'react-helmet' import { Helmet } from 'react-helmet'
import { graphql } from 'gatsby' import { graphql } from 'gatsby'
import Layout from '../components/Layout' import Layout from '../components/Layout'
import PostImage from '../components/Post/PostImage' import PostImage from '../components/Post/PostImage'
@ -39,7 +39,9 @@ export default function Post({
{type === 'post' && <PostLead post={post} />} {type === 'post' && <PostLead post={post} />}
{type === 'photo' && <PostContent post={post} />} {type === 'photo' && <PostContent post={post} />}
{image && ( {image && (
<div className={styles.postImageWrap}>
<PostImage fluid={image.childImageSharp.fluid} alt={title} /> <PostImage fluid={image.childImageSharp.fluid} alt={title} />
</div>
)} )}
{image && image.fields && <Exif exif={image.fields.exif} />} {image && image.fields && <Exif exif={image.fields.exif} />}

View File

@ -11,6 +11,7 @@ import SEO from '../components/atoms/SEO'
import Pagination from '../components/molecules/Pagination' import Pagination from '../components/molecules/Pagination'
import Featured from '../components/molecules/Featured' import Featured from '../components/molecules/Featured'
import styles from './Posts.module.scss' import styles from './Posts.module.scss'
import stylesPost from './Post.module.scss'
export default function Posts({ export default function Posts({
data, data,
@ -36,11 +37,13 @@ export default function Posts({
{image && ( {image && (
<Link to={slug} title={title}> <Link to={slug} title={title}>
<div className={stylesPost.postImageWrap}>
<PostImage <PostImage
title={type === 'photo' ? title : null} title={type === 'photo' ? title : null}
fluid={image.childImageSharp.fluid} fluid={image.childImageSharp.fluid}
alt={title} alt={title}
/> />
</div>
</Link> </Link>
)} )}

View File

@ -1,18 +1,16 @@
{ {
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs", "module": "commonjs",
"target": "esnext", "target": "esnext",
"jsx": "preserve", "jsx": "react",
"lib": ["dom", "es2015", "es2017"], "lib": ["dom", "esnext"],
"strict": true, "allowSyntheticDefaultImports": true,
"noEmit": true,
"isolatedModules": true,
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "allowJs": true
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true
}, },
"exclude": ["node_modules", "public", ".cache", "*.js"], "exclude": ["node_modules", "public", ".cache", "*.js"],
"include": ["./src/**/*"] "include": ["./src/**/*", "./jest/**/*"]
} }