feat(v2): doc navbar item type (#3539)

* provide DocNavbarItem type

* update snapshots

* Fix Docusaurus tabs CSS

* revert navbar tabs css/style changes, instead apply simple navbar__link--active class + make it configurable

* Update website/docs/theme-classic.md

Co-authored-by: Alexey Pyltsyn <lex61rus@gmail.com>

* add dropdownActiveClassDisabled option

Co-authored-by: Alexey Pyltsyn <lex61rus@gmail.com>
This commit is contained in:
Sébastien Lorber 2020-10-07 13:42:24 +02:00 committed by GitHub
parent 9134b1396b
commit 9ba28a378f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 198 additions and 37 deletions

View file

@ -60,54 +60,67 @@ Object {
Object { Object {
"id": "foo/bar", "id": "foo/bar",
"path": "/docs/foo/bar", "path": "/docs/foo/bar",
"sidebar": "docs",
}, },
Object { Object {
"id": "foo/baz", "id": "foo/baz",
"path": "/docs/foo/bazSlug.html", "path": "/docs/foo/bazSlug.html",
"sidebar": "docs",
}, },
Object { Object {
"id": "hello", "id": "hello",
"path": "/docs/", "path": "/docs/",
"sidebar": "docs",
}, },
Object { Object {
"id": "ipsum", "id": "ipsum",
"path": "/docs/ipsum", "path": "/docs/ipsum",
"sidebar": undefined,
}, },
Object { Object {
"id": "lorem", "id": "lorem",
"path": "/docs/lorem", "path": "/docs/lorem",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootAbsoluteSlug", "id": "rootAbsoluteSlug",
"path": "/docs/rootAbsoluteSlug", "path": "/docs/rootAbsoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootRelativeSlug", "id": "rootRelativeSlug",
"path": "/docs/rootRelativeSlug", "path": "/docs/rootRelativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootResolvedSlug", "id": "rootResolvedSlug",
"path": "/docs/hey/rootResolvedSlug", "path": "/docs/hey/rootResolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootTryToEscapeSlug", "id": "rootTryToEscapeSlug",
"path": "/docs/rootTryToEscapeSlug", "path": "/docs/rootTryToEscapeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/absoluteSlug", "id": "slugs/absoluteSlug",
"path": "/docs/absoluteSlug", "path": "/docs/absoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/relativeSlug", "id": "slugs/relativeSlug",
"path": "/docs/slugs/relativeSlug", "path": "/docs/slugs/relativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/resolvedSlug", "id": "slugs/resolvedSlug",
"path": "/docs/slugs/hey/resolvedSlug", "path": "/docs/slugs/hey/resolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/tryToEscapeSlug", "id": "slugs/tryToEscapeSlug",
"path": "/docs/tryToEscapeSlug", "path": "/docs/tryToEscapeSlug",
"sidebar": undefined,
}, },
], ],
"isLast": true, "isLast": true,
@ -362,54 +375,67 @@ Object {
Object { Object {
"id": "foo/bar", "id": "foo/bar",
"path": "/docs/foo/bar", "path": "/docs/foo/bar",
"sidebar": "docs",
}, },
Object { Object {
"id": "foo/baz", "id": "foo/baz",
"path": "/docs/foo/bazSlug.html", "path": "/docs/foo/bazSlug.html",
"sidebar": "docs",
}, },
Object { Object {
"id": "hello", "id": "hello",
"path": "/docs/", "path": "/docs/",
"sidebar": "docs",
}, },
Object { Object {
"id": "ipsum", "id": "ipsum",
"path": "/docs/ipsum", "path": "/docs/ipsum",
"sidebar": undefined,
}, },
Object { Object {
"id": "lorem", "id": "lorem",
"path": "/docs/lorem", "path": "/docs/lorem",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootAbsoluteSlug", "id": "rootAbsoluteSlug",
"path": "/docs/rootAbsoluteSlug", "path": "/docs/rootAbsoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootRelativeSlug", "id": "rootRelativeSlug",
"path": "/docs/rootRelativeSlug", "path": "/docs/rootRelativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootResolvedSlug", "id": "rootResolvedSlug",
"path": "/docs/hey/rootResolvedSlug", "path": "/docs/hey/rootResolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootTryToEscapeSlug", "id": "rootTryToEscapeSlug",
"path": "/docs/rootTryToEscapeSlug", "path": "/docs/rootTryToEscapeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/absoluteSlug", "id": "slugs/absoluteSlug",
"path": "/docs/absoluteSlug", "path": "/docs/absoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/relativeSlug", "id": "slugs/relativeSlug",
"path": "/docs/slugs/relativeSlug", "path": "/docs/slugs/relativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/resolvedSlug", "id": "slugs/resolvedSlug",
"path": "/docs/slugs/hey/resolvedSlug", "path": "/docs/slugs/hey/resolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/tryToEscapeSlug", "id": "slugs/tryToEscapeSlug",
"path": "/docs/tryToEscapeSlug", "path": "/docs/tryToEscapeSlug",
"sidebar": undefined,
}, },
], ],
"isLast": true, "isLast": true,
@ -661,6 +687,7 @@ Object {
Object { Object {
"id": "team", "id": "team",
"path": "/community/next/team", "path": "/community/next/team",
"sidebar": "community",
}, },
], ],
"isLast": false, "isLast": false,
@ -674,6 +701,7 @@ Object {
Object { Object {
"id": "team", "id": "team",
"path": "/community/team", "path": "/community/team",
"sidebar": "version-1.0.0/community",
}, },
], ],
"isLast": true, "isLast": true,
@ -1228,26 +1256,32 @@ Object {
Object { Object {
"id": "foo/bar", "id": "foo/bar",
"path": "/docs/next/foo/barSlug", "path": "/docs/next/foo/barSlug",
"sidebar": "docs",
}, },
Object { Object {
"id": "hello", "id": "hello",
"path": "/docs/next/", "path": "/docs/next/",
"sidebar": "docs",
}, },
Object { Object {
"id": "slugs/absoluteSlug", "id": "slugs/absoluteSlug",
"path": "/docs/next/absoluteSlug", "path": "/docs/next/absoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/relativeSlug", "id": "slugs/relativeSlug",
"path": "/docs/next/slugs/relativeSlug", "path": "/docs/next/slugs/relativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/resolvedSlug", "id": "slugs/resolvedSlug",
"path": "/docs/next/slugs/hey/resolvedSlug", "path": "/docs/next/slugs/hey/resolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/tryToEscapeSlug", "id": "slugs/tryToEscapeSlug",
"path": "/docs/next/tryToEscapeSlug", "path": "/docs/next/tryToEscapeSlug",
"sidebar": undefined,
}, },
], ],
"isLast": false, "isLast": false,
@ -1261,10 +1295,12 @@ Object {
Object { Object {
"id": "foo/bar", "id": "foo/bar",
"path": "/docs/foo/bar", "path": "/docs/foo/bar",
"sidebar": "version-1.0.1/docs",
}, },
Object { Object {
"id": "hello", "id": "hello",
"path": "/docs/", "path": "/docs/",
"sidebar": "version-1.0.1/docs",
}, },
], ],
"isLast": true, "isLast": true,
@ -1278,14 +1314,17 @@ Object {
Object { Object {
"id": "foo/bar", "id": "foo/bar",
"path": "/docs/1.0.0/foo/barSlug", "path": "/docs/1.0.0/foo/barSlug",
"sidebar": "version-1.0.0/docs",
}, },
Object { Object {
"id": "foo/baz", "id": "foo/baz",
"path": "/docs/1.0.0/foo/baz", "path": "/docs/1.0.0/foo/baz",
"sidebar": "version-1.0.0/docs",
}, },
Object { Object {
"id": "hello", "id": "hello",
"path": "/docs/1.0.0/", "path": "/docs/1.0.0/",
"sidebar": "version-1.0.0/docs",
}, },
], ],
"isLast": false, "isLast": false,
@ -1299,34 +1338,42 @@ Object {
Object { Object {
"id": "rootAbsoluteSlug", "id": "rootAbsoluteSlug",
"path": "/docs/withSlugs/rootAbsoluteSlug", "path": "/docs/withSlugs/rootAbsoluteSlug",
"sidebar": "version-1.0.1/docs",
}, },
Object { Object {
"id": "rootRelativeSlug", "id": "rootRelativeSlug",
"path": "/docs/withSlugs/rootRelativeSlug", "path": "/docs/withSlugs/rootRelativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootResolvedSlug", "id": "rootResolvedSlug",
"path": "/docs/withSlugs/hey/rootResolvedSlug", "path": "/docs/withSlugs/hey/rootResolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "rootTryToEscapeSlug", "id": "rootTryToEscapeSlug",
"path": "/docs/withSlugs/rootTryToEscapeSlug", "path": "/docs/withSlugs/rootTryToEscapeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/absoluteSlug", "id": "slugs/absoluteSlug",
"path": "/docs/withSlugs/absoluteSlug", "path": "/docs/withSlugs/absoluteSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/relativeSlug", "id": "slugs/relativeSlug",
"path": "/docs/withSlugs/slugs/relativeSlug", "path": "/docs/withSlugs/slugs/relativeSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/resolvedSlug", "id": "slugs/resolvedSlug",
"path": "/docs/withSlugs/slugs/hey/resolvedSlug", "path": "/docs/withSlugs/slugs/hey/resolvedSlug",
"sidebar": undefined,
}, },
Object { Object {
"id": "slugs/tryToEscapeSlug", "id": "slugs/tryToEscapeSlug",
"path": "/docs/withSlugs/tryToEscapeSlug", "path": "/docs/withSlugs/tryToEscapeSlug",
"sidebar": undefined,
}, },
], ],
"isLast": false, "isLast": false,

View file

@ -11,6 +11,7 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
return { return {
id: doc.unversionedId, id: doc.unversionedId,
path: doc.permalink, path: doc.permalink,
sidebar: doc.sidebar,
}; };
} }

View file

@ -148,6 +148,7 @@ export type LoadedContent = {
export type GlobalDoc = { export type GlobalDoc = {
id: string; id: string;
path: string; path: string;
sidebar: string | undefined;
}; };
export type GlobalVersion = { export type GlobalVersion = {

View file

@ -0,0 +1,48 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import DefaultNavbarItem from './DefaultNavbarItem';
import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs';
import clsx from 'clsx';
import type {Props} from '@theme/NavbarItem/DocNavbarItem';
export default function DocNavbarItem({
docId,
activeSidebarClassName,
label: staticLabel,
docsPluginId,
...props
}: Props): JSX.Element {
const latestVersion = useLatestVersion(docsPluginId);
const {activeVersion, activeDoc} = useActiveDocContext(docsPluginId);
const version = activeVersion ?? latestVersion;
const doc = version.docs.find((versionDoc) => versionDoc.id === docId);
if (!doc) {
throw new Error(
`DocNavbarItem: couldn't find any doc with id=${docId} in version ${
version.name
}.
Available docIds=\n- ${version.docs.join('\n- ')}`,
);
}
return (
<DefaultNavbarItem
exact
{...props}
className={clsx(props.className, {
[activeSidebarClassName]:
activeDoc && activeDoc.sidebar === doc.sidebar,
})}
label={staticLabel ?? doc.id}
to={doc.path}
/>
);
}

View file

@ -20,6 +20,7 @@ const getVersionMainDoc = (version) =>
export default function DocsVersionDropdownNavbarItem({ export default function DocsVersionDropdownNavbarItem({
mobile, mobile,
docsPluginId, docsPluginId,
dropdownActiveClassDisabled,
...props ...props
}: Props): JSX.Element { }: Props): JSX.Element {
const activeDocContext = useActiveDocContext(docsPluginId); const activeDocContext = useActiveDocContext(docsPluginId);
@ -64,6 +65,7 @@ export default function DocsVersionDropdownNavbarItem({
label={dropdownLabel} label={dropdownLabel}
to={dropdownTo} to={dropdownTo}
items={getItems()} items={getItems()}
isActive={dropdownActiveClassDisabled ? () => false : undefined}
/> />
); );
} }

View file

@ -20,6 +20,9 @@ const NavbarItemComponents = {
docsVersionDropdown: () => docsVersionDropdown: () =>
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
require('@theme/NavbarItem/DocsVersionDropdownNavbarItem').default, require('@theme/NavbarItem/DocsVersionDropdownNavbarItem').default,
doc: () =>
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@theme/NavbarItem/DocNavbarItem').default,
} as const; } as const;
const getNavbarItemComponent = ( const getNavbarItemComponent = (

View file

@ -284,10 +284,12 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
activeBasePath?: string; activeBasePath?: string;
activeBaseRegex?: string; activeBaseRegex?: string;
to?: string; to?: string;
exact?: boolean;
href?: string; href?: string;
label?: string; label?: string;
activeClassName?: string; activeClassName?: string;
prependBaseUrlToHref?: string; prependBaseUrlToHref?: string;
isActive?: () => boolean;
} & ComponentProps<'a'>; } & ComponentProps<'a'>;
export type DesktopOrMobileNavBarItemProps = NavLinkProps & { export type DesktopOrMobileNavBarItemProps = NavLinkProps & {
@ -307,7 +309,10 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' { declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
export type Props = DefaultNavbarItemProps & {readonly docsPluginId?: string}; export type Props = DefaultNavbarItemProps & {
readonly docsPluginId?: string;
dropdownActiveClassDisabled?: boolean;
};
const DocsVersionDropdownNavbarItem: (props: Props) => JSX.Element; const DocsVersionDropdownNavbarItem: (props: Props) => JSX.Element;
export default DocsVersionDropdownNavbarItem; export default DocsVersionDropdownNavbarItem;
@ -322,6 +327,19 @@ declare module '@theme/NavbarItem/DocsVersionNavbarItem' {
export default DocsVersionNavbarItem; export default DocsVersionNavbarItem;
} }
declare module '@theme/NavbarItem/DocNavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
export type Props = DefaultNavbarItemProps & {
readonly docId: string;
readonly activeSidebarClassName: string;
readonly docsPluginId?: string;
};
const DocsSidebarNavbarItem: (props: Props) => JSX.Element;
export default DocsSidebarNavbarItem;
}
declare module '@theme/NavbarItem' { declare module '@theme/NavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {Props as DocsVersionDropdownNavbarItemProps} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; import type {Props as DocsVersionDropdownNavbarItemProps} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';

View file

@ -68,6 +68,16 @@ const DocsVersionDropdownNavbarItemSchema = Joi.object({
type: Joi.string().equal('docsVersionDropdown').required(), type: Joi.string().equal('docsVersionDropdown').required(),
position: NavbarItemPosition, position: NavbarItemPosition,
docsPluginId: Joi.string(), docsPluginId: Joi.string(),
dropdownActiveClassDisabled: Joi.boolean(),
});
const DocItemSchema = Joi.object({
type: Joi.string().equal('doc').required(),
position: NavbarItemPosition,
docId: Joi.string().required(),
label: Joi.string(),
docsPluginId: Joi.string(),
activeSidebarClassName: Joi.string().default('navbar__link--active'),
}); });
// Can this be made easier? :/ // Can this be made easier? :/
@ -94,6 +104,10 @@ const NavbarItemSchema = Joi.object().when({
is: isOfType('docsVersionDropdown'), is: isOfType('docsVersionDropdown'),
then: DocsVersionDropdownNavbarItemSchema, then: DocsVersionDropdownNavbarItemSchema,
}, },
{
is: isOfType('doc'),
then: DocItemSchema,
},
{ {
is: isOfType(undefined), is: isOfType(undefined),
then: Joi.forbidden().messages({ then: Joi.forbidden().messages({

View file

@ -262,6 +262,28 @@ module.exports = {
}; };
``` ```
### Navbar doc link
If you want to link to a specific doc, this special navbar item type will render the link to the doc of the provided `docId`. It will get the class `navbar__link--active` as long as you browse a doc of the same sidebar.
```js {5-10} title="docusaurus.config.js"
module.exports = {
themeConfig: {
navbar: {
items: [
{
type: 'doc',
position: 'left',
docId: 'introduction',
label: 'Docs',
activeSidebarClassName: 'navbar__link--active',
},
],
},
},
};
```
### Navbar docs version dropdown ### Navbar docs version dropdown
If you use docs with versioning, this special navbar item type that will render a dropdown with all your site's available versions. The user will be able to switch from one version to another, while staying on the same doc (as long as the doc id is constant across versions). If you use docs with versioning, this special navbar item type that will render a dropdown with all your site's available versions. The user will be able to switch from one version to another, while staying on the same doc (as long as the doc id is constant across versions).
@ -274,6 +296,7 @@ module.exports = {
{ {
type: 'docsVersionDropdown', type: 'docsVersionDropdown',
position: 'left', position: 'left',
// dropdownActiveClassDisabled: true, //
}, },
], ],
}, },

View file

@ -264,6 +264,19 @@ module.exports = {
{ {
type: 'docsVersionDropdown', type: 'docsVersionDropdown',
position: 'left', position: 'left',
dropdownActiveClassDisabled: true,
},
{
type: 'doc',
position: 'left',
docId: 'introduction',
label: 'Docs',
},
{
type: 'doc',
position: 'left',
docId: 'cli',
label: 'API',
}, },
{to: 'blog', label: 'Blog', position: 'left'}, {to: 'blog', label: 'Blog', position: 'left'},
{to: 'showcase', label: 'Showcase', position: 'left'}, {to: 'showcase', label: 'Showcase', position: 'left'},

View file

@ -39,16 +39,12 @@ module.exports = {
label: 'Advanced Guides', label: 'Advanced Guides',
items: ['using-plugins', 'using-themes', 'presets'], items: ['using-plugins', 'using-themes', 'presets'],
}, },
{ ],
type: 'category', api: [
label: 'API Reference', 'cli',
items: [ 'docusaurus-core',
'cli', 'api/docusaurus.config.js',
'docusaurus-core', 'lifecycle-apis',
'api/docusaurus.config.js', 'theme-classic',
'lifecycle-apis',
'theme-classic',
],
},
], ],
}; };

View file

@ -110,33 +110,28 @@
"id": "version-2.0.0-alpha.65/presets" "id": "version-2.0.0-alpha.65/presets"
} }
] ]
}
],
"api": [
{
"type": "doc",
"id": "version-2.0.0-alpha.65/cli"
}, },
{ {
"collapsed": true, "type": "doc",
"type": "category", "id": "version-2.0.0-alpha.65/docusaurus-core"
"label": "API Reference", },
"items": [ {
{ "type": "doc",
"type": "doc", "id": "version-2.0.0-alpha.65/api/docusaurus.config.js"
"id": "version-2.0.0-alpha.65/cli" },
}, {
{ "type": "doc",
"type": "doc", "id": "version-2.0.0-alpha.65/lifecycle-apis"
"id": "version-2.0.0-alpha.65/docusaurus-core" },
}, {
{ "type": "doc",
"type": "doc", "id": "version-2.0.0-alpha.65/theme-classic"
"id": "version-2.0.0-alpha.65/api/docusaurus.config.js"
},
{
"type": "doc",
"id": "version-2.0.0-alpha.65/lifecycle-apis"
},
{
"type": "doc",
"id": "version-2.0.0-alpha.65/theme-classic"
}
]
} }
] ]
} }