From 8ffc3eeb5b25646fb97b0cc0908ea1a0399179f0 Mon Sep 17 00:00:00 2001 From: Aaron Carlino Date: Thu, 19 Dec 2019 11:54:23 +1300 Subject: [PATCH] WIP: Add userhelp to new site (#216) * Docs/userguide switching * Initial commit of userdocs merge * Remove service worker, fix rendering * Remove limited sources * UX improvements per Paul design * Tweak version select for FF * Fix mobile view * Final tweaks to UI * Tweaks to search bar, clean up conflicts * Fix icons * Hide search if no API key --- README.md | 30 +- gatsby-browser.js | 23 +- gatsby-config.js | 37 +-- gatsby-node.js | 61 ++-- gatsby-ssr.js | 20 +- package.json | 6 +- sources-docs.js | 20 ++ sources-user.js | 447 +++++++++++++++++++++++++++++ src/components/DocsPage.tsx | 34 +++ src/components/Header.tsx | 64 +++-- src/components/Layout.tsx | 12 +- src/components/Nav.tsx | 13 +- src/components/NodeProvider.tsx | 66 +++++ src/components/SearchBox.tsx | 28 +- src/contexts/NodeContext.ts | 5 + src/hooks/useDocContext.ts | 17 ++ src/hooks/useHierarchy.ts | 24 ++ src/templates/docs-template.tsx | 46 ++- src/theme/assets/scss/ss-docs.scss | 121 ++++++-- src/types/index.ts | 6 + src/utils/nodes.ts | 99 +++---- src/utils/parseChildrenOf.ts | 2 +- src/utils/rewriteLink.ts | 25 +- src/utils/sortFiles.ts | 2 +- 24 files changed, 984 insertions(+), 224 deletions(-) create mode 100644 sources-docs.js create mode 100644 sources-user.js create mode 100644 src/components/DocsPage.tsx create mode 100644 src/components/NodeProvider.tsx create mode 100644 src/contexts/NodeContext.ts create mode 100644 src/hooks/useDocContext.ts create mode 100644 src/hooks/useHierarchy.ts diff --git a/README.md b/README.md index fb78edb..0b643d0 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ # doc.silverstripe.org -This repository contains the source code powering [SilverStripe's -developer documentation website](https://docs.silverstripe.org). +This repository contains the source code powering [the Silverstripe CMS +developer documentation website](https://docs.silverstripe.org) and +[userhelp website](https://userhelp.silverstripe.org). This application is build on [Gatsby](https://gatsbyjs.com), a static site generator based on [React](https://reactjs.org). It sources content @@ -61,6 +62,20 @@ gatsby serve These commands will give you an exact representation of how the site will run on a production server, with statically generated html files and server-side rendering. +## Toggling between docs and userhelp + +Whether the application uses the `docs.silverstripe.org` content or `userhelp.silverstripe.org` is determined +by the environment variable, `DOCS_CONTEXT`. You can set this in the `.env.development` file, or use one of +the script shortcuts: + +``` +yarn dev-docs +yarn dev-user +yarn build-docs +yarn build-user +``` + + ## Developing From within `path/to/ssdocs`, run the command @@ -85,7 +100,7 @@ your content changes, since the remote repositories are the source of truth. ## Deploying content changes Once your contribution has been merged into the master branch, it will be deployed to production via a -Github action in the `silverstripe-framework` repository. +Github action in the repository that holds the markdown files (e.g. `silverstripe/silverstripe-framework` for docs). ## Deploying app changes @@ -93,11 +108,12 @@ Once your change is merged in to the `master` branch of this repository, it will ## Contribution -To contribute an improvement to the https://docs.silverstripe.org functionality or +To contribute an improvement to the https://docs.silverstripe.org or https://userhelp.silverstripe.org functionality or theme, submit a pull request on the [GitHub project](https://github.com/silverstripe/doc.silverstripe.org). Any approved pull requests will make -their way onto the https://docs.silverstripe.org site in the next release. +their way onto the https://docs.silverstripe.org or https://userhelp.silverstripe.org sites in the next release. If you wish to edit the documentation content, submit a pull request on the -[framework Github project](https://github.com/silverstripe/silverstripe-framework). Updated -documentation content is uploaded daily to [doc.silverstripe.org](https://docs.silverstripe.org) via a build hook. \ No newline at end of file +[framework Github project](https://github.com/silverstripe/silverstripe-framework) or the +[userhelp documentation](https://github.com/silverstripe/silverstripe-userhelp-content) repository +or corresponding module. \ No newline at end of file diff --git a/gatsby-browser.js b/gatsby-browser.js index 9306848..e3937fa 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -2,22 +2,39 @@ require("prismjs/themes/prism-okaidia.css"); require("./src/theme/assets/scss/theme.scss"); require('./src/theme/assets/fontawesome/css/all.css'); require('./src/theme/assets/search/algolia.css'); -const smoothScroll = require('smooth-scroll'); -const Layout = require('./src/components/Layout').default; + const React = require('react'); +const Layout = require('./src/components/Layout').default; +const NodeProvider = require('./src/components/NodeProvider').default; +const smoothScroll = require('smooth-scroll'); + if (typeof window !== "undefined") { smoothScroll('a[href*="#"]') } +/** + * Ensures the chrome doesn't rerender every page load, which makes the sidebar reset its scroll. + */ exports.wrapPageElement = ({ element, props }) => { - return {element} + return ( + + + {element} + + + ); }; + exports.onRouteUpdate = ({location}) => { + if (window.ga && process.env.NODE_ENV === 'production') { + window.ga('send', 'pageview'); + } anchorScroll(location); return true; }; + exports.shouldUpdateScroll = ({ routerProps: { location }, }) => { diff --git a/gatsby-config.js b/gatsby-config.js index 741eb47..b4b34d8 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -1,4 +1,7 @@ const path = require('path'); +const sources = process.env.DOCS_CONTEXT === 'user' + ? require('./sources-user') + : require('./sources-docs'); module.exports = { siteMetadata: { @@ -14,36 +17,14 @@ module.exports = { `gatsby-plugin-sharp`, `gatsby-plugin-sitemap`, `gatsby-plugin-netlify`, - { - resolve: `gatsby-source-git`, - options: { - name: `4`, - remote: `https://github.com/silverstripe/silverstripe-framework.git`, - branch: `4`, - patterns: `docs/en/**` - } - }, - { - resolve: `gatsby-source-git`, - options: { - name: `3`, - remote: `https://github.com/silverstripe/silverstripe-framework.git`, - branch: `3.7`, - patterns: `docs/en/**` - } - }, + + ...sources, + { resolve: `gatsby-source-filesystem`, options: { - name: `watcher--ss3`, - path: `${__dirname}/.cache/gatsby-source-git/3/docs/en` - } - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: `watcher--ss4`, - path: `${__dirname}/.cache/gatsby-source-git/4/docs/en` + name: `watcher`, + path: `${__dirname}/.cache/gatsby-source-git/` } }, { @@ -108,7 +89,7 @@ module.exports = { // allowed selectors defined in FontAwesome. Everything else in FA should be removed. extractor: class { static extract(content) { - const selectors = [`file-alt`] + const selectors = [`fa-file-alt`] const matches = content.match(/icon(Brand)?: ([a-zA-Z0-9_-]+)/); if (matches) { const isBrand = typeof matches[1] !== 'undefined'; diff --git a/gatsby-node.js b/gatsby-node.js index 20e3a37..42daa5a 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -3,31 +3,41 @@ const fs = require('fs'); const { createFilePath } = require(`gatsby-source-filesystem`); const fileToTitle = require('./src/utils/fileToTitle'); -const createSlug = (filePath, version) => { - const parts = filePath.split('/'); - const langIndex = parts.indexOf('en'); - parts.splice(langIndex + 1, 0, version); - return parts - .map(part => part.replace(/^\d+_/, '')) - .join('/') - .toLowerCase() +const createSlug = ({path, version, thirdparty}) => { + const rest = path.split('/'); + const parts = [ + 'en', + version, + // thirdparty modules are explicitly pathed + thirdparty, + ...rest, + ].filter(p => p); + + const slug = parts + .map(part => part.replace(/^\d+_/, '')) + .join('/') + .toLowerCase(); + + return `/${slug}/`; }; -exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNodeId, createContentDigest }) => { +const parseName = name => name.split('--'); + +exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNodeId, createContentDigest }) => { if (node.internal.type !== 'MarkdownRemark') { return; } const { createNode } = actions; const fileNode = getNode(node.parent); - const version = fileNode.sourceInstanceName; + const [category, version, thirdparty] = parseName(fileNode.sourceInstanceName); - // The gatsby-source-filesystem plugins are registered to collect from the same path + // The gatsby-sgatsbource-filesystem plugins are registered to collect from the same path // that the git source writes to, so we get the watch task (hot reload on content changes) // But we don't want duplicate document pages for each source plugin, so // we bail out if we already have the file. However, we need to ensure // the file is injected into the template as a dependency, so when the content changes, // the pages get refreshed on the fly. - if (version.match(/^watcher--/)) { + if (category === 'watcher') { const existing = getNodesByType('SilverstripeDocument') .find(n => n.fileAbsolutePath === node.fileAbsolutePath); @@ -35,14 +45,14 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo // Pair the document with its watched file so we can inject it into the template // as a dependency. existing.watchFile___NODE = node.id; - return; } - } - + return; + } + const basePath = category === 'user' ? `docs/en/userguide` : `docs/en`; const filePath = createFilePath({ node, getNode, - basePath: `docs` + basePath, }); let fileTitle = path.basename(node.fileAbsolutePath, '.md'); const isIndex = fileTitle === 'index'; @@ -50,10 +60,24 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo fileTitle = path.basename(path.dirname(node.fileAbsolutePath)); } const docTitle = fileToTitle(fileTitle); - const slug = createSlug(filePath, version); + const slug = createSlug({ + path: filePath, + version, + thirdparty, + }); + const parentSlug = `${path.resolve(slug, '../')}/`; const unhideSelf = false; + // Most of these don't exist in userhelp, so force them into the schema by un-nulling them. + const frontmatter = { + introduction: ``, + icon: `file-alt`, + iconBrand: ``, + hideChildren: false, + ...node.frontmatter, + }; + const docData = { isIndex, filePath, @@ -61,7 +85,8 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo slug, parentSlug, unhideSelf, - ...node.frontmatter, + category, + ...frontmatter, }; if (!docData.title || docData.title === '') { diff --git a/gatsby-ssr.js b/gatsby-ssr.js index e8350e0..9cd58e3 100644 --- a/gatsby-ssr.js +++ b/gatsby-ssr.js @@ -1,13 +1,25 @@ -const Layout = require('./src/components/Layout').default; const React = require('react'); +const Layout = require('./src/components/Layout').default; +const NodeProvider = require('./src/components/NodeProvider').default; +const { setCurrentPath } = require('./src/utils/nodes'); +/** + * Applies the node provider (static query of all documents) + * Ensures the chrome doesn't rerender every page load, which makes the sidebar reset its scroll. + */ exports.wrapPageElement = ({ element, props }) => { - return {element} + return ( + + + {element} + + + ); }; -exports.onRenderBody = ({ setPostBodyComponents, setHeadComponents }) => { +exports.onRenderBody = ({ setPostBodyComponents, setHeadComponents, pathname }) => { // Rules that cannot be touched by purgecss because they come in from client side rendering - setHeadComponents([ + setHeadComponents([