feat(nx-dev): improve related docs section (#20796)

This commit is contained in:
Juri Strumpflohner 2023-12-15 21:49:11 +01:00 committed by GitHub
parent 2346aaf78e
commit 3099547de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 31 deletions

View File

@ -1,6 +1,5 @@
import { import {
categorizeRelatedDocuments, categorizeRelatedDocuments,
generateRelatedDocumentsTemplate,
ProcessedDocument, ProcessedDocument,
RelatedDocument, RelatedDocument,
} from '@nx/nx-dev/models-document'; } from '@nx/nx-dev/models-document';
@ -11,6 +10,7 @@ import { useRouter } from 'next/router';
import { cx } from '@nx/nx-dev/ui-primitives'; import { cx } from '@nx/nx-dev/ui-primitives';
import { useRef } from 'react'; import { useRef } from 'react';
import { collectHeadings, TableOfContents } from './table-of-contents'; import { collectHeadings, TableOfContents } from './table-of-contents';
import { RelatedDocumentsSection } from './related-documents-section';
export function DocViewer({ export function DocViewer({
document, document,
@ -42,14 +42,7 @@ export function DocViewer({
description: metadata['description'] ?? document.description, description: metadata['description'] ?? document.description,
mediaImage: document.mediaImage, mediaImage: document.mediaImage,
content: node, content: node,
relatedContent: renderMarkdown( relatedContentData: categorizeRelatedDocuments(relatedDocuments),
generateRelatedDocumentsTemplate(
categorizeRelatedDocuments(relatedDocuments)
),
{
filePath: '',
}
).node,
tableOfContent: collectHeadings(treeNode), tableOfContent: collectHeadings(treeNode),
}; };
@ -129,11 +122,17 @@ export function DocViewer({
)} )}
</div> </div>
{/*RELATED CONTENT*/} {/*RELATED CONTENT*/}
<div <div
data-document="related" data-document="related"
className="prose prose-slate dark:prose-invert max-w-none" className={cx(
'pt-8 prose prose-slate dark:prose-invert w-full max-w-none 2xl:max-w-4xl',
{ 'xl:max-w-2xl': !hideTableOfContent }
)}
> >
{vm.relatedContent} <RelatedDocumentsSection
relatedCategories={vm.relatedContentData}
/>
</div> </div>
</div> </div>
<div className="flex w-full items-center space-x-2 pt-24 pb-24 sm:px-6 lg:pb-16 xl:px-8"> <div className="flex w-full items-center space-x-2 pt-24 pb-24 sm:px-6 lg:pb-16 xl:px-8">

View File

@ -0,0 +1,65 @@
import { RelatedDocumentsCategory } from '@nx/nx-dev/models-document';
import {
CubeTransparentIcon,
ArrowRightIcon,
ClipboardDocumentIcon,
LightBulbIcon,
MagnifyingGlassIcon,
InformationCircleIcon,
} from '@heroicons/react/24/outline';
export function RelatedDocumentsSection({
relatedCategories,
}: {
relatedCategories: RelatedDocumentsCategory[];
}) {
return (
<div className="grid lg:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-3 items-stretch">
{relatedCategories.length > 0 &&
relatedCategories.map((category) => (
<CategoryBox key={category.id} category={category} />
))}
</div>
);
}
const iconMap: { [key: string]: JSX.Element } = {
concepts: <CubeTransparentIcon className="w-6 h-6 mr-2" aria-hidden="true" />,
recipes: (
<ClipboardDocumentIcon className="w-6 h-6 mr-2" aria-hidden="true" />
),
reference: <LightBulbIcon className="w-6 h-6 mr-2" aria-hidden="true" />,
'see-also': (
<MagnifyingGlassIcon className="w-6 h-6 mr-2" aria-hidden="true" />
),
default: (
<InformationCircleIcon className="w-6 h-6 mr-2" aria-hidden="true" />
),
};
function CategoryBox({ category }: { category: RelatedDocumentsCategory }) {
return (
<div className="rounded-lg border border-slate-200 bg-white/60 p-5 dark:border-slate-800/40 dark:bg-slate-800/60">
<h4 className="flex items-center mt-0 mb-2 text-xl font-bold">
{iconMap[category.id] ?? iconMap.default}
{category.name}
</h4>
<ul className="divide-y divide-slate-300 dark:divide-slate-700 list-none pl-0">
{category.relatedDocuments.map((d) => (
<li
key={d.id}
className="flex justify-between items-center py-1 pl-0 text-sm"
>
<a
href={d.path}
className="no-underline hover:underline hover:text-sky-600 dark:hover:text-sky-400 flex-grow flex justify-between items-center"
>
<span>{d.name}</span>
<ArrowRightIcon className="w-4 h-4 text-slate-500 dark:text-slate-400" />
</a>
</li>
))}
</ul>
</div>
);
}

View File

@ -1,6 +1,6 @@
import { RelatedDocument } from './documents.models'; import { RelatedDocument } from './documents.models';
interface RelatedDocumentsCategory { export interface RelatedDocumentsCategory {
id: string; id: string;
/** /**
* Matcher that will be evaluated against a path. * Matcher that will be evaluated against a path.
@ -49,19 +49,3 @@ export function categorizeRelatedDocuments(
return categories.filter((c) => !!c.relatedDocuments.length); return categories.filter((c) => !!c.relatedDocuments.length);
} }
export function generateRelatedDocumentsTemplate(
relatedDocumentCategories: RelatedDocumentsCategory[]
): string {
if (!relatedDocumentCategories.length) return '';
const template = relatedDocumentCategories.map((c) => {
const header = `### ${c.name}`;
const template = c.relatedDocuments
.map((d) => `- [${d.name}](${d.path})`)
.join('\n');
return [header, template].join('\n');
});
return ['\n## Related Documentation\n', ...template].join('\n');
}

View File

@ -3,9 +3,7 @@ import { test, expect } from '@playwright/test';
test.describe('nx-dev: Recipes pages', () => { test.describe('nx-dev: Recipes pages', () => {
test('should list related recipes based on tags', async ({ page }) => { test('should list related recipes based on tags', async ({ page }) => {
await page.goto('/recipes/other/deno-deploy'); await page.goto('/recipes/other/deno-deploy');
const relatedDocs = page.locator( const relatedDocs = page.locator('[data-document="related"] li');
'[data-document="related"] > article > ul > li'
);
const relatedDocsText = await relatedDocs.allInnerTexts(); const relatedDocsText = await relatedDocs.allInnerTexts();
expect(relatedDocsText.length, 'has related docs').toBeGreaterThan(0); expect(relatedDocsText.length, 'has related docs').toBeGreaterThan(0);