Skip to content

Commit

Permalink
Create changelog permalinks (#622)
Browse files Browse the repository at this point in the history
* Create changelog permalinks

* Use HTML in feed

* Use plain in RSS

* Add logo to static folder
  • Loading branch information
dtuite authored Mar 11, 2022
1 parent 0f9e62b commit 131deff
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 44 deletions.
18 changes: 18 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ exports.createPages = async ({ graphql, actions }) => {
itemsPerPage: 20,
});

await createPagesFromQuery({
templatePath: './src/templates/ChangeSet.js',
query: CHANGELOG_QUERY,
resultName: 'result.edges',
actions,
graphql,
basePath: '/changelog/',
processor: ({ node }, component) => {
return {
path: `/changelog/${node.slug}/`,
component,
context: {
slug: node.slug,
},
};
},
});

await createLatestLegalNotices({
graphql,
actions,
Expand Down
103 changes: 76 additions & 27 deletions src/components/changelog/ChangeSet.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,100 @@
import React, { useState } from 'react';
import classnames from 'classnames';
import kebabCase from 'lodash/kebabCase';
import format from 'date-fns/format';
import { TextLink as Link } from 'components';

const ReleasedAt = ({ releasedAt }) => (
<time className="uppercase text-xs lg:text-sm leading-7 lg:pt-1 text-gray-500 font-bold md:w-32 lg:w-48 flex-shrink-0">
{format(new Date(releasedAt), 'MMM d, yyyy')}
</time>
);

const Title = ({ isCollapsible, toggleOpen, title, slug }) => {
let className = 'tracking-tight text-gray-900 text-base sm:text-xl';

if (isCollapsible) {
className = className + ' hover:underline';
}

const heading = (
<h3 className={className} id={slug}>
{title}
</h3>
);

if (!isCollapsible) return heading;

return (
<button onClick={toggleOpen} className="text-left">
{heading}
</button>
);
};

const CollapsiblePart = ({ description, isOpen, isCollapsible, slug }) => {
if (!description) return null;

let className = 'mt-6 prose max-w-none';
if (isCollapsible) {
className = classnames(className, {
'h-0 hidden': !isOpen,
});
}

return (
<div className={className}>
<div dangerouslySetInnerHTML={{ __html: description.html }} />

{isCollapsible && (
<div>
<Link to={`/changelog/${slug}`}>Permalink</Link>
</div>
)}
</div>
);
};

const Wrapper = ({ children, isCollapsible }) => {
if (isCollapsible) {
return (
<>
<hr className="w-full bg-gray-100 my-8" style={{ height: 1 }} />
<li className="mt-6 list-reset lg:flex items-start">
{children}
</li>
</>
);
}

return children;
};

const ChangeSet = ({
releasedAt,
description,
title,
isCollapsible = true,
slug,
}) => {
const [isOpen, setOpen] = useState(false);

const toggleOpen = () => setOpen(!isOpen);

return (
<>
<hr className="w-full bg-gray-100 my-8" style={{ height: 1 }} />
<li className="mt-6 list-reset lg:flex items-start">
<div className="md:flex">
<ReleasedAt releasedAt={releasedAt} />
<div>
<button onClick={toggleOpen} className="text-left">
<h3
className="tracking-tight text-gray-900 text-base sm:text-xl hover:underline"
id={kebabCase(title)}
>
{title}
</h3>
</button>

{description && (
<div
className={classnames('mt-6 prose max-w-none', {
'h-0 hidden': !isOpen,
})}
dangerouslySetInnerHTML={{ __html: description.html }}
/>
)}
</div>
<Wrapper isCollapsible={isCollapsible}>
<div className="md:flex">
<ReleasedAt releasedAt={releasedAt} />
<div>
<Title toggleOpen={toggleOpen} title={title} slug={slug} isCollapsible={isCollapsible} />
<CollapsiblePart
description={description}
isOpen={isOpen}
slug={slug}
isCollapsible={isCollapsible}
/>
</div>
</li>
</>
</div>
</Wrapper>
);
};

Expand Down
26 changes: 13 additions & 13 deletions src/gatsby/rssFeedPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,18 @@ const blogFeed = {

const changelogFeed = {
serialize: ({ query: { site, changeSets } }) => {
return changeSets.edges.map(({ node }) => {
return {
title: node.title,
date: node.releasedAt,
description: get(node, 'description.childMarkdownRemark.rawMarkdownBody'),
url: site.siteMetadata.siteUrl + `/changelog/`,
guid: site.siteMetadata.siteUrl + `/changelog/${node.slug}/`,
custom_elements: [{
'content:encoded': get(node, 'description.childMarkdownRemark.html'),
}],
};
});
return changeSets.edges.map(({ node }) => ({
title: node.title,
date: node.releasedAt,
// This is plain so we can push it out in Slack messages, which do not support HTML
// or markdown.
description: get(node, 'description.childMarkdownRemark.excerpt'),
url: site.siteMetadata.siteUrl + `/changelog/${node.slug}/`,
guid: site.siteMetadata.siteUrl + `/changelog/${node.slug}/`,
custom_elements: [{
'content:encoded': get(node, 'description.childMarkdownRemark.html'),
}],
}));
},

query: `
Expand All @@ -78,7 +78,7 @@ const changelogFeed = {
description {
childMarkdownRemark {
html
rawMarkdownBody
excerpt(pruneLength: 160, format: PLAIN)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/pageCreation/createListPagesFromQuery.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require(`path`);
const get = require('lodash/get');

const createPagesFromQuery = async ({
const createListPagesFromQuery = async ({
graphql,
templatePath,
query,
Expand Down Expand Up @@ -36,4 +36,4 @@ const createPagesFromQuery = async ({
});
};

module.exports = createPagesFromQuery;
module.exports = createListPagesFromQuery;
2 changes: 1 addition & 1 deletion src/queries/gatsbyNodeQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ module.exports.CHANGELOG_QUERY = `
) {
edges {
node {
title
slug
}
}
}
Expand Down
70 changes: 70 additions & 0 deletions src/templates/ChangeSet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { graphql } from 'gatsby';
import { SEO, SitewideHeader, SitewideFooter, TextLink as Link } from 'components';
import { ChangeSet } from 'components/changelog';
import { ArrowCircleLeftIcon } from '@heroicons/react/outline';
import get from 'lodash/get';

const ChangeSetPage = ({
data: {
site: {
siteMetadata: {
title: siteTitle,
},
},

changeSet,
},
}) => (
<>
<SEO
title={`Release: ${changeSet.title} | ${siteTitle}`}
description={get(changeSet, 'description.childMarkdownRemark.excerpt', 'Read the changelog')}
/>

<SitewideHeader />

<main className="max-w-5xl sm:max-w-4xl mx-auto px-8 pb-20 pt-8 lg:pb-28">
<ChangeSet
title={changeSet.title}
releasedAt={changeSet.releasedAt}
description={get(changeSet, 'description.childMarkdownRemark', {})}
isCollapsible={false}
/>

<div className="mt-6 list-reset lg:flex items-start">
<div className="md:w-32 lg:w-48 flex-shrink-0" />
<Link to="/changelog/" color="primary">
<ArrowCircleLeftIcon className="mr-1 h-6 w-6 inline" />
<span className="align-middle">Browse the full changlog</span>
</Link>
</div>
</main>

<SitewideFooter />
</>
);

export default ChangeSetPage;

export const pageQuery = graphql`
query ChangeSetBySlug($slug: String!) {
changeSet: contentfulChangeSet(slug: { eq: $slug }) {
title
releasedAt
slug
description {
childMarkdownRemark {
html
excerpt(pruneLength: 160, format: PLAIN)
}
}
}
site {
siteMetadata {
title
}
}
}
`;
5 changes: 4 additions & 1 deletion src/templates/ChangelogList.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ const Changelog = ({
/>

<ul className="container mt-12 mb-8">
{changeSets.map(({ node: { title, releasedAt, description } }) => (
{changeSets.map(({ node: { title, releasedAt, description, slug } }) => (
<ChangeSet
key={`${title} ${releasedAt}`}
title={title}
releasedAt={releasedAt}
description={description && description.childMarkdownRemark}
slug={slug}
isCollapsible={true}
/>
))}
</ul>
Expand Down Expand Up @@ -72,6 +74,7 @@ export const pageQuery = graphql`
node {
title
releasedAt
slug
description {
childMarkdownRemark {
html
Expand Down
Binary file added static/images/roadie-r-square-border-96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 131deff

Please sign in to comment.