diff --git a/src/plugins/dashboard/public/application/app.tsx b/src/plugins/dashboard/public/application/app.tsx index c516e88fae90..93ccdac0a7fa 100644 --- a/src/plugins/dashboard/public/application/app.tsx +++ b/src/plugins/dashboard/public/application/app.tsx @@ -11,30 +11,48 @@ import './app.scss'; import { AppMountParameters } from 'opensearch-dashboards/public'; -import React from 'react'; -import { Route, Switch } from 'react-router-dom'; +import React, { useEffect } from 'react'; +import { Route, Switch, useLocation } from 'react-router-dom'; import { DashboardConstants, createDashboardEditUrl } from '../dashboard_constants'; import { DashboardEditor, DashboardListing, DashboardNoMatch } from './components'; +import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public'; +import { DashboardServices } from '../types'; +import { syncQueryStateWithUrl } from '../../../data/public'; export interface DashboardAppProps { onAppLeave: AppMountParameters['onAppLeave']; } export const DashboardApp = ({ onAppLeave }: DashboardAppProps) => { + const { + services: { + data: { query }, + osdUrlStateStorage, + }, + } = useOpenSearchDashboards(); + const { pathname } = useLocation(); + + useEffect(() => { + // syncs `_g` portion of url with query services + const { stop } = syncQueryStateWithUrl(query, osdUrlStateStorage); + + return () => stop(); + + // this effect should re-run when pathname is changed to preserve querystring part, + // so the global state is always preserved + }, [query, osdUrlStateStorage, pathname]); + return ( - - - - +
+ + + ); diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing.tsx b/src/plugins/dashboard/public/application/components/dashboard_listing.tsx index bd92b599ae2b..0c105fb2a240 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/components/dashboard_listing.tsx @@ -31,7 +31,6 @@ export const DashboardListing = () => { notifications, savedDashboards, dashboardProviders, - addBasePath, }, } = useOpenSearchDashboards(); @@ -90,21 +89,14 @@ export const DashboardListing = () => { }; const editItem = useCallback( - ({ editUrl }: any) => { - if (addBasePath) { - history.push(addBasePath(editUrl)); + ({ appId, editUrl }: any) => { + if (appId === 'dashboard') { + history.push(editUrl); + } else { + application.navigateToUrl(editUrl); } }, - [history, addBasePath] - ); - - const viewItem = useCallback( - ({ viewUrl }: any) => { - if (addBasePath) { - history.push(addBasePath(viewUrl)); - } - }, - [history, addBasePath] + [history, application] ); const deleteItems = useCallback( diff --git a/src/plugins/dashboard/public/application/components/dashboard_no_match.tsx b/src/plugins/dashboard/public/application/components/dashboard_no_match.tsx index bd3c40dcf1c4..a5adc59198a2 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_no_match.tsx +++ b/src/plugins/dashboard/public/application/components/dashboard_no_match.tsx @@ -3,8 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; +import { useEffect } from 'react'; +import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; +import { DashboardServices } from '../../types'; export const DashboardNoMatch = () => { - return
Dashboard No Match
; + const { services } = useOpenSearchDashboards(); + useEffect(() => { + const path = window.location.hash.substr(1); + services.restorePreviousUrl(); + + const { navigated } = services.navigateToLegacyOpenSearchDashboardsUrl(path); + if (!navigated) { + services.navigateToDefaultApp(); + } + }, [services]); + + return null; }; diff --git a/src/plugins/dashboard/public/application/utils/use/use_dashboard_container.tsx b/src/plugins/dashboard/public/application/utils/use/use_dashboard_container.tsx index fe55af0c6f85..583a59ec2ffc 100644 --- a/src/plugins/dashboard/public/application/utils/use/use_dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/utils/use/use_dashboard_container.tsx @@ -86,7 +86,7 @@ export const useDashboardContainer = ( useEffect(() => { const incomingEmbeddable = services.embeddable - .getStateTransfer(services.scopedHistory()) + .getStateTransfer(services.scopedHistory) .getIncomingEmbeddablePackage(); if ( diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 8f3942f32e8e..7f6342ad0362 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -385,6 +385,12 @@ export class DashboardPlugin savedObjects, } = pluginsStart; + // dispatch synthetic hash change event to update hash history objects + // this is necessary because hash updates triggered by using popState won't trigger this event naturally. + const unlistenParentHistory = params.history.listen(() => { + window.dispatchEvent(new HashChangeEvent('hashchange')); + }); + const history = createHashHistory(); // need more research const services: DashboardServices = { ...coreStart, @@ -418,7 +424,7 @@ export class DashboardPlugin }, localStorage: new Storage(localStorage), usageCollection, - scopedHistory: () => this.currentHistory!, + scopedHistory: params.history, setHeaderActionMenu: params.setHeaderActionMenu, savedObjectsPublic: savedObjects, restorePreviousUrl, @@ -430,6 +436,8 @@ export class DashboardPlugin const { renderApp } = await import('./application'); const unmount = renderApp(params, services); return () => { + params.element.classList.remove('dshAppContainer'); + unlistenParentHistory(); unmount(); appUnMounted(); }; diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts index 8d95fe25f921..efd571ca74fa 100644 --- a/src/plugins/dashboard/public/types.ts +++ b/src/plugins/dashboard/public/types.ts @@ -270,7 +270,7 @@ export interface DashboardServices extends CoreStart { usageCollection?: UsageCollectionSetup; navigateToDefaultApp: UrlForwardingStart['navigateToDefaultApp']; navigateToLegacyOpenSearchDashboardsUrl: UrlForwardingStart['navigateToLegacyOpenSearchDashboardsUrl']; - scopedHistory: () => ScopedHistory; + scopedHistory: ScopedHistory; setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; savedObjectsPublic: SavedObjectsStart; restorePreviousUrl: () => void;