diff --git a/changelogs/fragments/6674.yml b/changelogs/fragments/6674.yml
new file mode 100644
index 000000000000..7a559d02fa55
--- /dev/null
+++ b/changelogs/fragments/6674.yml
@@ -0,0 +1,2 @@
+fix:
+- [VisBuilder][BUG] Flat render structure in Metric and Table Vis ([#6674](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6674))
\ No newline at end of file
diff --git a/changelogs/fragments/7401.yml b/changelogs/fragments/7401.yml
new file mode 100644
index 000000000000..5aebdb156692
--- /dev/null
+++ b/changelogs/fragments/7401.yml
@@ -0,0 +1,2 @@
+fix:
+- Make breadcrumb of 4 new added applications comply with BrowserRouter. ([#7401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7401))
\ No newline at end of file
diff --git a/changelogs/fragments/7405.yml b/changelogs/fragments/7405.yml
new file mode 100644
index 000000000000..81fa1b1780c2
--- /dev/null
+++ b/changelogs/fragments/7405.yml
@@ -0,0 +1,2 @@
+fix:
+- [Bug][Workspace] Navigate to detail page when clicking all use case workspace ([#7405](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7405))
\ No newline at end of file
diff --git a/changelogs/fragments/7420.yml b/changelogs/fragments/7420.yml
new file mode 100644
index 000000000000..7426906bfd47
--- /dev/null
+++ b/changelogs/fragments/7420.yml
@@ -0,0 +1,2 @@
+fix:
+- [Version Decoupling] Add data source version and installed plugins in data source viewer returns ([#7420](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7420))
\ No newline at end of file
diff --git a/changelogs/fragments/7440.yml b/changelogs/fragments/7440.yml
new file mode 100644
index 000000000000..9c314cc7263a
--- /dev/null
+++ b/changelogs/fragments/7440.yml
@@ -0,0 +1,2 @@
+fix:
+- [Bug][Data Source] Move data source manageable feature flag to DSM plugin ([#7440](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7440))
\ No newline at end of file
diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml
index 4833c3f96e30..7ba83c9248b6 100644
--- a/config/opensearch_dashboards.yml
+++ b/config/opensearch_dashboards.yml
@@ -325,7 +325,7 @@
# "none": The data source is readonly for all users.
# "dashboard_admin": The data source can only be managed by dashboard admin.
# "all": The data source can be managed by all users. Default to "all".
-# data_source.manageableBy: "all"
+# data_source_management.manageableBy: "all"
# Set the value of this setting to false to hide the help menu link to the OpenSearch Dashboards user survey
# opensearchDashboards.survey.url: "https://survey.opensearch.org"
diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap
index 1bd0458ef1b5..4201e5146669 100644
--- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap
+++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap
@@ -1,34 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[` should hide left navigation when in home page when workspace is enabled 1`] = `
-
-`;
-
exports[` should render correctly 1`] = `
should show all use case by default and
`;
+exports[` should show all use case when current nav group is \`all\` 1`] = `
+
+`;
+
exports[` should render correctly 1`] = `
', () => {
function mockProps(
props?: Partial
& {
navGroupsMap?: Record;
+ currentNavGroupId?: string;
+ navLinks?: ChromeNavLink[];
}
): CollapsibleNavGroupEnabledProps {
- const currentNavGroup$ = new BehaviorSubject(undefined);
const navGroupsMap$ = new BehaviorSubject>({
[ALL_USE_CASE_ID]: {
...DEFAULT_NAV_GROUPS[ALL_USE_CASE_ID],
@@ -121,6 +122,9 @@ describe('', () => {
},
...props?.navGroupsMap,
});
+ const currentNavGroup$ = new BehaviorSubject(
+ props?.currentNavGroupId ? navGroupsMap$.getValue()[props.currentNavGroupId] : undefined
+ );
return {
appId$: new BehaviorSubject('test'),
basePath: mockBasePath,
@@ -146,6 +150,7 @@ describe('', () => {
baseUrl: '',
href: '',
},
+ ...(props?.navLinks || []),
]),
storage: new StubBrowserStorage(),
onIsLockedUpdate: () => {},
@@ -226,8 +231,9 @@ describe('', () => {
expect(getAllByTestId('collapsibleNavAppLink-link-in-analytics').length).toEqual(2);
});
- it('should hide left navigation when in home page when workspace is enabled', async () => {
+ it('should show all use case when current nav group is `all`', async () => {
const props = mockProps({
+ currentNavGroupId: ALL_USE_CASE_ID,
navGroupsMap: {
[DEFAULT_NAV_GROUPS.analytics.id]: {
...DEFAULT_NAV_GROUPS.analytics,
@@ -241,12 +247,45 @@ describe('', () => {
},
},
});
- props.appId$ = new BehaviorSubject('home');
- if (props.capabilities.workspaces) {
- (props.capabilities.workspaces as Record) = {};
- (props.capabilities.workspaces as Record).enabled = true;
- }
- const { container } = render();
+ const { container, getAllByTestId, getByTestId } = render(
+
+ );
+ fireEvent.click(getAllByTestId('collapsibleNavAppLink-link-in-analytics')[1]);
+ expect(getAllByTestId('collapsibleNavAppLink-link-in-analytics').length).toEqual(1);
expect(container).toMatchSnapshot();
+ fireEvent.click(getByTestId('back'));
+ expect(getAllByTestId('collapsibleNavAppLink-link-in-analytics').length).toEqual(2);
+ });
+
+ it('should not show group if the nav link is hidden', async () => {
+ const props = mockProps({
+ currentNavGroupId: ALL_USE_CASE_ID,
+ navGroupsMap: {
+ [DEFAULT_NAV_GROUPS.analytics.id]: {
+ ...DEFAULT_NAV_GROUPS.analytics,
+ navLinks: [
+ {
+ id: 'link-in-analytics-but-hidden',
+ title: 'link-in-analytics-but-hidden',
+ showInAllNavGroup: true,
+ },
+ ],
+ },
+ },
+ navLinks: [
+ {
+ id: 'link-in-analytics-but-hidden',
+ hidden: true,
+ title: 'link-in-analytics-but-hidden',
+ baseUrl: '',
+ href: '',
+ },
+ ],
+ });
+ const { queryAllByTestId } = render();
+ expect(queryAllByTestId('collapsibleNavAppLink-link-in-analytics-but-hidden').length).toEqual(
+ 0
+ );
+ expect(queryAllByTestId('collapsibleNavAppLink-link-in-all').length).toEqual(1);
});
});
diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
index c6669a6b99f8..ea5510ad5994 100644
--- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
+++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
@@ -177,7 +177,7 @@ export function CollapsibleNavGroupEnabled({
basePath,
id,
isLocked,
- isNavOpen: isNavOpenProps,
+ isNavOpen,
storage = window.localStorage,
onIsLockedUpdate,
closeNav,
@@ -194,7 +194,7 @@ export function CollapsibleNavGroupEnabled({
const currentNavGroup = useObservable(observables.currentNavGroup$, undefined);
const navLinksForRender: ChromeNavLink[] = useMemo(() => {
- if (currentNavGroup) {
+ if (currentNavGroup && currentNavGroup.id !== ALL_USE_CASE_ID) {
return fulfillRegistrationLinksToChromeNavLinks(
navGroupsMap[currentNavGroup.id].navLinks || [],
navLinks
@@ -241,7 +241,10 @@ export function CollapsibleNavGroupEnabled({
label: group.title,
order: group.order,
};
- const linksForAllUseCaseWithinNavGroup = group.navLinks
+ const linksForAllUseCaseWithinNavGroup = fulfillRegistrationLinksToChromeNavLinks(
+ group.navLinks,
+ navLinks
+ )
.filter((navLink) => navLink.showInAllNavGroup)
.map((navLink) => ({
...navLink,
@@ -263,18 +266,6 @@ export function CollapsibleNavGroupEnabled({
return fulfillRegistrationLinksToChromeNavLinks(navLinksForAll, navLinks);
}, [navLinks, navGroupsMap, currentNavGroup]);
- const isNavOpen = useMemo(() => {
- // For now, only home page need to always collapse left navigation
- // when workspace is enabled.
- // If there are more pages need to collapse left navigation in the future
- // need to come up with a mechanism to register.
- if (capabilities.workspaces.enabled && appId === 'home') {
- return false;
- }
-
- return isNavOpenProps;
- }, [isNavOpenProps, capabilities.workspaces.enabled, appId]);
-
const width = useMemo(() => {
if (!isNavOpen) {
return 50;
diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled_top.tsx b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled_top.tsx
index 9e89155a8e4e..568290da727b 100644
--- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled_top.tsx
+++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled_top.tsx
@@ -18,6 +18,7 @@ import { i18n } from '@osd/i18n';
import { createEuiListItem } from './nav_link';
import { NavGroupItemInMap } from '../../nav_group';
import { ChromeNavLink } from '../../nav_links';
+import { ALL_USE_CASE_ID } from '../../../../../core/utils';
export interface CollapsibleNavTopProps {
navLinks: ChromeNavLink[];
@@ -44,6 +45,7 @@ export const CollapsibleNavTop = ({
const shouldShowBackButton = useMemo(
() =>
+ currentNavGroup?.id !== ALL_USE_CASE_ID &&
!shouldShrinkNavigation &&
Object.values(navGroupsMap).filter((item) => !item.type).length > 1 &&
currentNavGroup,
diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx
index 4e3539f2e53b..bef0f152c6a4 100644
--- a/src/core/public/chrome/ui/header/header.test.tsx
+++ b/src/core/public/chrome/ui/header/header.test.tsx
@@ -187,4 +187,22 @@ describe('Header', () => {
expect(component.find('CollapsibleNavGroupEnabled').exists()).toBeTruthy();
});
+
+ it('show hide expand icon in top left navigation when workspace enabled + homepage + new navigation enabled', () => {
+ const branding = {
+ useExpandedHeader: false,
+ };
+ const props = {
+ ...mockProps(),
+ branding,
+ };
+ props.application.currentAppId$ = new BehaviorSubject('home');
+ props.application.capabilities = { ...props.application.capabilities };
+ (props.application.capabilities.workspaces as Record) = {};
+ (props.application.capabilities.workspaces as Record).enabled = true;
+
+ const component = mountWithIntl();
+
+ expect(component.find('.header__toggleNavButtonSection').exists()).toBeFalsy();
+ });
});
diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx
index dc161456ba84..adcbba00fe8c 100644
--- a/src/core/public/chrome/ui/header/header.tsx
+++ b/src/core/public/chrome/ui/header/header.tsx
@@ -131,9 +131,17 @@ export function Header({
}: HeaderProps) {
const isVisible = useObservable(observables.isVisible$, false);
const isLocked = useObservable(observables.isLocked$, false);
+ const appId = useObservable(application.currentAppId$, '');
const [isNavOpen, setIsNavOpen] = useState(false);
const sidecarConfig = useObservable(observables.sidecarConfig$, undefined);
+ /**
+ * This is a workaround on 2.16 to hide the navigation items within left navigation
+ * when user is in homepage with workspace enabled + new navigation enabled
+ */
+ const shouldHideExpandIcon =
+ navGroupEnabled && appId === 'home' && application.capabilities.workspaces.enabled;
+
const sidecarPaddingStyle = useMemo(() => {
return getOsdSidecarPaddingStyle(sidecarConfig);
}, [sidecarConfig]);
@@ -198,29 +206,31 @@ export function Header({
-
-
-
+ setIsNavOpen(!isNavOpen)}
- aria-expanded={isNavOpen}
- aria-pressed={isNavOpen}
- aria-controls={navId}
- ref={toggleCollapsibleNavRef}
+ delay="long"
+ position="bottom"
>
-
-
-
-
+ setIsNavOpen(!isNavOpen)}
+ aria-expanded={isNavOpen}
+ aria-pressed={isNavOpen}
+ aria-controls={navId}
+ ref={toggleCollapsibleNavRef}
+ >
+
+
+
+
+ )}
@@ -293,7 +303,7 @@ export function Header({
id={navId}
isLocked={isLocked}
navLinks$={observables.navLinks$}
- isNavOpen={isNavOpen}
+ isNavOpen={shouldHideExpandIcon ? false : isNavOpen}
basePath={basePath}
navigateToApp={application.navigateToApp}
navigateToUrl={application.navigateToUrl}
diff --git a/src/plugins/advanced_settings/public/plugin.ts b/src/plugins/advanced_settings/public/plugin.ts
index f2bf05da4114..17cb7508c440 100644
--- a/src/plugins/advanced_settings/public/plugin.ts
+++ b/src/plugins/advanced_settings/public/plugin.ts
@@ -35,6 +35,7 @@ import { ComponentRegistry } from './component_registry';
import { AdvancedSettingsSetup, AdvancedSettingsStart, AdvancedSettingsPluginSetup } from './types';
import { setupTopNavThemeButton } from './register_nav_control';
import { DEFAULT_NAV_GROUPS, AppNavLinkStatus, WorkspaceAvailability } from '../../../core/public';
+import { getScopedBreadcrumbs } from '../../opensearch_dashboards_react/public';
const component = new ComponentRegistry();
@@ -81,7 +82,8 @@ export class AdvancedSettingsPlugin
{
...params,
basePath: core.http.basePath.get(),
- setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
+ setBreadcrumbs: (breadCrumbs) =>
+ coreStart.chrome.setBreadcrumbs(getScopedBreadcrumbs(breadCrumbs, params.history)),
wrapInPage: true,
},
component.start
diff --git a/src/plugins/data_source/common/data_sources/types.ts b/src/plugins/data_source/common/data_sources/types.ts
index bd147ac00c04..cde21f648c61 100644
--- a/src/plugins/data_source/common/data_sources/types.ts
+++ b/src/plugins/data_source/common/data_sources/types.ts
@@ -60,9 +60,3 @@ export enum DataSourceEngineType {
Elasticsearch = 'Elasticsearch',
NA = 'No Engine Type Available',
}
-
-export enum ManageableBy {
- All = 'all',
- DashboardAdmin = 'dashboard_admin',
- None = 'none',
-}
diff --git a/src/plugins/data_source/config.ts b/src/plugins/data_source/config.ts
index 36c298cde119..30824b486257 100644
--- a/src/plugins/data_source/config.ts
+++ b/src/plugins/data_source/config.ts
@@ -59,10 +59,6 @@ export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: true }),
}),
}),
- manageableBy: schema.oneOf(
- [schema.literal('all'), schema.literal('dashboard_admin'), schema.literal('none')],
- { defaultValue: 'all' }
- ),
});
export type DataSourcePluginConfigType = TypeOf;
diff --git a/src/plugins/data_source/server/plugin.ts b/src/plugins/data_source/server/plugin.ts
index fa3085a63935..bbf5a89d1b53 100644
--- a/src/plugins/data_source/server/plugin.ts
+++ b/src/plugins/data_source/server/plugin.ts
@@ -33,8 +33,6 @@ import { registerTestConnectionRoute } from './routes/test_connection';
import { registerFetchDataSourceMetaDataRoute } from './routes/fetch_data_source_metadata';
import { AuthenticationMethodRegistry, IAuthenticationMethodRegistry } from './auth_registry';
import { CustomApiSchemaRegistry } from './schema_registry';
-import { ManageableBy } from '../common/data_sources';
-import { getWorkspaceState } from '../../../../src/core/server/utils';
export class DataSourcePlugin implements Plugin {
private readonly logger: Logger;
@@ -83,25 +81,6 @@ export class DataSourcePlugin implements Plugin ({
- dataSource: {
- canManage: false,
- },
- }));
-
- core.capabilities.registerSwitcher((request) => {
- const { requestWorkspaceId, isDashboardAdmin } = getWorkspaceState(request);
- // User can not manage data source in the workspace.
- const canManage =
- (manageableBy === ManageableBy.All && !requestWorkspaceId) ||
- (manageableBy === ManageableBy.DashboardAdmin &&
- isDashboardAdmin !== false &&
- !requestWorkspaceId);
-
- return { dataSource: { canManage } };
- });
-
core.logging.configure(
this.config$.pipe(
map((dataSourceConfig) => ({
diff --git a/src/plugins/data_source_management/common/index.ts b/src/plugins/data_source_management/common/index.ts
index 980bf8939456..2b9b3a4454bf 100644
--- a/src/plugins/data_source_management/common/index.ts
+++ b/src/plugins/data_source_management/common/index.ts
@@ -6,3 +6,4 @@
export const PLUGIN_ID = 'dataSourceManagement';
export const PLUGIN_NAME = 'Data sources';
export const DEFAULT_DATA_SOURCE_UI_SETTINGS_ID = 'defaultDataSource';
+export * from './types';
diff --git a/src/plugins/data_source_management/common/types.ts b/src/plugins/data_source_management/common/types.ts
new file mode 100644
index 000000000000..23b2f437e248
--- /dev/null
+++ b/src/plugins/data_source_management/common/types.ts
@@ -0,0 +1,10 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export enum ManageableBy {
+ All = 'all',
+ DashboardAdmin = 'dashboard_admin',
+ None = 'none',
+}
diff --git a/src/plugins/data_source_management/config.ts b/src/plugins/data_source_management/config.ts
new file mode 100644
index 000000000000..1a56a126a943
--- /dev/null
+++ b/src/plugins/data_source_management/config.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { schema, TypeOf } from '@osd/config-schema';
+
+export const configSchema = schema.object({
+ manageableBy: schema.oneOf(
+ [schema.literal('all'), schema.literal('dashboard_admin'), schema.literal('none')],
+ { defaultValue: 'all' }
+ ),
+});
+
+export type ConfigSchema = TypeOf;
diff --git a/src/plugins/data_source_management/opensearch_dashboards.json b/src/plugins/data_source_management/opensearch_dashboards.json
index a8790d04823d..fb37a16eea0f 100644
--- a/src/plugins/data_source_management/opensearch_dashboards.json
+++ b/src/plugins/data_source_management/opensearch_dashboards.json
@@ -6,5 +6,6 @@
"requiredPlugins": ["management", "indexPatternManagement"],
"optionalPlugins": ["dataSource"],
"requiredBundles": ["opensearchDashboardsReact", "dataSource", "opensearchDashboardsUtils"],
- "extraPublicDirs": ["public/components/utils"]
+ "extraPublicDirs": ["public/components/utils"],
+ "configPath": ["data_source_management"]
}
diff --git a/src/plugins/data_source_management/public/components/data_source_view/__snapshots__/data_source_view.test.tsx.snap b/src/plugins/data_source_management/public/components/data_source_view/__snapshots__/data_source_view.test.tsx.snap
index 86ce46dbfa27..368173c0d0a9 100644
--- a/src/plugins/data_source_management/public/components/data_source_view/__snapshots__/data_source_view.test.tsx.snap
+++ b/src/plugins/data_source_management/public/components/data_source_view/__snapshots__/data_source_view.test.tsx.snap
@@ -60,7 +60,7 @@ exports[`DataSourceView Should return error when provided datasource has been fi
button={
}
@@ -88,7 +88,16 @@ exports[`DataSourceView Should return error when provided datasource has been fi
+ coreStart.chrome.setBreadcrumbs(getScopedBreadcrumbs(breadCrumbs, params.history)),
wrapInPage: true,
},
this.authMethodsRegistry,
@@ -224,6 +221,11 @@ export class DataSourceManagementPlugin
},
]);
+ // when the feature flag is disabled, we don't need to register any of the mds components
+ if (!this.featureFlagStatus) {
+ return undefined;
+ }
+
const registerAuthenticationMethod = (authMethod: AuthenticationMethod) => {
if (this.started) {
throw new Error(
diff --git a/src/plugins/data_source_management/server/index.ts b/src/plugins/data_source_management/server/index.ts
index bf4fb4377c93..f3d229ea3f1d 100644
--- a/src/plugins/data_source_management/server/index.ts
+++ b/src/plugins/data_source_management/server/index.ts
@@ -3,7 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PluginInitializerContext } from '../../../core/server';
+import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server';
+import { ConfigSchema, configSchema } from '../config';
import { DataSourceManagementPlugin } from './plugin';
// This exports static code and TypeScript types,
@@ -13,4 +14,8 @@ export function plugin(initializerContext: PluginInitializerContext) {
return new DataSourceManagementPlugin(initializerContext);
}
+export const config: PluginConfigDescriptor = {
+ schema: configSchema,
+};
+
export { DataSourceManagementPluginSetup, DataSourceManagementPluginStart } from './types';
diff --git a/src/plugins/data_source_management/server/plugin.ts b/src/plugins/data_source_management/server/plugin.ts
index 97ac8cacc237..de8dcf74cf96 100644
--- a/src/plugins/data_source_management/server/plugin.ts
+++ b/src/plugins/data_source_management/server/plugin.ts
@@ -5,6 +5,8 @@
// eslint-disable-next-line @osd/eslint/no-restricted-paths
import { DataSourcePluginSetup } from 'src/plugins/data_source/server/types';
+import { Observable } from 'rxjs';
+import { first } from 'rxjs/operators';
import {
CoreSetup,
CoreStart,
@@ -18,6 +20,9 @@ import { setupRoutes } from './routes';
import { DataSourceManagementPluginSetup, DataSourceManagementPluginStart } from './types';
import { OpenSearchDataSourceManagementPlugin } from './adaptors/opensearch_data_source_management_plugin';
import { PPLPlugin } from './adaptors/ppl_plugin';
+import { ConfigSchema } from '../config';
+import { getWorkspaceState } from '../../../../src/core/server/utils';
+import { ManageableBy } from '../common';
export interface DataSourceManagementPluginDependencies {
dataSource: DataSourcePluginSetup;
@@ -25,13 +30,15 @@ export interface DataSourceManagementPluginDependencies {
export class DataSourceManagementPlugin
implements Plugin {
+ private readonly config$: Observable;
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
+ this.config$ = initializerContext.config.create();
}
- public setup(
+ public async setup(
core: CoreSetup,
deps: {
dataSource: DataSourceManagementPluginDependencies;
@@ -39,6 +46,8 @@ export class DataSourceManagementPlugin
) {
const { dataSource } = deps;
+ const config: ConfigSchema = await this.config$.pipe(first()).toPromise();
+
const dataSourceEnabled = !!dataSource;
const openSearchDataSourceManagementClient: ILegacyClusterClient = core.opensearch.legacy.createClient(
@@ -51,6 +60,25 @@ export class DataSourceManagementPlugin
this.logger.debug('dataSourceManagement: Setup');
const router = core.http.createRouter();
+ const { manageableBy } = config;
+ core.capabilities.registerProvider(() => ({
+ dataSource: {
+ canManage: false,
+ },
+ }));
+
+ core.capabilities.registerSwitcher((request) => {
+ const { requestWorkspaceId, isDashboardAdmin } = getWorkspaceState(request);
+ // User can not manage data source in the workspace.
+ const canManage =
+ (manageableBy === ManageableBy.All && !requestWorkspaceId) ||
+ (manageableBy === ManageableBy.DashboardAdmin &&
+ isDashboardAdmin !== false &&
+ !requestWorkspaceId);
+
+ return { dataSource: { canManage } };
+ });
+
if (dataSourceEnabled) {
dataSource.registerCustomApiSchema(PPLPlugin);
dataSource.registerCustomApiSchema(OpenSearchDataSourceManagementPlugin);
diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts
index ef462374129e..36bc22c5d659 100644
--- a/src/plugins/index_pattern_management/public/plugin.ts
+++ b/src/plugins/index_pattern_management/public/plugin.ts
@@ -47,6 +47,7 @@ import {
import { ManagementSetup } from '../../management/public';
import { DEFAULT_NAV_GROUPS, AppStatus, DEFAULT_APP_CATEGORIES } from '../../../core/public';
+import { getScopedBreadcrumbs } from '../../opensearch_dashboards_react/public';
export interface IndexPatternManagementSetupDependencies {
management: ManagementSetup;
@@ -148,7 +149,8 @@ export class IndexPatternManagementPlugin
{
...params,
basePath: core.http.basePath.get(),
- setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
+ setBreadcrumbs: (breadCrumbs) =>
+ coreStart.chrome.setBreadcrumbs(getScopedBreadcrumbs(breadCrumbs, params.history)),
wrapInPage: true,
},
() => this.indexPatternManagementService.environmentService.getEnvironment().ml(),
diff --git a/src/plugins/opensearch_dashboards_react/public/react_router_navigate/index.ts b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/index.ts
index 8ba6b1e2fe90..f5a3ecceac35 100644
--- a/src/plugins/opensearch_dashboards_react/public/react_router_navigate/index.ts
+++ b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/index.ts
@@ -28,4 +28,8 @@
* under the License.
*/
-export { reactRouterNavigate, reactRouterOnClickHandler } from './react_router_navigate';
+export {
+ reactRouterNavigate,
+ reactRouterOnClickHandler,
+ getScopedBreadcrumbs,
+} from './react_router_navigate';
diff --git a/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.test.tsx b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.test.tsx
new file mode 100644
index 000000000000..138e33906ac7
--- /dev/null
+++ b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.test.tsx
@@ -0,0 +1,41 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { scopedHistoryMock } from '../../../../core/public/mocks';
+import { getScopedBreadcrumbs } from './react_router_navigate';
+
+describe('getScopedBreadcrumbs', () => {
+ it('should return scoped bread crumbs when given an array', () => {
+ const history = scopedHistoryMock.create({
+ pathname: '/base',
+ });
+ history.createHref.mockImplementation((location) => `/base${location.pathname}`);
+ const scopedBreadcrumbs = getScopedBreadcrumbs(
+ [
+ {
+ text: 'Home',
+ href: '/',
+ },
+ {
+ text: 'Dashboard',
+ href: '/dashboard',
+ },
+ ],
+ history
+ );
+ expect(scopedBreadcrumbs[0]).toEqual(
+ expect.objectContaining({
+ href: '/base/',
+ text: 'Home',
+ })
+ );
+ expect(scopedBreadcrumbs[1]).toEqual(
+ expect.objectContaining({
+ href: '/base/dashboard',
+ text: 'Dashboard',
+ })
+ );
+ });
+});
diff --git a/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.tsx b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.tsx
index 5e367f8cb94b..ae249bdadea9 100644
--- a/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.tsx
+++ b/src/plugins/opensearch_dashboards_react/public/react_router_navigate/react_router_navigate.tsx
@@ -28,7 +28,7 @@
* under the License.
*/
-import { ScopedHistory } from 'opensearch-dashboards/public';
+import { ChromeBreadcrumb, ScopedHistory } from 'opensearch-dashboards/public';
import { History } from 'history';
interface LocationObject {
@@ -79,3 +79,14 @@ export const reactRouterOnClickHandler = (
event.preventDefault();
history.push(toLocationObject(to));
};
+
+export const getScopedBreadcrumbs = (
+ crumbs: ChromeBreadcrumb[] = [],
+ appHistory: ScopedHistory
+) => {
+ const wrapBreadcrumb = (item: ChromeBreadcrumb, scopedHistory: ScopedHistory) => ({
+ ...item,
+ ...(item.href ? reactRouterNavigate(scopedHistory, item.href) : {}),
+ });
+ return crumbs.map((item) => wrapBreadcrumb(item, appHistory));
+};
diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts
index 1247b56b5555..8529a9555681 100644
--- a/src/plugins/saved_objects_management/public/plugin.ts
+++ b/src/plugins/saved_objects_management/public/plugin.ts
@@ -66,6 +66,7 @@ import { bootstrap } from './ui_actions_bootstrap';
import { DEFAULT_NAV_GROUPS, DEFAULT_APP_CATEGORIES } from '../../../core/public';
import { RecentWork } from './management_section/recent_work';
import { HOME_CONTENT_AREAS } from '../../../plugins/home/public';
+import { getScopedBreadcrumbs } from '../../opensearch_dashboards_react/public';
export interface SavedObjectsManagementPluginSetup {
actions: SavedObjectsManagementActionServiceSetup;
@@ -173,7 +174,8 @@ export class SavedObjectsManagementPlugin
mountParams: {
...params,
basePath: core.http.basePath.get(),
- setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
+ setBreadcrumbs: (breadCrumbs) =>
+ coreStart.chrome.setBreadcrumbs(getScopedBreadcrumbs(breadCrumbs, params.history)),
wrapInPage: true,
},
dataSourceEnabled: !!dataSource,
diff --git a/src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap b/src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
index f07fdfa682d8..27c9507a5493 100644
--- a/src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
+++ b/src/plugins/vis_type_metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap
@@ -1,7 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MetricVisComponent should render correct structure for multi-value metrics 1`] = `
-Array [
+
,
+ />
,
-]
+ />
+
`;
exports[`MetricVisComponent should render correct structure for single metric 1`] = `
-
+
+ showLabel={true}
+ />
+
`;
diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
index 8162eb7d1978..42e46054b9d8 100644
--- a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
+++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx
@@ -40,6 +40,7 @@ import { VisParams, MetricVisMetric } from '../types';
import { getFormatService } from '../services';
import { SchemaConfig } from '../../../visualizations/public';
import { Range } from '../../../expressions/public';
+import { VisualizationContainer } from '../../../visualizations/public';
import './metric_vis.scss';
@@ -214,12 +215,12 @@ class MetricVisComponent extends Component {
}
render() {
- let metricsHtml;
- if (this.props.visData) {
- const metrics = this.processTableGroups(this.props.visData);
- metricsHtml = metrics.map(this.renderMetric);
- }
- return metricsHtml;
+ const metrics = this.props.visData.rows ? this.processTableGroups(this.props.visData) : [];
+ return (
+
+ {metrics.length > 0 ? metrics.map(this.renderMetric) : null}
+
+ );
}
}
diff --git a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx
index dcd3fcd0a727..14b0c28f2c56 100644
--- a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx
+++ b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx
@@ -28,14 +28,14 @@
* under the License.
*/
-import React, { lazy } from 'react';
+import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
-import { VisualizationContainer } from '../../visualizations/public';
import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers';
import { MetricVisRenderValue } from './metric_vis_fn';
+import MetricVisComponent from './components/metric_vis_component';
+
// @ts-ignore
-const MetricVisComponent = lazy(() => import('./components/metric_vis_component'));
export const metricVisRenderer: () => ExpressionRenderDefinition = () => ({
name: 'metric_vis',
@@ -47,14 +47,12 @@ export const metricVisRenderer: () => ExpressionRenderDefinition
-
- ,
+ ,
domNode
);
},
diff --git a/src/plugins/vis_type_table/public/components/table_vis_app.test.tsx b/src/plugins/vis_type_table/public/components/table_vis_app.test.tsx
index f2a347dcd0a2..58e38a346365 100644
--- a/src/plugins/vis_type_table/public/components/table_vis_app.test.tsx
+++ b/src/plugins/vis_type_table/public/components/table_vis_app.test.tsx
@@ -9,7 +9,7 @@ import { coreMock } from '../../../../core/public/mocks';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { TableVisApp } from './table_vis_app';
import { TableVisConfig } from '../types';
-import { TableVisData } from '../table_vis_response_handler';
+import { TableVisData, FormattedTableContext } from '../table_vis_response_handler';
jest.mock('./table_vis_component_group', () => ({
TableVisComponentGroup: () => (
@@ -42,15 +42,24 @@ describe('TableVisApp', () => {
} as unknown) as IInterpreterRenderHandlers;
const visConfigMock = ({} as unknown) as TableVisConfig;
- it('should render TableVisComponent if no split table', () => {
- const visDataMock = {
- table: {
- columns: [],
- rows: [],
- formattedColumns: [],
+ const createMockFormattedTableContext = (rowCount: number): FormattedTableContext => ({
+ columns: [{ id: 'col1', name: 'Column 1' }],
+ rows: Array(rowCount).fill({ col1: 'value' }),
+ formattedColumns: [
+ {
+ id: 'col1',
+ title: 'Column 1',
+ formatter: {} as any,
+ filterable: true,
},
+ ],
+ });
+
+ it('should render TableVisComponent if no split table and has rows', () => {
+ const visDataMock: TableVisData = {
+ table: createMockFormattedTableContext(1),
tableGroups: [],
- } as TableVisData;
+ };
const { getByTestId } = render(
{
});
it('should render TableVisComponentGroup component if split direction is column', () => {
- const visDataMock = {
- tableGroups: [],
+ const visDataMock: TableVisData = {
+ tableGroups: [
+ {
+ table: createMockFormattedTableContext(1),
+ title: 'Group 1',
+ },
+ ],
direction: 'column',
- } as TableVisData;
+ };
const { container, getByTestId } = render(
{
handlers={handlersMock}
/>
);
- expect(container.outerHTML.includes('visTable')).toBe(true);
+ expect(container.querySelector('.visTable')).not.toBeNull();
expect(getByTestId('TableVisComponentGroup')).toBeInTheDocument();
});
it('should render TableVisComponentGroup component if split direction is row', () => {
- const visDataMock = {
- tableGroups: [],
+ const visDataMock: TableVisData = {
+ tableGroups: [
+ {
+ table: createMockFormattedTableContext(1),
+ title: 'Group 1',
+ },
+ ],
direction: 'row',
- } as TableVisData;
+ };
const { container, getByTestId } = render(
{
handlers={handlersMock}
/>
);
- expect(container.outerHTML.includes('visTable')).toBe(true);
+ expect(container.querySelector('.visTable')).not.toBeNull();
expect(getByTestId('TableVisComponentGroup')).toBeInTheDocument();
});
+
+ it('should render "No results found" when there are no rows', () => {
+ const visDataMock: TableVisData = {
+ table: createMockFormattedTableContext(0),
+ tableGroups: [],
+ };
+ const { getByText } = render(
+
+ );
+ expect(getByText('No results found')).toBeInTheDocument();
+ });
});
diff --git a/src/plugins/vis_type_table/public/components/table_vis_app.tsx b/src/plugins/vis_type_table/public/components/table_vis_app.tsx
index cbde913452d1..ccdab8cdaf83 100644
--- a/src/plugins/vis_type_table/public/components/table_vis_app.tsx
+++ b/src/plugins/vis_type_table/public/components/table_vis_app.tsx
@@ -16,6 +16,7 @@ import { TableVisConfig } from '../types';
import { TableVisComponent } from './table_vis_component';
import { TableVisComponentGroup } from './table_vis_component_group';
import { getTableUIState, TableUiState } from '../utils';
+import { VisualizationContainer } from '../../../visualizations/public';
interface TableVisAppProps {
services: CoreStart;
@@ -36,32 +37,35 @@ export const TableVisApp = ({
}, [handlers]);
const tableUiState: TableUiState = getTableUIState(handlers.uiState as PersistedState);
+ const showNoResult = table ? table.rows.length === 0 : tableGroups?.length === 0;
return (
-
- {table ? (
-
- ) : (
-
- )}
-
+
+
+ {table ? (
+
+ ) : (
+
+ )}
+
+
);
diff --git a/src/plugins/vis_type_table/public/table_vis_renderer.tsx b/src/plugins/vis_type_table/public/table_vis_renderer.tsx
index 8e467112528d..01b995e64fbc 100644
--- a/src/plugins/vis_type_table/public/table_vis_renderer.tsx
+++ b/src/plugins/vis_type_table/public/table_vis_renderer.tsx
@@ -7,7 +7,6 @@ import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { CoreStart } from 'opensearch-dashboards/public';
-import { VisualizationContainer } from '../../visualizations/public';
import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers';
import { TableVisRenderValue } from './table_vis_fn';
import { TableVisApp } from './components/table_vis_app';
@@ -23,13 +22,8 @@ export const getTableVisRenderer: (
unmountComponentAtNode(domNode);
});
- const showNoResult = visData.table
- ? visData.table.rows.length === 0
- : visData.tableGroups?.length === 0;
render(
-
-
- ,
+ ,
domNode
);
},
diff --git a/src/plugins/vis_type_table/public/utils/get_table_ui_state.ts b/src/plugins/vis_type_table/public/utils/get_table_ui_state.ts
index 58fc6b472a40..67c1a1864f4b 100644
--- a/src/plugins/vis_type_table/public/utils/get_table_ui_state.ts
+++ b/src/plugins/vis_type_table/public/utils/get_table_ui_state.ts
@@ -14,8 +14,8 @@ export interface TableUiState {
}
export function getTableUIState(uiState: PersistedState): TableUiState {
- const sort: ColumnSort = uiState.get('vis.sortColumn') || {};
- const colWidth: ColumnWidth[] = uiState.get('vis.columnsWidth') || [];
+ const sort: ColumnSort = uiState?.get('vis.sortColumn') || {};
+ const colWidth: ColumnWidth[] = uiState?.get('vis.columnsWidth') || [];
const setSort = (newSort: ColumnSort) => {
uiState.set('vis.sortColumn', newSort);
diff --git a/src/plugins/workspace/public/components/workspace_detail_app.tsx b/src/plugins/workspace/public/components/workspace_detail_app.tsx
index 2236cd67c139..294851502567 100644
--- a/src/plugins/workspace/public/components/workspace_detail_app.tsx
+++ b/src/plugins/workspace/public/components/workspace_detail_app.tsx
@@ -5,6 +5,7 @@
import React, { useEffect } from 'react';
import { I18nProvider } from '@osd/i18n/react';
+import { i18n } from '@osd/i18n';
import { CoreStart } from 'opensearch-dashboards/public';
import { useObservable } from 'react-use';
import { EuiBreadcrumb } from '@elastic/eui';
@@ -34,6 +35,9 @@ export const WorkspaceDetailApp = (props: WorkspaceDetailProps) => {
breadcrumbs.push({
text: currentWorkspace.name,
});
+ breadcrumbs.push({
+ text: i18n.translate('workspace.detail.breadcrumb', { defaultMessage: 'Workspace Detail' }),
+ });
}
chrome?.setBreadcrumbs(breadcrumbs);
}, [chrome, currentWorkspace, application]);
diff --git a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.test.tsx b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.test.tsx
index 6b1779a8f847..fa3ab9a4ff06 100644
--- a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.test.tsx
+++ b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.test.tsx
@@ -4,12 +4,12 @@
*/
import React from 'react';
-import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
import { WorkspaceMenu } from './workspace_menu';
import { coreMock } from '../../../../../core/public/mocks';
import { CoreStart } from '../../../../../core/public';
-import { BehaviorSubject, of } from 'rxjs';
+import { BehaviorSubject } from 'rxjs';
import { IntlProvider } from 'react-intl';
import { recentWorkspaceManager } from '../../recent_workspace_manager';
import { WORKSPACE_USE_CASES } from '../../../common/constants';
@@ -109,7 +109,7 @@ describe('', () => {
expect(screen.getByText('Observability')).toBeInTheDocument();
});
- it('should navigate to the workspace', () => {
+ it('should navigate to the first feature of workspace use case', () => {
coreStartMock.workspaces.workspaceList$.next([
{ id: 'workspace-1', name: 'workspace 1', features: ['use-case-observability'] },
]);
@@ -134,6 +134,31 @@ describe('', () => {
});
});
+ it('should navigate to the workspace detail page when use case is all', () => {
+ coreStartMock.workspaces.workspaceList$.next([
+ { id: 'workspace-1', name: 'workspace 1', features: ['use-case-all'] },
+ ]);
+
+ const originalLocation = window.location;
+ Object.defineProperty(window, 'location', {
+ value: {
+ assign: jest.fn(),
+ },
+ });
+
+ render();
+ fireEvent.click(screen.getByTestId('workspace-select-button'));
+ fireEvent.click(screen.getByText(/workspace 1/i));
+
+ expect(window.location.assign).toHaveBeenCalledWith(
+ 'https://test.com/w/workspace-1/app/workspace_detail'
+ );
+
+ Object.defineProperty(window, 'location', {
+ value: originalLocation,
+ });
+ });
+
it('should navigate to workspace management page', () => {
coreStartMock.workspaces.currentWorkspace$.next({
id: 'workspace-1',
diff --git a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx
index df276d8c43bb..aadfab05900d 100644
--- a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx
+++ b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx
@@ -29,7 +29,7 @@ import {
WORKSPACE_DETAIL_APP_ID,
} from '../../../common/constants';
import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils';
-import { CoreStart, WorkspaceObject } from '../../../../../core/public';
+import { ALL_USE_CASE_ID, CoreStart, WorkspaceObject } from '../../../../../core/public';
import { getFirstUseCaseOfFeatureConfigs } from '../../utils';
import { recentWorkspaceManager } from '../../recent_workspace_manager';
import { WorkspaceUseCase } from '../../types';
@@ -126,7 +126,9 @@ export const WorkspaceMenu = ({ coreStart, registeredUseCases$ }: Props) => {
const getWorkspaceListGroup = (filterWorkspaceList: WorkspaceObject[], itemType: string) => {
const listItems = filterWorkspaceList.map((workspace: WorkspaceObject) => {
- const appId = getUseCase(workspace)?.features[0] ?? WORKSPACE_DETAIL_APP_ID;
+ const useCase = getUseCase(workspace);
+ const appId =
+ (useCase?.id !== ALL_USE_CASE_ID && useCase?.features?.[0]) || WORKSPACE_DETAIL_APP_ID;
const useCaseURL = formatUrlWithWorkspaceId(
coreStart.application.getUrlForApp(appId, {
absolute: false,
@@ -138,6 +140,7 @@ export const WorkspaceMenu = ({ coreStart, registeredUseCases$ }: Props) => {
{
initialsLength={2}
/>
}
- label={
-
-
- {workspace.name}
-
-
- }
+ label={workspace.name}
onClick={() => {
closePopover();
window.location.assign(useCaseURL);
@@ -172,7 +165,7 @@ export const WorkspaceMenu = ({ coreStart, registeredUseCases$ }: Props) => {
{itemType === 'all' ? allWorkspacesTitle : recentWorkspacesTitle}
-
+
{listItems}
>
@@ -210,16 +203,16 @@ export const WorkspaceMenu = ({ coreStart, registeredUseCases$ }: Props) => {
-
+
{currentWorkspaceName}
- {getUseCase(currentWorkspace)?.title ?? ''}
+ {getUseCase(currentWorkspace)?.title ?? ''}
{
expect(setupMock.application.register).toHaveBeenCalledWith(
expect.objectContaining({
id: 'workspace_detail',
- navLinkStatus: AppNavLinkStatus.visible,
+ navLinkStatus: AppNavLinkStatus.hidden,
})
);
diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts
index 5452bdd7f2fd..dd4dc78fdf2c 100644
--- a/src/plugins/workspace/public/plugin.ts
+++ b/src/plugins/workspace/public/plugin.ts
@@ -115,6 +115,12 @@ export class WorkspacePlugin
if (app.id === 'home' && isAllUseCase) {
return { navLinkStatus: AppNavLinkStatus.hidden };
}
+
+ // show the overview page in all use case
+ if (app.id === WORKSPACE_DETAIL_APP_ID && isAllUseCase) {
+ return { navLinkStatus: AppNavLinkStatus.visible };
+ }
+
if (isAppAccessibleInWorkspace(app, currentWorkspace, registeredUseCases)) {
return;
}
@@ -334,9 +340,7 @@ export class WorkspacePlugin
title: i18n.translate('workspace.settings.workspaceDetail', {
defaultMessage: 'Workspace Detail',
}),
- navLinkStatus: core.chrome.navGroup.getNavGroupEnabled()
- ? AppNavLinkStatus.visible
- : AppNavLinkStatus.hidden,
+ navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const { renderDetailApp } = await import('./application');
return mountWorkspaceApp(params, renderDetailApp);
diff --git a/src/plugins/workspace/public/utils.test.ts b/src/plugins/workspace/public/utils.test.ts
index 5c91effc4146..0f31a70722a1 100644
--- a/src/plugins/workspace/public/utils.test.ts
+++ b/src/plugins/workspace/public/utils.test.ts
@@ -515,7 +515,7 @@ describe('workspace utils: prependWorkspaceToBreadcrumbs', () => {
it('should not enrich breadcrumbs when out a workspace', async () => {
const coreStart = coreMock.createStart();
prependWorkspaceToBreadcrumbs(coreStart, null, 'app1', undefined, {});
- expect(coreStart.chrome.setBreadcrumbsEnricher).toHaveBeenCalledWith(undefined);
+ expect(coreStart.chrome.setBreadcrumbsEnricher).not.toHaveBeenCalled();
});
it('should enrich breadcrumbs when in a workspace and use workspace use case as current nav group', async () => {
diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts
index e1093495c049..8e0846a7593a 100644
--- a/src/plugins/workspace/public/utils.ts
+++ b/src/plugins/workspace/public/utils.ts
@@ -295,6 +295,16 @@ export function prependWorkspaceToBreadcrumbs(
core.chrome.setBreadcrumbsEnricher(undefined);
return;
}
+
+ /**
+ * There has 3 cases
+ * nav group is enable + workspace enable + in a workspace -> workspace enricher
+ * nav group is enable + workspace enable + out a workspace -> nav group enricher
+ * nav group is enable + workspace disabled -> nav group enricher
+ *
+ * switch workspace will cause page refresh, breadcrumbs enricher will reset automatically
+ * so we don't need to have reset logic for workspace
+ */
if (currentWorkspace) {
const useCase = getFirstUseCaseOfFeatureConfigs(currentWorkspace?.features || []);
// get workspace the only use case
@@ -336,7 +346,5 @@ export function prependWorkspaceToBreadcrumbs(
return [homeBreadcrumb, navGroupBreadcrumb, ...breadcrumbs];
}
});
- } else {
- core.chrome.setBreadcrumbsEnricher(undefined);
}
}
diff --git a/yarn.lock b/yarn.lock
index a3e3becf27d6..747e2210d233 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5310,14 +5310,14 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@*, browserslist@^4.21.10, browserslist@^4.21.5, browserslist@^4.21.9, browserslist@^4.22.1:
- version "4.22.1"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619"
- integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==
+ version "4.23.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.2.tgz#244fe803641f1c19c28c48c4b6ec9736eb3d32ed"
+ integrity sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==
dependencies:
- caniuse-lite "^1.0.30001541"
- electron-to-chromium "^1.4.535"
- node-releases "^2.0.13"
- update-browserslist-db "^1.0.13"
+ caniuse-lite "^1.0.30001640"
+ electron-to-chromium "^1.4.820"
+ node-releases "^2.0.14"
+ update-browserslist-db "^1.1.0"
bser@2.1.1:
version "2.1.1"
@@ -5526,11 +5526,16 @@ camelize@^1.0.0:
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
-caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001541:
+caniuse-lite@^1.0.30001464:
version "1.0.30001587"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz"
integrity sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==
+caniuse-lite@^1.0.30001640:
+ version "1.0.30001643"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz#9c004caef315de9452ab970c3da71085f8241dbd"
+ integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==
+
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -7415,10 +7420,10 @@ elasticsearch@^16.4.0, elasticsearch@^16.7.0:
chalk "^1.0.0"
lodash "^4.17.10"
-electron-to-chromium@^1.4.535:
- version "1.4.573"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.573.tgz#aa6e5edf86448bb9398f529357abcc6a17b6341d"
- integrity sha512-tzxxvKDTO3V5vzN2F+3v9jrK9gEbCdf1YYJUx/zVq1cyzyh+x1ddeYNNWh0ZS2ETNCVK3+Pns1LHIBq4w20X2Q==
+electron-to-chromium@^1.4.820:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz#0d3123a9f09189b9c7ab4b5d6848d71b3c1fd0e8"
+ integrity sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==
elegant-spinner@^1.0.1:
version "1.0.1"
@@ -7787,6 +7792,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+escalade@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
+ integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
+
escape-latex@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1"
@@ -12861,10 +12871,10 @@ node-preload@^0.2.1:
dependencies:
process-on-spawn "^1.0.0"
-node-releases@^2.0.13:
- version "2.0.13"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
- integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
+node-releases@^2.0.14:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+ integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
node-stream-zip@^1.15.0:
version "1.15.0"
@@ -13623,6 +13633,11 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+picocolors@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
+ integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
+
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -17315,13 +17330,13 @@ untildify@^4.0.0:
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
-update-browserslist-db@^1.0.13:
- version "1.0.13"
- resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
- integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+update-browserslist-db@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
+ integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
dependencies:
- escalade "^3.1.1"
- picocolors "^1.0.0"
+ escalade "^3.1.2"
+ picocolors "^1.0.1"
uri-js@^4.2.2:
version "4.4.1"