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={