From 4db18c908375cc6d0877862b1f7b64068a95e2ef Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 11:41:36 -0700 Subject: [PATCH] [BUG] Fix management overview page duplicate rendering (#4636) (#4871) * refactor for management overview page Signed-off-by: Hailong Cui * Add missing changelog Signed-off-by: Hailong Cui * Add unit test Signed-off-by: Hailong Cui * remove duplicate snapshot validation Signed-off-by: Hailong Cui * Update src/plugins/management_overview/public/application.test.tsx Co-authored-by: Josh Romero Signed-off-by: Hailong Cui * Update src/plugins/management_overview/public/application.test.tsx Co-authored-by: Josh Romero Signed-off-by: Hailong Cui * export empty plugin start to make it consistent Signed-off-by: Hailong Cui * export empty plugin start to make it consistent Signed-off-by: Hailong Cui --------- Signed-off-by: Hailong Cui Co-authored-by: Josh Romero Co-authored-by: Ashwin P Chandran (cherry picked from commit 16b1e291234de2836aafbc2daecbc4be86e791aa) Signed-off-by: github-actions[bot] # Conflicts: # CHANGELOG.md Co-authored-by: github-actions[bot] --- .../__snapshots__/application.test.tsx.snap | 59 +++++++++ .../public/application.test.tsx | 121 ++++++++++++++++++ .../public/application.tsx | 28 ++-- .../management_overview/public/plugin.ts | 11 +- 4 files changed, 201 insertions(+), 18 deletions(-) create mode 100644 src/plugins/management_overview/public/__snapshots__/application.test.tsx.snap create mode 100644 src/plugins/management_overview/public/application.test.tsx diff --git a/src/plugins/management_overview/public/__snapshots__/application.test.tsx.snap b/src/plugins/management_overview/public/__snapshots__/application.test.tsx.snap new file mode 100644 index 000000000000..666f1b50c696 --- /dev/null +++ b/src/plugins/management_overview/public/__snapshots__/application.test.tsx.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Overview page rendering should render normally 1`] = ` +
+
+

+ + Overview + +

+
+
+
+
+
+

+ +

+
+

+ dev tools description +

+
+
+
+
+
+
+
+`; diff --git a/src/plugins/management_overview/public/application.test.tsx b/src/plugins/management_overview/public/application.test.tsx new file mode 100644 index 000000000000..3f879f05b5da --- /dev/null +++ b/src/plugins/management_overview/public/application.test.tsx @@ -0,0 +1,121 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { render } from '@testing-library/react'; +import { ManagementOverviewWrapper } from './application'; +import React from 'react'; +import { ApplicationStart, PublicAppInfo } from 'opensearch-dashboards/public'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { deepFreeze } from '@osd/std'; +import { OverviewApp } from './overview_app'; +import { AppNavLinkStatus, AppStatus } from '../../../core/public'; + +const applicationStartMock = (apps: Map): jest.Mocked => { + const currentAppId$ = new Subject(); + + return { + applications$: new BehaviorSubject>(apps), + currentAppId$: currentAppId$.asObservable(), + capabilities: deepFreeze({ + catalogue: {}, + management: {}, + navLinks: {}, + }), + navigateToApp: jest.fn(), + navigateToUrl: jest.fn(), + getUrlForApp: jest.fn(), + registerMountContext: jest.fn(), + }; +}; + +function renderOverviewPage(apps: Map, overviewApps?: OverviewApp[]) { + return render( + + ); +} + +describe('Overview page rendering', () => { + it('should render normally', () => { + const overviewApps: OverviewApp[] = [ + { + id: 'dev_tools', + title: 'Dev Tools', + description: 'dev tools description', + order: 0, + }, + ]; + + const apps: Map = new Map(); + apps.set('dev_tools', { + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + appRoute: '/app/console', + } as PublicAppInfo); + const { container, queryByText } = renderOverviewPage(apps, overviewApps); + expect(container.firstChild).toMatchSnapshot(); + expect(queryByText('Dev Tools')).not.toBeNull(); + }); + + it('should render normally when no overview app', () => { + const { queryByText } = renderOverviewPage(new Map()); + expect(queryByText('Overview')).not.toBeNull(); + }); + + it('should render normally when no application available', () => { + const overviewApps: OverviewApp[] = [ + { + id: 'dev_tools', + title: 'Dev Tools', + description: 'dev tools description', + order: 0, + }, + ]; + const { queryByText } = renderOverviewPage(new Map(), overviewApps); + expect(queryByText('Dev Tools')).toBeNull(); + }); + + it('should not display overview app when nav link status is hidden', () => { + const overviewApps: OverviewApp[] = [ + { + id: 'dev_tools', + title: 'Dev Tools', + description: 'dev tools description', + order: 0, + }, + ]; + + const apps: Map = new Map(); + apps.set('dev_tools', { + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.hidden, + appRoute: '/app/console', + } as PublicAppInfo); + const { queryByText } = renderOverviewPage(apps, overviewApps); + expect(queryByText('Dev Tools')).toBeNull(); + }); + + it('should not display overview app when it is invalid app', () => { + const overviewApps: OverviewApp[] = [ + { + id: 'invalid_app_id', + title: 'Dev Tools', + description: 'dev tools description', + order: 0, + }, + ]; + + const apps: Map = new Map(); + apps.set('dev_tools', { + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.hidden, + appRoute: '/app/console', + } as PublicAppInfo); + const { queryByText } = renderOverviewPage(apps, overviewApps); + expect(queryByText('Dev Tools')).toBeNull(); + }); +}); diff --git a/src/plugins/management_overview/public/application.tsx b/src/plugins/management_overview/public/application.tsx index 26ee63e78252..805c43081fdd 100644 --- a/src/plugins/management_overview/public/application.tsx +++ b/src/plugins/management_overview/public/application.tsx @@ -5,28 +5,28 @@ import ReactDOM from 'react-dom'; import { I18nProvider, FormattedMessage } from '@osd/i18n/react'; -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGrid, EuiFlexItem, EuiPage, EuiPageBody, EuiSpacer, EuiTitle } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; -import { ApplicationStart, ChromeStart, CoreStart } from '../../../core/public'; +import { ApplicationStart, AppNavLinkStatus, CoreStart } from '../../../core/public'; import { OverviewApp } from '.'; import { OverviewCard } from './components/overview_card'; export interface ManagementOverviewProps { application: ApplicationStart; - chrome: ChromeStart; overviewApps?: OverviewApp[]; } -function ManagementOverviewWrapper(props: ManagementOverviewProps) { - const { chrome, application, overviewApps } = props; +export function ManagementOverviewWrapper(props: ManagementOverviewProps) { + const { application, overviewApps } = props; + const applications = useObservable(application.applications$); - const hiddenAppIds = - useObservable(chrome.navLinks.getNavLinks$()) - ?.filter((link) => link.hidden) - .map((link) => link.id) || []; - - const availableApps = overviewApps?.filter((app) => hiddenAppIds.indexOf(app.id) === -1); + const availableApps = useMemo(() => { + return overviewApps?.filter((overviewApp) => { + const app = applications?.get(overviewApp.id); + return app && app.navLinkStatus !== AppNavLinkStatus.hidden; + }); + }, [applications, overviewApps]); return ( @@ -61,11 +61,7 @@ export function renderApp( ) { ReactDOM.render( - + , element ); diff --git a/src/plugins/management_overview/public/plugin.ts b/src/plugins/management_overview/public/plugin.ts index c1b77be0f795..f11b58ef9b41 100644 --- a/src/plugins/management_overview/public/plugin.ts +++ b/src/plugins/management_overview/public/plugin.ts @@ -22,8 +22,13 @@ interface ManagementOverviewSetupDeps { export interface ManagementOverViewPluginSetup { register: (overviewApp: OverviewApp) => void; } + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ManagementOverViewPluginStart {} + /** @public */ -export class ManagementOverViewPlugin implements Plugin { +export class ManagementOverViewPlugin + implements Plugin { private readonly overviewApps = new Map(); private getSortedOverviewApps(): OverviewApp[] { @@ -82,5 +87,7 @@ export class ManagementOverViewPlugin implements Plugin