From 8ed755bd9ab6735481febebd8030c81a48f4948f Mon Sep 17 00:00:00 2001 From: Sergei Kurnevich Date: Wed, 8 Sep 2021 17:17:52 +0200 Subject: [PATCH 1/4] POC for docs filtering feature Signed-off-by: Sergei Kurnevich --- docs/getting-started/overview.md | 5 + docs/getting-started/zowe-architecture.md | 10 + src/theme/DocItem/TagsWrapper.js | 54 ++++ src/theme/DocItem/index.js | 51 +++- src/theme/DocSidebar/ComponentSelector.js | 36 +++ src/theme/DocSidebar/index.js | 354 ++++++++++++++++++++++ src/theme/DocSidebar/styles.module.css | 124 ++++++++ 7 files changed, 632 insertions(+), 2 deletions(-) create mode 100644 src/theme/DocItem/TagsWrapper.js create mode 100644 src/theme/DocSidebar/ComponentSelector.js create mode 100644 src/theme/DocSidebar/index.js create mode 100644 src/theme/DocSidebar/styles.module.css diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index ab03b72106..cb110f6862 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -2,6 +2,11 @@ meta: - name: description content: Zowe is an open source project within the Open Mainframe Project that is part of The Linux Foundation. Zowe is an extensible framework that simplifies and speeds application development, deployment, and operations on z/OS, and provides the ability for extension through CLI plug-ins, new applications to be added to the web desktop, and onboarding of REST APIs to the API Mediation Layer. It narrows the skills gap between new and legacy z/OS developers by offering the choice to work with z/OS either through a Command Line Interface, a Zowe Explorer Visual Studio extension, a web browser served from the Zowe Application Framework, or through REST APIs and web sockets served through the API Mediation Layer. + tags: { + desktop: ["Zowe Application Framework", "z/OS Services"], + cli: ["Test Header 2"], + "mediation layer": ["API Mediation Layer", "z/OS Services"] + } --- # Zowe overview diff --git a/docs/getting-started/zowe-architecture.md b/docs/getting-started/zowe-architecture.md index 4ca697aaa7..c4d36347c7 100644 --- a/docs/getting-started/zowe-architecture.md +++ b/docs/getting-started/zowe-architecture.md @@ -1,3 +1,13 @@ +--- +meta: + - tags: { + architecture: ["Zowe architecture with high availability enablement on Sysplex", "Zowe architecture when using Docker image"], + desktop: ["App Server"], + zss: ["Cross memory server", "ZSS"], + "mediation layer": ["API Gateway", "API Catalog", "API Discovery"] + } +--- + # Zowe architecture Zowe™ is a collection of components that together form a framework that allows Z-based functionality to be accessible across an organization. This includes exposing Z-based components such as z/OSMF as Rest APIs. The framework provides an environment where other components can be included and exposed to a broader non-Z based audience. diff --git a/src/theme/DocItem/TagsWrapper.js b/src/theme/DocItem/TagsWrapper.js new file mode 100644 index 0000000000..df5a0ccb44 --- /dev/null +++ b/src/theme/DocItem/TagsWrapper.js @@ -0,0 +1,54 @@ +import React from "react"; + +// groom code, toc, make pr + +function TagsWrapper({props, activeIDs}) { + // React.Children.toArray() + const {children, ...rest} = props; // do we need 'rest' for anything? + + if (!activeIDs) { + return ; // page does not contain any tags or nothing is selected or everything is selected. + } + + const isNextChildLower = (nextItem, currentHeader) => { + const headers = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']; + if (!headers.includes(nextItem.toLowerCase())) { + return true; // next item is not a header + } else if (headers.indexOf(currentHeader.toLowerCase()) < headers.indexOf(nextItem.toLowerCase())) { + return true; // next item is header but it is lower + } else { + return false; + } + } + + const addHighterHeaders = (acc, headerLevel, index) => { + if (headerLevel < 3) { + return acc.reverse(); + } + const highterHeader = children.slice(0, index).filter(i => i.props.originalType === `h${headerLevel - 1}`).pop(); + const accumulatedHeaders = highterHeader ? [...acc, highterHeader] : acc; + return addHighterHeaders(accumulatedHeaders, headerLevel - 1, index); + } + + const activeChildren = children.reduce((acc, child, index) => { + // Works only with headers that aren't hidden under details + // Could be modified to walk recursively through child.props.children if it exists, but i doubt that we need it. + + if (acc.showChild && (!child.props || isNextChildLower(child.props.originalType, acc.currentHeaderType))) { + acc.showChild = true; + } else if (child.props && activeIDs.includes(child.props.id)) { + acc.showChild = true; + acc.currentHeaderType = child.props.originalType; + const highterHeaders = addHighterHeaders([], parseInt(child.props.originalType.slice(1, 2), 10), index); + acc.children = [...acc.children, ...highterHeaders]; + } else { + acc.showChild = false; + } + return acc.showChild ? {...acc, children: [...acc.children, child]} : acc; + + }, {showChild: false, currentHeaderType: '', children: []}); + + return +}; + +export default TagsWrapper; \ No newline at end of file diff --git a/src/theme/DocItem/index.js b/src/theme/DocItem/index.js index 90f27caa55..3dc1c04202 100644 --- a/src/theme/DocItem/index.js +++ b/src/theme/DocItem/index.js @@ -12,6 +12,7 @@ import TOC from "@theme/TOC"; import clsx from "clsx"; import styles from "./styles.module.css"; import { useActivePlugin, useVersions } from "@theme/hooks/useDocs"; +import TagsWrapper from "./TagsWrapper"; //Components import DocsInfo from "./DocsInfo"; @@ -57,6 +58,50 @@ function DocItem(props) { ); }); + const getActiveIDs = (selectedComponents) => { + if (!metadata.frontMatter.meta || !metadata.frontMatter.meta[0].tags || !Object.keys(metadata.frontMatter.meta[0].tags).length) { + return undefined; // No tags defined for that page + } + const numberOfSelectedComponents = Object.values(selectedComponents).filter(i => !!i).length; + if (!numberOfSelectedComponents || numberOfSelectedComponents === Object.keys(selectedComponents).length) { + return undefined; // Nothing is selected or everything is selected + } + const components = JSON.parse(window.sessionStorage.getItem('ZoweDocs::selectedComponents') || "{}"); + const activeTags = Object.keys(components).filter(tag => !!components[tag]).map(i => i.toLowerCase()); + const tagsDictionary = metadata.frontMatter.meta[0].tags; + const activeHeaders = activeTags.reduce((acc, tag) => tagsDictionary[tag] ? [...acc, ...tagsDictionary[tag]] : acc, []); + return activeHeaders.map(i => i.toLowerCase().replaceAll(' ', '-').replaceAll(/[^\w-]/ig, '')); + } + + const getTOC = (activeIDs, toc) => { + if (!activeIDs) { + return toc; + } + const filterChildren = (arr) => { + return arr.reduce((acc, i) => { + if (activeIDs.includes(i.id)) { + acc.push(i); + } else if (i.children.length) { + const c = filterChildren(i.children); + if (c.length) { + acc.push({...i, children: c}); + } + } + return acc; + }, []); + } + return filterChildren(toc); + } + + const [activeIDs, setActiveIDs] = useState(getActiveIDs(JSON.parse(window.sessionStorage.getItem('ZoweDocs::selectedComponents') || "{}"))); + + window.onstorage = (e) => { + const newComponentsSelection = window.sessionStorage.getItem('ZoweDocs::selectedComponents'); + if (newComponentsSelection) { + setActiveIDs(getActiveIDs(JSON.parse(newComponentsSelection))); + } + }; + return ( <> @@ -108,7 +153,9 @@ function DocItem(props) { title={title} /> )} - + + }}>
@@ -125,7 +172,7 @@ function DocItem(props) { {!hideTableOfContents && DocContent.toc && (
- +
)} diff --git a/src/theme/DocSidebar/ComponentSelector.js b/src/theme/DocSidebar/ComponentSelector.js new file mode 100644 index 0000000000..eb06ba32c7 --- /dev/null +++ b/src/theme/DocSidebar/ComponentSelector.js @@ -0,0 +1,36 @@ +import React, { useState } from "react"; + +const makeNewComponentsObject = () => { + return { + "CLI": false, + "Desktop": false, + "Explorer": false, + "Mediation Layer": false, + "ZSS": false, + } +} + +const ComponentSelector = () => { + // If we can get the tags for each page here, then we can filter the side panel and we could disable this selector if no tags defined for that page? + const currentComponents = window.sessionStorage.getItem("ZoweDocs::selectedComponents"); + const [components, setComponents] = useState(currentComponents ? JSON.parse(currentComponents) : makeNewComponentsObject()); + + const setStorage = (value) => { + const item = JSON.stringify({...components, [value]: !components[value]}); + window.sessionStorage.setItem("ZoweDocs::selectedComponents", item); + window.dispatchEvent( new Event('storage') ); + setComponents(JSON.parse(window.sessionStorage.getItem("ZoweDocs::selectedComponents"))); + } + + return ( +
+ {Object.keys(components).map(i => ( +
+ setStorage(i)}/> + +
))} +
+ ); +}; + +export default ComponentSelector; diff --git a/src/theme/DocSidebar/index.js b/src/theme/DocSidebar/index.js new file mode 100644 index 0000000000..4875796fde --- /dev/null +++ b/src/theme/DocSidebar/index.js @@ -0,0 +1,354 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import React, {useState, useCallback, useEffect, useRef, memo} from 'react'; +import clsx from 'clsx'; +import { + useThemeConfig, + isSamePath, + usePrevious, + useAnnouncementBar, +} from '@docusaurus/theme-common'; +import useLockBodyScroll from '@theme/hooks/useLockBodyScroll'; +import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize'; +import useScrollPosition from '@theme/hooks/useScrollPosition'; +import Link from '@docusaurus/Link'; +import isInternalUrl from '@docusaurus/isInternalUrl'; +import Logo from '@theme/Logo'; +import IconArrow from '@theme/IconArrow'; +import IconMenu from '@theme/IconMenu'; +import IconExternalLink from '@theme/IconExternalLink'; +import {translate} from '@docusaurus/Translate'; +import styles from './styles.module.css'; +const MOBILE_TOGGLE_SIZE = 24; +import ComponentSelector from './ComponentSelector'; + +const isActiveSidebarItem = (item, activePath) => { + if (item.type === 'link') { + return isSamePath(item.href, activePath); + } + + if (item.type === 'category') { + return item.items.some((subItem) => + isActiveSidebarItem(subItem, activePath), + ); + } + + return false; +}; // Optimize sidebar at each "level" +// TODO this item should probably not receive the "activePath" props +// TODO this triggers whole sidebar re-renders on navigation + +const DocSidebarItems = memo(function DocSidebarItems({items, ...props}) { + return items.map((item, index) => ( + + )); +}); + +function DocSidebarItem(props) { + switch (props.item.type) { + case 'category': + return ; + + case 'link': + default: + return ; + } +} + +function DocSidebarItemCategory({ + item, + onItemClick, + collapsible, + activePath, + ...props +}) { + const {items, label} = item; + const isActive = isActiveSidebarItem(item, activePath); + const wasActive = usePrevious(isActive); // active categories are always initialized as expanded + // the default (item.collapsed) is only used for non-active categories + + const [collapsed, setCollapsed] = useState(() => { + if (!collapsible) { + return false; + } + + return isActive ? false : item.collapsed; + }); + const menuListRef = useRef(null); + const [menuListHeight, setMenuListHeight] = useState(undefined); + + const handleMenuListHeight = (calc = true) => { + setMenuListHeight( + calc ? `${menuListRef.current?.scrollHeight}px` : undefined, + ); + }; // If we navigate to a category, it should automatically expand itself + + useEffect(() => { + const justBecameActive = isActive && !wasActive; + + if (justBecameActive && collapsed) { + setCollapsed(false); + } + }, [isActive, wasActive, collapsed]); + const handleItemClick = useCallback( + (e) => { + e.preventDefault(); + + if (!menuListHeight) { + handleMenuListHeight(); + } + + setTimeout(() => setCollapsed((state) => !state), 100); + }, + [menuListHeight], + ); + + if (items.length === 0) { + return null; + } + + return ( +
  • + {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} + + {label} + +
      { + if (!collapsed) { + handleMenuListHeight(false); + } + }}> + +
    +
  • + ); +} + +function DocSidebarItemLink({ + item, + onItemClick, + activePath, + collapsible: _collapsible, + ...props +}) { + const {href, label} = item; + const isActive = isActiveSidebarItem(item, activePath); + return ( +
  • + + {isInternalUrl(href) ? ( + label + ) : ( + + {label} + + + )} + +
  • + ); +} + +function useShowAnnouncementBar() { + const {isClosed} = useAnnouncementBar(); + const [showAnnouncementBar, setShowAnnouncementBar] = useState(!isClosed); + useScrollPosition(({scrollY}) => { + if (!isClosed) { + setShowAnnouncementBar(scrollY === 0); + } + }); + return showAnnouncementBar; +} + +function useResponsiveSidebar() { + const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false); + useLockBodyScroll(showResponsiveSidebar); + const windowSize = useWindowSize(); + useEffect(() => { + if (windowSize === windowSizes.desktop) { + setShowResponsiveSidebar(false); + } + }, [windowSize]); + const closeResponsiveSidebar = useCallback( + (e) => { + e.target.blur(); + setShowResponsiveSidebar(false); + }, + [setShowResponsiveSidebar], + ); + const toggleResponsiveSidebar = useCallback(() => { + setShowResponsiveSidebar((value) => !value); + }, [setShowResponsiveSidebar]); + return { + showResponsiveSidebar, + closeResponsiveSidebar, + toggleResponsiveSidebar, + }; +} + +function HideableSidebarButton({onClick}) { + return ( + + ); +} + +function ResponsiveSidebarButton({responsiveSidebarOpened, onClick}) { + return ( + + ); +} + +function DocSidebar({ + path, + sidebar, + sidebarCollapsible = true, + onCollapse, + isHidden, +}) { + const showAnnouncementBar = useShowAnnouncementBar(); + const { + navbar: {hideOnScroll}, + hideableSidebar, + } = useThemeConfig(); + const {isClosed: isAnnouncementBarClosed} = useAnnouncementBar(); + const { + showResponsiveSidebar, + closeResponsiveSidebar, + toggleResponsiveSidebar, + } = useResponsiveSidebar(); + return ( +
    + {hideOnScroll && } + + {hideableSidebar && } +
    + ); +} + +export default DocSidebar; diff --git a/src/theme/DocSidebar/styles.module.css b/src/theme/DocSidebar/styles.module.css new file mode 100644 index 0000000000..f1c4da47d0 --- /dev/null +++ b/src/theme/DocSidebar/styles.module.css @@ -0,0 +1,124 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +:root { + --collapse-button-bg-color-dark: #2e333a; +} + +@media (min-width: 997px) { + .sidebar { + display: flex; + flex-direction: column; + max-height: 100vh; + height: 100%; + position: sticky; + top: 0; + padding-top: var(--ifm-navbar-height); + width: var(--doc-sidebar-width); + transition: opacity 50ms ease; + } + + .sidebarWithHideableNavbar { + padding-top: 0; + } + + .sidebarHidden { + opacity: 0; + height: 0; + overflow: hidden; + visibility: hidden; + } + + .sidebarLogo { + display: flex !important; + align-items: center; + margin: 0 var(--ifm-navbar-padding-horizontal); + min-height: var(--ifm-navbar-height); + max-height: var(--ifm-navbar-height); + color: inherit !important; + text-decoration: none !important; + } + + .sidebarLogo img { + margin-right: 0.5rem; + height: 2rem; + } + + .menu { + flex-grow: 1; + padding: 0.5rem; + } + + .menuLinkText { + cursor: initial; + } + + .menuLinkText:hover { + background: none; + } + + .menuWithAnnouncementBar { + margin-bottom: var(--docusaurus-announcement-bar-height); + } + + .collapseSidebarButton { + display: block !important; + background-color: var(--ifm-button-background-color); + height: 40px; + position: sticky; + bottom: 0; + border-radius: 0; + border: 1px solid var(--ifm-toc-border-color); + } + + .collapseSidebarButtonIcon { + transform: rotate(180deg); + margin-top: 4px; + } + html[dir='rtl'] .collapseSidebarButtonIcon { + transform: rotate(0); + } + + html[data-theme='dark'] .collapseSidebarButton { + background-color: var(--collapse-button-bg-color-dark); + } + + html[data-theme='dark'] .collapseSidebarButton:hover, + html[data-theme='dark'] .collapseSidebarButton:focus { + background-color: var(--ifm-color-emphasis-200); + } +} + +.sidebarLogo, +.collapseSidebarButton { + display: none; +} + +.sidebarMenuIcon { + vertical-align: middle; +} + +.sidebarMenuCloseIcon { + display: inline-flex; + justify-content: center; + align-items: center; + height: 24px; + font-size: 1.5rem; + font-weight: var(--ifm-font-weight-bold); + line-height: 0.9; + width: 24px; +} + +:global(.menu__list) :global(.menu__list) { + overflow-y: hidden; + will-change: height; + transition: height var(--ifm-transition-fast) linear; +} + +:global(.menu__list-item--collapsed) :global(.menu__list) { + height: 0 !important; +} From 92d3ab2f652f34724cd2d6963adc638bf2f59502 Mon Sep 17 00:00:00 2001 From: Sergei Kurnevich Date: Wed, 15 Sep 2021 16:31:26 +0200 Subject: [PATCH 2/4] Support of hidden h6 Signed-off-by: Sergei Kurnevich --- src/theme/DocItem/TagsWrapper.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/theme/DocItem/TagsWrapper.js b/src/theme/DocItem/TagsWrapper.js index df5a0ccb44..5b5bd0d9bf 100644 --- a/src/theme/DocItem/TagsWrapper.js +++ b/src/theme/DocItem/TagsWrapper.js @@ -1,17 +1,19 @@ import React from "react"; -// groom code, toc, make pr - function TagsWrapper({props, activeIDs}) { - // React.Children.toArray() const {children, ...rest} = props; // do we need 'rest' for anything? + const removeEmptyH6 = (arr) => { + return arr.filter(i => !(i.props && i.props.originalType === 'h6' && i.props.children === '')); + } + if (!activeIDs) { - return ; // page does not contain any tags or nothing is selected or everything is selected. + const newProps = {...props, children: removeEmptyH6(children)}; + return ; // page does not contain any tags or nothing is selected or everything is selected. } const isNextChildLower = (nextItem, currentHeader) => { - const headers = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7']; + const headers = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; if (!headers.includes(nextItem.toLowerCase())) { return true; // next item is not a header } else if (headers.indexOf(currentHeader.toLowerCase()) < headers.indexOf(nextItem.toLowerCase())) { @@ -43,12 +45,16 @@ function TagsWrapper({props, activeIDs}) { acc.children = [...acc.children, ...highterHeaders]; } else { acc.showChild = false; + // Note: Manipulating children style is possible like that by adding the child.props.style, i.e. to gray out some of them instead of filtering. + // newChild = {...child, props: {...child.props, style: {opacity: '0.3'}}} } return acc.showChild ? {...acc, children: [...acc.children, child]} : acc; }, {showChild: false, currentHeaderType: '', children: []}); - return + activeChildren.children.push(

    Not found what you was looking for? Check the active filters

    ); + + return }; export default TagsWrapper; \ No newline at end of file From 536a442f040b97167921ae9e90188e9bbd4c3ee3 Mon Sep 17 00:00:00 2001 From: Sergei Kurnevich Date: Tue, 21 Sep 2021 16:59:49 +0200 Subject: [PATCH 3/4] Updated components selector structure and UI Signed-off-by: Sergei Kurnevich --- docs/getting-started/overview.md | 5 -- docs/getting-started/zowe-architecture.md | 10 --- src/theme/DocItem/TagsWrapper.js | 9 +- src/theme/DocItem/index.js | 19 ++-- src/theme/DocSidebar/ComponentSelector.js | 105 ++++++++++++++++++---- src/theme/DocSidebar/index.js | 2 +- src/theme/DocSidebar/styles.module.css | 53 +++++++++++ 7 files changed, 162 insertions(+), 41 deletions(-) diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index cb110f6862..ab03b72106 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -2,11 +2,6 @@ meta: - name: description content: Zowe is an open source project within the Open Mainframe Project that is part of The Linux Foundation. Zowe is an extensible framework that simplifies and speeds application development, deployment, and operations on z/OS, and provides the ability for extension through CLI plug-ins, new applications to be added to the web desktop, and onboarding of REST APIs to the API Mediation Layer. It narrows the skills gap between new and legacy z/OS developers by offering the choice to work with z/OS either through a Command Line Interface, a Zowe Explorer Visual Studio extension, a web browser served from the Zowe Application Framework, or through REST APIs and web sockets served through the API Mediation Layer. - tags: { - desktop: ["Zowe Application Framework", "z/OS Services"], - cli: ["Test Header 2"], - "mediation layer": ["API Mediation Layer", "z/OS Services"] - } --- # Zowe overview diff --git a/docs/getting-started/zowe-architecture.md b/docs/getting-started/zowe-architecture.md index c4d36347c7..4ca697aaa7 100644 --- a/docs/getting-started/zowe-architecture.md +++ b/docs/getting-started/zowe-architecture.md @@ -1,13 +1,3 @@ ---- -meta: - - tags: { - architecture: ["Zowe architecture with high availability enablement on Sysplex", "Zowe architecture when using Docker image"], - desktop: ["App Server"], - zss: ["Cross memory server", "ZSS"], - "mediation layer": ["API Gateway", "API Catalog", "API Discovery"] - } ---- - # Zowe architecture Zowe™ is a collection of components that together form a framework that allows Z-based functionality to be accessible across an organization. This includes exposing Z-based components such as z/OSMF as Rest APIs. The framework provides an environment where other components can be included and exposed to a broader non-Z based audience. diff --git a/src/theme/DocItem/TagsWrapper.js b/src/theme/DocItem/TagsWrapper.js index 5b5bd0d9bf..74069322bf 100644 --- a/src/theme/DocItem/TagsWrapper.js +++ b/src/theme/DocItem/TagsWrapper.js @@ -23,17 +23,18 @@ function TagsWrapper({props, activeIDs}) { } } - const addHighterHeaders = (acc, headerLevel, index) => { + const addHighterHeaders = (acc, headerLevel, index, activeChildren) => { if (headerLevel < 3) { return acc.reverse(); } const highterHeader = children.slice(0, index).filter(i => i.props.originalType === `h${headerLevel - 1}`).pop(); - const accumulatedHeaders = highterHeader ? [...acc, highterHeader] : acc; + const isHeaderAlreadyDisplayed = !!(highterHeader && activeChildren && activeChildren.filter(c => c.props.id === highterHeader.props.id).length); + const accumulatedHeaders = highterHeader && !isHeaderAlreadyDisplayed ? [...acc, highterHeader] : acc; return addHighterHeaders(accumulatedHeaders, headerLevel - 1, index); } const activeChildren = children.reduce((acc, child, index) => { - // Works only with headers that aren't hidden under details + // Works only with headers that aren't hidden under
    // Could be modified to walk recursively through child.props.children if it exists, but i doubt that we need it. if (acc.showChild && (!child.props || isNextChildLower(child.props.originalType, acc.currentHeaderType))) { @@ -41,7 +42,7 @@ function TagsWrapper({props, activeIDs}) { } else if (child.props && activeIDs.includes(child.props.id)) { acc.showChild = true; acc.currentHeaderType = child.props.originalType; - const highterHeaders = addHighterHeaders([], parseInt(child.props.originalType.slice(1, 2), 10), index); + const highterHeaders = addHighterHeaders([], parseInt(child.props.originalType.slice(1, 2), 10), index, acc.children); acc.children = [...acc.children, ...highterHeaders]; } else { acc.showChild = false; diff --git a/src/theme/DocItem/index.js b/src/theme/DocItem/index.js index 3dc1c04202..e3908b241f 100644 --- a/src/theme/DocItem/index.js +++ b/src/theme/DocItem/index.js @@ -62,14 +62,21 @@ function DocItem(props) { if (!metadata.frontMatter.meta || !metadata.frontMatter.meta[0].tags || !Object.keys(metadata.frontMatter.meta[0].tags).length) { return undefined; // No tags defined for that page } - const numberOfSelectedComponents = Object.values(selectedComponents).filter(i => !!i).length; - if (!numberOfSelectedComponents || numberOfSelectedComponents === Object.keys(selectedComponents).length) { - return undefined; // Nothing is selected or everything is selected + if (!selectedComponents || !(selectedComponents.tags instanceof Object)) { + return undefined; // No selectedComponents or object has wrong structure } - const components = JSON.parse(window.sessionStorage.getItem('ZoweDocs::selectedComponents') || "{}"); - const activeTags = Object.keys(components).filter(tag => !!components[tag]).map(i => i.toLowerCase()); + const numberOfSelectedComponents = Object.values(selectedComponents.tags).filter(i => i.value).length; + const activeDownloadType = selectedComponents.downloadType && selectedComponents.downloadType.toLowerCase() !== 'all'; + const activeESM = selectedComponents.esm && selectedComponents.esm.toLowerCase() !== 'all'; + if (!numberOfSelectedComponents && !activeDownloadType && !activeESM) { + return undefined; // Nothing is selected + } + const activeTags = Object.keys(selectedComponents.tags).filter(tag => selectedComponents.tags[tag].value).map(tag => tag.toLowerCase()); + activeDownloadType && activeTags.push(selectedComponents.downloadType.toLowerCase()); + activeESM && activeTags.push(selectedComponents.esm.toLowerCase()); const tagsDictionary = metadata.frontMatter.meta[0].tags; - const activeHeaders = activeTags.reduce((acc, tag) => tagsDictionary[tag] ? [...acc, ...tagsDictionary[tag]] : acc, []); + const lowerCaseTagsDict = Object.keys(tagsDictionary).reduce((acc, i) => ({...acc, [i.toLowerCase()]: tagsDictionary[i]}), {}); + const activeHeaders = activeTags.reduce((acc, tag) => lowerCaseTagsDict[tag] ? [...acc, ...lowerCaseTagsDict[tag]] : acc, []); return activeHeaders.map(i => i.toLowerCase().replaceAll(' ', '-').replaceAll(/[^\w-]/ig, '')); } diff --git a/src/theme/DocSidebar/ComponentSelector.js b/src/theme/DocSidebar/ComponentSelector.js index eb06ba32c7..203cd2a1c3 100644 --- a/src/theme/DocSidebar/ComponentSelector.js +++ b/src/theme/DocSidebar/ComponentSelector.js @@ -1,34 +1,109 @@ import React, { useState } from "react"; +import styles from './styles.module.css'; +// Notes: +// DocSideBarItem can take optional props - customProps: item={{...item, customProps: {testprop: true}}} +// Rewrite in TS? + +// FIXME: Cross dependency of Components and ESMs/Download types. Selecting of ESM should not affect components if no other ESMs are specified for page / topic? +const ESMs = ['All', 'RACF', 'TopSecret', 'ACF2']; +const downloadTypes = ['All', 'SMPE', 'Pax', 'Container']; const makeNewComponentsObject = () => { - return { - "CLI": false, - "Desktop": false, - "Explorer": false, - "Mediation Layer": false, - "ZSS": false, + const item = { + tags: { + cli: {label: "CLI", value: false}, + desktop: {label: "Desktop", value: false}, + explorer: {label: "Explorer", value: false}, + apiml: {label: "Mediation Layer", value: false}, + zss: {label: "ZSS", value: false} + }, + esm: ESMs[0], + downloadType: downloadTypes[0], } + window.sessionStorage.setItem("ZoweDocs::selectedComponents", JSON.stringify(item)); + return item; } const ComponentSelector = () => { - // If we can get the tags for each page here, then we can filter the side panel and we could disable this selector if no tags defined for that page? const currentComponents = window.sessionStorage.getItem("ZoweDocs::selectedComponents"); const [components, setComponents] = useState(currentComponents ? JSON.parse(currentComponents) : makeNewComponentsObject()); + const [showTagsSelector, toggleTagsSelector] = useState(false); - const setStorage = (value) => { - const item = JSON.stringify({...components, [value]: !components[value]}); + const setStorage = item => { window.sessionStorage.setItem("ZoweDocs::selectedComponents", item); window.dispatchEvent( new Event('storage') ); setComponents(JSON.parse(window.sessionStorage.getItem("ZoweDocs::selectedComponents"))); } + const setTags = tag => { + const tags = components.tags; + tags[tag].value = !tags[tag].value; + const item = JSON.stringify({...components, tags}); + setStorage(item); + } + + const setESM = type => { + const item = JSON.stringify({...components, esm: type}); + setStorage(item); + } + + const setDownloadType = type => { + const item = JSON.stringify({...components, downloadType: type}); + setStorage(item); + } + return ( -
    - {Object.keys(components).map(i => ( -
    - setStorage(i)}/> - -
    ))} +
    + ); }; diff --git a/src/theme/DocSidebar/index.js b/src/theme/DocSidebar/index.js index 4875796fde..8cddf05d4b 100644 --- a/src/theme/DocSidebar/index.js +++ b/src/theme/DocSidebar/index.js @@ -343,8 +343,8 @@ function DocSidebar({ collapsible={sidebarCollapsible} activePath={path} /> + - {hideableSidebar && }
    diff --git a/src/theme/DocSidebar/styles.module.css b/src/theme/DocSidebar/styles.module.css index f1c4da47d0..ed893f4c9b 100644 --- a/src/theme/DocSidebar/styles.module.css +++ b/src/theme/DocSidebar/styles.module.css @@ -65,6 +65,59 @@ margin-bottom: var(--docusaurus-announcement-bar-height); } + .tagsSelectorContainer { + padding: 24px 0px 0px; + } + + .collapsibleTagsSelector { + cursor: pointer; + padding: 4px 16px; + border-radius: 4px; + display: flex; + justify-content: space-between; + align-items: center; + } + + .collapsibleTagsSelector:hover { + background-color: var(--ifm-color-secondary); + } + + .tagsSeparator { + border-top: solid 1px var(--ifm-toc-border-color); + margin: 0 16px 8px 16px; + } + + .tagsSection { + padding-bottom: 12px; + } + + .tagSectionLabel { + color: var(--ifm-font-color-base) + } + + .tagOption { + display: flex; + align-items: center; + height: 24px; + } + + .tagRadioInput { + margin: 0 7px 0 4px; + } + + .arrowIcon { + transition: transform 0.2s linear; + max-width: 20px; + max-height: 20px; + } + + .tagsSelectorContent { + color: var(--ifm-menu-color); + padding: 8px 16px 0 32px; + overflow: hidden; + transition: height 0.2s linear; + } + .collapseSidebarButton { display: block !important; background-color: var(--ifm-button-background-color); From da6ebd444d5785e9bec0f2953abb1515945e459b Mon Sep 17 00:00:00 2001 From: Sergei Kurnevich Date: Wed, 22 Sep 2021 14:24:09 +0200 Subject: [PATCH 4/4] Minor fix for dark theme styles Signed-off-by: Sergei Kurnevich --- src/theme/DocSidebar/ComponentSelector.js | 2 +- src/theme/DocSidebar/styles.module.css | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/theme/DocSidebar/ComponentSelector.js b/src/theme/DocSidebar/ComponentSelector.js index 203cd2a1c3..af7766c16f 100644 --- a/src/theme/DocSidebar/ComponentSelector.js +++ b/src/theme/DocSidebar/ComponentSelector.js @@ -56,7 +56,7 @@ const ComponentSelector = () => {