docs(core): Add blog author details to nx-dev blog page (#23206)

This PR adds a context view for blog authors that displays details about
their social.

It will be show on the blog details page.

Here is an example:
![Screenshot 2024-05-07 at 7 31
16 AM](https://github.com/nrwl/nx/assets/338948/3abb1cce-e4bd-400c-9a1b-151254630bef)

![Screenshot 2024-05-07 at 7 37
38 AM](https://github.com/nrwl/nx/assets/338948/042bf376-a33d-44a3-addd-812953dd4d65)
This commit is contained in:
Nicholas Cunningham 2024-05-07 10:20:01 -06:00 committed by GitHub
parent 8160f5879b
commit 5edc64af92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 140 additions and 16 deletions

50
docs/blog/authors.json Normal file
View File

@ -0,0 +1,50 @@
[
{
"name": "Juri Strumpflohner",
"image": "/blog/images/Juri Strumpfloner.jpeg",
"twitter": "juristr",
"github": "juristr"
},
{
"name": "Colum Ferry",
"image": "/blog/images/Colum Ferry.jpeg",
"twitter": "FerryColum",
"github": "Coly010"
},
{
"name": "Emily Xiong",
"image": "/blog/images/Emily Xiong.jpeg",
"twitter": "xiongemily",
"github": "xiongemi"
},
{
"name": "Isaac Mann",
"image": "/blog/images/Isaac Mann.jpeg",
"twitter": "mannisaac",
"github": "isaacplmann"
},
{
"name": "Katerina Skroumpelou",
"image": "/blog/images/Katerina Skroumpelou.jpeg",
"twitter": "psybercity",
"github": "mandarini"
},
{
"name": "Max Kless",
"image": "/blog/images/Max Kless.jpeg",
"twitter": "MaxKless",
"github": "MaxKless"
},
{
"name": "Victor Savkin",
"image": "/blog/images/Victor Savkin.jpeg",
"twitter": "victorsavkin",
"github": "vsavkin"
},
{
"name": "Zack DeRose",
"image": "/blog/images/Zack DeRose.jpeg",
"twitter": "zackderose",
"github": "ZackDeRose"
}
]

View File

@ -21,6 +21,9 @@ export class BlogApi {
getBlogPosts(): BlogPostDataEntry[] {
const files: string[] = readdirSync(this.options.blogRoot);
const authors = JSON.parse(
readFileSync(join(this.options.blogRoot, 'authors.json'), 'utf8')
);
const allPosts: BlogPostDataEntry[] = [];
for (const file of files) {
@ -35,7 +38,9 @@ export class BlogApi {
content,
title: frontmatter.title ?? null,
description: frontmatter.description ?? null,
authors: frontmatter.authors ?? [],
authors: authors.filter((author) =>
frontmatter.authors.includes(author.name)
),
date: this.calculateDate(file, frontmatter),
cover_image: frontmatter.cover_image
? `/documentation${frontmatter.cover_image}` // Match the prefix used by markdown parser

View File

@ -2,7 +2,7 @@ export type BlogPostDataEntry = {
title: string;
content: string;
description: string;
authors: string[];
authors: BlogAuthor[];
date: string;
cover_image: string | null;
tags: string[];
@ -12,3 +12,10 @@ export type BlogPostDataEntry = {
filePath: string;
slug: string;
};
export type BlogAuthor = {
name: string;
image: string;
twitter: string;
github: string;
};

View File

@ -0,0 +1,39 @@
import type { BlogAuthor } from '@nx/nx-dev/data-access-documents/node-only';
import { GithubIcon, TwitterIcon } from '@nx/nx-dev/ui-common';
import Image from 'next/image';
interface AuthorDetailProps {
author: BlogAuthor;
}
export default function AuthorDetail({ author }: AuthorDetailProps) {
return (
<div className="space-between invisible absolute left-[65%] right-0 z-30 mt-2 flex w-60 translate-x-[-50%] items-center gap-4 rounded bg-slate-50 p-4 text-sm text-slate-700 opacity-0 shadow-lg ring-1 ring-slate-200 transition-all delay-75 duration-300 ease-in-out md:group-hover:visible md:group-hover:opacity-100 dark:bg-slate-900 dark:text-slate-400 dark:ring-slate-800">
<span>
<Image
alt={author.name}
title={author.name}
loading="lazy"
width="40"
height="40"
decoding="async"
src={`/documentation/blog/images/authors/${author.name}.jpeg`}
className="rounded-full ring-1 ring-white grayscale dark:ring-slate-900"
/>
</span>
<span className="text-balance">{author.name}</span>
<a
href={`https://twitter.com/${author.twitter}`}
aria-label={`Follow ${author.name} on X`}
>
<TwitterIcon aria-hidden="true" className="h-5 w-5" />
</a>
<a
href={`https://github.com/${author.github}`}
aria-label={`View ${author.name}'s GitHub profile`}
>
<GithubIcon aria-hidden="true" className="h-5 w-5" />
</a>
</div>
);
}

View File

@ -1,20 +1,28 @@
import Image from 'next/image';
import AuthorDetail from './author-detail';
import type { BlogAuthor } from '@nx/nx-dev/data-access-documents/node-only';
export function BlogAuthors({ authors }: { authors: string[] }): JSX.Element {
export function BlogAuthors({
authors,
}: {
authors: BlogAuthor[];
}): JSX.Element {
return (
<div className="isolate flex items-center -space-x-2 overflow-hidden">
<div className="relative isolate flex items-center -space-x-2">
{authors.map((author, index) => (
<Image
key={index}
alt={author}
title={author}
loading="lazy"
width="48"
height="48"
decoding="async"
src={`/documentation/blog/images/authors/${author}.jpeg`}
className="relative inline-block h-6 w-6 rounded-full ring-1 ring-white grayscale dark:ring-slate-900"
/>
<div key={index} className="group">
<Image
alt={author.name}
title={author.name}
loading="lazy"
width="48"
height="48"
decoding="async"
src={`/documentation/blog/images/authors/${author.name}.jpeg`}
className="relative inline-block h-6 w-6 rounded-full ring-1 ring-white grayscale dark:ring-slate-900"
/>
<AuthorDetail author={author} />
</div>
))}
</div>
);

View File

@ -40,7 +40,7 @@ export function MoreBlogs({ blogs }: MoreBlogsProps) {
<span className="hidden w-2/12 flex-none sm:inline-block">
{formattedDate}
</span>
<span className="hidden w-2/12 flex-none sm:inline-block">
<span className="hidden flex-1 overflow-hidden sm:inline-block">
<BlogAuthors authors={post.authors} />
</span>
</Link>

View File

@ -20,6 +20,7 @@ export * from './lib/typography';
export * from './lib/github-star-widget';
export * from './lib/youtube.component';
export * from './lib/image-theme';
export * from './lib/twitter-icon';
export { resourceMenuItems } from './lib/headers/menu-items';
export { solutionsMenuItems } from './lib/headers/menu-items';
export { eventItems } from './lib/headers/menu-items';

View File

@ -0,0 +1,14 @@
import { FC, SVGProps } from 'react';
export const TwitterIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
<svg
fill="currentColor"
role="img"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z" />
</svg>
);