diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1cc9b258..f539344d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: push jobs: audit: runs-on: ubuntu-latest @@ -29,4 +29,44 @@ jobs: run: npm ci - name: Run tests run: npm test - + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Cache npm dependencies + uses: actions/cache@v2 + with: + path: ./.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Cache Gatsby cache folder + uses: actions/cache@v2 + with: + path: ./.cache + key: ${{ runner.os }}-cache-gatsby + restore-keys: | + ${{ runner.os }}-cache-gatsby + - name: Cache Gatsby build folder + uses: actions/cache@v2 + with: + path: ./public + key: ${{ runner.os }}-public-gatsby + restore-keys: | + ${{ runner.os }}-public-gatsby + - name: Install dependencies + run: npm ci + - name: Build site + run: npm run build + - name: Finished build stats + run: | + echo "Number of files in build folder:" + find ./public -type f | wc -l + echo "Size of build folder:" + du -hd 0 ./public + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@3.5.9 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: public # The folder the action should deploy. diff --git a/gatsby-node.js b/gatsby-node.js index ae99dd9a..8495867e 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -4,12 +4,10 @@ * See: https://www.gatsbyjs.org/docs/node-apis/ */ -const sermonFactory = require('./src/templates/sermon.factory.js'), - sermonsFactory = require('./src/templates/sermons.factory.js') +const sermonFactory = require('./src/templates/sermons.factory.js') exports.createPages = async ({graphql, actions}) => { const {createPage} = actions await sermonFactory.createPages(graphql, createPage) - await sermonsFactory.createPages(graphql, createPage) -} \ No newline at end of file +} diff --git a/gatsby-node.spec.js b/gatsby-node.spec.js index 69291a8b..bf2f76de 100644 --- a/gatsby-node.spec.js +++ b/gatsby-node.spec.js @@ -10,10 +10,7 @@ describe("gatsby node", () => { data: { avorg: { sermons: { - nodes: [], - pageInfo: { - hasNextPage: false - } + nodes: [{}] } } } @@ -25,6 +22,6 @@ describe("gatsby node", () => { await createPages(args) - expect(path.resolve).toBeCalledWith(`./src/templates/sermons.js`) + expect(path.resolve).toBeCalledWith(`./src/templates/sermons.list.js`) }) -}) \ No newline at end of file +}) diff --git a/package.json b/package.json index be824666..67edf0ad 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "gatsby-source-graphql": "^2.6.1", "gatsby-transformer-sharp": "^2.5.7", "lodash": "^4.17.19", + "moment": "^2.27.0", "node-sass": "^4.14.1", "prop-types": "^15.7.2", "react": "^16.12.0", @@ -38,7 +39,7 @@ ], "license": "0BSD", "scripts": { - "build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages", + "build": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build", "develop": "gatsby develop", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"", "start": "npm run develop", diff --git a/src/common.scss b/src/common.scss index abd20849..fe7041b7 100644 --- a/src/common.scss +++ b/src/common.scss @@ -1,9 +1,10 @@ $gray-night: #1c1c1d; -$gray-dark: #242426; -$gray-mid: #262627; -$gray-light: #747474; -$gray-lighter: #dbdbdb; -$gray-lightest: #e5e5e5; -$gray-bleached: #eee; +$gray-darkest: #242426; +$gray-darker: #262627; +$gray-dark: #747474; +$gray-mid: #dbdbdb; +$gray-light: #e5e5e5; +$gray-lighter: #eee; +$gray-lightest: #f7f7f7; $accent: #3DA88E; diff --git a/src/components/layout.scss b/src/components/layout.scss index f0af6170..140d00aa 100644 --- a/src/components/layout.scss +++ b/src/components/layout.scss @@ -5,7 +5,7 @@ } .organism-header { - background-color: $gray-dark; + background-color: $gray-darkest; } // CSS Reset, Etc: @@ -571,7 +571,6 @@ thead { td, th { text-align: left; - border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); font-feature-settings: "tnum"; -moz-font-feature-settings: "tnum"; -ms-font-feature-settings: "tnum"; diff --git a/src/components/molecules/pagination.scss b/src/components/molecules/pagination.scss index ebb3a4c1..e53d88d3 100644 --- a/src/components/molecules/pagination.scss +++ b/src/components/molecules/pagination.scss @@ -9,7 +9,7 @@ margin-right: .2em; a { - color: $gray-light; + color: $gray-dark; text-decoration: none; display: block; border-radius: 3px; @@ -17,7 +17,7 @@ } &:not(.active) a:hover { - background-color: $gray-bleached; + background-color: $gray-lighter; } } diff --git a/src/helpers/paths.js b/src/helpers/paths.js new file mode 100644 index 00000000..2e835ab9 --- /dev/null +++ b/src/helpers/paths.js @@ -0,0 +1 @@ +export const getSermonPath = (node, languageCode) => `/${languageCode}/sermons/${node.id}` diff --git a/src/helpers/queries.js b/src/helpers/queries.js index c8842307..b8f4db3c 100644 --- a/src/helpers/queries.js +++ b/src/helpers/queries.js @@ -1,30 +1,28 @@ const _ = require(`lodash`), constants = require(`../constants`) -// Must include pageInfo.hasNextPage and pageInfo.endCursor in generated queries -exports.getPages = async (graphql, query, queryArgs, pageSelector, cursor = null, pages = []) => { - queryArgs.cursor = cursor +const NUM_PER_PAGE = 250; - const result = await graphql(query, queryArgs), - page = _.get(result, pageSelector) - - pages.push(page) +exports.NUM_PER_PAGE = NUM_PER_PAGE; - const hasNextPage = _.get(page, 'pageInfo.hasNextPage') && - pages.length < constants.query_page_limit +// Required in query: +// pageInfo.hasNextPage +// pageInfo.endCursor +// aggregate.count +exports.getPages = async (graphql, query, queryArgs, pageSelector) => { + const result = await graphql(query, queryArgs), + page = _.get(result, pageSelector), + pages = [page], + pageCount = Math.ceil(_.get(page, 'aggregate.count') / NUM_PER_PAGE), + maxPage = Math.min(pageCount, constants.query_page_limit) - if (hasNextPage) { - const nextCursor = _.get(page, 'pageInfo.endCursor') + if (!page) return []; + if (!maxPage || maxPage < 1) return pages; - return exports.getPages( - graphql, - query, - queryArgs, - pageSelector, - nextCursor, - pages - ) - } + pages.push(...await Promise.all(_.range(1, maxPage).map(i => graphql(query, { + ...queryArgs, + cursor: Buffer.from(i * NUM_PER_PAGE + 1 + '').toString('base64') + }).then(result => _.get(result, pageSelector))))); return pages } diff --git a/src/pages/page-2.js b/src/pages/page-2.js index 666c23ef..3a611cdd 100644 --- a/src/pages/page-2.js +++ b/src/pages/page-2.js @@ -8,7 +8,7 @@ const SecondPage = () => ( Hi from the second page - Welcome to page 2 + Welcome to page two Go back to the homepage ) diff --git a/src/templates/sermon.factory.js b/src/templates/sermon.factory.js deleted file mode 100644 index a55a5151..00000000 --- a/src/templates/sermon.factory.js +++ /dev/null @@ -1,69 +0,0 @@ -const _ = require(`lodash`), - path = require(`path`), - constants = require(`../constants.js`), - queries = require(`../helpers/queries`) - -const query = ` -query loadPagesQuery($language: AVORG_Language!, $cursor: String) { - avorg { - sermons(language: $language, first: 50, after: $cursor) { - nodes { - title - id - persons { - name - imageWithFallback { - url(size: 50) - } - } - audioFiles { - url - } - recordingDate - description - } - pageInfo { - hasNextPage - endCursor - } - } - } -}` - -const getSermons = async (graphql, language = "ENGLISH") => { - const pages = await queries.getPages( - graphql, - query, - {language}, - 'data.avorg.sermons' - ) - - return pages.map(p => p.nodes).flat() -} - -const createSermon = async (createPage, node, pathPrefix) => { - const nodeId = _.get(node, 'id') - - await createPage({ - path: `${pathPrefix}/sermons/${nodeId}`, - component: path.resolve(`./src/templates/sermon.js`), - context: {node} - }) -} - -const createLanguageSermons = async (graphql, createPage, pathPrefix, language) => { - const sermons = await getSermons(graphql, language) - - await Promise.all(sermons.map(node => createSermon(createPage, node, pathPrefix))) -} - -exports.createPages = async (graphql, createPage) => { - await Promise.all(Object.keys(constants.languages).map((language) => { - return createLanguageSermons( - graphql, - createPage, - constants.languages[language].base_url, - language - ) - })) -}; diff --git a/src/templates/sermon.factory.spec.js b/src/templates/sermon.factory.spec.js deleted file mode 100644 index 2fffc43a..00000000 --- a/src/templates/sermon.factory.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; -import {describe, expect, it, jest} from "@jest/globals"; - -const factory = require('./sermon.factory.js') - -const testCreatePages = async (nodes = []) => { - const returnValue = { - data: { - avorg: { - sermons: { - nodes, - pageInfo: { - hasNextPage: false - } - } - } - } - }, - graphql = jest.fn(() => Promise.resolve(returnValue)), - createPage = jest.fn(); - - await factory.createPages(graphql, createPage) - - return {graphql, createPage} -} - -describe("sermon factory", () => { - it("gets English sermons", async () => { - const {graphql} = await testCreatePages() - - expect(graphql.mock.calls[0][1]).toStrictEqual({ - language: 'ENGLISH', - cursor: null - }) - }) - - it("creates English page", async () => { - const {createPage} = await testCreatePages([{}]) - - expect(createPage.mock.calls[0][0].path).toContain('en/sermons/') - }) - - it("passes sermon data", async () => { - const {createPage} = await testCreatePages([{ - 'title': 'the_title' - }]) - - expect(createPage.mock.calls[0][0].context.node).toEqual({'title': 'the_title'}) - }) - - it("uses sermon id in url", async () => { - const {createPage} = await testCreatePages([{id: 3}]) - - expect(createPage.mock.calls[0][0].path).toContain('en/sermons/3') - }) - - it("defines query variables", async () => { - const {graphql} = await testCreatePages() - - expect(graphql.mock.calls[0][0]).toContain("loadPagesQuery($language: AVORG_Language!, $cursor: String)") - }) -}) diff --git a/src/templates/sermon.js b/src/templates/sermons.detail.js similarity index 82% rename from src/templates/sermon.js rename to src/templates/sermons.detail.js index 14ac669b..7e4312c5 100644 --- a/src/templates/sermon.js +++ b/src/templates/sermons.detail.js @@ -1,12 +1,11 @@ import React from "react" import Layout from "../components/layout" -import './sermon.scss' +import './sermons.detail.scss' -export default function Sermon({ pageContext }) { +export default function SermonsDetail({ pageContext }) { const sermon = pageContext.node, - firstPresenter = sermon.persons[0], - imageSrc = firstPresenter && firstPresenter.imageWithFallback.url, - imageAlt = firstPresenter && firstPresenter.name; + imageSrc = sermon.imageWithFallback.url, + imageAlt = sermon.title; return ( diff --git a/src/templates/sermon.scss b/src/templates/sermons.detail.scss similarity index 100% rename from src/templates/sermon.scss rename to src/templates/sermons.detail.scss diff --git a/src/templates/sermons.factory.js b/src/templates/sermons.factory.js index 8e07220e..0d02ed86 100644 --- a/src/templates/sermons.factory.js +++ b/src/templates/sermons.factory.js @@ -4,16 +4,15 @@ const _ = require(`lodash`), queries = require(`../helpers/queries`) const query = ` -query loadPagesQuery($language: AVORG_Language!, $cursor: String) { +query loadPagesQuery($language: AVORG_Language!, $cursor: String, $first: Int!) { avorg { - sermons(language: $language, after: $cursor) { + sermons(language: $language, first: $first, after: $cursor, orderBy: {direction: DESC, field: CREATED_AT}) { nodes { - title + ...SermonsFragment + ...SermonFragment } pageInfo { hasNextPage - hasPreviousPage - startCursor endCursor } aggregate { @@ -21,6 +20,33 @@ query loadPagesQuery($language: AVORG_Language!, $cursor: String) { } } } +} +fragment SermonsFragment on AVORG_Recording { + id + title + imageWithFallback { + url(size: 50) + } + persons { + name + } + duration + recordingDate +} +fragment SermonFragment on AVORG_Recording { + id + title + persons { + name + } + audioFiles { + url + } + description + imageWithFallback { + url(size: 50) + } + recordingDate }` const createPagesByLang = async ( @@ -28,35 +54,43 @@ const createPagesByLang = async ( graphql, createPage ) => { - const pages = await queries.getPages( + const queryPages = await queries.getPages( graphql, query, - {language: langKey}, + {language: langKey, first: queries.NUM_PER_PAGE}, 'data.avorg.sermons' - ) + ), + nodes = queryPages.map(p => _.get(p, 'nodes')).flat(), + baseUrl = constants.languages[langKey].base_url, + sermonCount = nodes.length, + pageSize = 10, + pageCount = Math.ceil(sermonCount / pageSize), + pageNumbers = Array.from(Array(pageCount).keys()), + pages = pageNumbers.map(i => nodes.slice(i, i + pageSize)) - await Promise.all(pages.map((page, i) => { - const baseUrl = constants.languages[langKey].base_url, - nodes = _.get(page, 'nodes'), - sermonCount = _.get(page, 'aggregate.count', 0), - pageNumber = i + 1 - - return createPage({ - path: `${baseUrl}/sermons/page/${pageNumber}`, - component: path.resolve(`./src/templates/sermons.js`), + await Promise.all([ + ...pages.map((p, i) => createPage({ + path: `${baseUrl}/sermons/page/${i + 1}`, + component: path.resolve(`./src/templates/sermons.list.js`), context: { - nodes, pagination: { - total: Math.ceil(sermonCount / 10), - current: pageNumber - } + total: pageCount, + current: i + 1 + }, + lang: baseUrl, + nodes: p } - }) - })) + })), + ...nodes.map(node => createPage({ + path: `${baseUrl}/sermons/${_.get(node, 'id')}`, + component: path.resolve(`./src/templates/sermons.detail.js`), + context: {node} + })) + ]) } exports.createPages = async (graphql, createPage) => { const langKeys = Object.keys(constants.languages) await Promise.all(langKeys.map((key) => createPagesByLang(key, graphql, createPage))) -} +}; diff --git a/src/templates/sermons.factory.spec.js b/src/templates/sermons.factory.spec.js index a1b4db32..2641fb04 100644 --- a/src/templates/sermons.factory.spec.js +++ b/src/templates/sermons.factory.spec.js @@ -1,7 +1,8 @@ import React from "react"; +import {describe, expect, it, jest} from "@jest/globals"; import path from "path" -import _ from "lodash" -import constants from "../constants" +import constants from "../constants"; +import _ from "lodash"; jest.mock(`path`) @@ -20,6 +21,23 @@ const testCreatePages = async ({returnValue, returnValues} = {}) => { return {graphql, createPage} } +const testCreateSermons = async (nodes = []) => { + const returnValue = { + data: { + avorg: { + sermons: { + nodes, + pageInfo: { + hasNextPage: false + } + } + } + } + }; + + return testCreatePages({returnValue}) +} + const runWithMockedConstants = async (modifications, closure) => { const backup = {...constants} @@ -36,7 +54,60 @@ const expectAnyCallToMatch = (mock, callable) => { expect(matches.length).toBeGreaterThan(0) } -describe("sermons factory", () => { +describe("sermon detail pages creation", () => { + it("gets English sermons", async () => { + const {graphql} = await testCreateSermons() + + expect(graphql.mock.calls[0][1]).toStrictEqual({ + language: 'ENGLISH', + first: 250 + }) + }) + + it("creates English page", async () => { + const {createPage} = await testCreateSermons([{}]) + + expect(createPage.mock.calls[0][0].path).toContain('en/sermons/') + }) + + it("uses sermon id in url", async () => { + const {createPage} = await testCreateSermons([{id: 3}]) + + const allPaths = createPage.mock.calls.map(c => _.get(c[0], 'path')) + + expect(allPaths).toContain('en/sermons/3') + }) + + it("defines query variables", async () => { + const {graphql} = await testCreateSermons() + + const allQueries = graphql.mock.calls.map(c => c[0]) + + expect(allQueries).toEqual( + expect.arrayContaining([ + expect.stringContaining( + "language: $language" + ) + ]) + ) + }) + + it("passes node to page", async () => { + const {createPage} = await testCreateSermons([{'the': 'node'}]) + + const candidates = createPage.mock.calls.map(c => _.get(c[0], 'context.node.the')) + + expect(candidates).toContain('node') + }) + + it("gets component", async () => { + await testCreateSermons() + + expect(path.resolve).toBeCalledWith(`./src/templates/sermons.detail.js`) + }) +}) + +describe("sermon list pages creation", () => { it("has factory method", () => { expect(factory.createPages).toBeDefined() }) @@ -44,22 +115,32 @@ describe("sermons factory", () => { it("queries sermons type", async () => { const {graphql} = await testCreatePages() - expect(graphql.mock.calls[0][0]) - .toContain('sermons') + const all_queries = graphql.mock.calls.map(call => call[0]) + + expect(all_queries).toEqual( + expect.arrayContaining([ + expect.stringContaining('sermons') + ]) + ) }) it("creates English page", async () => { - const {createPage} = await testCreatePages() + const {createPage} = await testCreateSermons([{}]) expect(createPage.mock.calls[0][0].path) .toContain('en/sermons') }) it("creates Spanish page", async () => { - const {createPage} = await testCreatePages() + const {createPage} = await testCreateSermons([{}]) + + const allPaths = createPage.mock.calls.map(c => _.get(c[0], 'path')) - expect(createPage.mock.calls[1][0].path) - .toContain('es/sermons') + expect(allPaths).toEqual( + expect.arrayContaining([ + expect.stringContaining('es/sermons') + ]) + ) }) it("gets Spanish sermons", async () => { @@ -68,24 +149,16 @@ describe("sermons factory", () => { expect(graphql.mock.calls[1][1]) .toStrictEqual({ language: 'SPANISH', - cursor: null + first: 250 }) }) - it("passes sermons to createPage", async () => { - const sermons = ["sermons"], - returnValue = {data:{avorg:{sermons:{nodes:sermons}}}} - - const {createPage} = await testCreatePages({returnValue}) - - expect(createPage.mock.calls[0][0].context.nodes) - .toEqual(sermons) - }) - it("awaits page create", async () => { let done = false; - const graphql = jest.fn(() => Promise.resolve()), + const returnValue = _.set({}, 'data.avorg.sermons.nodes', [{}]) + + const graphql = jest.fn(() => Promise.resolve(returnValue)), createPage = jest.fn(async () => { await new Promise(r => setTimeout(r, 2)); done = true; @@ -97,85 +170,93 @@ describe("sermons factory", () => { }) it("gets component", async () => { - await testCreatePages() + await testCreateSermons([{},{}]) - expect(path.resolve).toBeCalledWith(`./src/templates/sermons.js`) + expect(path.resolve).toBeCalledWith(`./src/templates/sermons.list.js`) }) it("creates first English page", async () => { - const {createPage} = await testCreatePages({}) + const {createPage} = await testCreateSermons([{}]) - expect(createPage.mock.calls[0][0].path) - .toContain('en/sermons/page/1') + const all_paths = createPage.mock.calls.map(c => c[0].path) + + expect(all_paths).toEqual( + expect.arrayContaining([ + expect.stringContaining('en/sermons/page/1') + ]) + ) }) it("creates second English page", async () => { - const {createPage} = await testCreatePages({ - returnValues: [ - {data:{avorg:{sermons:{pageInfo:{hasNextPage:true}}}}} - ] - }) + const {createPage} = await testCreateSermons((new Array(20)).fill({})) - expectAnyCallToMatch(createPage, call => { - return call[0].path.includes('en/sermons/page/2') - }) + const allPaths = createPage.mock.calls.map(c => _.get(c[0], 'path')) + + expect(allPaths).toEqual( + expect.arrayContaining([ + expect.stringContaining( + 'en/sermons/page/2' + ) + ]) + ) }) it("uses cursors", async () => { const {graphql} = await testCreatePages({ returnValues: [ - {data:{avorg:{sermons:{pageInfo:{ - hasNextPage:true, endCursor:'the_cursor' - }}}}} + { + data: { + avorg: { + sermons: { + pageInfo: { + hasNextPage: true, endCursor: 'the_cursor' + } + } + } + } + } ] }) - expectAnyCallToMatch(graphql, call => { - return call[1].cursor === "the_cursor" - }) + const allCursors = graphql.mock.calls.map(c => _.get(c[1], 'cursor')) + + expect(typeof allCursors[0] === 'string' || allCursors[0] instanceof String) }) it("provides number of pages", async () => { - const {createPage} = await testCreatePages({ - returnValues: [ - {data:{avorg:{sermons:{aggregate:{count:50}}}}} - ] - }) + const {createPage} = await testCreateSermons((new Array(10 * 5)).fill({})) - expectAnyCallToMatch(createPage, call => { - return call[0].context.pagination.total === 5 - }) + const all_totals = createPage.mock.calls.map(c => _.get(c[0], 'context.pagination.total')) + + expect(all_totals).toContain(5) }) it("rounds number of pages up", async () => { - const {createPage} = await testCreatePages({ - returnValues: [ - {data:{avorg:{sermons:{aggregate:{count:55}}}}} - ] - }) + const {createPage} = await testCreateSermons((new Array(10 * 5.5)).fill({})) - expectAnyCallToMatch(createPage, call => { - return call[0].context.pagination.total === 6 - }) + const all_totals = createPage.mock.calls.map(c => _.get(c[0], 'context.pagination.total')) + + expect(all_totals).toContain(6) }) it("includes current page number", async () => { - const {createPage} = await testCreatePages() + const {createPage} = await testCreateSermons([{}]) - expectAnyCallToMatch(createPage, call => { - return call[0].context.pagination.current === 1 - }) + const all_current = createPage.mock.calls.map(c => _.get(c[0], 'context.pagination.current')) + + expect(all_current).toContain(1) }) it("respects dev query limits", async () => { await runWithMockedConstants({ - languages: {'ENGLISH': { base_url: 'en' },}, + languages: {'ENGLISH': {base_url: 'en'},}, query_page_limit: 10 }, async () => { const returnValue = {} _.set(returnValue, 'data.avorg.sermons.pageInfo.hasNextPage', true) _.set(returnValue, 'data.avorg.sermons.pageInfo.endCursor', 'the_cursor') + _.set(returnValue, 'data.avorg.sermons.aggregate.count', 10000) const returnValues = new Array(11).fill(returnValue); @@ -184,4 +265,40 @@ describe("sermons factory", () => { expect(graphql).toBeCalledTimes(10) }) }) + + it("passes nodes to page", async () => { + const {createPage} = await testCreatePages({ + returnValue: {data: {avorg: {sermons: {nodes: [{'the': 'node'}]}}}} + }) + + const all_nodes = createPage.mock.calls.map(call => _.get(call[0], 'context.nodes')) + + expect(all_nodes).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + expect.objectContaining({ + the: 'node' + }) + ]) + ]) + ) + }) + + it("passes lang to page", async () => { + const {createPage} = await testCreateSermons([{}]) + + const all_lang = createPage.mock.calls.map(c => _.get(c[0], 'context.lang')) + + expect(all_lang).toContain('en') + }) + + it("paginates by 10", async () => { + const nodes = (new Array(250)).fill({}) + + const {createPage} = await testCreateSermons(nodes) + + const allNodes = createPage.mock.calls.map(c => _.get(c[0], 'context.nodes')) + + expect(allNodes[0].length).toEqual(10) + }) }) diff --git a/src/templates/sermons.js b/src/templates/sermons.js deleted file mode 100644 index dd7fe178..00000000 --- a/src/templates/sermons.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react" -import Layout from "../components/layout" -import './sermons.scss' -import Pagination from "../components/molecules/pagination" - -export default function Sermons({ pageContext }) { - const sermons = pageContext.nodes, - {total, current} = pageContext.pagination; - - return ( - - {sermons.map((n, i) => {n.title})} - - - - ) -} \ No newline at end of file diff --git a/src/templates/sermons.list.js b/src/templates/sermons.list.js new file mode 100644 index 00000000..d53681e6 --- /dev/null +++ b/src/templates/sermons.list.js @@ -0,0 +1,27 @@ +import React from "react" +import Layout from "../components/layout" +import './sermons.list.scss' +import Pagination from "../components/molecules/pagination" +import moment from "moment"; +import {getSermonPath} from "../helpers/paths"; + +export default function SermonsList({pageContext}) { + const {nodes, lang} = pageContext, + {total, current} = pageContext.pagination; + + return ( + + + {nodes.map((n, i) => + + {n.title} + {n.persons.map(p => p.name).join(', ')} + {moment(n.recordingDate).fromNow()} + {new Date(1000 * n.duration).toISOString().substr(11, 8)} + )} + + + + + ) +} diff --git a/src/templates/sermons.list.scss b/src/templates/sermons.list.scss new file mode 100644 index 00000000..c1d214bb --- /dev/null +++ b/src/templates/sermons.list.scss @@ -0,0 +1,16 @@ +@import "../common"; + +.template-sermons { + img { + border-radius: 100%; + } + tr { + border-top: 2px solid $gray-lightest; + } + tr:nth-child(even) { + background-color: $gray-lightest; + } + td { + padding: .5em; + } +} diff --git a/src/templates/sermons.scss b/src/templates/sermons.scss deleted file mode 100644 index ff5d6d26..00000000 --- a/src/templates/sermons.scss +++ /dev/null @@ -1,2 +0,0 @@ -.template-sermons { -} \ No newline at end of file diff --git a/src/templates/sermons.spec.js b/src/templates/sermons.spec.js index fc708e35..90a82694 100644 --- a/src/templates/sermons.spec.js +++ b/src/templates/sermons.spec.js @@ -1,7 +1,7 @@ import {describe, expect, it, jest} from "@jest/globals"; import {render} from "@testing-library/react" import React from "react"; -import Sermons from "./sermons"; +import SermonsList from "./sermons.list"; import Layout from "../components/layout" jest.mock("../components/layout") @@ -10,7 +10,7 @@ Layout.mockImplementation(({children}) => <>{children}>) describe("sermons component", () => { it("hides next when unneeded", () => { - const {getByText} = render( { }) it("uses pagination total", () => { - const {getByText} = render() expect(getByText('42')).toBeDefined() }) it("highlights active page", () => { - const {getByText} = render() const listItem = getByText('3').closest('li') @@ -48,14 +60,20 @@ describe("sermons component", () => { }) it("links to pages", () => { - const {getByText} = render() expect(getByText('3').href).toContain('/en/sermons/page/3') }) -}) \ No newline at end of file +})
Welcome to page 2
Welcome to page two