[docs] Separate some pages out of the Docs section (#1626)

This PR changes the structure of the docs site's sidebar.


![image](https://github.com/tldraw/tldraw/assets/15892272/ffe1e152-c921-43f0-9ba2-d084bda5e1e3)

I think this signposts more clearly what the different pages are for.
And it also paves the way for some work I want to do on
expanding+refining the Editor docs.

This PR also simplifies URL for all sidebar links.
It's a bit scrappy, but I think it feels simple enough to work with, and
easy-enough to change in the future.
> But hey! I've been doing this a couple times recently. Maybe we should
refactor? Or maybe we should keep going with what we've got and focus on
getting these docs *done*.

### Change Type

- [x] `documentation` — Changes to the documentation only[^2]

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version

### Test Plan

1. Check that all the sidebar links go to where you expect.
2. Check that old URLs redirect to the right pages, eg: `/docs/usage`
should go to the usage page.


### Release Notes

- Documentation: Restructured the sidebar for clarity.
This commit is contained in:
Lu Wilson 2023-06-23 10:53:22 +01:00 committed by GitHub
parent d2c51ae3ba
commit c5fe399842
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 119 additions and 36 deletions

View file

@ -15,8 +15,12 @@ export function Breadcrumb({
<Link href={`/`}>tldraw</Link> <Link href={`/`}>tldraw</Link>
{section && ( {section && (
<> <>
{` / `} {section.title && (
<Link href={`/${section.id}`}>{section.title}</Link> <>
{` / `}
<Link href={`/${section.id}`}>{section.title}</Link>
</>
)}
{category && ( {category && (
<> <>
{category.id === 'ucg' ? null : ( {category.id === 'ucg' ? null : (

View file

@ -106,11 +106,13 @@ function SidebarLink(props: SidebarContentLink) {
function SidebarSection({ title, url, children }: SidebarContentSectionLink) { function SidebarSection({ title, url, children }: SidebarContentSectionLink) {
return ( return (
<li className="sidebar__section"> <li className="sidebar__section">
<Link href={url}> {title && (
<div className="sidebar__link sidebar__section__title" data-active={false}> <Link href={url}>
{title} <div className="sidebar__link sidebar__section__title" data-active={false}>
</div> {title}
</Link> </div>
</Link>
)}
<ul className="sidebar__list sidebar__section__list"> <ul className="sidebar__list sidebar__section__list">
{children.map((link) => ( {children.map((link) => (
<SidebarLink key={link.url} {...link} /> <SidebarLink key={link.url} {...link} />

View file

@ -1,7 +1,13 @@
[ [
{
"id": "getting-started",
"title": "",
"description": "",
"categories": []
},
{ {
"id": "docs", "id": "docs",
"title": "Docs", "title": "Documentation",
"description": "Developer documentation for tldraw.", "description": "Developer documentation for tldraw.",
"categories": [] "categories": []
}, },

View file

@ -8,10 +8,35 @@ const nextConfig = {
async redirects() { async redirects() {
return [ return [
{ {
// For reverse compatibility with old links
source: '/:sectionId/ucg/:articleId', source: '/:sectionId/ucg/:articleId',
destination: '/:sectionId/:articleId', destination: '/:sectionId/:articleId',
permanent: true, permanent: true,
}, },
{
// For reverse compatibility with old links
source: '/docs/introduction',
destination: '/introduction',
permanent: true,
},
{
// For reverse compatibility with old links
source: '/docs/installation',
destination: '/installation',
permanent: true,
},
{
// For reverse compatibility with old links
source: '/docs/usage',
destination: '/usage',
permanent: true,
},
{
// To reflect that these are at the top level of the sidebar
source: '/getting-started/:childId',
destination: '/:childId',
permanent: true,
},
] ]
}, },
} }

View file

@ -3,22 +3,39 @@ import { Mdx } from '@/components/Mdx'
import { MetaHead } from '@/components/MetaHead' import { MetaHead } from '@/components/MetaHead'
import { Sidebar } from '@/components/Sidebar' import { Sidebar } from '@/components/Sidebar'
import { Article, Category, Section, SidebarContentList } from '@/types/content-types' import { Article, Category, Section, SidebarContentList } from '@/types/content-types'
import { getArticleSource, getArticles, getSection, getSections } from '@/utils/content' import {
getArticle,
getArticleSource,
getArticles,
getCategory,
getLinks,
getSection,
getSections,
} from '@/utils/content'
import { getSidebarContentList } from '@/utils/getSidebarContentList' import { getSidebarContentList } from '@/utils/getSidebarContentList'
import { GetStaticPaths, GetStaticProps } from 'next' import { GetStaticPaths, GetStaticProps } from 'next'
import { MDXRemoteSerializeResult } from 'next-mdx-remote' import { MDXRemoteSerializeResult } from 'next-mdx-remote'
import { useTheme } from 'next-themes' import { useTheme } from 'next-themes'
import Link from 'next/link' import Link from 'next/link'
import ArticlePage, { ArticleProps } from './[childId]/[articleId]'
type Props = { type SectionProps = {
type: 'section'
sidebar: SidebarContentList sidebar: SidebarContentList
section: Section section: Section
categories: { category: Category; articles: Article[] }[] categories: { category: Category; articles: Article[] }[]
mdxSource: MDXRemoteSerializeResult | null mdxSource: MDXRemoteSerializeResult | null
} }
export default function SectionListPage({ sidebar, section, categories, mdxSource }: Props) { type Props = SectionProps | ArticleProps
export default function SectionListPage(props: Props) {
const theme = useTheme() const theme = useTheme()
if (props.type === 'article') {
return <ArticlePage {...props} />
}
const { sidebar, section, categories, mdxSource } = props
const ucg = categories.find((category) => category.category.id === 'ucg')! const ucg = categories.find((category) => category.category.id === 'ucg')!
return ( return (
<> <>
@ -62,20 +79,58 @@ export const getStaticPaths: GetStaticPaths = async () => {
const paths: { params: { sectionId: string } }[] = [] const paths: { params: { sectionId: string } }[] = []
for (const section of sections) { for (const section of sections) {
paths.push({ params: { sectionId: section.id } }) if (section.id !== 'getting-started') {
paths.push({ params: { sectionId: section.id } })
continue
}
// Add paths for getting-started articles (not the section itself)
// ... because we keep those at the top level of the sidebar
for (const category of section.categories) {
if (category.id !== 'ucg') continue
for (const articleId of category.articleIds) {
paths.push({ params: { sectionId: articleId } })
}
}
} }
return { paths, fallback: false } return { paths, fallback: false }
} }
export const getStaticProps: GetStaticProps<Props> = async (ctx) => { export const getStaticProps: GetStaticProps<Props> = async (ctx) => {
const sectionId = ctx.params?.sectionId?.toString() const id = ctx.params?.sectionId?.toString()
if (!sectionId) throw Error() if (!id) throw Error()
const sidebar = await getSidebarContentList({ // If the path goes to an article in the getting-started section
sectionId, // ... show the article page instead
}) // ... because we keep those ones at the top level
const sections = await getSections()
if (!sections.some((section) => section.id === id)) {
const sectionId = 'getting-started'
const categoryId = 'ucg'
const articleId = id
const sidebar = await getSidebarContentList({ sectionId, categoryId, articleId })
const section = await getSection(sectionId)
const category = await getCategory(sectionId, categoryId)
const article = await getArticle(articleId)
const links = await getLinks(articleId)
const mdxSource = await getArticleSource(articleId)
return {
props: {
type: 'article',
sidebar,
section,
category,
article,
links,
mdxSource,
},
}
}
// Otherwise, show the section page
const sectionId = id
const sidebar = await getSidebarContentList({ sectionId })
const articles = await getArticles() const articles = await getArticles()
const section = await getSection(sectionId) const section = await getSection(sectionId)
const categories = [] as { category: Category; articles: Article[] }[] const categories = [] as { category: Category; articles: Article[] }[]
@ -87,5 +142,5 @@ export const getStaticProps: GetStaticProps<Props> = async (ctx) => {
const article = articles[sectionId + '_index'] ?? null const article = articles[sectionId + '_index'] ?? null
const mdxSource = article ? await getArticleSource(sectionId + '_index') : null const mdxSource = article ? await getArticleSource(sectionId + '_index') : null
return { props: { sidebar, section, categories, mdxSource } } return { props: { type: 'section', sidebar, section, categories, mdxSource } }
} }

View file

@ -19,23 +19,14 @@ export async function getSidebarContentList({
if (section.id === 'gen') { if (section.id === 'gen') {
links.push({ type: 'article', title: 'API Reference', url: '/gen' }) links.push({ type: 'article', title: 'API Reference', url: '/gen' })
// for (const category of section.categories) {
// if (category.id === 'ucg') {
// continue
// } else {
// children.push({
// type: 'article',
// title: category.title,
// url: `/${section.id}/${category.id}`,
// })
// }
// }
// links.push({ type: 'section', title: 'API Reference', url: '/gen', children })
continue continue
} }
// If the article is in the getting-started section
// ... we place it at the top level of the sidebar
// ... so let's simplify its URL to reflect that
const sectionUrl = section.id === 'getting-started' ? '' : `/${section.id}`
for (const category of section.categories) { for (const category of section.categories) {
if (category.id === 'ucg') { if (category.id === 'ucg') {
continue continue
@ -43,13 +34,13 @@ export async function getSidebarContentList({
children.push({ children.push({
type: 'category', type: 'category',
title: category.title, title: category.title,
url: `/${section.id}/${category.id}`, url: `${sectionUrl}/${category.id}`,
children: category.articleIds.map((articleId) => { children: category.articleIds.map((articleId) => {
const article = articles[articleId] const article = articles[articleId]
return { return {
type: 'article' as const, type: 'article' as const,
title: article.title, title: article.title,
url: `/${section.id}/${category.id}/${articleId}`, url: `${sectionUrl}/${category.id}/${articleId}`,
} }
}), }),
}) })
@ -64,14 +55,14 @@ export async function getSidebarContentList({
return { return {
type: 'article' as const, type: 'article' as const,
title: article.title, title: article.title,
url: `/${section.id}/${category.id}/${articleId}`, url: `${sectionUrl}/${articleId}`,
} }
}) })
) )
} }
} }
links.push({ type: 'section', title: section.title, url: `/${section.id}`, children }) links.push({ type: 'section', title: section.title, url: sectionUrl, children })
} }
return { return {