diff --git a/src/components/TOCNode.js b/src/components/TOCNode.js index d0d0c7c1b..459427cd2 100644 --- a/src/components/TOCNode.js +++ b/src/components/TOCNode.js @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; import PropTypes from 'prop-types'; import Link from './Link'; import { formatText } from '../utils/format-text'; @@ -19,53 +19,61 @@ const TOCNode = ({ node, level = BASE_NODE_LEVEL }) => { const target = slug || url; const hasChildren = !!children.length; const isExternalLink = !!url; - const { activeSection, toggleDrawer } = useContext(TOCContext); + const { activeSection, setActiveSection } = useContext(TOCContext); const isActive = isActiveTocNode(activeSection, slug, children); const anchorTagClassNames = `reference ${isActive ? 'current' : ''} ${isExternalLink ? 'external' : 'internal'}`; const isSelected = isSelectedTocNode(activeSection, slug); const toctreeSectionClasses = `toctree-l${level} ${isActive ? 'current' : ''} ${isSelected ? 'selected-item' : ''}`; + const isDrawer = !!(options && options.drawer); + + const [isOpen, setIsOpen] = useState(isActive); + + // Show caret if not on first level of TOC + const caretIcon = + level !== BASE_NODE_LEVEL ? ( + + ) : null; const NodeLink = () => { // If title is a plaintext string, render as-is. Otherwise, iterate over the text nodes to properly format titles. const formattedTitle = formatText(title); - if (level === BASE_NODE_LEVEL) { - const isDrawer = !!(options && options.drawer); - if (isDrawer && children.length > 0) { - const _toggleDrawerOnEnter = e => { - if (e.key === 'Enter') { - toggleDrawer(slug); - } - }; - // TODO: Ideally, this value should be a button, but to keep consistent with CSS render as anchor - return ( - toggleDrawer(slug)} - onKeyDown={_toggleDrawerOnEnter} - className={anchorTagClassNames} - aria-expanded={hasChildren ? isActive : undefined} - role="button" - tabIndex="0" - > - {formattedTitle} - - ); - } + + if (isDrawer && children.length > 0) { + const _toggleDrawerOnEnter = e => { + if (e.key === 'Enter') { + setIsOpen(!isOpen); + } + }; + // TODO: Ideally, this value should be a button, but to keep consistent with CSS render as anchor return ( - { + e.preventDefault(); + setIsOpen(!isOpen); + }} + onKeyDown={_toggleDrawerOnEnter} className={anchorTagClassNames} - onClick={() => toggleDrawer(slug)} + aria-expanded={hasChildren ? isActive : undefined} + role="button" + tabIndex="0" + href={target} > + {caretIcon} {formattedTitle} ); } - - // In this case, we have a node which should be rendered with the 'expand-icon' return ( - - + setActiveSection(slug)} + > + {caretIcon} {formattedTitle} ); @@ -73,13 +81,11 @@ const TOCNode = ({ node, level = BASE_NODE_LEVEL }) => { return (
  • - {isActive ? ( + {isOpen ? (
      {children.map(c => { const key = c.slug || c.url; - return ( - - ); + return ; })}
    ) : null} diff --git a/src/components/TableOfContents.js b/src/components/TableOfContents.js index c222d65ad..faee98da4 100644 --- a/src/components/TableOfContents.js +++ b/src/components/TableOfContents.js @@ -14,16 +14,9 @@ const TableOfContents = ({ toctreeData: { children } }) => { currentPage = window.location.pathname; } const [activeSection, setActiveSection] = useState(currentPage); - const toggleDrawer = newSlug => { - if (activeSection === newSlug) { - setActiveSection(null); - } else { - setActiveSection(newSlug); - } - }; return ( - +
      {children.map(c => { const key = c.slug || c.url; diff --git a/src/components/Target.js b/src/components/Target.js index a5844a49d..f6bc73a89 100644 --- a/src/components/Target.js +++ b/src/components/Target.js @@ -49,7 +49,7 @@ const Target = ({ nodeData: { children, name, target } }) => { Target.propTypes = { nodeData: PropTypes.shape({ - children: PropTypes.arrayOf(PropTypes.node).isRequired, + children: PropTypes.arrayOf(PropTypes.object).isRequired, name: PropTypes.string.isRequired, target: PropTypes.string.isRequired, }).isRequired, diff --git a/src/components/toc-context.js b/src/components/toc-context.js index 2345790cf..4fa5149ba 100644 --- a/src/components/toc-context.js +++ b/src/components/toc-context.js @@ -2,5 +2,5 @@ import React from 'react'; export const TOCContext = React.createContext({ activeSection: undefined, - toggleDrawer: () => {}, + setActiveSection: () => {}, }); diff --git a/tests/unit/TableOfContents.test.js b/tests/unit/TableOfContents.test.js index d903fde20..d42320d50 100644 --- a/tests/unit/TableOfContents.test.js +++ b/tests/unit/TableOfContents.test.js @@ -85,9 +85,8 @@ describe('Table of Contents testing', () => { const drawer = testComponent .find('.toctree-l1') .last() - .find('.reference'); - // Drawers should not redirect to a page, so href should be undefined - expect(drawer.prop('href')).toBe(undefined); + .find('.reference') + .first(); drawer.simulate('click'); const numUseCasesNodes = mockTocData.children[3].children.length; expect(testComponent.find('.toctree-l2')).toHaveLength(numUseCasesNodes); @@ -95,10 +94,11 @@ describe('Table of Contents testing', () => { const otherDrawer = testComponent .find('.toctree-l1') .at(2) - .find('.reference'); + .find('.reference') + .first(); otherDrawer.simulate('click'); const numPlatformNodes = mockTocData.children[2].children.length; - expect(testComponent.find('.toctree-l2')).toHaveLength(numPlatformNodes); + expect(testComponent.find('.toctree-l2')).toHaveLength(numUseCasesNodes + numPlatformNodes); }); }); }); diff --git a/tests/unit/__snapshots__/TableOfContents.test.js.snap b/tests/unit/__snapshots__/TableOfContents.test.js.snap index 6e61767a9..89ef65627 100644 --- a/tests/unit/__snapshots__/TableOfContents.test.js.snap +++ b/tests/unit/__snapshots__/TableOfContents.test.js.snap @@ -5,7 +5,7 @@ exports[`Table of Contents testing Table of Contents unit tests renders correctl value={ Object { "activeSection": "/", - "toggleDrawer": [Function], + "setActiveSection": [Function], } } >