Skip to content

Commit

Permalink
Merge pull request #164 from datacamp/ljb/reduce-page-payload-with-se…
Browse files Browse the repository at this point in the history
…o-solve

[OGE-1829] feat: reduce amount of JSON in page payloads with SEO solution

- Drastically reducing the initial page size rendered in HTML / remove redundant JSON in the payload
- Ensuring links that can produce `404` are no longer rendered in the HTML
- This will solve the Google search console reporting for these pages
- Page load will improve too with less JSON sent "over the wire" and included as properties of the page
- The difference is `56kB` down to `16kB` of page payload (this is `1,600` lines of formatted JSON down to just `475` lines with the change)
  • Loading branch information
louiejakebell authored Sep 3, 2024
2 parents a73288a + eab3252 commit 68b971f
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 68 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RDocumentation provides an easy way to search the documentation for every versio

To run locally, clone this repo and `cd` into the directory.

Change the name of `.env.local.example` to `.env.local`. Since RDocumentation fetches data from the GitHub API, you'll need to create a GitHub [personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) (with `public_repo` scope) and add it as an environment variable.
Create a `.env.local` file at the root of the repository. Since RDocumentation fetches data from the GitHub API, you'll need to create a GitHub [personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token) (with `public_repo` scope) and add it as an environment variable.

```
GITHUB_TOKEN=YOUR_PERSONAL_ACCESS_TOKEN
Expand Down
82 changes: 57 additions & 25 deletions components/PackageFunctionList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Heading } from '@datacamp/waffles/heading';
import { mediaQuery } from '@datacamp/waffles/helpers';
import { Input } from '@datacamp/waffles/input';
import styled from '@emotion/styled';
import { useState } from 'react';

import ClickableCard from './ClickableCard';
Expand All @@ -17,54 +20,83 @@ type Props = {
packageVersion: string;
};

const Flex = styled.div({
[mediaQuery.aboveMedium]: {
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
},
display: 'block',
});

const InputWrapper = styled.div({
[mediaQuery.aboveMedium]: {
marginTop: 0,
width: 'max-content',
},
marginTop: '1.25rem',
width: '100%',
});

const GridContainer = styled.div({
[mediaQuery.aboveSmall]: {
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
},
[mediaQuery.aboveMedium]: {
gap: '1.25rem',
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
},
display: 'grid',
gap: '1rem',
gridTemplateColumns: 'repeat(1, minmax(0, 1fr))',
marginTop: '1.25rem',
});

export default function PackageFunctionList({
functions,
packageName,
packageVersion,
}: Props) {
const [searchInput, setSearchInput] = useState('');

let filteredFunctions = functions;
const cleanSearchValue = searchInput.trim().toLowerCase();

if (searchInput !== '') {
filteredFunctions = filteredFunctions.filter(
(f) =>
f.name.toLowerCase().indexOf(cleanSearchValue) > -1 ||
f.title.toLowerCase().indexOf(cleanSearchValue) > -1,
);
}
const filteredFunctions =
searchInput !== ''
? [...functions].filter(
(f) =>
f.name.toLowerCase().indexOf(cleanSearchValue) > -1 ||
f.title.toLowerCase().indexOf(cleanSearchValue) > -1,
)
: functions;

return (
<div>
<CourseAds />
<div className="block md:flex md:items-center md:justify-between">
<h2 className="text-2xl font-bold">{`Functions in ${packageName} (${packageVersion})`}</h2>
<div className="mt-5 dc-input md:mt-0">
<Flex>
<Heading>{`Functions in ${packageName} (${packageVersion})`}</Heading>
<InputWrapper>
<label className="sr-only" htmlFor="functionSearch">
Search functions
Search all functions
</label>
<Input
className="w-full md:w-72"
id="functionSearch"
name="functionSearch"
onChange={(event) => setSearchInput(event.target.value)}
placeholder="Search all functions"
value={searchInput}
/>
</div>
</div>
<div className="grid grid-cols-1 gap-4 mt-5 sm:grid-cols-2 md:grid-cols-3 md:gap-5">
{filteredFunctions.map((f) => (
</InputWrapper>
</Flex>
<GridContainer>
{filteredFunctions.map((fn) => (
<ClickableCard
description={f.title}
href={`/packages/${packageName}/versions/${packageVersion}/topics/${f.name}`}
id={f.id}
key={f.id}
name={f.name}
description={fn.title}
href={`/packages/${packageName}/versions/${packageVersion}/topics/${fn.name}`}
id={fn.id}
key={fn.id}
name={fn.name}
/>
))}
</div>
</GridContainer>
</div>
);
}
171 changes: 133 additions & 38 deletions pages/packages/[package]/versions/[version].tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { mediaQuery } from '@datacamp/waffles/helpers';
import { tokens } from '@datacamp/waffles/tokens';
import styled from '@emotion/styled';
import { graphql } from '@octokit/graphql';
import { GetServerSideProps } from 'next';
import Link from 'next/link';
import { useContext } from 'react';

import { ThemeContext } from '../../../_app';
import Layout from '../../../../components/Layout';
import PackageFunctionList from '../../../../components/PackageFunctionList';
import PackageReadme from '../../../../components/PackageReadme';
Expand Down Expand Up @@ -71,25 +76,85 @@ type Props = {
versionsArray: string[];
};

const Container = styled.div({
'@media (min-width: 768px)': {
marginTop: '3rem',
},
marginTop: '2rem',
});

const FlexContainer = styled.div({
[mediaQuery.aboveMedium]: {
flexDirection: 'row',
},
display: 'flex',
flexDirection: 'column',
});

const MainContent = styled.div({
[mediaQuery.aboveMedium]: {
paddingBottom: 0,
width: 'calc(100% * 2 / 3)',
},
paddingBottom: '2rem',
width: '100%',
});

const SidebarContainer = styled.div({
[mediaQuery.aboveMedium]: {
borderTop: 0,
paddingLeft: '2rem',
paddingTop: 0,
width: 'calc(100% / 3)',
},
paddingTop: '2rem',
width: '100%',
});

const WarningBox = styled.div({
'&[data-theme="dark"]': {
border: `2px solid ${tokens.colors.yellow}`,
},
'&[data-theme="light"]': {
border: `2px solid ${tokens.colors.navy}`,
},
a: {
color: 'white',
textDecoration: 'underline',
},
backgroundColor: tokens.colors.navy,
borderRadius: '0.375rem',
color: 'white',
marginBottom: '1.25rem',
padding: '1rem',
span: {
marginLeft: '0.75rem',
},
});

const AdditionalContent = styled.div({
'@media (min-width: 1024px)': {
borderTop: 0,
paddingTop: 0,
},
marginTop: '3rem',
paddingTop: '2rem',
width: '100%',
});

export default function PackageVersionPage({
metadata,
monthlyDownloads,
repository,
urls,
versionsArray,
}: Props) {
// construct link to the current package version
const { theme } = useContext(ThemeContext);
const linkToCurrentVersion = `https://rdocumentation.org${metadata.uri}`;

// get the latest version of the package
const latestVersion = versionsArray[0];

// get the number of downloads last month
const downloadsLastMonth = monthlyDownloads
? monthlyDownloads[monthlyDownloads.length - 1].downloads
: null;

// get the last published date
const lastPublished = metadata.release_date
? new Date(metadata.release_date)
: null;
Expand All @@ -100,33 +165,30 @@ export default function PackageVersionPage({
description={metadata.description}
title={metadata.pageTitle}
>
<div className="mt-8 md:mt-12">
<div className="block lg:flex">
<div className="w-full pb-8 lg:pb-0 lg:w-2/3 lg:pr-8">
{/* show a warning if looking at an older package version */}
<Container>
<FlexContainer>
<MainContent>
{metadata.version !== latestVersion && (
<div className="px-4 py-2 mb-5 text-white border-2 rounded-md border-dc-navy dark:border-dc-yellow bg-dc-navy">
<WarningBox data-theme={theme}>
<span>⚠️</span>
<span className="ml-3">{`There's a newer version (${latestVersion}) of this package.`}</span>{' '}
<span>{`There's a newer version (${latestVersion}) of this package.`}</span>
<Link href={metadata.canonicalLink}>
<a className="text-white underline">Take me there.</a>
<a>Take me there.</a>
</Link>
</div>
</WarningBox>
)}
{
metadata.readmemd
?
<PackageReadme readme={metadata.readmemd} />
:
<PackageReadMePlaceholder
packageName = {metadata.package_name}
version = {metadata.version}
title = {metadata.title}
description = {metadata.description}
{metadata.readmemd ? (
<PackageReadme readme={metadata.readmemd} />
) : (
<PackageReadMePlaceholder
description={metadata.description}
packageName={metadata.package_name}
title={metadata.title}
version={metadata.version}
/>
}
</div>
<div className="w-full pt-8 border-t lg:border-t-0 lg:w-1/3 lg:pt-0 lg:pl-8 lg:border-l">
)}
</MainContent>
<SidebarContainer>
<PackageSidebar
downloadsLastMonth={downloadsLastMonth}
githubUrl={urls.githubUrl}
Expand All @@ -142,20 +204,18 @@ export default function PackageVersionPage({
version={metadata.version}
versionsArray={versionsArray}
/>
</div>
</div>
<div className="w-full pt-8 mt-12 border-t lg:pt-0 lg:border-t-0 max-w-none">
{
metadata.topics?.length > 0
&&
</SidebarContainer>
</FlexContainer>
<AdditionalContent>
{metadata.topics?.length > 0 && (
<PackageFunctionList
functions={metadata.topics}
packageName={metadata.package_name}
packageVersion={metadata.version}
/>
}
</div>
</div>
)}
</AdditionalContent>
</Container>
</Layout>
);
}
Expand All @@ -169,6 +229,41 @@ export const getServerSideProps: GetServerSideProps = async ({
`${API_URL}/api/packages/${packageName}/versions/${version}`,
);
const metadata = await res.json();
const {
canonicalLink,
description,
license,
maintainer,
package: { type_id },
package_name,
pageTitle,
readmemd,
release_date,
title,
topics,
uri,
version: metadataVersion,
} = metadata;
const reducedMetaData = {
canonicalLink,
description,
license,
maintainer,
package: { type_id },
package_name,
pageTitle,
readmemd,
release_date,
title,
topics: topics.map((topic) => ({
id: topic.id,
name: topic.name,
title: topic.title,
})),
type_id,
uri,
version: metadataVersion,
};

// create an array of all package versions
const versionsArray = metadata.package.versions.map((v) => v.version);
Expand Down Expand Up @@ -226,7 +321,7 @@ export const getServerSideProps: GetServerSideProps = async ({

return {
props: {
metadata,
metadata: reducedMetaData,
monthlyDownloads,
repository,
urls: {
Expand Down
4 changes: 0 additions & 4 deletions styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
.prose * {
@apply dark:text-white;
}

.dc-input > div {
@apply w-full;
}
}

.list-view dl {
Expand Down

0 comments on commit 68b971f

Please sign in to comment.