From 9749918bf6b4a421af40145b3b307a356dbbfcb9 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 27 Sep 2025 14:51:02 +0200 Subject: [PATCH 1/4] chore: add deper nav for docs Signed-off-by: Gabriel Dugny --- .../docs/deeper-nav/even-deeper/index.md | 9 +++++++++ .../documented-component/docs/deeper-nav/index.md | 11 +++++++++++ .../examples/documented-component/mkdocs.yml | 4 ++++ 3 files changed, 24 insertions(+) create mode 100644 plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/even-deeper/index.md create mode 100644 plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/index.md diff --git a/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/even-deeper/index.md b/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/even-deeper/index.md new file mode 100644 index 0000000000..33f63ea079 --- /dev/null +++ b/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/even-deeper/index.md @@ -0,0 +1,9 @@ +# Deeper Nav + +Useful to test ExpandableNavigationAddon! + +## I'm in too deep! + +## And I'm trying to keep + +### ...up above my head diff --git a/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/index.md b/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/index.md new file mode 100644 index 0000000000..3f3800a1d5 --- /dev/null +++ b/plugins/techdocs-backend/examples/documented-component/docs/deeper-nav/index.md @@ -0,0 +1,11 @@ +# Deeper Nav + +Useful to test ExpandableNavigationAddon! + +## Some nav for TOC + +Yes! + +### Some deeper TOC nav + +## What? A second element with long text!? diff --git a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml index fd954225a3..4632c6eb13 100644 --- a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml +++ b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml @@ -8,6 +8,10 @@ nav: - 'Code Sample': code/code-sample.md - Extensions: extensions.md - 'Inner Component Docs': inner-component-docs/index.md + - 'Deeper Nav': + - deeper-nav/index.md + - 'Inner Deeper Nav': + - 'Inner Inner Deeper Nav': deeper-nav/even-deeper/index.md plugins: - techdocs-core From 69294800c16476e5e22837f47dcc1e7068ffdca2 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Thu, 10 Jul 2025 11:39:23 +0200 Subject: [PATCH 2/4] fix(techdocs): ExpandableNavigation addons does not work on Firefox Signed-off-by: Gabriel Dugny --- .changeset/gentle-bikes-relax.md | 5 + .../ExpandableNavigation.tsx | 103 +++++++++--------- 2 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 .changeset/gentle-bikes-relax.md diff --git a/.changeset/gentle-bikes-relax.md b/.changeset/gentle-bikes-relax.md new file mode 100644 index 0000000000..7a4aeda5f8 --- /dev/null +++ b/.changeset/gentle-bikes-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +--- + +ExpandableCollapse Techdocs Addon was breaking native sidebar collapse on Firefox diff --git a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx index 83e34f411a..08761d1f15 100644 --- a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useEffect, useCallback, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useLocalStorageValue } from '@react-hookz/web'; import { Button, withStyles } from '@material-ui/core'; import ChevronRightIcon from '@material-ui/icons/ChevronRight'; @@ -73,71 +73,72 @@ export const ExpandableNavigationAddon = () => { NESTED_LIST_TOGGLE, ]); - const shouldToggle = useCallback( - (item: HTMLInputElement) => { - const isExpanded = item.checked; - const shouldExpand = expanded?.expandAllNestedNavs; - - // Is collapsed but should expand - if (shouldExpand && !isExpanded) { - return true; - } - - // Is expanded but should collapse - if (!shouldExpand && isExpanded) { - return true; - } - - return false; - }, - [expanded], - ); - const handleKeyPass = ( + // Define handleKeyPass as a named function + function handleKeyPass( event: React.KeyboardEvent, toggleAction: () => void, - ) => { + ) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleAction(); } - }; - useEffect(() => { - // There is no nested navs - if (!checkboxToggles?.length) return; + } + useEffect(() => { + if (!checkboxToggles?.length) return; setHasNavSubLevels(true); - checkboxToggles.forEach(item => { - item.tabIndex = 0; - const toggleAction = () => { - if (shouldToggle(item)) { - item.click(); + function createKeydownHandler(item: HTMLInputElement) { + return function handleKeydown(event: KeyboardEvent) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + item.checked = !item.checked; + item.dispatchEvent(new Event('change', { bubbles: true })); } }; - // Add keyboard event listener - const keydownHandler = (event: KeyboardEvent) => { - handleKeyPass( - event as unknown as React.KeyboardEvent, - toggleAction, - ); + } + function createCleanup( + item: HTMLInputElement, + handler: (event: KeyboardEvent) => void, + ) { + return function cleanup() { + item.removeEventListener('keydown', handler); }; + } + const cleanupFunctions: Array<() => void> = []; + for (const item of checkboxToggles) { + item.tabIndex = 0; + const keydownHandler = createKeydownHandler(item); item.addEventListener('keydown', keydownHandler); - item.addEventListener('click', toggleAction); + cleanupFunctions.push(createCleanup(item, keydownHandler)); + } + function cleanupAll() { + for (const cleanup of cleanupFunctions) { + cleanup(); + } + } + // eslint-disable-next-line consistent-return + return cleanupAll; + }, [checkboxToggles, expanded]); - // Clean up event listener or unmount - return () => { - item.removeEventListener('keydown', keydownHandler); - item.removeEventListener('click', toggleAction); - }; - }); - }, [checkboxToggles, shouldToggle]); useEffect(() => { if (!checkboxToggles?.length) return; - checkboxToggles.forEach(item => { + function shouldToggle(item: HTMLInputElement) { + const isExpanded = item.checked; + const shouldExpand = expanded?.expandAllNestedNavs; + if (shouldExpand && !isExpanded) { + return true; + } + if (!shouldExpand && isExpanded) { + return true; + } + return false; + } + for (const item of checkboxToggles) { if (shouldToggle(item)) { item.click(); } - }); - }, [expanded, checkboxToggles, shouldToggle]); + } + }, [expanded, checkboxToggles]); const handleState = () => { setExpanded(prevState => ({ @@ -145,13 +146,17 @@ export const ExpandableNavigationAddon = () => { })); }; + function handleButtonKeyDown(event: React.KeyboardEvent) { + handleKeyPass(event, handleState); + } + return ( <> {hasNavSubLevels ? ( handleKeyPass(event, handleState)} + onKeyDown={handleButtonKeyDown} tabIndex={0} // Ensuring keyboard focus aria-expanded={expanded?.expandAllNestedNavs} // Accessibility aria-label={ From 98f4e6339d4da656de65c60594b840be5e68bbaa Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Thu, 10 Jul 2025 15:36:06 +0200 Subject: [PATCH 3/4] chore: deduplicate Signed-off-by: Gabriel Dugny --- .../ExpandableNavigation.tsx | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx index 08761d1f15..a9dccb1f12 100644 --- a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx @@ -87,38 +87,7 @@ export const ExpandableNavigationAddon = () => { useEffect(() => { if (!checkboxToggles?.length) return; setHasNavSubLevels(true); - function createKeydownHandler(item: HTMLInputElement) { - return function handleKeydown(event: KeyboardEvent) { - if (event.key === 'Enter' || event.key === ' ') { - event.preventDefault(); - item.checked = !item.checked; - item.dispatchEvent(new Event('change', { bubbles: true })); - } - }; - } - function createCleanup( - item: HTMLInputElement, - handler: (event: KeyboardEvent) => void, - ) { - return function cleanup() { - item.removeEventListener('keydown', handler); - }; - } - const cleanupFunctions: Array<() => void> = []; - for (const item of checkboxToggles) { - item.tabIndex = 0; - const keydownHandler = createKeydownHandler(item); - item.addEventListener('keydown', keydownHandler); - cleanupFunctions.push(createCleanup(item, keydownHandler)); - } - function cleanupAll() { - for (const cleanup of cleanupFunctions) { - cleanup(); - } - } - // eslint-disable-next-line consistent-return - return cleanupAll; - }, [checkboxToggles, expanded]); + }, [checkboxToggles]); useEffect(() => { if (!checkboxToggles?.length) return; From f912df08fcf53404c2157cbd95f3e6c89409b365 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Tue, 16 Sep 2025 10:25:10 +0200 Subject: [PATCH 4/4] chore: Clean techdocs-module-addons-contrib ExpandableNavigation Co-authored-by: Mark Avery Signed-off-by: Gabriel Dugny Signed-off-by: Gabriel Dugny --- .../src/ExpandableNavigation/ExpandableNavigation.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx index a9dccb1f12..2a18e642fa 100644 --- a/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ExpandableNavigation/ExpandableNavigation.tsx @@ -92,15 +92,7 @@ export const ExpandableNavigationAddon = () => { useEffect(() => { if (!checkboxToggles?.length) return; function shouldToggle(item: HTMLInputElement) { - const isExpanded = item.checked; - const shouldExpand = expanded?.expandAllNestedNavs; - if (shouldExpand && !isExpanded) { - return true; - } - if (!shouldExpand && isExpanded) { - return true; - } - return false; + return expanded?.expandAllNestedNavs !== item.checked; } for (const item of checkboxToggles) { if (shouldToggle(item)) {