From df615c617fed55d26ad0d1accee3e22b1a0d0ff0 Mon Sep 17 00:00:00 2001 From: Will Brock Date: Mon, 6 Aug 2018 20:16:56 -0400 Subject: [PATCH 1/4] Add ability to specify sub categories in sidebar.json --- docs/guides-navigation.md | 23 +++ .../__fixtures__/metadata-subcategories.js | 65 ++++++++ .../__fixtures__/sidebar-subcategories.js | 11 ++ lib/server/__tests__/__fixtures__/sidebar.js | 6 + lib/server/__tests__/readCategories.test.js | 91 +++++++++++ lib/server/__tests__/readMetadata.test.js | 77 ++++++++++ v1/lib/core/DocsSidebar.js | 18 ++- v1/lib/core/nav/SideNav.js | 17 ++- .../server/__tests__/__fixtures__/metadata.js | 17 +++ v1/lib/server/__tests__/utils.test.js | 59 ++++++++ v1/lib/server/readCategories.js | 143 ++++++++++-------- v1/lib/server/readMetadata.js | 57 +++++-- v1/lib/server/utils.js | 20 +++ v1/lib/static/css/main.css | 11 +- 14 files changed, 531 insertions(+), 84 deletions(-) create mode 100644 lib/server/__tests__/__fixtures__/metadata-subcategories.js create mode 100644 lib/server/__tests__/__fixtures__/sidebar-subcategories.js create mode 100644 lib/server/__tests__/__fixtures__/sidebar.js create mode 100644 lib/server/__tests__/readCategories.test.js create mode 100644 lib/server/__tests__/readMetadata.test.js diff --git a/docs/guides-navigation.md b/docs/guides-navigation.md index 8648d29a3440..800a66329d10 100644 --- a/docs/guides-navigation.md +++ b/docs/guides-navigation.md @@ -94,6 +94,29 @@ You should provide `directory/id` instead of `id` in `sidebars.json`. } ``` +### Adding Sub Categories + +It is possibile to add sub categories to a sidebar. Instead of passing an array to the category like the previous examples you can pass an object where +the keys will be the sub category name. You can then pass an array of document ids to the sub category. + +```js +{ + "examples-sidebar" : { + "My Example Category" : { + "My Example Sub Category" : [ + "my-examples", + ... + ], + "My Next Sub Category" : [ + "some-other-examples" + ] + ... + }, + ... + } +} +``` + ### Adding New Sidebars You can also put a document in a new sidebar. In the following example, we are creating an `examples-sidebar` sidebar within `sidebars.json` that has a category called `My Example Category` containing a document with an `id` of `my-examples`. diff --git a/lib/server/__tests__/__fixtures__/metadata-subcategories.js b/lib/server/__tests__/__fixtures__/metadata-subcategories.js new file mode 100644 index 000000000000..005ff534d215 --- /dev/null +++ b/lib/server/__tests__/__fixtures__/metadata-subcategories.js @@ -0,0 +1,65 @@ +module.exports = { + 'en-doc1': { + id: 'en-doc1', + title: 'Document 1', + source: 'doc1.md', + version: 'next', + permalink: 'docs/en/next/doc1.html', + localized_id: 'doc1', + language: 'en', + sidebar: 'docs', + category: 'Test', + next_id: 'doc2', + next: 'en-doc2', + next_title: 'Document 2', + sub_category: 'Sub Cat 1', + sort: 1, + }, + 'en-doc2': { + id: 'en-doc2', + title: 'Document 2', + source: 'doc2.md', + version: 'next', + permalink: 'docs/en/next/doc2.html', + localized_id: 'doc2', + language: 'en', + sidebar: 'docs', + category: 'Test', + previous_id: 'doc1', + previous: 'en-doc1', + previous_title: 'Document 1', + sub_category: 'Sub Cat 1', + sort: 2, + }, + 'en-doc3': { + id: 'en-doc3', + title: 'Document 3', + source: 'doc3.md', + version: 'next', + permalink: 'docs/en/next/doc3.html', + localized_id: 'doc3', + language: 'en', + sidebar: 'docs', + category: 'Test', + previous_id: 'doc2', + previous: 'en-doc2', + previous_title: 'Document 2', + sub_category: 'Sub Cat 2', + sort: 3, + }, + 'en-doc4': { + id: 'en-doc4', + title: 'Document 4', + source: 'doc4.md', + version: 'next', + permalink: 'docs/en/next/doc4.html', + localized_id: 'doc4', + language: 'en', + sidebar: 'docs', + category: 'Test 2', + previous_id: 'doc3', + previous: 'en-doc3', + previous_title: 'Document 3', + sort: 4, + }, +}; diff --git a/lib/server/__tests__/__fixtures__/sidebar-subcategories.js b/lib/server/__tests__/__fixtures__/sidebar-subcategories.js new file mode 100644 index 000000000000..3af6e72ada7c --- /dev/null +++ b/lib/server/__tests__/__fixtures__/sidebar-subcategories.js @@ -0,0 +1,11 @@ +module.exports = { + docs: { + 'First Category': { + 'Sub Cat One': ['doc2', 'doc1'], + 'Sub Cat Two': ['doc3', 'doc5'], + }, + 'Second Category': { + Hello: ['doc4'], + }, + }, +}; diff --git a/lib/server/__tests__/__fixtures__/sidebar.js b/lib/server/__tests__/__fixtures__/sidebar.js new file mode 100644 index 000000000000..8da95c267674 --- /dev/null +++ b/lib/server/__tests__/__fixtures__/sidebar.js @@ -0,0 +1,6 @@ +module.exports = { + docs: { + 'First Category': ['doc1', 'doc2'], + 'Second Category': ['doc4', 'doc3'], + }, +}; diff --git a/lib/server/__tests__/readCategories.test.js b/lib/server/__tests__/readCategories.test.js new file mode 100644 index 000000000000..a2add3c68033 --- /dev/null +++ b/lib/server/__tests__/readCategories.test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const readCategories = require('../readCategories'); +const generalMetadata = require('./__fixtures__/metadata.js'); +const subCategoryMetadata = require('./__fixtures__/metadata-subcategories.js'); + +const languages = [ + { + enabled: true, + name: 'English', + tag: 'en', + }, + { + enabled: true, + name: 'Foo', + tag: 'ko', + }, +]; + +const languagesMultiple = [ + { + enabled: false, + name: 'English', + tag: 'en', + }, + { + enabled: true, + name: 'Foo', + tag: 'ko', + }, +]; + +describe('readCategories', () => { + test('should return proper categories and their pages', () => { + const categories = readCategories('docs', generalMetadata, languages); + + expect(categories.en).toBeDefined(); + expect(categories.en.length).toBe(2); + + expect(categories.en[0].name).toBe('Test'); + expect(categories.en[0].links.length).toBe(2); + expect(categories.en[0].links[0].id).toBe('en-doc1'); + expect(categories.en[0].links[1].id).toBe('en-doc2'); + + expect(categories.en[1].name).toBe('Test 2'); + expect(categories.en[1].links.length).toBe(1); + expect(categories.en[1].links[0].id).toBe('en-doc3'); + }); + + test('should return proper data with categories and sub categories', () => { + const categories = readCategories('docs', subCategoryMetadata, languages); + + expect(categories.en).toBeDefined(); + expect(categories.ko).toBeDefined(); + expect(categories.en.length).toBe(2); + + expect(categories.en[0].name).toBe('Test'); + expect(categories.en[0].links.length).toBe(0); + expect(categories.en[0].sub_categories.length).toBe(2); + + expect(categories.en[0].sub_categories[0].name).toBe('Sub Cat 1'); + expect(categories.en[0].sub_categories[0].links.length).toBe(2); + expect(categories.en[0].sub_categories[0].links[0].id).toBe('en-doc1'); + expect(categories.en[0].sub_categories[0].links[1].id).toBe('en-doc2'); + + expect(categories.en[0].sub_categories[1].name).toBe('Sub Cat 2'); + expect(categories.en[0].sub_categories[1].links.length).toBe(1); + expect(categories.en[0].sub_categories[1].links[0].id).toBe('en-doc3'); + + expect(categories.en[1].name).toBe('Test 2'); + expect(categories.en[1].links.length).toBe(1); + expect(categories.en[1].links[0].id).toBe('en-doc4'); + expect(categories.en[1].sub_categories).not.toBeDefined(); + }); + + test('should return proper languages when not enabled', () => { + const categories = readCategories( + 'docs', + generalMetadata, + languagesMultiple + ); + + expect(categories.en).not.toBeDefined(); + expect(categories.ko).toBeDefined(); + }); +}); diff --git a/lib/server/__tests__/readMetadata.test.js b/lib/server/__tests__/readMetadata.test.js new file mode 100644 index 000000000000..0edc3f7087c8 --- /dev/null +++ b/lib/server/__tests__/readMetadata.test.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {readSidebar} = require('../readMetadata'); +const sidebar = require('./__fixtures__/sidebar'); +const sidebarSubCategories = require('./__fixtures__/sidebar-subcategories'); + +jest.mock('../env', () => ({ + translation: { + enabled: true, + enabledLanguages: () => [ + { + enabled: true, + name: 'English', + tag: 'en', + }, + { + enabled: true, + name: '한국어', + tag: 'ko', + }, + ], + }, + versioning: { + enabled: true, + defaultVersion: '1.0.0', + }, +})); + +jest.mock(`${process.cwd()}/siteConfig.js`, () => true, {virtual: true}); +jest.mock(`${process.cwd()}/sidebar.json`, () => true, {virtual: true}); + +describe('readMetadata', () => { + describe('readSidebar', () => { + it('should verify regular category data and verify sort', () => { + const order = readSidebar(sidebar); + + // Put in this order to verify sort + ['doc1', 'doc2', 'doc4', 'doc3'].forEach((id, index) => { + expect(order[id]).toBeDefined(); + expect(order[id].sort).toBe(index + 1); + }); + + expect(order.doc1.previous).toBeUndefined(); + expect(order.doc2.previous).toBe('doc1'); + + expect(order.doc1.next).toBe('doc2'); + expect(order.doc2.next).toBe('doc4'); + + expect(order.doc1.sub_category).toBeFalsy(); + }); + + test('should verify sub category data and verify sort', () => { + const order = readSidebar(sidebarSubCategories); + + // Put in this order to verify sort + ['doc2', 'doc1', 'doc3', 'doc5', 'doc4'].forEach((id, index) => { + expect(order[id]).toBeDefined(); + expect(order[id].sort).toBe(index + 1); + }); + + expect(order.doc2.sidebar).toBe('docs'); + expect(order.doc2.category).toBe('First Category'); + expect(order.doc2.sub_category).toBe('Sub Cat One'); + + expect(order.doc1.category).toBe('First Category'); + expect(order.doc1.sub_category).toBe('Sub Cat One'); + + expect(order.doc3.category).toBe('First Category'); + expect(order.doc3.sub_category).toBe('Sub Cat Two'); + }); + }); +}); diff --git a/v1/lib/core/DocsSidebar.js b/v1/lib/core/DocsSidebar.js index 87f1ce13ecd7..fee51480c5e4 100644 --- a/v1/lib/core/DocsSidebar.js +++ b/v1/lib/core/DocsSidebar.js @@ -6,14 +6,30 @@ */ const React = require('react'); +const fs = require('fs'); const Container = require('./Container.js'); const SideNav = require('./nav/SideNav.js'); +const Metadata = require('../core/metadata.js'); const readCategories = require('../server/readCategories.js'); +let languages; + +if (fs.existsSync(`../server/languages.js`)) { + languages = require(`../server/languages.js`); +} else { + languages = [ + { + enabled: true, + name: 'English', + tag: 'en', + }, + ]; +} + class DocsSidebar extends React.Component { render() { const sidebar = this.props.metadata.sidebar; - const docsCategories = readCategories(sidebar); + const docsCategories = readCategories(sidebar, Metadata, languages); const categoryName = docsCategories[this.props.metadata.language][0].name; if (!categoryName) { return null; diff --git a/v1/lib/core/nav/SideNav.js b/v1/lib/core/nav/SideNav.js index 56c980c9152c..0f76d732542e 100644 --- a/v1/lib/core/nav/SideNav.js +++ b/v1/lib/core/nav/SideNav.js @@ -67,7 +67,22 @@ class SideNav extends React.Component {

{this.getLocalizedCategoryString(category.name)}

- + + + ); + } + + renderSubCategory(subCategory) { + return ( +
+

+ {this.getLocalizedCategoryString(subCategory.name)} +

+
); } diff --git a/v1/lib/server/__tests__/__fixtures__/metadata.js b/v1/lib/server/__tests__/__fixtures__/metadata.js index bf4ca08f3e14..8ca4afeaf039 100644 --- a/v1/lib/server/__tests__/__fixtures__/metadata.js +++ b/v1/lib/server/__tests__/__fixtures__/metadata.js @@ -12,6 +12,7 @@ module.exports = { next_id: 'doc2', next: 'en-doc2', next_title: 'Document 2', + sort: 1, }, 'en-doc2': { id: 'en-doc2', @@ -26,6 +27,22 @@ module.exports = { previous_id: 'doc1', previous: 'en-doc1', previous_title: 'Document 1', + sort: 2, + }, + 'en-doc3': { + id: 'en-doc3', + title: 'Document 3', + source: 'doc3.md', + version: 'next', + permalink: 'docs/en/next/doc3.html', + localized_id: 'doc3', + language: 'en', + sidebar: 'docs', + category: 'Test 2', + previous_id: 'doc2', + previous: 'en-doc2', + previous_title: 'Document 2', + sort: 3, }, 'ko-doc1': { id: 'ko-doc1', diff --git a/v1/lib/server/__tests__/utils.test.js b/v1/lib/server/__tests__/utils.test.js index 017140e09822..60d5ce7579b7 100644 --- a/v1/lib/server/__tests__/utils.test.js +++ b/v1/lib/server/__tests__/utils.test.js @@ -88,4 +88,63 @@ describe('server utils', () => { expect(utils.getSubDir(docE, docsDir)).toBeNull(); expect(utils.getSubDir(docE, translatedDir)).toEqual('lol/lah'); }); + + describe('validateSidebar', () => { + test('should throw an error for invalid pages', () => { + const metadata = { + id: 'doc1', + sidebar: 'docs', + next_id: 'doc2', + next: 'doc2', + }; + + const pages = { + doc1: {}, + }; + + expect(() => { + utils.validateSidebar(metadata, pages); + }).toThrow( + `Improper sidebars.json file, document with id 'doc2' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.` + ); + }); + + test('should throw an error for invalid version pages', () => { + const metadata = { + id: 'doc1', + version: 'foo', + sidebar: 'docs', + next_id: 'doc2', + next: 'doc2', + }; + + const pages = { + doc1: {}, + }; + + expect(() => { + utils.validateSidebar(metadata, pages); + }).toThrow( + `Improper sidebars file for version foo, document with id 'doc2' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.` + ); + }); + + test('should pass validate', () => { + const metadata = { + id: 'doc1', + sidebar: 'docs', + next_id: 'doc2', + next: 'doc2', + }; + + const pages = { + doc1: {}, + doc2: {}, + }; + + expect(() => { + utils.validateSidebar(metadata, pages); + }).not.toThrow(); + }); + }); }); diff --git a/v1/lib/server/readCategories.js b/v1/lib/server/readCategories.js index 472d7521074f..2a265a0ba3e4 100644 --- a/v1/lib/server/readCategories.js +++ b/v1/lib/server/readCategories.js @@ -5,101 +5,112 @@ * LICENSE file in the root directory of this source tree. */ -const fs = require('fs'); - -const Metadata = require('../core/metadata.js'); - -const CWD = process.cwd(); -let languages; -if (fs.existsSync(`${CWD}/languages.js`)) { - languages = require(`${CWD}/languages.js`); -} else { - languages = [ - { - enabled: true, - name: 'English', - tag: 'en', - }, - ]; -} +const {validateSidebar} = require('./utils'); // returns data broken up into categories for a sidebar -function readCategories(sidebar) { +function readCategories(sidebar, allMetadata, languages) { const enabledLanguages = languages .filter(lang => lang.enabled) .map(lang => lang.tag); const allCategories = {}; + // Go through each language that might be defined for (let k = 0; k < enabledLanguages.length; ++k) { const language = enabledLanguages[k]; - const metadatas = []; - Object.keys(Metadata).forEach(id => { - const metadata = Metadata[id]; + const categories = []; + const pages = {}; + + // Get the metadata for the current sidebar + Object.keys(allMetadata).forEach(id => { + const metadata = allMetadata[id]; if (metadata.sidebar === sidebar && metadata.language === language) { metadatas.push(metadata); + pages[metadata.id] = metadata; } }); - // Build a hashmap of article_id -> metadata - const articles = {}; - for (let i = 0; i < metadatas.length; ++i) { - const metadata = metadatas[i]; - articles[metadata.id] = metadata; - } + // Sort the metadata + metadatas.sort((a, b) => a.sort - b.sort); - // Build a hashmap of article_id -> previous_id - const previous = {}; + // Store the correct sort of categories and sub categories for later + const sortedCategories = []; + const sortedSubCategories = []; for (let i = 0; i < metadatas.length; ++i) { const metadata = metadatas[i]; - if (metadata.next) { - if (!articles[metadata.next]) { - throw new Error( - metadata.version - ? `Improper sidebars file for version ${ - metadata.version - }, document with id '${ - metadata.next - }' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.` - : `Improper sidebars.json file, document with id '${ - metadata.next - }' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.`, - ); - } - previous[articles[metadata.next].id] = metadata.id; + const category = metadata.category; + const subCategory = metadata.sub_category; + + if (!sortedCategories.includes(category)) { + sortedCategories.push(category); + } + + if (subCategory && !sortedSubCategories.includes(subCategory)) { + sortedSubCategories.push(subCategory); } } - // Find the first element which doesn't have any previous - let first = null; - for (let i = 0; i < metadatas.length; ++i) { + // Index categories and sub categories with all of their documents + const indexedCategories = {}; + const indexedSubCategories = {}; + for (let i = 0; i < metadatas.length; i++) { const metadata = metadatas[i]; - if (!previous[metadata.id]) { - first = metadata; - break; + const category = metadata.category; + const subCategory = metadata.sub_category; + + // Validate pages in the sidebar + validateSidebar(metadata, pages); + + if (!indexedCategories[category]) { + indexedCategories[category] = []; + } + + if (!subCategory) { + indexedCategories[category].push(metadata); + } + + if (subCategory) { + if (!indexedSubCategories[category]) { + indexedSubCategories[category] = {}; + } + + if (!indexedSubCategories[category][subCategory]) { + indexedSubCategories[category][subCategory] = []; + } + + indexedSubCategories[category][subCategory].push(metadata); } } - const categories = []; - let currentCategory = null; - - let metadata = first; - let i = 0; - while (metadata && i++ < 1000) { - if (!currentCategory || metadata.category !== currentCategory.name) { - if (currentCategory) { - categories.push(currentCategory); + // Generate data for each category and sub categories + for (let i = 0; i < sortedCategories.length; i++) { + const category = sortedCategories[i]; + const currentCategory = { + name: category, + links: indexedCategories[category], + }; + + for (let ii = 0; ii < sortedSubCategories.length; ii++) { + const subCategory = sortedSubCategories[ii]; + + if ( + indexedSubCategories[category] && + indexedSubCategories[category][subCategory] + ) { + if (!currentCategory.sub_categories) { + currentCategory.sub_categories = []; + } + + currentCategory.sub_categories.push({ + name: subCategory, + links: indexedSubCategories[category][subCategory], + }); } - currentCategory = { - name: metadata.category, - links: [], - }; } - currentCategory.links.push(metadata); - metadata = articles[metadata.next]; + + categories.push(currentCategory); } - categories.push(currentCategory); allCategories[language] = categories; } diff --git a/v1/lib/server/readMetadata.js b/v1/lib/server/readMetadata.js index 120a4681ffee..41eae0ff6804 100644 --- a/v1/lib/server/readMetadata.js +++ b/v1/lib/server/readMetadata.js @@ -33,6 +33,13 @@ const SupportedHeaderFields = new Set([ 'custom_edit_url', ]); +let allSidebars; +if (fs.existsSync(`${CWD}/sidebars.json`)) { + allSidebars = require(`${CWD}/sidebars.json`); +} else { + allSidebars = {}; +} + // Can have a custom docs path. Top level folder still needs to be in directory // at the same level as `website`, not inside `website`. // e.g., docs/whereDocsReallyExist @@ -42,27 +49,36 @@ const SupportedHeaderFields = new Set([ function getDocsPath() { return siteConfig.customDocsPath ? siteConfig.customDocsPath : 'docs'; } + // returns map from id to object containing sidebar ordering info -function readSidebar() { - let allSidebars; - if (fs.existsSync(`${CWD}/sidebars.json`)) { - allSidebars = require(`${CWD}/sidebars.json`); - } else { - allSidebars = {}; - } - Object.assign(allSidebars, versionFallback.sidebarData()); +function readSidebar(sidebars = {}) { + Object.assign(sidebars, versionFallback.sidebarData()); const order = {}; - Object.keys(allSidebars).forEach(sidebar => { - const categories = allSidebars[sidebar]; + Object.keys(sidebars).forEach(sidebar => { + const categories = sidebars[sidebar]; let ids = []; const categoryOrder = []; + const subCategoryOrder = []; Object.keys(categories).forEach(category => { - ids = ids.concat(categories[category]); - for (let i = 0; i < categories[category].length; i++) { - categoryOrder.push(category); + if (Array.isArray(categories[category])) { + ids = ids.concat(categories[category]); + + for (let i = 0; i < categories[category].length; i++) { + categoryOrder.push(category); + subCategoryOrder.push(''); + } + } else { + Object.keys(categories[category]).forEach(subCategory => { + ids = ids.concat(categories[category][subCategory]); + + for (let i = 0; i < categories[category][subCategory].length; i++) { + categoryOrder.push(category); + subCategoryOrder.push(subCategory); + } + }); } }); @@ -70,16 +86,22 @@ function readSidebar() { const id = ids[i]; let previous; let next; + if (i > 0) previous = ids[i - 1]; + if (i < ids.length - 1) next = ids[i + 1]; + order[id] = { previous, next, sidebar, category: categoryOrder[i], + sub_category: subCategoryOrder[i], + sort: i + 1, }; } }); + return order; } @@ -139,12 +161,14 @@ function processMetadata(file, refDir) { metadata.id = (env.translation.enabled ? `${language}-` : '') + metadata.id; metadata.language = env.translation.enabled ? language : 'en'; - const order = readSidebar(); + const order = readSidebar(allSidebars); const id = metadata.localized_id; if (order[id]) { metadata.sidebar = order[id].sidebar; metadata.category = order[id].category; + metadata.sub_category = order[id].sub_category; + metadata.sort = order[id].sort; if (order[id].next) { metadata.next_id = order[id].next; @@ -165,7 +189,7 @@ function processMetadata(file, refDir) { function generateMetadataDocs() { let order; try { - order = readSidebar(); + order = readSidebar(allSidebars); } catch (e) { console.error(e); process.exit(1); @@ -248,6 +272,9 @@ function generateMetadataDocs() { if (order[id]) { metadata.sidebar = order[id].sidebar; metadata.category = order[id].category; + metadata.sub_category = order[id].sub_category; + metadata.sort = order[id].sort; + if (order[id].next) { metadata.next_id = order[id].next.replace( `version-${metadata.version}-`, diff --git a/v1/lib/server/utils.js b/v1/lib/server/utils.js index 430086a078b0..d33c867fccd4 100644 --- a/v1/lib/server/utils.js +++ b/v1/lib/server/utils.js @@ -66,10 +66,30 @@ function autoPrefixCss(cssContent) { .then(result => result.css); } +// Validate the docs in the sidebar are valid +function validateSidebar(metadata, pages) { + if (metadata.next) { + if (!pages[metadata.next]) { + throw new Error( + metadata.version + ? `Improper sidebars file for version ${ + metadata.version + }, document with id '${ + metadata.next + }' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.` + : `Improper sidebars.json file, document with id '${ + metadata.next + }' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.` + ); + } + } +} + module.exports = { getSubDir, getLanguage, isSeparateCss, minifyCss, autoPrefixCss, + validateSidebar, }; diff --git a/v1/lib/static/css/main.css b/v1/lib/static/css/main.css index 8bcacfea3950..9d38766fa8bc 100644 --- a/v1/lib/static/css/main.css +++ b/v1/lib/static/css/main.css @@ -1756,7 +1756,12 @@ input::placeholder { margin-bottom: 16px; } -.toc .toggleNav .navGroup .navGroupCategoryTitle { +.toc .toggleNav .subNavGroup { + margin-top : 16px; +} + +.toc .toggleNav .navGroup .navGroupCategoryTitle, +.toc .toggleNav .navGroup .navGroupSubCategoryTitle { color: #393939; font-size: 18px; font-weight: 500; @@ -1765,6 +1770,10 @@ input::placeholder { margin-top: 0; } +.toc .toggleNav .navGroup .navGroupSubCategoryTitle { + font-size: 16px; +} + .toc .toggleNav .navGroup .navListItem { margin: 0; } From 18642a3af55b6af3f8d6faf42d3e0fe86f9413df Mon Sep 17 00:00:00 2001 From: endiliey Date: Mon, 8 Oct 2018 15:09:42 +0800 Subject: [PATCH 2/4] move v1 files to correct place --- .../server/__tests__/__fixtures__/metadata-subcategories.js | 0 .../server/__tests__/__fixtures__/sidebar-subcategories.js | 0 {lib => v1/lib}/server/__tests__/__fixtures__/sidebar.js | 0 {lib => v1/lib}/server/__tests__/readCategories.test.js | 2 +- {lib => v1/lib}/server/__tests__/readMetadata.test.js | 0 v1/lib/server/__tests__/utils.test.js | 4 ++-- v1/lib/server/utils.js | 2 +- 7 files changed, 4 insertions(+), 4 deletions(-) rename {lib => v1/lib}/server/__tests__/__fixtures__/metadata-subcategories.js (100%) rename {lib => v1/lib}/server/__tests__/__fixtures__/sidebar-subcategories.js (100%) rename {lib => v1/lib}/server/__tests__/__fixtures__/sidebar.js (100%) rename {lib => v1/lib}/server/__tests__/readCategories.test.js (99%) rename {lib => v1/lib}/server/__tests__/readMetadata.test.js (100%) diff --git a/lib/server/__tests__/__fixtures__/metadata-subcategories.js b/v1/lib/server/__tests__/__fixtures__/metadata-subcategories.js similarity index 100% rename from lib/server/__tests__/__fixtures__/metadata-subcategories.js rename to v1/lib/server/__tests__/__fixtures__/metadata-subcategories.js diff --git a/lib/server/__tests__/__fixtures__/sidebar-subcategories.js b/v1/lib/server/__tests__/__fixtures__/sidebar-subcategories.js similarity index 100% rename from lib/server/__tests__/__fixtures__/sidebar-subcategories.js rename to v1/lib/server/__tests__/__fixtures__/sidebar-subcategories.js diff --git a/lib/server/__tests__/__fixtures__/sidebar.js b/v1/lib/server/__tests__/__fixtures__/sidebar.js similarity index 100% rename from lib/server/__tests__/__fixtures__/sidebar.js rename to v1/lib/server/__tests__/__fixtures__/sidebar.js diff --git a/lib/server/__tests__/readCategories.test.js b/v1/lib/server/__tests__/readCategories.test.js similarity index 99% rename from lib/server/__tests__/readCategories.test.js rename to v1/lib/server/__tests__/readCategories.test.js index a2add3c68033..5904aaf8039f 100644 --- a/lib/server/__tests__/readCategories.test.js +++ b/v1/lib/server/__tests__/readCategories.test.js @@ -82,7 +82,7 @@ describe('readCategories', () => { const categories = readCategories( 'docs', generalMetadata, - languagesMultiple + languagesMultiple, ); expect(categories.en).not.toBeDefined(); diff --git a/lib/server/__tests__/readMetadata.test.js b/v1/lib/server/__tests__/readMetadata.test.js similarity index 100% rename from lib/server/__tests__/readMetadata.test.js rename to v1/lib/server/__tests__/readMetadata.test.js diff --git a/v1/lib/server/__tests__/utils.test.js b/v1/lib/server/__tests__/utils.test.js index 60d5ce7579b7..474f754db2a0 100644 --- a/v1/lib/server/__tests__/utils.test.js +++ b/v1/lib/server/__tests__/utils.test.js @@ -105,7 +105,7 @@ describe('server utils', () => { expect(() => { utils.validateSidebar(metadata, pages); }).toThrow( - `Improper sidebars.json file, document with id 'doc2' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.` + `Improper sidebars.json file, document with id 'doc2' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.`, ); }); @@ -125,7 +125,7 @@ describe('server utils', () => { expect(() => { utils.validateSidebar(metadata, pages); }).toThrow( - `Improper sidebars file for version foo, document with id 'doc2' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.` + `Improper sidebars file for version foo, document with id 'doc2' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.`, ); }); diff --git a/v1/lib/server/utils.js b/v1/lib/server/utils.js index d33c867fccd4..4b9a32143aca 100644 --- a/v1/lib/server/utils.js +++ b/v1/lib/server/utils.js @@ -79,7 +79,7 @@ function validateSidebar(metadata, pages) { }' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.` : `Improper sidebars.json file, document with id '${ metadata.next - }' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.` + }' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.`, ); } } From 39d8d877dffbc27d8389c90457b88ca593e84e88 Mon Sep 17 00:00:00 2001 From: endiliey Date: Mon, 8 Oct 2018 15:39:46 +0800 Subject: [PATCH 3/4] refactor implementation --- v1/lib/core/DocsSidebar.js | 2 +- v1/lib/server/readMetadata.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v1/lib/core/DocsSidebar.js b/v1/lib/core/DocsSidebar.js index fee51480c5e4..af58dafdda27 100644 --- a/v1/lib/core/DocsSidebar.js +++ b/v1/lib/core/DocsSidebar.js @@ -30,7 +30,7 @@ class DocsSidebar extends React.Component { render() { const sidebar = this.props.metadata.sidebar; const docsCategories = readCategories(sidebar, Metadata, languages); - const categoryName = docsCategories[this.props.metadata.language][0].name; + const categoryName = this.props.metadata.category; if (!categoryName) { return null; } diff --git a/v1/lib/server/readMetadata.js b/v1/lib/server/readMetadata.js index 41eae0ff6804..8245bc990ece 100644 --- a/v1/lib/server/readMetadata.js +++ b/v1/lib/server/readMetadata.js @@ -68,7 +68,7 @@ function readSidebar(sidebars = {}) { for (let i = 0; i < categories[category].length; i++) { categoryOrder.push(category); - subCategoryOrder.push(''); + subCategoryOrder.push(undefined); } } else { Object.keys(categories[category]).forEach(subCategory => { From ce549c00048928a23dc7e74e2080fa9842fc792d Mon Sep 17 00:00:00 2001 From: endiliey Date: Mon, 8 Oct 2018 16:01:53 +0800 Subject: [PATCH 4/4] refactor naming for implementation clarity --- v1/lib/server/__tests__/utils.test.js | 16 ++++++++-------- v1/lib/server/readCategories.js | 10 +++++----- v1/lib/server/utils.js | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/v1/lib/server/__tests__/utils.test.js b/v1/lib/server/__tests__/utils.test.js index 474f754db2a0..1ccc811f8bdf 100644 --- a/v1/lib/server/__tests__/utils.test.js +++ b/v1/lib/server/__tests__/utils.test.js @@ -90,7 +90,7 @@ describe('server utils', () => { }); describe('validateSidebar', () => { - test('should throw an error for invalid pages', () => { + test('should throw an error for invalid sidebarMetadatas', () => { const metadata = { id: 'doc1', sidebar: 'docs', @@ -98,18 +98,18 @@ describe('server utils', () => { next: 'doc2', }; - const pages = { + const sidebarMetadatas = { doc1: {}, }; expect(() => { - utils.validateSidebar(metadata, pages); + utils.validateSidebar(metadata, sidebarMetadatas); }).toThrow( `Improper sidebars.json file, document with id 'doc2' not found. Make sure that documents with the ids specified in sidebars.json exist and that no ids are repeated.`, ); }); - test('should throw an error for invalid version pages', () => { + test('should throw an error for invalid version sidebarMetadatas', () => { const metadata = { id: 'doc1', version: 'foo', @@ -118,12 +118,12 @@ describe('server utils', () => { next: 'doc2', }; - const pages = { + const sidebarMetadatas = { doc1: {}, }; expect(() => { - utils.validateSidebar(metadata, pages); + utils.validateSidebar(metadata, sidebarMetadatas); }).toThrow( `Improper sidebars file for version foo, document with id 'doc2' not found. Make sure that all documents with ids specified in this version's sidebar file exist and that no ids are repeated.`, ); @@ -137,13 +137,13 @@ describe('server utils', () => { next: 'doc2', }; - const pages = { + const sidebarMetadatas = { doc1: {}, doc2: {}, }; expect(() => { - utils.validateSidebar(metadata, pages); + utils.validateSidebar(metadata, sidebarMetadatas); }).not.toThrow(); }); }); diff --git a/v1/lib/server/readCategories.js b/v1/lib/server/readCategories.js index 2a265a0ba3e4..a7fc6af690df 100644 --- a/v1/lib/server/readCategories.js +++ b/v1/lib/server/readCategories.js @@ -20,14 +20,14 @@ function readCategories(sidebar, allMetadata, languages) { const language = enabledLanguages[k]; const metadatas = []; const categories = []; - const pages = {}; + const sidebarMetadatas = {}; - // Get the metadata for the current sidebar + // Get all related metadata for the current sidebar Object.keys(allMetadata).forEach(id => { const metadata = allMetadata[id]; if (metadata.sidebar === sidebar && metadata.language === language) { metadatas.push(metadata); - pages[metadata.id] = metadata; + sidebarMetadatas[metadata.id] = metadata; } }); @@ -59,8 +59,8 @@ function readCategories(sidebar, allMetadata, languages) { const category = metadata.category; const subCategory = metadata.sub_category; - // Validate pages in the sidebar - validateSidebar(metadata, pages); + // Validate sidebarMetadatas in the sidebar + validateSidebar(metadata, sidebarMetadatas); if (!indexedCategories[category]) { indexedCategories[category] = []; diff --git a/v1/lib/server/utils.js b/v1/lib/server/utils.js index 4b9a32143aca..a8027fa988a2 100644 --- a/v1/lib/server/utils.js +++ b/v1/lib/server/utils.js @@ -67,9 +67,9 @@ function autoPrefixCss(cssContent) { } // Validate the docs in the sidebar are valid -function validateSidebar(metadata, pages) { +function validateSidebar(metadata, sidebarMetadatas) { if (metadata.next) { - if (!pages[metadata.next]) { + if (!sidebarMetadatas[metadata.next]) { throw new Error( metadata.version ? `Improper sidebars file for version ${