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);
}