From fd1b1623c48e4fa0d53719db2164d48a8e6eb27a Mon Sep 17 00:00:00 2001 From: Seung Park Date: Mon, 23 Dec 2024 09:11:08 -0500 Subject: [PATCH] DOP-5258: make collapsibles interactive offline (#1330) Co-authored-by: rayangler <27821750+rayangler@users.noreply.github.com> --- src/components/Collapsible/index.js | 21 ++++++++++++---- src/components/Collapsible/styles.js | 12 +++++++--- src/components/ComponentFactoryLazy.js | 2 +- .../head-scripts/offline-ui/collapsible.js | 24 +++++++++++++++++++ src/utils/head-scripts/offline-ui/index.js | 5 ++-- .../__snapshots__/Collapsible.test.js.snap | 13 ++++++++++ 6 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 src/utils/head-scripts/offline-ui/collapsible.js diff --git a/src/components/Collapsible/index.js b/src/components/Collapsible/index.js index c8518c8c3..c1a780890 100644 --- a/src/components/Collapsible/index.js +++ b/src/components/Collapsible/index.js @@ -8,7 +8,9 @@ import { cx } from '@leafygreen-ui/emotion'; import { Body } from '@leafygreen-ui/typography'; import { HeadingContextProvider } from '../../context/heading-context'; import { findAllNestedAttribute } from '../../utils/find-all-nested-attribute'; +import { OFFLINE_CLASSNAME } from '../../utils/head-scripts/offline-ui/collapsible'; import { isBrowser } from '../../utils/is-browser'; +import { isOfflineDocsBuild } from '../../utils/is-offline-docs-build'; import { reportAnalytics } from '../../utils/report-analytics'; import ComponentFactory from '../ComponentFactory'; import Heading from '../Heading'; @@ -25,7 +27,10 @@ const Collapsible = ({ nodeData: { children, options }, sectionDepth, ...rest }) return findAllNestedAttribute(children, 'id'); }, [children]); - const [open, setOpen] = useState(expanded ?? true); + const [open, setOpen] = useState(() => { + if (isOfflineDocsBuild) return true; + return expanded ?? true; + }); const headingNodeData = { id, children: [{ type: 'text', value: heading }], @@ -67,7 +72,10 @@ const Collapsible = ({ nodeData: { children, options }, sectionDepth, ...rest }) return ( - + {/* Adding 1 to reflect logic in parser, but want to show up as H2 for SEO reasons */} @@ -76,11 +84,16 @@ const Collapsible = ({ nodeData: { children, options }, sectionDepth, ...rest }) {subHeading} - + - + {children.map((c, i) => ( ))} diff --git a/src/components/Collapsible/styles.js b/src/components/Collapsible/styles.js index c49981825..a409ad23d 100644 --- a/src/components/Collapsible/styles.js +++ b/src/components/Collapsible/styles.js @@ -1,6 +1,7 @@ import { css } from '@leafygreen-ui/emotion'; import { palette } from '@leafygreen-ui/palette'; import { theme } from '../../theme/docsTheme'; +import { OFFLINE_CLASSNAME } from '../../utils/head-scripts/offline-ui/collapsible'; export const collapsibleStyle = css` border-bottom: 1px solid ${palette.gray.light2}; @@ -29,13 +30,18 @@ export const headerStyle = css` export const iconStyle = css` align-self: center; flex-shrink: 0; + + .${OFFLINE_CLASSNAME}[aria-expanded=false] & { + transform: rotate(-90deg); + } `; -export const innerContentStyle = (open) => css` +export const innerContentStyle = css` overflow: hidden; height: 0; color: --font-color-primary; - ${open && `height: auto;`} - ${open && `visibility: visible;`} + [aria-expanded='true'] & { + height: auto; + } `; diff --git a/src/components/ComponentFactoryLazy.js b/src/components/ComponentFactoryLazy.js index 7cb6c9db7..e55e6d750 100644 --- a/src/components/ComponentFactoryLazy.js +++ b/src/components/ComponentFactoryLazy.js @@ -12,7 +12,7 @@ const ComponentMap = { }; export const LAZY_COMPONENTS = Object.keys(ComponentMap).reduce((res, key) => { - if (isOfflineDocsBuild && key === 'instruqt') { + if (isOfflineDocsBuild && ['video', 'instruqt'].includes(key)) { res[key] = (props) => (!props.nodeData?.options?.drawer ? : null); } else { const LazyComponent = ComponentMap[key]; diff --git a/src/utils/head-scripts/offline-ui/collapsible.js b/src/utils/head-scripts/offline-ui/collapsible.js new file mode 100644 index 000000000..880d5db3d --- /dev/null +++ b/src/utils/head-scripts/offline-ui/collapsible.js @@ -0,0 +1,24 @@ +function bindCollapsibleUI() { + const onContentLoaded = () => { + try { + const collapsibleComponents = document.querySelectorAll('.offline-collapsible'); + for (const collapsible of collapsibleComponents) { + // bind event to button + const button = collapsible.querySelector('button'); + button?.addEventListener('click', () => { + const newVal = button.getAttribute('aria-expanded') === 'false'; + button.setAttribute('aria-expanded', newVal); + collapsible.setAttribute('aria-expanded', newVal); + }); + } + } catch (e) { + console.error(e); + } + }; + + document.addEventListener('DOMContentLoaded', onContentLoaded, false); +} + +export default bindCollapsibleUI; + +export const OFFLINE_CLASSNAME = `offline-collapsible`; diff --git a/src/utils/head-scripts/offline-ui/index.js b/src/utils/head-scripts/offline-ui/index.js index 9c9138709..650026706 100644 --- a/src/utils/head-scripts/offline-ui/index.js +++ b/src/utils/head-scripts/offline-ui/index.js @@ -1,4 +1,5 @@ import bindTabUI from './tabs'; +import bindCollapsibleUI from './collapsible'; import updateSidenavHeight from './sidenav'; import bindTabsSelectorsUI from './tabs-selectors'; const OFFLINE_UI_CLASSNAME = 'snooty-offline-ui'; @@ -12,6 +13,6 @@ const getScript = ({ key, fn }) => ( /> ); -export const OFFLINE_HEAD_SCRIPTS = [bindTabUI, updateSidenavHeight, bindTabsSelectorsUI].map((fn, idx) => - getScript({ key: `offline-docs-ui-${idx}`, fn }) +export const OFFLINE_HEAD_SCRIPTS = [bindTabUI, updateSidenavHeight, bindTabsSelectorsUI, bindCollapsibleUI].map( + (fn, idx) => getScript({ key: `offline-docs-ui-${idx}`, fn }) ); diff --git a/tests/unit/__snapshots__/Collapsible.test.js.snap b/tests/unit/__snapshots__/Collapsible.test.js.snap index a1fd0eef9..c349fb8d2 100644 --- a/tests/unit/__snapshots__/Collapsible.test.js.snap +++ b/tests/unit/__snapshots__/Collapsible.test.js.snap @@ -160,6 +160,13 @@ exports[`collapsible component renders all the content in the options and childr background-color: rgba(61,79,88,0.1); } +.offline-collapsible[aria-expanded=false] .emotion-8 { + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg); +} + .emotion-9 { position: absolute; top: 0; @@ -186,6 +193,10 @@ exports[`collapsible component renders all the content in the options and childr color: --font-color-primary; } +[aria-expanded='true'] .emotion-10 { + height: auto; +} + .emotion-11 { margin: unset; font-family: 'Euclid Circular A','Helvetica Neue',Helvetica,Arial,sans-serif; @@ -509,6 +520,7 @@ exports[`collapsible component renders all the content in the options and childr }