diff --git a/changelogs/fragments/6923.yml b/changelogs/fragments/6923.yml new file mode 100644 index 000000000000..d442c19f7ae2 --- /dev/null +++ b/changelogs/fragments/6923.yml @@ -0,0 +1,2 @@ +fix: +- Close any open system flyout when changing view mode of the dashboard ([#6923](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6923)) diff --git a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap index 09be5ffdeb46..68a37d308066 100644 --- a/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap +++ b/src/core/public/overlays/flyout/__snapshots__/flyout_service.test.tsx.snap @@ -8,6 +8,14 @@ Array [ ] `; +exports[`FlyoutService closeFlyout() can be called multiple times 1`] = ` +Array [ + Array [ +
, + ], +] +`; + exports[`FlyoutService openFlyout() renders a flyout to the DOM 1`] = ` Array [ Array [ diff --git a/src/core/public/overlays/flyout/flyout_service.mock.ts b/src/core/public/overlays/flyout/flyout_service.mock.ts index 82fab5e4a8ad..8e087888f8e0 100644 --- a/src/core/public/overlays/flyout/flyout_service.mock.ts +++ b/src/core/public/overlays/flyout/flyout_service.mock.ts @@ -37,6 +37,7 @@ const createStartContractMock = () => { close: jest.fn(), onClose: Promise.resolve(), }), + close: jest.fn(), }; return startContract; }; diff --git a/src/core/public/overlays/flyout/flyout_service.test.tsx b/src/core/public/overlays/flyout/flyout_service.test.tsx index d8acfaa0e331..0e168ea78d55 100644 --- a/src/core/public/overlays/flyout/flyout_service.test.tsx +++ b/src/core/public/overlays/flyout/flyout_service.test.tsx @@ -91,6 +91,43 @@ describe('FlyoutService', () => { }); }); }); + + describe('closeFlyout()', () => { + it('resolves onClose on the previous ref', async () => { + const ref = flyouts.open(mountText('Flyout content')); + const onCloseComplete = jest.fn(); + ref.onClose.then(onCloseComplete); + flyouts.close(); + await ref.onClose; + expect(onCloseComplete).toBeCalledTimes(1); + }); + + it('can be called multiple times', async () => { + flyouts.open(mountText('Flyout content')); + expect(mockReactDomUnmount).not.toHaveBeenCalled(); + flyouts.close(); + expect(mockReactDomUnmount.mock.calls).toMatchSnapshot(); + flyouts.close(); + expect(mockReactDomUnmount).toHaveBeenCalledTimes(1); + }); + + it("doesn't affect an inactive flyout", async () => { + const ref = flyouts.open(mountText('Flyout content')); + flyouts.close(); + const onCloseComplete = jest.fn(); + ref.onClose.then(onCloseComplete); + await ref.onClose; + + mockReactDomUnmount.mockClear(); + onCloseComplete.mockClear(); + + flyouts.close(); + flyouts.close(); + expect(mockReactDomUnmount).toBeCalledTimes(0); + expect(onCloseComplete).toBeCalledTimes(0); + }); + }); + describe('FlyoutRef#close()', () => { it('resolves the onClose Promise', async () => { const ref = flyouts.open(mountText('Flyout content')); diff --git a/src/core/public/overlays/flyout/flyout_service.tsx b/src/core/public/overlays/flyout/flyout_service.tsx index 4d89b57d791a..954e9727966c 100644 --- a/src/core/public/overlays/flyout/flyout_service.tsx +++ b/src/core/public/overlays/flyout/flyout_service.tsx @@ -94,6 +94,11 @@ export interface OverlayFlyoutStart { * @return {@link OverlayRef} A reference to the opened flyout panel. */ open(mount: MountPoint, options?: OverlayFlyoutOpenOptions): OverlayRef; + + /** + * Closes any open flyout panel. + */ + close(): void; } /** @@ -149,6 +154,12 @@ export class FlyoutService { return flyout; }, + close: () => { + if (this.activeFlyout) { + this.activeFlyout.close(); + this.cleanupDom(); + } + }, }; } diff --git a/src/core/public/overlays/overlay_service.mock.ts b/src/core/public/overlays/overlay_service.mock.ts index 5f2d70eec5d3..993f40b62c16 100644 --- a/src/core/public/overlays/overlay_service.mock.ts +++ b/src/core/public/overlays/overlay_service.mock.ts @@ -36,11 +36,13 @@ import { overlayModalServiceMock } from './modal/modal_service.mock'; import { overlaySidecarServiceMock } from './sidecar/sidecar_service.mock'; const createStartContractMock = () => { - const overlayStart = overlayModalServiceMock.createStartContract(); + const overlayModalStart = overlayModalServiceMock.createStartContract(); + const overlayFlyoutStart = overlayFlyoutServiceMock.createStartContract(); const startContract: DeeplyMockedKeys = { - openFlyout: overlayFlyoutServiceMock.createStartContract().open, - openModal: overlayStart.open, - openConfirm: overlayStart.openConfirm, + openFlyout: overlayFlyoutStart.open, + closeFlyout: overlayFlyoutStart.close, + openModal: overlayModalStart.open, + openConfirm: overlayModalStart.openConfirm, banners: overlayBannersServiceMock.createStartContract(), sidecar: overlaySidecarServiceMock.createStartContract(), }; diff --git a/src/core/public/overlays/overlay_service.ts b/src/core/public/overlays/overlay_service.ts index 92144d34c45b..557b018ca189 100644 --- a/src/core/public/overlays/overlay_service.ts +++ b/src/core/public/overlays/overlay_service.ts @@ -68,6 +68,7 @@ export class OverlayService { return { banners, openFlyout: flyouts.open.bind(flyouts), + closeFlyout: flyouts.close.bind(flyouts), openModal: modals.open.bind(modals), openConfirm: modals.openConfirm.bind(modals), sidecar: sidecars, @@ -81,6 +82,8 @@ export interface OverlayStart { banners: OverlayBannersStart; /** {@link OverlayFlyoutStart#open} */ openFlyout: OverlayFlyoutStart['open']; + /** {@link OverlayFlyoutStart#close} */ + closeFlyout: OverlayFlyoutStart['close']; /** {@link OverlayModalStart#open} */ openModal: OverlayModalStart['open']; /** {@link OverlayModalStart#openConfirm} */ diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 6e60bd8bead2..4cd7dc041bcf 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -957,6 +957,7 @@ exports[`dashboard listing hideWriteControls 1`] = ` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -2135,6 +2136,7 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -3374,6 +3376,7 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -4613,6 +4616,7 @@ exports[`dashboard listing renders table rows 1`] = ` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -5852,6 +5856,7 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index da537e452c4e..37427037a55a 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -835,6 +835,7 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -1839,6 +1840,7 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -2843,6 +2845,7 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -3847,6 +3850,7 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -4851,6 +4855,7 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], @@ -5855,6 +5860,7 @@ exports[`Dashboard top nav render with all components 1`] = ` "remove": [MockFunction], "replace": [MockFunction], }, + "closeFlyout": [MockFunction], "openConfirm": [MockFunction], "openFlyout": [MockFunction], "openModal": [MockFunction], diff --git a/src/plugins/dashboard/public/application/utils/get_nav_actions.tsx b/src/plugins/dashboard/public/application/utils/get_nav_actions.tsx index 3f823f52676d..205489739eab 100644 --- a/src/plugins/dashboard/public/application/utils/get_nav_actions.tsx +++ b/src/plugins/dashboard/public/application/utils/get_nav_actions.tsx @@ -308,6 +308,7 @@ export const getNavActions = ( // If there are no changes, do not show the discard window if (!willLoseChanges) { + overlays.closeFlyout(); stateContainer.transitions.set('viewMode', newMode); return; } @@ -349,6 +350,8 @@ export const getNavActions = ( } } + overlays.closeFlyout(); + // Set the isDirty flag back to false since we discard all the changes dashboard.setIsDirty(false); }