From 34aebcc5204dbdd73ab3500457b61497ccc5c9a7 Mon Sep 17 00:00:00 2001 From: fcamgz Date: Mon, 13 Oct 2025 16:17:10 -0400 Subject: [PATCH] Fixed SearchModal not cleaning up body overflow #31402 **Changes:** - Added `isDialogOpen = open && !hidden` logic to properly unmount Dialog component - Removed `hidden` prop from Dialog component and use `isDialogOpen` for both `open` prop and conditional rendering - Added 2 tests verifying Dialog is completely unmounted (not just hidden) when closed #31402 Signed-off-by: fcamgz --- .changeset/orange-friends-march.md | 11 +++++++ .../SearchModal/SearchModal.test.tsx | 32 +++++++++++++++++++ .../components/SearchModal/SearchModal.tsx | 7 ++-- 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 .changeset/orange-friends-march.md diff --git a/.changeset/orange-friends-march.md b/.changeset/orange-friends-march.md new file mode 100644 index 0000000000..e2fc659439 --- /dev/null +++ b/.changeset/orange-friends-march.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-search': patch +--- + +Fix SearchModal Dialog not unmounting properly on close + +Fixed issue where Dialog was hidden but not unmounted, preventing +Material-UI from removing overflow:hidden from body element. +Added tests to verify Dialog is properly removed from DOM. + +Signed-off-by: fcamgz diff --git a/plugins/search/src/components/SearchModal/SearchModal.test.tsx b/plugins/search/src/components/SearchModal/SearchModal.test.tsx index 8d8e1bb9b9..4629017408 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.test.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.test.tsx @@ -248,4 +248,36 @@ describe('SearchModal', () => { expect(navigate).toHaveBeenCalledWith('/search?query=term'); }); + + it('should completely unmount the Dialog from DOM when open prop is false', async () => { + await renderInTestApp( + + , + { + mountedRoutes: { + '/search': rootRouteRef, + }, + }, + ); + + // Dialog should not exist in the DOM at all (unmounted, not just hidden) + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); + + it('should completely unmount the Dialog from DOM when hidden prop is true', async () => { + await renderInTestApp( + + , + { + mountedRoutes: { + '/search': rootRouteRef, + }, + }, + ); + + // Dialog should not exist in the DOM at all (unmounted, not just hidden) + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + }); }); diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index af7e1c6e10..86fa4f1b73 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -211,6 +211,8 @@ export const SearchModal = (props: SearchModalProps) => { const classes = useStyles(); + const isDialogOpen = open && !hidden; + return ( { aria-modal="true" fullWidth maxWidth="lg" - open={open} - hidden={hidden} + open={isDialogOpen} > - {open && ( + {isDialogOpen && ( {(children && children({