Skip to content

Commit

Permalink
RFC for a docs version selector
Browse files Browse the repository at this point in the history
An idea how we could do versioning of docs that I think might be sustainable.

- In the reference we have a version selector (design TBD)
- `sidebarReference.json` can contain a version now to conditionally include entries
- a `<VersionCondidion />` component allows to conditionally include content in a doc when APIs change over time

Code quality is very rough. Would probably put the version into the path somewhere, but that needs some more changes.
  • Loading branch information
kassens committed Apr 23, 2024
1 parent 07cbd00 commit 5280366
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 11 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -38,7 +38,8 @@
"react-collapsed": "4.0.4",
"react-dom": "^0.0.0-experimental-16d053d59-20230506",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1"
"remark-gfm": "^3.0.1",
"semver": "^7.6.0"
},
"devDependencies": {
"@babel/core": "^7.12.9",
Expand All @@ -54,6 +55,7 @@
"@types/parse-numeric-range": "^0.0.1",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.5",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"asyncro": "^3.0.0",
Expand Down
15 changes: 11 additions & 4 deletions src/components/Layout/Sidebar/SidebarRouteTree.tsx
Expand Up @@ -5,6 +5,7 @@
import {useRef, useLayoutEffect, Fragment} from 'react';

import cn from 'classnames';
import * as Semver from 'semver';
import {useRouter} from 'next/router';
import {SidebarLink} from './SidebarLink';
import {useCollapse} from 'react-collapsed';
Expand Down Expand Up @@ -77,6 +78,8 @@ export function SidebarRouteTree({
}: SidebarRouteTreeProps) {
const slug = useRouter().asPath.split(/[\?\#]/)[0];
const pendingRoute = usePendingRoute();
const selectedVersion: string =
(useRouter().query.version as string) ?? '19.0.0';
const currentRoutes = routeTree.routes as RouteItem[];
return (
<ul>
Expand All @@ -86,13 +89,19 @@ export function SidebarRouteTree({
path,
title,
routes,
canary,
heading,
hasSectionHeader,
sectionHeader,
version,
},
index
) => {
if (
version &&
!Semver.satisfies(Semver.coerce(selectedVersion), version)

Check failure on line 101 in src/components/Layout/Sidebar/SidebarRouteTree.tsx

View workflow job for this annotation

GitHub Actions / Lint on node 20.x and ubuntu-latest

Argument of type 'SemVer | null' is not assignable to parameter of type 'string | SemVer'.
) {
return null;
}
const selected = slug === path;
let listItem = null;
if (!path || heading) {
Expand Down Expand Up @@ -120,7 +129,6 @@ export function SidebarRouteTree({
selected={selected}
level={level}
title={title}
canary={canary}
isExpanded={isExpanded}
hideArrow={isForceExpanded}
/>
Expand All @@ -144,7 +152,6 @@ export function SidebarRouteTree({
selected={selected}
level={level}
title={title}
canary={canary}
/>
</li>
);
Expand All @@ -163,7 +170,7 @@ export function SidebarRouteTree({
'mb-1 text-sm font-bold ms-5 text-tertiary dark:text-tertiary-dark',
index !== 0 && 'mt-2'
)}>
{sectionHeader}
{sectionHeader?.replace('%VERSION%', selectedVersion)}
</h3>
</Fragment>
);
Expand Down
15 changes: 15 additions & 0 deletions src/components/Layout/SidebarNav/SidebarNav.tsx
Expand Up @@ -8,6 +8,7 @@ import cn from 'classnames';
import {Feedback} from '../Feedback';
import {SidebarRouteTree} from '../Sidebar/SidebarRouteTree';
import type {RouteItem} from '../getRouteMeta';
import {useRouter} from 'next/router';

declare global {
interface Window {
Expand All @@ -23,6 +24,14 @@ export default function SidebarNav({
routeTree: RouteItem;
breadcrumbs: RouteItem[];
}) {
const {push, query} = useRouter();
const onChangeVersion = React.useCallback(
(e) => {

Check failure on line 29 in src/components/Layout/SidebarNav/SidebarNav.tsx

View workflow job for this annotation

GitHub Actions / Lint on node 20.x and ubuntu-latest

Parameter 'e' implicitly has an 'any' type.
push({query: {...query, version: e.target.value}});
},
[push, query]
);

// HACK. Fix up the data structures instead.
if ((routeTree as any).routes.length === 1) {
routeTree = (routeTree as any).routes[0];
Expand All @@ -48,6 +57,12 @@ export default function SidebarNav({
className="w-full pt-6 scrolling-touch lg:h-auto grow pe-0 lg:pe-5 lg:pb-16 md:pt-4 lg:pt-4 scrolling-gpu">
{/* No fallback UI so need to be careful not to suspend directly inside. */}
<Suspense fallback={null}>
<select
value={query.version ?? '19.0.0'}
onChange={onChangeVersion}>
<option value="18.0.0">Version 18.0.0</option>
<option value="19.0.0">Version 19.0.0</option>
</select>
<SidebarRouteTree
routeTree={routeTree}
breadcrumbs={breadcrumbs}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Layout/getRouteMeta.tsx
Expand Up @@ -19,8 +19,8 @@ export type RouteTag =
export interface RouteItem {
/** Page title (for the sidebar) */
title: string;
/** Optional canary flag for heading */
canary?: boolean;
/** Optional version that supports this item */
version?: string;
/** Optional page description for heading */
description?: string;
/* Additional meta info for page tagging */
Expand Down
13 changes: 13 additions & 0 deletions src/components/MDX/MDXComponents.tsx
Expand Up @@ -31,9 +31,21 @@ import ButtonLink from 'components/ButtonLink';
import {TocContext} from './TocContext';
import type {Toc, TocItem} from './TocContext';
import {TeamMember} from './TeamMember';
import * as Semver from 'semver';

import ErrorDecoder from './ErrorDecoder';
import {IconCanary} from '../Icon/IconCanary';
import {useRouter} from 'next/router';

function VersionCondition({children, range}: {children: any; range: string}) {
const router = useRouter();
return Semver.satisfies(
Semver.coerce(router.query.version ?? '19.0.0'),

Check failure on line 43 in src/components/MDX/MDXComponents.tsx

View workflow job for this annotation

GitHub Actions / Lint on node 20.x and ubuntu-latest

Argument of type 'SemVer | null' is not assignable to parameter of type 'string | SemVer'.

Check failure on line 43 in src/components/MDX/MDXComponents.tsx

View workflow job for this annotation

GitHub Actions / Lint on node 20.x and ubuntu-latest

Argument of type 'string | string[]' is not assignable to parameter of type 'string | number | SemVer | null | undefined'.
range
)
? children
: null;
}

function CodeStep({children, step}: {children: any; step: number}) {
return (
Expand Down Expand Up @@ -462,6 +474,7 @@ export const MDXComponents = {
CodeStep,
YouTubeIframe,
ErrorDecoder,
VersionCondition,
};

for (let key in MDXComponents) {
Expand Down
8 changes: 8 additions & 0 deletions src/content/reference/react/useCallback.md
Expand Up @@ -20,6 +20,14 @@ const cachedFn = useCallback(fn, dependencies)

### `useCallback(fn, dependencies)` {/*usecallback*/}

<VersionCondition range=">= 19.0.0">
# 19+ content example
</VersionCondition>

<VersionCondition range="< 19.0.0">
# pre 19 content example
</VersionCondition>

Call `useCallback` at the top level of your component to cache a function definition between re-renders:

```js {4,9}
Expand Down
8 changes: 4 additions & 4 deletions src/sidebarReference.json
Expand Up @@ -4,7 +4,7 @@
"routes": [
{
"hasSectionHeader": true,
"sectionHeader": "react@18.2.0"
"sectionHeader": "react@%VERSION%"
},
{
"title": "Overview",
Expand All @@ -17,7 +17,7 @@
{
"title": "use",
"path": "/reference/react/use",
"canary": true
"version": ">= 19"
},
{
"title": "useCallback",
Expand Down Expand Up @@ -62,7 +62,7 @@
{
"title": "useOptimistic",
"path": "/reference/react/useOptimistic",
"canary": true
"version": ">= 19"
},
{
"title": "useReducer",
Expand Down Expand Up @@ -168,7 +168,7 @@
},
{
"hasSectionHeader": true,
"sectionHeader": "react-dom@18.2.0"
"sectionHeader": "react-dom@%VERSION%"
},
{
"title": "Hooks",
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Expand Up @@ -1073,6 +1073,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==

"@types/semver@^7.5.8":
version "7.5.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==

"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
Expand Down Expand Up @@ -5704,6 +5709,13 @@ semver@^7.3.7:
dependencies:
lru-cache "^6.0.0"

semver@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
dependencies:
lru-cache "^6.0.0"

[email protected]:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
Expand Down

0 comments on commit 5280366

Please sign in to comment.