From 30e04df3538cb9a533db016e231f0dfd58897e8a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:54:32 +1100 Subject: [PATCH] [8.16] [dashboard] Do not reset panel to undefined or empty last saved state (#203158) (#203510) # Backport This will backport the following commits from `main` to `8.16`: - [[dashboard] Do not reset panel to undefined or empty last saved state (#203158)](https://github.com/elastic/kibana/pull/203158) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Nathan Reese --- .../app/presentation_container_example/page_api.ts | 1 + .../unsaved_changes/children_unsaved_changes.test.ts | 6 +++--- .../unsaved_changes/initialize_unsaved_changes.ts | 9 +++++++++ .../interfaces/publishes_unsaved_changes.ts | 2 +- .../controls/public/controls/mocks/control_mocks.ts | 4 +++- .../get_timeslider_control_factory.test.tsx | 4 +++- .../embeddable/dashboard_container.tsx | 12 +++++++++++- 7 files changed, 31 insertions(+), 7 deletions(-) diff --git a/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts b/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts index 517f0534993ef..59f06847a1538 100644 --- a/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts +++ b/examples/embeddable_examples/public/app/presentation_container_example/page_api.ts @@ -250,6 +250,7 @@ export function getPageApi() { children$.next(children); } newPanels = {}; + return true; }, timeRange$, unsavedChanges: unsavedChanges$ as PublishingSubject, diff --git a/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts b/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts index fd708d14d97be..cd03db5431bcc 100644 --- a/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts +++ b/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts @@ -14,11 +14,11 @@ import { waitFor } from '@testing-library/react'; describe('childrenUnsavedChanges$', () => { const child1Api = { unsavedChanges: new BehaviorSubject(undefined), - resetUnsavedChanges: () => undefined, + resetUnsavedChanges: () => true, }; const child2Api = { unsavedChanges: new BehaviorSubject(undefined), - resetUnsavedChanges: () => undefined, + resetUnsavedChanges: () => true, }; const children$ = new BehaviorSubject<{ [key: string]: unknown }>({}); const onFireMock = jest.fn(); @@ -99,7 +99,7 @@ describe('childrenUnsavedChanges$', () => { ...children$.value, child3: { unsavedChanges: new BehaviorSubject({ key1: 'modified value' }), - resetUnsavedChanges: () => undefined, + resetUnsavedChanges: () => true, }, }); diff --git a/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts b/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts index 70085b88e1a8b..fb767fb11cffd 100644 --- a/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts +++ b/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.ts @@ -95,10 +95,19 @@ export const initializeUnsavedChanges = ( unsavedChanges, resetUnsavedChanges: () => { const lastSaved = lastSavedState$.getValue(); + + // Do not reset to undefined or empty last saved state + // Temporary fix for https://github.com/elastic/kibana/issues/201627 + // TODO remove when architecture fix resolves issue. + if (comparatorKeys.length && (!lastSaved || Object.keys(lastSaved).length === 0)) { + return false; + } + for (const key of comparatorKeys) { const setter = comparators[key][1]; // setter function is the 1st element of the tuple setter(lastSaved?.[key] as RuntimeState[typeof key]); } + return true; }, snapshotRuntimeState, } as PublishesUnsavedChanges & HasSnapshottableState, diff --git a/packages/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts b/packages/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts index 90a01d5bc736e..e9b4adbec5384 100644 --- a/packages/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts +++ b/packages/presentation/presentation_publishing/interfaces/publishes_unsaved_changes.ts @@ -11,7 +11,7 @@ import { PublishingSubject } from '../publishing_subject'; export interface PublishesUnsavedChanges { unsavedChanges: PublishingSubject | undefined>; - resetUnsavedChanges: () => void; + resetUnsavedChanges: () => boolean; } export const apiPublishesUnsavedChanges = (api: unknown): api is PublishesUnsavedChanges => { diff --git a/src/plugins/controls/public/controls/mocks/control_mocks.ts b/src/plugins/controls/public/controls/mocks/control_mocks.ts index e71ecb12e030b..128e89c5c6028 100644 --- a/src/plugins/controls/public/controls/mocks/control_mocks.ts +++ b/src/plugins/controls/public/controls/mocks/control_mocks.ts @@ -43,7 +43,9 @@ export const getMockedBuildApi = uuid, parentApi: controlGroupApi ?? getMockedControlGroupApi(), unsavedChanges: new BehaviorSubject | undefined>(undefined), - resetUnsavedChanges: () => {}, + resetUnsavedChanges: () => { + return true; + }, type: factory.type, }; }; diff --git a/src/plugins/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx b/src/plugins/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx index a49f1489d31d1..44574757837ce 100644 --- a/src/plugins/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx +++ b/src/plugins/controls/public/controls/timeslider_control/get_timeslider_control_factory.test.tsx @@ -48,7 +48,9 @@ describe('TimesliderControlApi', () => { uuid, parentApi: controlGroupApi, unsavedChanges: new BehaviorSubject | undefined>(undefined), - resetUnsavedChanges: () => {}, + resetUnsavedChanges: () => { + return true; + }, type: factory.type, }; } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index 525fc7e05dfa2..dd1df689629c9 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -68,6 +68,7 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { LocatorPublic } from '@kbn/share-plugin/common'; import { ExitFullScreenButtonKibanaProvider } from '@kbn/shared-ux-button-exit-full-screen'; +import { i18n } from '@kbn/i18n'; import { DASHBOARD_CONTAINER_TYPE, DashboardApi, DashboardLocatorParams } from '../..'; import { DashboardAttributes, @@ -957,7 +958,16 @@ export class DashboardContainer for (const panelId of Object.keys(currentChildren)) { if (this.getInput().panels[panelId]) { const child = currentChildren[panelId]; - if (apiPublishesUnsavedChanges(child)) child.resetUnsavedChanges(); + if (apiPublishesUnsavedChanges(child)) { + const success = child.resetUnsavedChanges(); + if (!success) { + coreServices.notifications.toasts.addWarning( + i18n.translate('dashboard.reset.panelError', { + defaultMessage: 'Unable to reset panel changes', + }) + ); + } + } } else { // if reset resulted in panel removal, we need to update the list of children delete currentChildren[panelId];