feat(nx-dev): improve related docs section (#20796)
This commit is contained in:
parent
2346aaf78e
commit
3099547de6
@ -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">
|
||||||
|
|||||||
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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');
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user