From 5da6ba278e5e35c8bc0c494d8e638252c6034b3d Mon Sep 17 00:00:00 2001 From: Joey Liu Date: Wed, 12 Nov 2025 00:47:25 +0000 Subject: [PATCH 1/4] Add a dashboard breadcrumb when user naviagte from dashboard Signed-off-by: Joey Liu --- .../hooks/use_dataset_selector.tsx | 2 +- .../utils/hooks/use_page_initialization.ts | 25 ++++- .../explore/public/helpers/save_explore.ts | 2 +- .../utils/get_previous_page_breadcrumb.ts | 94 +++++++++++++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts diff --git a/src/plugins/dataset_management/public/components/dataset_table/dataset_table_v2/hooks/use_dataset_selector.tsx b/src/plugins/dataset_management/public/components/dataset_table/dataset_table_v2/hooks/use_dataset_selector.tsx index 72d6ee0c0e95..d7320d86e61f 100644 --- a/src/plugins/dataset_management/public/components/dataset_table/dataset_table_v2/hooks/use_dataset_selector.tsx +++ b/src/plugins/dataset_management/public/components/dataset_table/dataset_table_v2/hooks/use_dataset_selector.tsx @@ -62,7 +62,7 @@ export const useDatasetSelector = ({ 'datasetManagement.dataset.titleExistsLabel', { values: { title: query.dataset.title }, - defaultMessage: "An index pattern with the title '{title}' already exists.", + defaultMessage: "An dataset with the title '{title}' already exists.", } ); diff --git a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts index 629ffbc84a4b..856e15219aaa 100644 --- a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts +++ b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts @@ -24,6 +24,7 @@ import { ExploreFlavor } from '../../../../common'; import { useSetEditorText } from '../../hooks'; import { EditorMode } from '../state_management/types'; import { getVisualizationBuilder } from '../../../components/visualizations/visualization_builder'; +import { getPreviousPageBreadcrumb } from '../../../utils/get_previous_page_breadcrumb'; export const useInitPage = () => { const dispatch = useDispatch(); @@ -42,7 +43,29 @@ export const useInitPage = () => { // Update browser title and breadcrumbs chrome.docTitle.change(title); - chrome.setBreadcrumbs([{ text: 'Explore', href: '#/' }, { text: title }]); + + // Get previous page info for smart breadcrumb navigation + const { breadcrumbConfig, previousPage } = getPreviousPageBreadcrumb({ + chrome, + application: services.core.application, + currentPageId: savedExplore.id, + }); + + // Build breadcrumbs array + const breadcrumbs = []; + + // If user came from a dashboard, show dashboard name as a breadcrumb + if (previousPage && previousPage.meta?.type === 'dashboard') { + breadcrumbs.push({ + text: `Dashboard:${previousPage.label}`, + ...breadcrumbConfig, + }); + } + + // Always show the current explore title + breadcrumbs.push({ text: title }); + + chrome.setBreadcrumbs(breadcrumbs); // Sync query from saved object to data plugin (explore doesn't use filters) const searchSourceFields = savedExplore.kibanaSavedObjectMeta; diff --git a/src/plugins/explore/public/helpers/save_explore.ts b/src/plugins/explore/public/helpers/save_explore.ts index 0405968f9113..deee18c5b9df 100644 --- a/src/plugins/explore/public/helpers/save_explore.ts +++ b/src/plugins/explore/public/helpers/save_explore.ts @@ -81,7 +81,7 @@ export async function saveSavedExplore({ } else { // Update browser title and breadcrumbs chrome.docTitle.change(newTitle); - chrome.setBreadcrumbs([...getRootBreadcrumbs(), { text: savedExplore.title }]); + chrome.setBreadcrumbs([{ text: savedExplore.title }]); } store.dispatch(setSavedSearch(id)); diff --git a/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts b/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts new file mode 100644 index 000000000000..2c9ed4874d50 --- /dev/null +++ b/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts @@ -0,0 +1,94 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ChromeStart, ChromeRecentlyAccessedHistoryItem, ApplicationStart } from 'src/core/public'; + +export interface BreadcrumbConfig { + href?: string; + onClick?: () => void; +} + +export interface PreviousPageInfo { + breadcrumbConfig: BreadcrumbConfig; + previousPage?: ChromeRecentlyAccessedHistoryItem; +} + +export interface GetBreadcrumbOptions { + chrome: ChromeStart; + application?: ApplicationStart; + currentPageId?: string; +} + +/** + * Get breadcrumb configuration that navigates to the previous page + * Uses chrome.recentlyAccessed to determine where the user came from + * + * @param options - Configuration options + * @returns Previous page info with breadcrumb config + */ +export function getPreviousPageBreadcrumb(options: GetBreadcrumbOptions): PreviousPageInfo; +export function getPreviousPageBreadcrumb( + chrome: ChromeStart, + currentPageId?: string +): PreviousPageInfo; +export function getPreviousPageBreadcrumb( + optionsOrChrome: GetBreadcrumbOptions | ChromeStart, + currentPageId?: string +): PreviousPageInfo { + // Handle both function signatures for backward compatibility + const chrome = + 'chrome' in optionsOrChrome + ? (optionsOrChrome as GetBreadcrumbOptions).chrome + : optionsOrChrome; + const application = + 'application' in optionsOrChrome + ? (optionsOrChrome as GetBreadcrumbOptions).application + : undefined; + const pageId = + 'currentPageId' in optionsOrChrome + ? (optionsOrChrome as GetBreadcrumbOptions).currentPageId + : currentPageId; + const recentlyAccessed = chrome.recentlyAccessed.get(); + + // Get the most recently accessed page (first item, which is most recent) + // Skip if it's the current page + const previousPage = + recentlyAccessed.length > 0 && recentlyAccessed[0].id !== pageId + ? recentlyAccessed[0] + : undefined; + + if (previousPage && previousPage.link) { + // We found a previous page - construct the full URL with workspace ID + let href = previousPage.link; + + // Check if the current URL has a workspace ID and the previous page link doesn't + const currentPathname = window.location.pathname; + const workspaceMatch = currentPathname.match(/\/w\/([^/]+)\//); + + if (workspaceMatch && !href.includes('/w/')) { + // Add workspace ID to the href + const workspaceId = workspaceMatch[1]; + href = `/w/${workspaceId}${href}`; + } + + // If application service is available, use onClick with navigateToUrl for SPA navigation + if (application) { + return { + breadcrumbConfig: { + onClick: () => { + application.navigateToUrl(href); + }, + }, + previousPage, + }; + } + + // Fallback to href (may cause hard refresh) + return { breadcrumbConfig: { href }, previousPage }; + } else { + // No previous page found, fallback to explore home + return { breadcrumbConfig: { href: '#/' } }; + } +} From 7e3732e72067c070588036bacbff1f76b76ff723 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Wed, 12 Nov 2025 10:49:08 +0800 Subject: [PATCH 2/4] fix(breadcrumbs): update discover breadcrumbs to include dashboard list Signed-off-by: Yulong Ruan --- .../application/utils/hooks/use_page_initialization.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts index 856e15219aaa..e0216c93356b 100644 --- a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts +++ b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts @@ -57,7 +57,13 @@ export const useInitPage = () => { // If user came from a dashboard, show dashboard name as a breadcrumb if (previousPage && previousPage.meta?.type === 'dashboard') { breadcrumbs.push({ - text: `Dashboard:${previousPage.label}`, + text: 'Dashboard', + onClick: () => { + services.core.application.navigateToApp('dashboards', { path: '#/' }); + }, + }); + breadcrumbs.push({ + text: `${previousPage.label}`, ...breadcrumbConfig, }); } From 9cd3d5fdcc5fd83895b574828b2257a0ac9a81eb Mon Sep 17 00:00:00 2001 From: "opensearch-changeset-bot[bot]" <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 02:49:39 +0000 Subject: [PATCH 3/4] Changeset file for PR #10904 created/updated --- changelogs/fragments/10904.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/10904.yml diff --git a/changelogs/fragments/10904.yml b/changelogs/fragments/10904.yml new file mode 100644 index 000000000000..382e5dfbc3db --- /dev/null +++ b/changelogs/fragments/10904.yml @@ -0,0 +1,2 @@ +feat: +- Add a dashboard breadcrumb when user naviagte from dashboard ([#10904](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/10904)) \ No newline at end of file From 359b1b90cd345b322757adffe3183d6996e7e037 Mon Sep 17 00:00:00 2001 From: Joey Liu Date: Thu, 13 Nov 2025 02:58:38 +0000 Subject: [PATCH 4/4] Address comments Signed-off-by: Joey Liu --- .../utils/hooks/use_page_initialization.ts | 2 +- .../utils/get_previous_page_breadcrumb.ts | 24 ++----------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts index e0216c93356b..196d59f6ec9e 100644 --- a/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts +++ b/src/plugins/explore/public/application/utils/hooks/use_page_initialization.ts @@ -63,7 +63,7 @@ export const useInitPage = () => { }, }); breadcrumbs.push({ - text: `${previousPage.label}`, + text: previousPage.label, ...breadcrumbConfig, }); } diff --git a/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts b/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts index 2c9ed4874d50..5fde152c8ed3 100644 --- a/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts +++ b/src/plugins/explore/public/utils/get_previous_page_breadcrumb.ts @@ -28,28 +28,8 @@ export interface GetBreadcrumbOptions { * @param options - Configuration options * @returns Previous page info with breadcrumb config */ -export function getPreviousPageBreadcrumb(options: GetBreadcrumbOptions): PreviousPageInfo; -export function getPreviousPageBreadcrumb( - chrome: ChromeStart, - currentPageId?: string -): PreviousPageInfo; -export function getPreviousPageBreadcrumb( - optionsOrChrome: GetBreadcrumbOptions | ChromeStart, - currentPageId?: string -): PreviousPageInfo { - // Handle both function signatures for backward compatibility - const chrome = - 'chrome' in optionsOrChrome - ? (optionsOrChrome as GetBreadcrumbOptions).chrome - : optionsOrChrome; - const application = - 'application' in optionsOrChrome - ? (optionsOrChrome as GetBreadcrumbOptions).application - : undefined; - const pageId = - 'currentPageId' in optionsOrChrome - ? (optionsOrChrome as GetBreadcrumbOptions).currentPageId - : currentPageId; +export function getPreviousPageBreadcrumb(options: GetBreadcrumbOptions): PreviousPageInfo { + const { chrome, application, currentPageId: pageId } = options; const recentlyAccessed = chrome.recentlyAccessed.get(); // Get the most recently accessed page (first item, which is most recent)