From 215d2045d7a492b7e66a0d5d4b547ba1cee23d72 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Sat, 5 Sep 2020 15:12:48 -0700 Subject: [PATCH] Fix docs deep linking (#910) * add link to documentation, fix styling and use hook for mq, fix docs deep linking * change routing to be all hashed based * only use matchMedia if available * remove set page * removed some console log errors * adding in explicit lint rules, and snaps * revert back to current docs link * snaps --- .eslintrc.js | 2 + .../__snapshots__/header.test.tsx.snap | 14 +- src/components/Header/index.tsx | 184 ++-- .../__snapshots__/authors-list.test.tsx.snap | 4 +- src/containers/AuthorList/index.tsx | 2 +- src/hooks/useMediaQuery.tsx | 17 + src/pages/docs.tsx | 834 +++++++++--------- src/styles/layout.scss | 2 +- 8 files changed, 553 insertions(+), 506 deletions(-) create mode 100644 src/hooks/useMediaQuery.tsx diff --git a/.eslintrc.js b/.eslintrc.js index 4db7016044..5e5e484b8c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -27,6 +27,8 @@ module.exports = { jest: true, }, rules: { + 'react/no-unused-prop-types': 'off', + 'react/require-default-props': 'off', 'react/jsx-filename-extension': 'off', 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', diff --git a/src/components/Header/__tests__/__snapshots__/header.test.tsx.snap b/src/components/Header/__tests__/__snapshots__/header.test.tsx.snap index 8e6607ae27..4b94ca12ee 100644 --- a/src/components/Header/__tests__/__snapshots__/header.test.tsx.snap +++ b/src/components/Header/__tests__/__snapshots__/header.test.tsx.snap @@ -45,7 +45,7 @@ exports[`Tests for Header component renders correctly 1`] = `
  • -
  • - - Docs - -
  • diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index f8beb115b5..0d5c37c88c 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -3,6 +3,7 @@ import React from 'react'; import logoLight from '../../images/logos/nodejs-logo-light-mode.svg'; import logoDark from '../../images/logos/nodejs-logo-dark-mode.svg'; import defaultDarkModeController from '../../util/darkModeController'; +import { useMediaQuery } from '../../hooks/useMediaQuery'; interface Props { darkModeController?: typeof defaultDarkModeController; @@ -10,105 +11,98 @@ interface Props { const Header = ({ darkModeController = defaultDarkModeController, -}: Props): JSX.Element => ( - -); + + ); +}; export default Header; diff --git a/src/containers/AuthorList/__tests__/__snapshots__/authors-list.test.tsx.snap b/src/containers/AuthorList/__tests__/__snapshots__/authors-list.test.tsx.snap index 5efc6a40e4..3b0c7f11ef 100644 --- a/src/containers/AuthorList/__tests__/__snapshots__/authors-list.test.tsx.snap +++ b/src/containers/AuthorList/__tests__/__snapshots__/authors-list.test.tsx.snap @@ -5,7 +5,7 @@ exports[`AuthorsList component renders correctly 1`] = ` css={ Object { "map": undefined, - "name": "ekkwel", + "name": "al6qib", "next": undefined, "styles": " display: flex; @@ -16,7 +16,7 @@ exports[`AuthorsList component renders correctly 1`] = ` text-transform: uppercase; padding-left: 0; - li:first-child a { + li:first-of-type a { margin-left: 0 !important; } diff --git a/src/containers/AuthorList/index.tsx b/src/containers/AuthorList/index.tsx index 73bc427df8..0a9d60aa75 100644 --- a/src/containers/AuthorList/index.tsx +++ b/src/containers/AuthorList/index.tsx @@ -11,7 +11,7 @@ const list: SerializedStyles = css` text-transform: uppercase; padding-left: 0; - li:first-child a { + li:first-of-type a { margin-left: 0 !important; } diff --git a/src/hooks/useMediaQuery.tsx b/src/hooks/useMediaQuery.tsx new file mode 100644 index 0000000000..debe2fe010 --- /dev/null +++ b/src/hooks/useMediaQuery.tsx @@ -0,0 +1,17 @@ +import { useState, useLayoutEffect } from 'react'; + +export const useMediaQuery = (query: string): boolean | undefined => { + const [matches, setMatches] = useState(); + + useLayoutEffect(() => { + if (typeof window.matchMedia === 'function') { + const mq = window.matchMedia(query); + setMatches(mq.matches); + const handler = (): void => setMatches(mq.matches); + mq.addListener(handler); + return (): void => mq.removeListener(handler); + } + }, [query]); + + return matches; +}; diff --git a/src/pages/docs.tsx b/src/pages/docs.tsx index e9f08eea5c..6d8f74e56e 100644 --- a/src/pages/docs.tsx +++ b/src/pages/docs.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { Link } from 'gatsby'; import dompurify from 'dompurify'; import { useApiData, useReleaseHistory } from '../hooks'; @@ -17,6 +17,7 @@ import '../styles/article-reader.scss'; const API_DOCS_OBJ_KEYS = ['events', 'methods', 'properties', 'classes']; const DOCUMENT_ELEMENT_TYPES = ['module', 'event', 'method', 'class']; const sanitizer = dompurify.sanitize; + function capitalizeFirstLetter(text: string): string { return text.charAt(0).toUpperCase() + text.slice(1); } @@ -25,14 +26,17 @@ function getHeadingForPage(page: ApiDocsObj, depth = 0): JSX.Element { const HeaderName = `h${Math.min(depth + 2, 6)}`; return React.createElement( HeaderName, - { className: `api-docs__title api-docs__title--${page.type}` }, + { + key: `${page.name}-heading`, + className: `api-docs__title api-docs__title--${page.type}`, + }, page.displayName || page.name ); } function getListItemForPage(page: ApiDocsObj): JSX.Element { return ( -
  • +
  • {DOCUMENT_ELEMENT_TYPES.includes(page.type) ? capitalizeFirstLetter(page.type) : 'Property'} @@ -50,7 +54,7 @@ function renderArticleOverview( ): JSX.Element[] { const children: JSX.Element[] = []; - function prepareArticleOverviewForApiDocObjKey(key: string): JSX.Element { + function prepareArticleOverviewForApiDocObjKey(key: string): void { if (obj[key]) { obj[key] .filter(function removeObjectTypeForProperties(property: ApiDocsObj) { @@ -100,7 +104,7 @@ function renderArticleSections( }; if (depth === 0) { - sections.push(
    ); + sections.push(
    ); } children.push(getHeadingForPage(page, depth)); @@ -109,12 +113,397 @@ function renderArticleSections( prepareArticleSections(); - sections.push(
    {children}
    ); + sections.push( +
    + {children} +
    + ); }); return sections; } +function renderDefaultArticle( + sourceCodeLink: string, + prebuiltInstallerLink: string, + version: string +): JSX.Element { + return ( +
    +

    + home + / + documentation + / + installing node +

    +

    {version}

    +

    Installing Node

    +

    + The easiest way to install Node is by using a  + + pre-built installer + +  but you can also install via package manager using bash (below), + or download the  + + Node.js source code + + . Official packages for all the major platforms are available  + + here + + . +

    +

    Android

    +

    + Android support is still experimental in Node.js, so precompiled + binaries are not yet provided by Node.js developers. +

    +

    + However, there are some third-party solutions. For example,{' '} + Termux +  community provides terminal emulator and Linux environment for + Android, as well as own package manager and{' '} + + extensive collection + +  of many precompiled applications. This command in Termux app will + install the last available Node.js version: +

    + + pkg  + install +  nodejs + +

    Arch Linux

    +

    + Node.js and npm packages are available in the Community Repository. +

    + + pacman -S nodejs  + npm + +

    + Debian and Ubuntu based Linux distributions, Enterprise Linux/Fedora and + Snap packages +

    +

    + + Node.js binary distributions + +  are available from NodeSource. +

    +

    FreeBSD

    +

    + The most recent release of Node.js is available via the  + www/node +  port. +

    + + pkg  + install +  node + +

    + Or compile it on your own using  + ports: +

    + + cd /usr/ports/www/node && make  + install + +

    Gentoo

    +

    Node.js is available in the portage tree.

    + emerge nodejs +

    IBM i

    +

    + LTS versions of Node.js are available from IBM, and are available + via  + + the 'yum' package manager + + . The package name is nodejs followed by the major version + number (for instance, nodejs8, nodejs10,{' '} + nodejs12, etc) +

    +

    + To install Node.js 12.x from the command line, run the following as a + user with *ALLOBJ special authority: +

    + + yum  + install +  nodejs12 + +

    + Node.js can also be installed with the IBM i Access Client Solutions + product. See this support document for more details +

    +

    macOS

    +

    + Download the  + macOS Installer +  directly from the  + nodejs.org +  web site. +

    +

    If you want to download the package with bash:

    + node-(.*)\\.pkg.*|\\1|p\')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"' + } + > + curl +   + { + // eslint-disable-next-line no-template-curly-in-string + 'https://nodejs.org/dist/latest/node-${VERSION:-$(' + } + wget +   + { + '-qO- https://nodejs.org/dist/latest/ | sed -nE \'s|.*>node-(.*)\\.pkg.*|\\1|p\')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"' + } + +

    + Node.js can also be installed with the IBM i Access Client Solutions + product. See this support document for more details +

    +

    Alternatives

    +

    + Using Homebrew: +

    + + brew  + install +  node + +

    + Using MacPorts: +

    + + port  + install +  {'nodejs'} + +

    + Using pkgsrc: +

    +

    Install the binary package:

    + + pkgin -y  + install +  nodejs + +

    Or build manually from pkgsrc:

    + + cd pkgsrc/lang/nodejs && bmake  + install + +

    NetBSD

    +

    Node.js is available in the pkgsrc tree:

    + + cd /usr/pkgsrc/lang/nodejs &&  + make install + +

    + Or install a binary package (if available for your platform) using + pkgin: +

    + + pkgin -y  + install +  nodejs + +

    Nodenv

    +

    + nodenv is a lightweight node version manager, similar + to  + nvm. It's simple and predictable. A rich plugin + ecosystem lets you tailor it to suit your needs. Use  + nodenv to pick a Node version for your application and + guarantee that your development environment matches production. +

    +

    + Nodenv installation instructions are maintained  + + on its Github page + + . Please visit that page to ensure you're following the latest + version of the installation steps. +

    +

    nvm

    +

    + Node Version Manager is a bash script used to manage multiple released + Node.js versions. It allows you to perform operations like install, + uninstall, switch version, etc. To install nvm, use this  + + install script + + . +

    +

    + On Unix / OS X systems Node.js built from source can be installed + using nvm by + installing into the location that nvm expects: +

    + + env +  VERSION=`python tools/getnodeversion.py` +  make install  + DESTDIR=`nvm_version_path v$VERSION` PREFIX="" + +

    + After this you can use nvm to switch between released + versions and versions built from source. For example, if the version of + Node.js is v8.0.0-pre: +

    + + nvm use  + 8 + +

    + Once the official release is out you will want to uninstall the version + built from source: +

    + + nvm uninstall  + 8 + +

    OpenBSD

    +

    Node.js is available through the ports system.

    + + /usr/ports/lang/node + +

    + Using  + + pkg_add + +  on OpenBSD: +

    + pkg_add node +

    openSUSE and SLE

    +

    + Node.js is available in the main repositories under the following + packages: +

    +
      +
    • +

      + openSUSE Leap 42.2:  + + nodejs4 + +

      +
    • +
    • +

      + openSUSE Leap 42.3:  + + nodejs4nodejs6 + +

      +
    • +
    • +

      + openSUSE Tumbleweed:  + + nodejs10nodejs12,  + nodejs14 + +

      +
    • +
    • +

      + SUSE Linux Enterprise Server (SLES) 12:  + + nodejs4nodejs6 (The + "Web and Scripting Module" must be  + + added before installing + + .) + +

      +
    • +
    +

    + For example, to install Node.js 4.x on openSUSE Leap 42.2, run the + following as root: +

    + + zypper +  install +  nodejs4 + +

    SmartOS and illumos

    +

    + SmartOS images come with pkgsrc pre-installed. On other illumos + distributions, first install pkgsrc, then you may install the binary + package as normal: +

    + + pkgin -y  + install +  nodejs + +

    Or build manually from pkgsrc:

    + + cd pkgsrc/lang/nodejs && bmake  + install + +

    Solus

    +

    Solus provides Node.js in its main repository.

    + + sudo +  eopkg  + install +  nodejs + +

    Void Linux

    +

    + Void Linux ships Node.js stable in the main repository. +

    + + xbps-install -Sy nodejs + +

    Windows

    +

    + Download the  + Windows Installer +   directly from the + nodejs.org + {' '} + web site. +

    +

    Alternatives

    +

    + Using Chocolatey: +

    + cinst nodejs +

    or for full install with npm

    + + cinst nodejs.install + +

    + Using Scoop: +

    + + scoop  + install +  nodejs + +
    + ); +} + function renderArticle( page: ApiDocsObj | null, userOS: UserOS, @@ -124,386 +513,7 @@ function renderArticle( const prebuiltInstallerLink = downloadUrlByOs(userOS, version); if (!page) { - return ( -
    -

    - home - / - documentation - / - installing node -

    -

    {version}

    -

    Installing Node

    -

    - The easiest way to install Node is by using a  - - pre-built installer - -  but you can also install via package manager using bash (below), - or download the  - - Node.js source code - - . Official packages for all the major platforms are available  - - here - - . -

    -

    Android

    -

    - Android support is still experimental in Node.js, so precompiled - binaries are not yet provided by Node.js developers. -

    -

    - However, there are some third-party solutions. For example,{' '} - Termux -  community provides terminal emulator and Linux environment for - Android, as well as own package manager and{' '} - - extensive collection - -  of many precompiled applications. This command in Termux app - will install the last available Node.js version: -

    - - pkg  - install -  nodejs - -

    Arch Linux

    -

    - Node.js and npm packages are available in the Community Repository. -

    - - pacman -S nodejs  - npm - -

    - Debian and Ubuntu based Linux distributions, Enterprise Linux/Fedora - and Snap packages -

    -

    - - Node.js binary distributions - -  are available from NodeSource. -

    -

    FreeBSD

    -

    - The most recent release of Node.js is available via the  - www/node -  port. -

    - - pkg  - install -  node - -

    - Or compile it on your own using  - ports: -

    - - cd /usr/ports/www/node && make  - install - -

    Gentoo

    -

    Node.js is available in the portage tree.

    - emerge nodejs -

    IBM i

    -

    - LTS versions of Node.js are available from IBM, and are available - via  - - the 'yum' package manager - - . The package name is nodejs followed by the major - version number (for instance, nodejs8,{' '} - nodejs10, nodejs12, etc) -

    -

    - To install Node.js 12.x from the command line, run the following as a - user with *ALLOBJ special authority: -

    - - yum  - install -  nodejs12 - -

    - Node.js can also be installed with the IBM i Access Client Solutions - product. See this support document for more details -

    -

    macOS

    -

    - Download the  - macOS Installer -  directly from the  - nodejs.org -  web site. -

    -

    - If you want to download the package with bash: -

    - node-(.*)\\.pkg.*|\\1|p\')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"' - } - > - curl -   - { - // eslint-disable-next-line no-template-curly-in-string - 'https://nodejs.org/dist/latest/node-${VERSION:-$(' - } - wget -   - { - '-qO- https://nodejs.org/dist/latest/ | sed -nE \'s|.*>node-(.*)\\.pkg.*|\\1|p\')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"' - } - -

    - Node.js can also be installed with the IBM i Access Client Solutions - product. See this support document for more details -

    -

    Alternatives

    -

    - Using Homebrew: -

    - - brew  - install -  node - -

    - Using MacPorts: -

    - - port  - install -  {'nodejs'} - -

    - Using pkgsrc: -

    -

    Install the binary package:

    - - pkgin -y  - install -  nodejs - -

    Or build manually from pkgsrc:

    - - cd pkgsrc/lang/nodejs && bmake  - install - -

    NetBSD

    -

    Node.js is available in the pkgsrc tree:

    - - cd /usr/pkgsrc/lang/nodejs &&  - make install - -

    - Or install a binary package (if available for your platform) using - pkgin: -

    - - pkgin -y  - install -  nodejs - -

    Nodenv

    -

    - nodenv is a lightweight node version manager, similar - to  - nvm. It's simple and predictable. A rich plugin - ecosystem lets you tailor it to suit your needs. Use  - nodenv to pick a Node version for your application and - guarantee that your development environment matches production. -

    -

    - Nodenv installation instructions are maintained  - - on its Github page - - . Please visit that page to ensure you're following the latest - version of the installation steps. -

    -

    nvm

    -

    - Node Version Manager is a bash script used to manage multiple released - Node.js versions. It allows you to perform operations like install, - uninstall, switch version, etc. To install nvm, use this  - - install script - - . -

    -

    - On Unix / OS X systems Node.js built from source can be installed - using nvm by - installing into the location that nvm expects: -

    - - env -  VERSION=`python tools/getnodeversion.py` -  make install  - DESTDIR=`nvm_version_path v$VERSION` PREFIX="" - -

    - After this you can use nvm to switch between released - versions and versions built from source. For example, if the version - of Node.js is v8.0.0-pre: -

    - - nvm use  - 8 - -

    - Once the official release is out you will want to uninstall the - version built from source: -

    - - nvm uninstall  - 8 - -

    OpenBSD

    -

    - Node.js is available through the ports system. -

    - - /usr/ports/lang/node - -

    - Using  - - pkg_add - -  on OpenBSD: -

    - pkg_add node -

    openSUSE and SLE

    -

    - Node.js is available in the main repositories under the following - packages: -

    -
      -
    • -

      - openSUSE Leap 42.2:  - - nodejs4 - -

      -
    • -
    • -

      - openSUSE Leap 42.3:  - - nodejs4nodejs6 - -

      -
    • -
    • -

      - openSUSE Tumbleweed:  - - nodejs10nodejs12,  - nodejs14 - -

      -
    • -
    • -

      - SUSE Linux Enterprise Server (SLES) 12:  - - nodejs4nodejs6 (The - "Web and Scripting Module" must be  - - added before installing - - .) - -

      -
    • -
    -

    - For example, to install Node.js 4.x on openSUSE Leap 42.2, run the - following as root: -

    - - zypper -  install -  nodejs4 - -

    SmartOS and illumos

    -

    - SmartOS images come with pkgsrc pre-installed. On other illumos - distributions, first install pkgsrc, then you may install the binary - package as normal: -

    - - pkgin -y  - install -  nodejs - -

    Or build manually from pkgsrc:

    - - cd pkgsrc/lang/nodejs && bmake  - install - -

    Solus

    -

    - Solus provides Node.js in its main repository. -

    - - sudo -  eopkg  - install -  nodejs - -

    Void Linux

    -

    - Void Linux ships Node.js stable in the main repository. -

    - - xbps-install -Sy nodejs - -

    Windows

    -

    - Download the  - - Windows Installer - -   directly from the nodejs.org{' '} - web site. -

    -

    Alternatives

    -

    - Using Chocolatey: -

    - cinst nodejs -

    or for full install with npm

    - - cinst nodejs.install - -

    - Using Scoop: -

    - - scoop  - install -  nodejs - -
    - ); + return renderDefaultArticle(sourceCodeLink, prebuiltInstallerLink, version); } return ( @@ -528,8 +538,7 @@ function renderArticle( function sideBarSection( title: string, section: keyof APIResponse, - data: APIResponse, - setPage: Function + data: APIResponse ): JSX.Element { return (
  • @@ -543,7 +552,6 @@ function sideBarSection(
  • setPage(module)} className="t-body2 api-nav__sub-list-link" > {module.displayName || module.name} @@ -574,6 +582,44 @@ export default function APIDocsPage(): JSX.Element { const apiData = useApiData(currentVersionSelected); + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(() => { + // run this every time the page loads. If there is no hash, return page back to default + if (!window.location.hash) { + setPage(null); + } + }); + + useEffect(() => { + const findModuleByHash = (hash: string): ApiDocsObj | undefined => { + const hashPrefix = '#temporary_path_for_'; + const moduleName = decodeURI(hash.replace(hashPrefix, '')); + let matchingModule; + Object.keys(apiData).forEach(section => { + const match = apiData[section].find( + (item: ApiDocsObj) => item.name === moduleName + ); + if (match !== undefined) { + matchingModule = match; + } + }); + return matchingModule; + }; + + const handleHashChange = (): void => { + const docsPage = findModuleByHash(window.location.hash); + setPage(docsPage || null); + }; + + window.addEventListener('hashchange', handleHashChange, true); + + handleHashChange(); + + return (): void => { + window.removeEventListener('hashchange', handleHashChange); + }; + }, [apiData]); + return ( <>
    @@ -605,11 +651,11 @@ export default function APIDocsPage(): JSX.Element { )}
  • - {sideBarSection('Globals', 'globals', apiData, setPage)} - {sideBarSection('Methods', 'methods', apiData, setPage)} - {sideBarSection('Misc', 'miscs', apiData, setPage)} - {sideBarSection('Modules', 'modules', apiData, setPage)} - {sideBarSection('Classes', 'classes', apiData, setPage)} + {sideBarSection('Globals', 'globals', apiData)} + {sideBarSection('Methods', 'methods', apiData)} + {sideBarSection('Misc', 'miscs', apiData)} + {sideBarSection('Modules', 'modules', apiData)} + {sideBarSection('Classes', 'classes', apiData)} {renderArticle(page, userOS, currentVersionSelected || '')} diff --git a/src/styles/layout.scss b/src/styles/layout.scss index cbda21852e..7d098fdaca 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -293,7 +293,7 @@ select { .nav__startwrapper { display: flex; align-items: center; - justify-content: start; + justify-content: flex-start; } .nav__endwrapper {