From 69985a2802335a211177542d2a077548250f8ff0 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Tue, 28 Mar 2023 06:36:19 +0000 Subject: [PATCH 1/3] Add toast ID Signed-off-by: Ashwin P Chandran --- .../notifications/toasts/toasts_api.test.ts | 23 +++++++++++++++++++ .../notifications/toasts/toasts_api.tsx | 13 ++++++++--- .../application/components/workspace.tsx | 7 +++++- .../utils/state_management/store.ts | 4 ++-- src/plugins/vis_builder/public/plugin.ts | 3 ++- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/core/public/notifications/toasts/toasts_api.test.ts b/src/core/public/notifications/toasts/toasts_api.test.ts index ef4f469194d7..dacfa98b623a 100644 --- a/src/core/public/notifications/toasts/toasts_api.test.ts +++ b/src/core/public/notifications/toasts/toasts_api.test.ts @@ -105,6 +105,24 @@ describe('#get$()', () => { toasts.remove('bar'); expect(onToasts).not.toHaveBeenCalled(); }); + + it('does not emit a new toast list when a toast with the same id is passed to add()', () => { + const toasts = new ToastsApi(toastDeps()); + const onToasts = jest.fn(); + + toasts.get$().subscribe(onToasts); + toasts.add({ + id: 'foo', + title: 'foo', + }); + onToasts.mockClear(); + + toasts.add({ + id: 'foo', + title: 'bar', + }); + expect(onToasts).not.toHaveBeenCalled(); + }); }); describe('#add()', () => { @@ -135,6 +153,11 @@ describe('#add()', () => { const toasts = new ToastsApi(toastDeps()); expect(toasts.add('foo')).toHaveProperty('title', 'foo'); }); + + it('accepts an id and does not auto increment', async () => { + const toasts = new ToastsApi(toastDeps()); + expect(toasts.add({ id: 'foo', title: 'not foo' })).toHaveProperty('id', 'foo'); + }); }); describe('#remove()', () => { diff --git a/src/core/public/notifications/toasts/toasts_api.tsx b/src/core/public/notifications/toasts/toasts_api.tsx index 008c4fb3c507..b5ed0a659ce6 100644 --- a/src/core/public/notifications/toasts/toasts_api.tsx +++ b/src/core/public/notifications/toasts/toasts_api.tsx @@ -42,12 +42,10 @@ import { I18nStart } from '../../i18n'; /** * Allowed fields for {@link ToastInput}. * - * @remarks - * `id` cannot be specified. - * * @public */ export type ToastInputFields = Pick> & { + id?: string; title?: string | MountPoint; text?: string | MountPoint; }; @@ -143,6 +141,15 @@ export class ToastsApi implements IToasts { * @returns a {@link Toast} */ public add(toastOrTitle: ToastInput) { + if (typeof toastOrTitle !== 'string') { + const list = this.toasts$.getValue(); + const existingToast = list.find((toast) => toast.id === toastOrTitle.id); + + if (existingToast) { + return existingToast; + } + } + const toast: Toast = { id: String(this.idCounter++), toastLifeTimeMs: this.uiSettings.get('notifications:lifetime:info'), diff --git a/src/plugins/vis_builder/public/application/components/workspace.tsx b/src/plugins/vis_builder/public/application/components/workspace.tsx index 6e3371404355..da838ad44db6 100644 --- a/src/plugins/vis_builder/public/application/components/workspace.tsx +++ b/src/plugins/vis_builder/public/application/components/workspace.tsx @@ -51,7 +51,12 @@ export const Workspace: FC = ({ children }) => { if (noAggs || !aggValidation.valid || !schemaValidation.valid) { const err = schemaValidation.errorMsg || aggValidation.errorMsg; - if (err) toasts.addWarning(err); + if (err) + toasts.addWarning({ + id: 'vb_expression_validation', + title: err, + }); + setExpression(undefined); return; diff --git a/src/plugins/vis_builder/public/application/utils/state_management/store.ts b/src/plugins/vis_builder/public/application/utils/state_management/store.ts index f1b1c0eeae2a..588c221a50fd 100644 --- a/src/plugins/vis_builder/public/application/utils/state_management/store.ts +++ b/src/plugins/vis_builder/public/application/utils/state_management/store.ts @@ -47,9 +47,9 @@ export const getPreloadedStore = async (services: VisBuilderServices) => { }; // the store subscriber will automatically detect changes and call handleChange function - store.subscribe(handleChange); + const unsubscribe = store.subscribe(handleChange); - return store; + return { store, unsubscribe }; }; // Infer the `RootState` and `AppDispatch` types from the store itself diff --git a/src/plugins/vis_builder/public/plugin.ts b/src/plugins/vis_builder/public/plugin.ts index 3995c1246de5..af1d8be54af7 100644 --- a/src/plugins/vis_builder/public/plugin.ts +++ b/src/plugins/vis_builder/public/plugin.ts @@ -161,7 +161,7 @@ export class VisBuilderPlugin }; // Instantiate the store - const store = await getPreloadedStore(services); + const { store, unsubscribe: unsubscribeStore } = await getPreloadedStore(services); const unmount = renderApp(params, services, store); // Render the application @@ -169,6 +169,7 @@ export class VisBuilderPlugin unlistenParentHistory(); unmount(); appUnMounted(); + unsubscribeStore(); }; }, }); From 257a1d0b8e0514075a90a049c10a5ae87435f938 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Fri, 31 Mar 2023 02:24:27 +0000 Subject: [PATCH 2/3] changelog Signed-off-by: Ashwin P Chandran --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0873a250d749..123eb46435b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Use mirrors to download Node.js binaries to escape sporadic 404 errors ([#3619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3619)) - [Multiple DataSource] Refactor dev tool console to use opensearch-js client to send requests ([#3544](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3544)) - [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) +- [Notifications] Adds id to toast api for deduplication ([#3752](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3752)) ### 🐛 Bug Fixes From 81a136c823f5f84789a06244e6702b5e1c255f05 Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Mon, 17 Apr 2023 13:54:19 -0700 Subject: [PATCH 3/3] Update src/core/public/notifications/toasts/toasts_api.tsx Signed-off-by: Ashwin P Chandran Co-authored-by: Josh Romero --- src/core/public/notifications/toasts/toasts_api.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/public/notifications/toasts/toasts_api.tsx b/src/core/public/notifications/toasts/toasts_api.tsx index b5ed0a659ce6..76d0bf9cf9b2 100644 --- a/src/core/public/notifications/toasts/toasts_api.tsx +++ b/src/core/public/notifications/toasts/toasts_api.tsx @@ -142,8 +142,9 @@ export class ToastsApi implements IToasts { */ public add(toastOrTitle: ToastInput) { if (typeof toastOrTitle !== 'string') { + const toastObject = toastOrTitle; const list = this.toasts$.getValue(); - const existingToast = list.find((toast) => toast.id === toastOrTitle.id); + const existingToast = list.find((toast) => toast.id === toastObject.id); if (existingToast) { return existingToast;