feat(nx-dev): improve related docs section (#20796)
This commit is contained in:
parent
2346aaf78e
commit
3099547de6
@ -1,6 +1,5 @@
|
||||
import {
|
||||
categorizeRelatedDocuments,
|
||||
generateRelatedDocumentsTemplate,
|
||||
ProcessedDocument,
|
||||
RelatedDocument,
|
||||
} from '@nx/nx-dev/models-document';
|
||||
@ -11,6 +10,7 @@ import { useRouter } from 'next/router';
|
||||
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||
import { useRef } from 'react';
|
||||
import { collectHeadings, TableOfContents } from './table-of-contents';
|
||||
import { RelatedDocumentsSection } from './related-documents-section';
|
||||
|
||||
export function DocViewer({
|
||||
document,
|
||||
@ -42,14 +42,7 @@ export function DocViewer({
|
||||
description: metadata['description'] ?? document.description,
|
||||
mediaImage: document.mediaImage,
|
||||
content: node,
|
||||
relatedContent: renderMarkdown(
|
||||
generateRelatedDocumentsTemplate(
|
||||
categorizeRelatedDocuments(relatedDocuments)
|
||||
),
|
||||
{
|
||||
filePath: '',
|
||||
}
|
||||
).node,
|
||||
relatedContentData: categorizeRelatedDocuments(relatedDocuments),
|
||||
tableOfContent: collectHeadings(treeNode),
|
||||
};
|
||||
|
||||
@ -129,11 +122,17 @@ export function DocViewer({
|
||||
)}
|
||||
</div>
|
||||
{/*RELATED CONTENT*/}
|
||||
|
||||
<div
|
||||
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 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';
|
||||
|
||||
interface RelatedDocumentsCategory {
|
||||
export interface RelatedDocumentsCategory {
|
||||
id: string;
|
||||
/**
|
||||
* Matcher that will be evaluated against a path.
|
||||
@ -49,19 +49,3 @@ export function categorizeRelatedDocuments(
|
||||
|
||||
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('should list related recipes based on tags', async ({ page }) => {
|
||||
await page.goto('/recipes/other/deno-deploy');
|
||||
const relatedDocs = page.locator(
|
||||
'[data-document="related"] > article > ul > li'
|
||||
);
|
||||
const relatedDocs = page.locator('[data-document="related"] li');
|
||||
const relatedDocsText = await relatedDocs.allInnerTexts();
|
||||
expect(relatedDocsText.length, 'has related docs').toBeGreaterThan(0);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user