Skip to content

Commit

Permalink
feat: blog sidebar grouped by year (#587)
Browse files Browse the repository at this point in the history
* feat: add patch package dependency

* feat: add patch to docusaurus

* feat: add swizzle for BlogSidebar

* build: add yarn script to make sure patch builds

* docs: update readme for swizzled components

* refactor: update blog count to ALL

* docs: update README with Erick's feedback <3
  • Loading branch information
alicelovescake authored Jun 27, 2024
1 parent 38c0c04 commit 1f8a694
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 32 deletions.
2 changes: 1 addition & 1 deletion docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const config: Config = {
},
blog: {
// See `node_modules/@docusaurus/plugin-content-blog/src/pluginOptionSchema.ts` for full undocumented options
blogSidebarCount: 50,
blogSidebarCount: 'ALL',
blogSidebarTitle: 'Latest posts',
blogTitle: `Electron's blog`,
blogDescription: `Keep up to date with what's going on with the Electron project`,
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"lint": "npx tsc --noEmit && prettier . --check && npm run lint:eslint && npm run lint:markdown",
"lint:fix": "prettier . --write && npm run lint:eslint --fix && npm run lint:markdown --fix",
"pre-build": "npx tsx ./scripts/pre-build.ts",
"prepare": "husky install"
"prepare": "husky install",
"heroku-cleanup": "npx patch-package",
"postinstall": "patch-package"
},
"dependencies": {
"@docusaurus/core": "3.4.0",
Expand Down Expand Up @@ -86,6 +88,8 @@
"make-dir": "^3.1.0",
"mdast-util-frontmatter": "^2.0.1",
"mdast-util-to-string": "^2.0.0",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.8.0",
"remark": "^15.0.0",
"remark-gfm": "^4.0.0",
Expand Down
36 changes: 36 additions & 0 deletions patches/@docusaurus+plugin-content-blog+3.4.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
diff --git a/node_modules/@docusaurus/plugin-content-blog/lib/routes.js b/node_modules/@docusaurus/plugin-content-blog/lib/routes.js
index 3400a26..98b1cd8 100644
--- a/node_modules/@docusaurus/plugin-content-blog/lib/routes.js
+++ b/node_modules/@docusaurus/plugin-content-blog/lib/routes.js
@@ -41,6 +41,7 @@ async function buildAllRoutes({ baseUrl, content, actions, options, aliasedSourc
title: blogPost.metadata.title,
permalink: blogPost.metadata.permalink,
unlisted: blogPost.metadata.unlisted,
+ date: blogPost.metadata.date,
})),
};
const modulePath = await createData(`blog-post-list-prop-${pluginId}.json`, sidebar);
diff --git a/node_modules/@docusaurus/plugin-content-blog/src/plugin-content-blog.d.ts b/node_modules/@docusaurus/plugin-content-blog/src/plugin-content-blog.d.ts
index f4d4f13..f56deee 100644
--- a/node_modules/@docusaurus/plugin-content-blog/src/plugin-content-blog.d.ts
+++ b/node_modules/@docusaurus/plugin-content-blog/src/plugin-content-blog.d.ts
@@ -469,6 +469,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
title: string;
permalink: string;
unlisted: boolean;
+ date: Date | string;
};

export type BlogSidebar = {
diff --git a/node_modules/@docusaurus/plugin-content-blog/src/routes.ts b/node_modules/@docusaurus/plugin-content-blog/src/routes.ts
index a810ce1..2bf5cee 100644
--- a/node_modules/@docusaurus/plugin-content-blog/src/routes.ts
+++ b/node_modules/@docusaurus/plugin-content-blog/src/routes.ts
@@ -94,6 +94,7 @@ export async function buildAllRoutes({
title: blogPost.metadata.title,
permalink: blogPost.metadata.permalink,
unlisted: blogPost.metadata.unlisted,
+ date: blogPost.metadata.date
})),
};
const modulePath = await createData(
62 changes: 62 additions & 0 deletions src/theme/BlogSidebar/Desktop/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import { translate } from '@docusaurus/Translate';
import type { Props } from '@theme/BlogSidebar/Desktop';
import styles from './styles.module.css';

const SidebarHeader = ({ title }: { title: string }) => (
<div className={clsx(styles.sidebarHeader, 'margin-bottom--md')}>{title}</div>
);

const YearHeader = ({ year }: { year: number }) => (
<h5 className={styles.sidebarItemTitle}>{year}</h5>
);

const SidebarItem = ({ item }: { item: Props['sidebar']['items'][number] }) => (
<li className={styles.sidebarItem}>
<Link
isNavLink
to={item.permalink}
className={styles.sidebarItemLink}
activeClassName={styles.sidebarItemLinkActive}
>
{item.title}
</Link>
</li>
);

export default function BlogSidebarDesktop({ sidebar }: Props) {
let currentYear = null;

return (
<aside className="col col--3">
<nav
className={clsx(styles.sidebar, 'thin-scrollbar')}
aria-label={translate({
id: 'theme.blog.sidebar.navAriaLabel',
message: 'Blog recent posts navigation',
description: 'The ARIA label for recent posts in the blog sidebar',
})}
>
<SidebarHeader title={sidebar.title} />
<ul className={clsx(styles.sidebarItemList, 'clean-list')}>
{sidebar.items.map((item) => {
const itemYear = new Date(item.date).getFullYear();
const yearHeader = currentYear !== itemYear && (
<YearHeader key={itemYear} year={itemYear} />
);
currentYear = itemYear;

return (
<React.Fragment key={item.permalink}>
{yearHeader}
<SidebarItem item={item} />
</React.Fragment>
);
})}
</ul>
</nav>
</aside>
);
}
60 changes: 60 additions & 0 deletions src/theme/BlogSidebar/Desktop/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.sidebar {
max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
overflow-y: auto;
position: sticky;
top: var(--ifm-navbar-height);
padding: 20px 12px 0 0;
margin-left: -20px;
}

.sidebarHeader {
font-size: var(--ifm-h4-font-size);
font-weight: var(--ifm-font-weight-bold);
padding-left: 12px;
display: block;
}

.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}

.sidebarItemList {
font-size: 13px;
}

.sidebarItem {
margin-top: 0.1rem;
line-height: 18px;
}

.sidebarItemLink {
color: var(--ifm-font-color-base);
padding: 4px 8px;
display: block;
border-left: 4px solid transparent;
border-radius: 0.25rem;
line-height: 18px;
}

.sidebarItemLink:hover {
background: var(--ifm-menu-color-background-active);
color: var(--ifm-font-color-base);
text-decoration: none;
}

.sidebarItemLinkActive {
color: var(--ifm-font-color-base);
background: var(--ifm-menu-color-background-active);
border-left-color: var(--ifm-menu-color-active);
font-weight: 700;
}

@media (max-width: 996px) {
.sidebar {
display: none;
}
}
61 changes: 61 additions & 0 deletions src/theme/BlogSidebar/Mobile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import Link from '@docusaurus/Link';
import { NavbarSecondaryMenuFiller } from '@docusaurus/theme-common';
import styles from './styles.module.css';
import clsx from 'clsx';
import type { Props } from '@theme/BlogSidebar/Mobile';

export const SidebarHeader = ({ title }: { title: string }) => (
<div className={clsx(styles.sidebarHeader, 'margin-bottom--md')}>{title}</div>
);

export const YearHeader = ({ year }: { year: number }) => (
<h5 className={styles.sidebarItemTitle}>{year}</h5>
);

export const SidebarItem = ({
item,
}: {
item: Props['sidebar']['items'][number];
}) => (
<li className="menu__list-item">
<Link
isNavLink
to={item.permalink}
className="menu__link"
activeClassName="menu__link--active"
>
{item.title}
</Link>
</li>
);

function BlogSidebarMobileSecondaryMenu({ sidebar }: Props) {
let currentYear = null;

return (
<ul className="menu__list blog-menu__list">
{sidebar.items.map((item) => {
const itemYear = new Date(item.date).getFullYear();
const yearHeader = currentYear !== itemYear && (
<YearHeader key={itemYear} year={itemYear} />
);
currentYear = itemYear;
return (
<React.Fragment key={item.permalink}>
{yearHeader}
<SidebarItem item={item} />
</React.Fragment>
);
})}
</ul>
);
}
export default function BlogSidebarMobile(props) {
return (
<NavbarSecondaryMenuFiller
component={BlogSidebarMobileSecondaryMenu}
props={props}
/>
);
}
7 changes: 7 additions & 0 deletions src/theme/BlogSidebar/Mobile/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.sidebarItemTitle {
margin: 0.75rem 0 0.5rem;
color: var(--subtle);
padding-left: 12px;
border-bottom: 0.01rem solid var(--ifm-table-border-color);
padding-bottom: 4px;
}
17 changes: 17 additions & 0 deletions src/theme/BlogSidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { useWindowSize } from '@docusaurus/theme-common';
import BlogSidebarDesktop from '@theme/BlogSidebar/Desktop';
import BlogSidebarMobile from '@theme/BlogSidebar/Mobile';
import type { Props } from '@theme/BlogSidebar';

export default function BlogSidebar({ sidebar }: Props): JSX.Element | null {
const windowSize = useWindowSize();
if (!sidebar?.items.length) {
return null;
}
// Mobile sidebar doesn't need to be server-rendered
if (windowSize === 'mobile') {
return <BlogSidebarMobile sidebar={sidebar} />;
}
return <BlogSidebarDesktop sidebar={sidebar} />;
}
9 changes: 9 additions & 0 deletions src/theme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ guide in the Docusaurus documentation.

## List of Swizzled components

### `BlogSidebar` (unsafe)

To enhance the functionality of our blog sidebar, we [patched Docusaurus](https://github.com/electron/website/pull/587/files#diff-e196318ff66c78116a07aaa92f5eb7191cf888f36bd942e609a12ff75167f9ed)
to extract the publication date of each blog post from its YAML frontmatter
in the sidebar prop passed to `BlogSidebar` component. Utilizing this
date, we can then group the posts by year, in both desktop and mobile.
This improves the navigational experience for users by making it easier
for them to find content from specific years.

### `DocSidebarItem` (unsafe)

Electron has a lot of platform-specific APIs, and guides to go along with them.
Expand Down
Loading

0 comments on commit 1f8a694

Please sign in to comment.