Skip to content

Commit 68b971f

Browse files
Merge pull request #164 from datacamp/ljb/reduce-page-payload-with-seo-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)
2 parents a73288a + eab3252 commit 68b971f

File tree

4 files changed

+191
-68
lines changed

4 files changed

+191
-68
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RDocumentation provides an easy way to search the documentation for every versio
1616

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

19-
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.
19+
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.
2020

2121
```
2222
GITHUB_TOKEN=YOUR_PERSONAL_ACCESS_TOKEN

components/PackageFunctionList.tsx

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { Heading } from '@datacamp/waffles/heading';
2+
import { mediaQuery } from '@datacamp/waffles/helpers';
13
import { Input } from '@datacamp/waffles/input';
4+
import styled from '@emotion/styled';
25
import { useState } from 'react';
36

47
import ClickableCard from './ClickableCard';
@@ -17,54 +20,83 @@ type Props = {
1720
packageVersion: string;
1821
};
1922

23+
const Flex = styled.div({
24+
[mediaQuery.aboveMedium]: {
25+
alignItems: 'center',
26+
display: 'flex',
27+
justifyContent: 'space-between',
28+
},
29+
display: 'block',
30+
});
31+
32+
const InputWrapper = styled.div({
33+
[mediaQuery.aboveMedium]: {
34+
marginTop: 0,
35+
width: 'max-content',
36+
},
37+
marginTop: '1.25rem',
38+
width: '100%',
39+
});
40+
41+
const GridContainer = styled.div({
42+
[mediaQuery.aboveSmall]: {
43+
gridTemplateColumns: 'repeat(2, minmax(0, 1fr))',
44+
},
45+
[mediaQuery.aboveMedium]: {
46+
gap: '1.25rem',
47+
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
48+
},
49+
display: 'grid',
50+
gap: '1rem',
51+
gridTemplateColumns: 'repeat(1, minmax(0, 1fr))',
52+
marginTop: '1.25rem',
53+
});
54+
2055
export default function PackageFunctionList({
2156
functions,
2257
packageName,
2358
packageVersion,
2459
}: Props) {
2560
const [searchInput, setSearchInput] = useState('');
26-
27-
let filteredFunctions = functions;
2861
const cleanSearchValue = searchInput.trim().toLowerCase();
29-
30-
if (searchInput !== '') {
31-
filteredFunctions = filteredFunctions.filter(
32-
(f) =>
33-
f.name.toLowerCase().indexOf(cleanSearchValue) > -1 ||
34-
f.title.toLowerCase().indexOf(cleanSearchValue) > -1,
35-
);
36-
}
62+
const filteredFunctions =
63+
searchInput !== ''
64+
? [...functions].filter(
65+
(f) =>
66+
f.name.toLowerCase().indexOf(cleanSearchValue) > -1 ||
67+
f.title.toLowerCase().indexOf(cleanSearchValue) > -1,
68+
)
69+
: functions;
3770

3871
return (
3972
<div>
4073
<CourseAds />
41-
<div className="block md:flex md:items-center md:justify-between">
42-
<h2 className="text-2xl font-bold">{`Functions in ${packageName} (${packageVersion})`}</h2>
43-
<div className="mt-5 dc-input md:mt-0">
74+
<Flex>
75+
<Heading>{`Functions in ${packageName} (${packageVersion})`}</Heading>
76+
<InputWrapper>
4477
<label className="sr-only" htmlFor="functionSearch">
45-
Search functions
78+
Search all functions
4679
</label>
4780
<Input
48-
className="w-full md:w-72"
4981
id="functionSearch"
5082
name="functionSearch"
5183
onChange={(event) => setSearchInput(event.target.value)}
5284
placeholder="Search all functions"
5385
value={searchInput}
5486
/>
55-
</div>
56-
</div>
57-
<div className="grid grid-cols-1 gap-4 mt-5 sm:grid-cols-2 md:grid-cols-3 md:gap-5">
58-
{filteredFunctions.map((f) => (
87+
</InputWrapper>
88+
</Flex>
89+
<GridContainer>
90+
{filteredFunctions.map((fn) => (
5991
<ClickableCard
60-
description={f.title}
61-
href={`/packages/${packageName}/versions/${packageVersion}/topics/${f.name}`}
62-
id={f.id}
63-
key={f.id}
64-
name={f.name}
92+
description={fn.title}
93+
href={`/packages/${packageName}/versions/${packageVersion}/topics/${fn.name}`}
94+
id={fn.id}
95+
key={fn.id}
96+
name={fn.name}
6597
/>
6698
))}
67-
</div>
99+
</GridContainer>
68100
</div>
69101
);
70102
}

pages/packages/[package]/versions/[version].tsx

Lines changed: 133 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import { mediaQuery } from '@datacamp/waffles/helpers';
2+
import { tokens } from '@datacamp/waffles/tokens';
3+
import styled from '@emotion/styled';
14
import { graphql } from '@octokit/graphql';
25
import { GetServerSideProps } from 'next';
36
import Link from 'next/link';
7+
import { useContext } from 'react';
48

9+
import { ThemeContext } from '../../../_app';
510
import Layout from '../../../../components/Layout';
611
import PackageFunctionList from '../../../../components/PackageFunctionList';
712
import PackageReadme from '../../../../components/PackageReadme';
@@ -71,25 +76,85 @@ type Props = {
7176
versionsArray: string[];
7277
};
7378

79+
const Container = styled.div({
80+
'@media (min-width: 768px)': {
81+
marginTop: '3rem',
82+
},
83+
marginTop: '2rem',
84+
});
85+
86+
const FlexContainer = styled.div({
87+
[mediaQuery.aboveMedium]: {
88+
flexDirection: 'row',
89+
},
90+
display: 'flex',
91+
flexDirection: 'column',
92+
});
93+
94+
const MainContent = styled.div({
95+
[mediaQuery.aboveMedium]: {
96+
paddingBottom: 0,
97+
width: 'calc(100% * 2 / 3)',
98+
},
99+
paddingBottom: '2rem',
100+
width: '100%',
101+
});
102+
103+
const SidebarContainer = styled.div({
104+
[mediaQuery.aboveMedium]: {
105+
borderTop: 0,
106+
paddingLeft: '2rem',
107+
paddingTop: 0,
108+
width: 'calc(100% / 3)',
109+
},
110+
paddingTop: '2rem',
111+
width: '100%',
112+
});
113+
114+
const WarningBox = styled.div({
115+
'&[data-theme="dark"]': {
116+
border: `2px solid ${tokens.colors.yellow}`,
117+
},
118+
'&[data-theme="light"]': {
119+
border: `2px solid ${tokens.colors.navy}`,
120+
},
121+
a: {
122+
color: 'white',
123+
textDecoration: 'underline',
124+
},
125+
backgroundColor: tokens.colors.navy,
126+
borderRadius: '0.375rem',
127+
color: 'white',
128+
marginBottom: '1.25rem',
129+
padding: '1rem',
130+
span: {
131+
marginLeft: '0.75rem',
132+
},
133+
});
134+
135+
const AdditionalContent = styled.div({
136+
'@media (min-width: 1024px)': {
137+
borderTop: 0,
138+
paddingTop: 0,
139+
},
140+
marginTop: '3rem',
141+
paddingTop: '2rem',
142+
width: '100%',
143+
});
144+
74145
export default function PackageVersionPage({
75146
metadata,
76147
monthlyDownloads,
77148
repository,
78149
urls,
79150
versionsArray,
80151
}: Props) {
81-
// construct link to the current package version
152+
const { theme } = useContext(ThemeContext);
82153
const linkToCurrentVersion = `https://rdocumentation.org${metadata.uri}`;
83-
84-
// get the latest version of the package
85154
const latestVersion = versionsArray[0];
86-
87-
// get the number of downloads last month
88155
const downloadsLastMonth = monthlyDownloads
89156
? monthlyDownloads[monthlyDownloads.length - 1].downloads
90157
: null;
91-
92-
// get the last published date
93158
const lastPublished = metadata.release_date
94159
? new Date(metadata.release_date)
95160
: null;
@@ -100,33 +165,30 @@ export default function PackageVersionPage({
100165
description={metadata.description}
101166
title={metadata.pageTitle}
102167
>
103-
<div className="mt-8 md:mt-12">
104-
<div className="block lg:flex">
105-
<div className="w-full pb-8 lg:pb-0 lg:w-2/3 lg:pr-8">
106-
{/* show a warning if looking at an older package version */}
168+
<Container>
169+
<FlexContainer>
170+
<MainContent>
107171
{metadata.version !== latestVersion && (
108-
<div className="px-4 py-2 mb-5 text-white border-2 rounded-md border-dc-navy dark:border-dc-yellow bg-dc-navy">
172+
<WarningBox data-theme={theme}>
109173
<span>⚠️</span>
110-
<span className="ml-3">{`There's a newer version (${latestVersion}) of this package.`}</span>{' '}
174+
<span>{`There's a newer version (${latestVersion}) of this package.`}</span>
111175
<Link href={metadata.canonicalLink}>
112-
<a className="text-white underline">Take me there.</a>
176+
<a>Take me there.</a>
113177
</Link>
114-
</div>
178+
</WarningBox>
115179
)}
116-
{
117-
metadata.readmemd
118-
?
119-
<PackageReadme readme={metadata.readmemd} />
120-
:
121-
<PackageReadMePlaceholder
122-
packageName = {metadata.package_name}
123-
version = {metadata.version}
124-
title = {metadata.title}
125-
description = {metadata.description}
180+
{metadata.readmemd ? (
181+
<PackageReadme readme={metadata.readmemd} />
182+
) : (
183+
<PackageReadMePlaceholder
184+
description={metadata.description}
185+
packageName={metadata.package_name}
186+
title={metadata.title}
187+
version={metadata.version}
126188
/>
127-
}
128-
</div>
129-
<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">
189+
)}
190+
</MainContent>
191+
<SidebarContainer>
130192
<PackageSidebar
131193
downloadsLastMonth={downloadsLastMonth}
132194
githubUrl={urls.githubUrl}
@@ -142,20 +204,18 @@ export default function PackageVersionPage({
142204
version={metadata.version}
143205
versionsArray={versionsArray}
144206
/>
145-
</div>
146-
</div>
147-
<div className="w-full pt-8 mt-12 border-t lg:pt-0 lg:border-t-0 max-w-none">
148-
{
149-
metadata.topics?.length > 0
150-
&&
207+
</SidebarContainer>
208+
</FlexContainer>
209+
<AdditionalContent>
210+
{metadata.topics?.length > 0 && (
151211
<PackageFunctionList
152212
functions={metadata.topics}
153213
packageName={metadata.package_name}
154214
packageVersion={metadata.version}
155215
/>
156-
}
157-
</div>
158-
</div>
216+
)}
217+
</AdditionalContent>
218+
</Container>
159219
</Layout>
160220
);
161221
}
@@ -169,6 +229,41 @@ export const getServerSideProps: GetServerSideProps = async ({
169229
`${API_URL}/api/packages/${packageName}/versions/${version}`,
170230
);
171231
const metadata = await res.json();
232+
const {
233+
canonicalLink,
234+
description,
235+
license,
236+
maintainer,
237+
package: { type_id },
238+
package_name,
239+
pageTitle,
240+
readmemd,
241+
release_date,
242+
title,
243+
topics,
244+
uri,
245+
version: metadataVersion,
246+
} = metadata;
247+
const reducedMetaData = {
248+
canonicalLink,
249+
description,
250+
license,
251+
maintainer,
252+
package: { type_id },
253+
package_name,
254+
pageTitle,
255+
readmemd,
256+
release_date,
257+
title,
258+
topics: topics.map((topic) => ({
259+
id: topic.id,
260+
name: topic.name,
261+
title: topic.title,
262+
})),
263+
type_id,
264+
uri,
265+
version: metadataVersion,
266+
};
172267

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

227322
return {
228323
props: {
229-
metadata,
324+
metadata: reducedMetaData,
230325
monthlyDownloads,
231326
repository,
232327
urls: {

styles/index.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020
.prose * {
2121
@apply dark:text-white;
2222
}
23-
24-
.dc-input > div {
25-
@apply w-full;
26-
}
2723
}
2824

2925
.list-view dl {

0 commit comments

Comments
 (0)