From 07b9e9cd62c900678cb1e359bcf061ac930f1fb1 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 27 May 2020 22:17:19 -0700 Subject: [PATCH] feat(v2): expanded sidebar categories by default (#2682) * feat: update sidebar categ to take collapsed prop * feat: add extra sidebars collapsed test * fix: only mutate item.collapsed if necessary * feat: update docs for SidebarItemCategory * fix: update snapshots * fix: update json to match new sidebar schema * fix: update last snapshot * refactor: check if item should be expanded * docs: update sidebar categories section * refactor: use new collpased on docusaurus * feat: only highlight category for active page * fix: check for window * refactor: use ExecutionEnviornment * refactor: make isCategoryOfActivePage pure * fix: rename docs to docs-introduction in sidebars * Update docs.md * misc: remove setting for every sidebar Co-authored-by: Yangshun Tay --- .../sidebars/sidebars-category.js | 5 ++ .../sidebars-collapsed-first-level.json | 20 +++++ .../sidebars/sidebars-collapsed.json | 21 +++++ .../__snapshots__/index.test.ts.snap | 14 ++++ .../__snapshots__/sidebars.test.ts.snap | 80 +++++++++++++++++++ .../__snapshots__/version.test.ts.snap | 4 + .../src/__tests__/sidebars.test.ts | 15 ++++ .../src/sidebars.ts | 9 ++- .../src/types.ts | 3 + .../src/theme/DocSidebar/index.js | 38 ++++++++- website/docs/docs.md | 21 +++++ website/sidebars.js | 69 ++++++++++------ 12 files changed, 273 insertions(+), 26 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category.js b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category.js index 402803c0c4..975f1203fe 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category.js +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-category.js @@ -9,25 +9,30 @@ module.exports = { docs: [ { type: 'category', + collapsed: true, label: 'level 1', items: [ 'a', { type: 'category', + collapsed: true, label: 'level 2', items: [ { type: 'category', + collapsed: true, label: 'level 3', items: [ 'c', { type: 'category', + collapsed: true, label: 'level 4', items: [ 'd', { type: 'category', + collapsed: true, label: 'deeper more more', items: ['e'], }, diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json new file mode 100644 index 0000000000..1251a7d454 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed-first-level.json @@ -0,0 +1,20 @@ +{ + "docs": [ + { + "type": "category", + "label": "Introduction", + "items": [ + "doc1" + ], + "collapsed": false + }, + { + "type": "category", + "label": "Powering MDX", + "items": [ + "doc2" + ], + "collapsed": false + } + ] +} \ No newline at end of file diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json new file mode 100644 index 0000000000..2199c5f557 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/sidebars/sidebars-collapsed.json @@ -0,0 +1,21 @@ +{ + "docs": { + "Test": [ + { + "type": "category", + "label": "Introduction", + "items": ["doc1"], + "collapsed": false + } + ], + "Reference": [ + { + "type": "category", + "label": "Powering MDX", + "items": ["doc2"], + "collapsed": false + } + ] + } + } + \ No newline at end of file diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index 321cb87ce3..9c2e00cd32 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -4,6 +4,7 @@ exports[`simple website content 1`] = ` Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "items": Array [ @@ -36,6 +37,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/hello", @@ -236,6 +238,7 @@ exports[`versioned website content: all sidebars 1`] = ` Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/next/foo/bar", @@ -247,6 +250,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/next/hello", @@ -260,6 +264,7 @@ Object { ], "version-1.0.0/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/1.0.0/foo/bar", @@ -276,6 +281,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/1.0.0/hello", @@ -289,6 +295,7 @@ Object { ], "version-1.0.1/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/foo/bar", @@ -300,6 +307,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/hello", @@ -319,6 +327,7 @@ Object { "docsSidebars": Object { "version-1.0.0/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/1.0.0/foo/bar", @@ -335,6 +344,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/1.0.0/hello", @@ -361,6 +371,7 @@ Object { "docsSidebars": Object { "version-1.0.1/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/foo/bar", @@ -372,6 +383,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/hello", @@ -397,6 +409,7 @@ Object { "docsSidebars": Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/next/foo/bar", @@ -408,6 +421,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "href": "/docs/next/hello", diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/sidebars.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/sidebars.test.ts.snap index e0bc01bdc9..0aa670c44b 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/sidebars.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/sidebars.test.ts.snap @@ -4,6 +4,7 @@ exports[`loadSidebars sidebars link 1`] = ` Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "href": "https://github.com", @@ -18,30 +19,107 @@ Object { } `; +exports[`loadSidebars sidebars with category.collapsed property 1`] = ` +Object { + "docs": Array [ + Object { + "collapsed": true, + "items": Array [ + Object { + "collapsed": false, + "items": Array [ + Object { + "id": "doc1", + "type": "doc", + }, + ], + "label": "Introduction", + "type": "category", + }, + ], + "label": "Test", + "type": "category", + }, + Object { + "collapsed": true, + "items": Array [ + Object { + "collapsed": false, + "items": Array [ + Object { + "id": "doc2", + "type": "doc", + }, + ], + "label": "Powering MDX", + "type": "category", + }, + ], + "label": "Reference", + "type": "category", + }, + ], +} +`; + +exports[`loadSidebars sidebars with category.collapsed property at first level 1`] = ` +Object { + "docs": Array [ + Object { + "collapsed": false, + "items": Array [ + Object { + "id": "doc1", + "type": "doc", + }, + ], + "label": "Introduction", + "type": "category", + }, + Object { + "collapsed": false, + "items": Array [ + Object { + "id": "doc2", + "type": "doc", + }, + ], + "label": "Powering MDX", + "type": "category", + }, + ], +} +`; + exports[`loadSidebars sidebars with deep level of category 1`] = ` Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "id": "a", "type": "doc", }, Object { + "collapsed": true, "items": Array [ Object { + "collapsed": true, "items": Array [ Object { "id": "c", "type": "doc", }, Object { + "collapsed": true, "items": Array [ Object { "id": "d", "type": "doc", }, Object { + "collapsed": true, "items": Array [ Object { "id": "e", @@ -100,6 +178,7 @@ exports[`loadSidebars sidebars with known sidebar item type 1`] = ` Object { "docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "id": "foo/bar", @@ -123,6 +202,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "id": "hello", diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap index 47ffaef77f..26c10f3c0a 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/version.test.ts.snap @@ -4,6 +4,7 @@ exports[`docsVersion first time versioning 1`] = ` Object { "version-1.0.0/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "items": Array [ @@ -33,6 +34,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "id": "version-1.0.0/hello", @@ -50,6 +52,7 @@ exports[`docsVersion not the first time versioning 1`] = ` Object { "version-2.0.0/docs": Array [ Object { + "collapsed": true, "items": Array [ Object { "id": "version-2.0.0/foo/bar", @@ -60,6 +63,7 @@ Object { "type": "category", }, Object { + "collapsed": true, "items": Array [ Object { "id": "version-2.0.0/hello", diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts index 49a788dfd9..11d882b22a 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/sidebars.test.ts @@ -126,4 +126,19 @@ describe('loadSidebars', () => { const result = loadSidebars(null); expect(result).toEqual({}); }); + + test('sidebars with category.collapsed property', async () => { + const sidebarPath = path.join(fixtureDir, 'sidebars-collapsed.json'); + const result = loadSidebars([sidebarPath]); + expect(result).toMatchSnapshot(); + }); + + test('sidebars with category.collapsed property at first level', async () => { + const sidebarPath = path.join( + fixtureDir, + 'sidebars-collapsed-first-level.json', + ); + const result = loadSidebars([sidebarPath]); + expect(result).toMatchSnapshot(); + }); }); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars.ts b/packages/docusaurus-plugin-content-docs/src/sidebars.ts index cfe1b36254..abe988984b 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars.ts @@ -33,6 +33,7 @@ function normalizeCategoryShorthand( ): SidebarItemCategoryRaw[] { return Object.entries(sidebar).map(([label, items]) => ({ type: 'category', + collapsed: true, label, items, })); @@ -56,7 +57,7 @@ function assertItem(item: Object, keys: string[]): void { } function assertIsCategory(item: any): asserts item is SidebarItemCategoryRaw { - assertItem(item, ['items', 'label']); + assertItem(item, ['items', 'label', 'collapsed']); if (typeof item.label !== 'string') { throw new Error( `Error loading ${JSON.stringify(item)}. "label" must be a string.`, @@ -67,6 +68,12 @@ function assertIsCategory(item: any): asserts item is SidebarItemCategoryRaw { `Error loading ${JSON.stringify(item)}. "items" must be an array.`, ); } + // "collapsed" is an optional property + if (item.hasOwnProperty('collapsed') && typeof item.collapsed !== 'boolean') { + throw new Error( + `Error loading ${JSON.stringify(item)}. "collapsed" must be a boolean.`, + ); + } } function assertIsDoc(item: any): asserts item is SidebarItemDoc { diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 16c3a034ad..642d9703ab 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -42,12 +42,14 @@ export interface SidebarItemCategory { type: 'category'; label: string; items: SidebarItem[]; + collapsed?: boolean; } export interface SidebarItemCategoryRaw { type: 'category'; label: string; items: SidebarItemRaw[]; + collapsed?: boolean; } export type SidebarItem = @@ -83,6 +85,7 @@ export interface DocsSidebarItemCategory { type: 'category'; label: string; items: DocsSidebarItem[]; + collapsed?: boolean; } export type DocsSidebarItem = SidebarItemLink | DocsSidebarItemCategory; diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js index cad9db38d5..b8c8cff518 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js @@ -6,6 +6,7 @@ */ import React, {useState, useCallback} from 'react'; +import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import classnames from 'classnames'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useAnnouncementBarContext from '@theme/hooks/useAnnouncementBarContext'; @@ -44,6 +45,28 @@ function DocSidebarItem({ setCollapsed((state) => !state); }); + // Make sure we have access to the window + const activePageRelativeUrl = ExecutionEnvironment.canUseDOM + ? window.location.pathname + window.location.search + : null; + + // We need to know if the category item + // is the parent of the active page + // If it is, this returns true and make sure to highlight this category + const isCategoryOfActivePage = (_items, _activePageRelativeUrl) => { + // Make sure we have items + if (typeof _items !== 'undefined') { + return _items.some((categoryItem) => { + // Grab the category item's href + const childHref = categoryItem.href; + // Compare it to the current active page + return _activePageRelativeUrl === childHref; + }); + } + + return false; + }; + switch (type) { case 'category': return ( @@ -56,7 +79,10 @@ function DocSidebarItem({ mutateSidebarCollapsingState(childItem, path)) .filter((val) => val).length > 0; + + // Check if the user wants the category to be expanded by default + const shouldExpand = item.collapsed === false; + // eslint-disable-next-line no-param-reassign item.collapsed = !anyChildItemsActive; + + if (shouldExpand) { + // eslint-disable-next-line no-param-reassign + item.collapsed = false; + } + return anyChildItemsActive; } diff --git a/website/docs/docs.md b/website/docs/docs.md index 5cfc0996fb..dcca52da45 100644 --- a/website/docs/docs.md +++ b/website/docs/docs.md @@ -243,6 +243,7 @@ type SidebarItemCategory = { type: 'category'; label: string; // Sidebar label text. items: SidebarItem[]; // Array of sidebar items. + collapsed: boolean; // Set the category to be collapsed or open by default }; ``` @@ -292,6 +293,26 @@ module.exports = { }; ``` +#### Expanded categories by default + +For docs that have collapsible categories, you may want more fine-grain control over certain categories. If you want specific categories to be always expanded, you can set `collapsed` to `false`: + +```js title="sidebars.js" +module.exports = { + docs: { + Guides: [ + 'creating-pages', + { + type: 'category', + label: 'Docs', + collapsed: false, + items: ['markdown-features', 'sidebar', 'versioning'], + }, + ], + }, +}; +``` + ## Docs-only mode If you just want the documentation feature, you can enable "docs-only mode". diff --git a/website/sidebars.js b/website/sidebars.js index 97658a054d..94f27479d4 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -6,30 +6,51 @@ */ module.exports = { - docs: { - Docusaurus: ['introduction', 'design-principles', 'contributing'], - 'Getting Started': ['installation', 'configuration'], - Guides: [ - 'creating-pages', - 'styling-layout', - 'static-assets', - { - Docs: ['docs-introduction', 'markdown-features', 'versioning'], - }, - 'blog', - 'search', - 'deployment', - 'migrating-from-v1-to-v2', - ], - 'Advanced Guides': ['using-plugins', 'using-themes', 'presets'], - 'API Reference': [ - 'cli', - 'docusaurus-core', - 'docusaurus.config.js', - 'lifecycle-apis', - 'theme-classic', - ], - }, + docs: [ + { + type: 'category', + label: 'Docusaurus', + items: ['introduction', 'design-principles', 'contributing'], + }, + { + type: 'category', + label: 'Getting Started', + collapsed: false, + items: ['installation', 'configuration'], + }, + { + type: 'category', + label: 'Guides', + items: [ + 'creating-pages', + 'styling-layout', + 'static-assets', + { + Docs: ['docs-introduction', 'markdown-features', 'versioning'], + }, + 'blog', + 'search', + 'deployment', + 'migrating-from-v1-to-v2', + ], + }, + { + type: 'category', + label: 'Advanced Guides', + items: ['using-plugins', 'using-themes', 'presets'], + }, + { + type: 'category', + label: 'API Reference', + items: [ + 'cli', + 'docusaurus-core', + 'docusaurus.config.js', + 'lifecycle-apis', + 'theme-classic', + ], + }, + ], community: [ 'support', 'team',