Skip to content

Commit

Permalink
[Serverless] Improve breadcrumbs in management (#166259)
Browse files Browse the repository at this point in the history
## Summary

Close #164507

This PR improves management breadcrumbs in serverless project.

![Screenshot 2023-09-13 at 16 45
28](https://github.com/elastic/kibana/assets/7784120/97f9dd25-aeed-468b-8ea6-9ffa66ce14d0)

- **Management**: I removed dependency from serverless -> management.
details:
#166259 (comment)
- **Search**: Search project links directly to some management sub-apps
from the side nav. In some cases I hid the breadcrumb that comes from
the navigation config to avoid duplication: for example there was`Index
Management > Index Management` where the first came from the nav and the
second from the management sub-app.
- **Security**: For security I disabled setting management sub-app
breadcrumbs from the navigation config as they are set from the apps.
This allows for deeper breadcrumbs, beyond just nav.
#166259 (comment)
  • Loading branch information
Dosant authored Sep 19, 2023
1 parent 36a7a80 commit d91fe9f
Show file tree
Hide file tree
Showing 14 changed files with 71 additions and 13 deletions.
3 changes: 2 additions & 1 deletion src/plugins/management/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"share"
],
"optionalPlugins": [
"home"
"home",
"serverless"
],
"requiredBundles": [
"kibanaReact",
Expand Down
14 changes: 12 additions & 2 deletions src/plugins/management/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
import { BehaviorSubject } from 'rxjs';
import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
import { HomePublicPluginSetup } from '@kbn/home-plugin/public';
import { ServerlessPluginStart } from '@kbn/serverless/public';
import {
CoreSetup,
CoreStart,
Expand Down Expand Up @@ -39,6 +40,7 @@ interface ManagementSetupDependencies {

interface ManagementStartDependencies {
share: SharePluginStart;
serverless?: ServerlessPluginStart;
}

export class ManagementPlugin
Expand Down Expand Up @@ -122,13 +124,21 @@ export class ManagementPlugin
updater$: this.appUpdater,
async mount(params: AppMountParameters) {
const { renderApp } = await import('./application');
const [coreStart] = await core.getStartServices();
const [coreStart, deps] = await core.getStartServices();

return renderApp(params, {
sections: getSectionsServiceStartPrivate(),
kibanaVersion,
coreStart,
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
setBreadcrumbs: (newBreadcrumbs) => {
if (deps.serverless) {
// drop the root management breadcrumb in serverless because it comes from the navigation tree
const [, ...trailingBreadcrumbs] = newBreadcrumbs;
deps.serverless.setBreadcrumbs(trailingBreadcrumbs);
} else {
coreStart.chrome.setBreadcrumbs(newBreadcrumbs);
}
},
isSidebarEnabled$: managementPlugin.isSidebarEnabled$,
cardsNavigationConfig$: managementPlugin.cardsNavigationConfig$,
landingPageRedirect$: managementPlugin.landingPageRedirect$,
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/management/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"@kbn/test-jest-helpers",
"@kbn/config-schema",
"@kbn/core-application-browser",
"@kbn/core-http-browser"
"@kbn/core-http-browser",
"@kbn/serverless"
],
"exclude": [
"target/**/*"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const configureNavigation = (
if (!serverConfig.developer.disableManagementUrlRedirect) {
management.setLandingPageRedirect(SECURITY_PROJECT_SETTINGS_PATH);
}
management.setIsSidebarEnabled(false);

serverless.setProjectHome(APP_PATH);
serverless.setSideNavComponent(getSecuritySideNavComponent(services));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const HIDDEN_BREADCRUMBS = new Set<ProjectPageName>([
SecurityPageName.sessions,
]);

const isBreadcrumbHidden = (id: ProjectPageName): boolean =>
HIDDEN_BREADCRUMBS.has(id) ||
id.startsWith('management:'); /* management sub-pages set their breadcrumbs themselves */

export const subscribeNavigationTree = (services: Services): void => {
const { serverless, getProjectNavLinks$ } = services;

Expand All @@ -59,13 +63,12 @@ export const getFormatChromeProjectNavNodes = (services: Services) => {
const navLinkId = getNavLinkIdFromProjectPageName(id);

if (chrome.navLinks.has(navLinkId)) {
const breadcrumbHidden = HIDDEN_BREADCRUMBS.has(id);
const link: ChromeProjectNavigationNode = {
id: navLinkId,
title,
path: [...path, navLinkId],
deepLink: chrome.navLinks.get(navLinkId),
...(breadcrumbHidden && { breadcrumbStatus: 'hidden' }),
...(isBreadcrumbHidden(id) && { breadcrumbStatus: 'hidden' }),
};
// check default navigation for children
const defaultChildrenNav = getDefaultChildrenNav(id, link);
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/serverless/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
],
"requiredPlugins": [
"kibanaReact",
"management",
"cloud"
],
"optionalPlugins": [],
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/serverless/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export class ServerlessPlugin
dependencies: ServerlessPluginStartDependencies
): ServerlessPluginStart {
const { developer } = this.config;
const { management } = dependencies;

if (developer && developer.projectSwitcher && developer.projectSwitcher.enabled) {
const { currentType } = developer.projectSwitcher;
Expand All @@ -61,7 +60,6 @@ export class ServerlessPlugin
}

core.chrome.setChromeStyle('project');
management.setIsSidebarEnabled(false);

// Casting the "chrome.projects" service to an "internal" type: this is intentional to obscure the property from Typescript.
const { project } = core.chrome as InternalChromeStart;
Expand Down
3 changes: 0 additions & 3 deletions x-pack/plugins/serverless/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type {
SideNavComponent,
ChromeProjectNavigationNode,
} from '@kbn/core-chrome-browser';
import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public';
import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public';
import type { Observable } from 'rxjs';

Expand All @@ -31,11 +30,9 @@ export interface ServerlessPluginStart {
}

export interface ServerlessPluginSetupDependencies {
management: ManagementSetup;
cloud: CloudSetup;
}

export interface ServerlessPluginStartDependencies {
management: ManagementStart;
cloud: CloudStart;
}
1 change: 0 additions & 1 deletion x-pack/plugins/serverless/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"@kbn/config-schema",
"@kbn/core",
"@kbn/kibana-react-plugin",
"@kbn/management-plugin",
"@kbn/serverless-project-switcher",
"@kbn/serverless-types",
"@kbn/utils",
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/serverless_observability/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class ServerlessObservabilityPlugin
observabilityShared.setIsSidebarEnabled(false);
serverless.setProjectHome('/app/observability/landing');
serverless.setSideNavComponent(getObservabilitySideNavComponent(core, { serverless, cloud }));
management.setIsSidebarEnabled(false);
management.setupCardsNavigation({
enabled: true,
hideLinksTo: [appIds.RULES],
Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/serverless_search/public/layout/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,16 @@ const navigationTree: NavigationTreeDefinition = {
defaultMessage: 'Index Management',
}),
link: 'management:index_management',
breadcrumbStatus:
'hidden' /* management sub-pages set their breadcrumbs themselves */,
},
{
title: i18n.translate('xpack.serverlessSearch.nav.content.pipelines', {
defaultMessage: 'Pipelines',
}),
link: 'management:ingest_pipelines',
breadcrumbStatus:
'hidden' /* management sub-pages set their breadcrumbs themselves */,
},
],
},
Expand All @@ -110,6 +114,8 @@ const navigationTree: NavigationTreeDefinition = {
children: [
{
link: 'management:api_keys',
breadcrumbStatus:
'hidden' /* management sub-pages set their breadcrumbs themselves */,
},
],
},
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/serverless_search/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class ServerlessSearchPlugin
): ServerlessSearchPluginStart {
serverless.setProjectHome('/app/elasticsearch');
serverless.setSideNavComponent(createComponent(core, { serverless, cloud }));
management.setIsSidebarEnabled(false);
management.setupCardsNavigation({
enabled: true,
hideLinksTo: [appIds.MAINTENANCE_WINDOWS],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,25 @@ export function SvlCommonNavigationProvider(ctx: FtrProviderContext) {
});
}
},
async expectBreadcrumbMissing(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
if ('deepLinkId' in by) {
await testSubjects.missingOrFail(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
} else {
await retry.try(async () => {
expect(await getByVisibleText('~breadcrumb', by.text)).be(null);
});
}
},
async expectBreadcrumbTexts(expectedBreadcrumbTexts: string[]) {
await retry.try(async () => {
const breadcrumbsContainer = await testSubjects.find('breadcrumbs');
const breadcrumbs = await breadcrumbsContainer.findAllByTestSubject('~breadcrumb');
breadcrumbs.shift(); // remove home
expect(expectedBreadcrumbTexts.length).to.eql(breadcrumbs.length);
const texts = await Promise.all(breadcrumbs.map((b) => b.getVisibleText()));
expect(expectedBreadcrumbTexts).to.eql(texts);
});
},
},
search: new SvlNavigationSearchPageObject(ctx),
recent: {
Expand Down
22 changes: 22 additions & 0 deletions x-pack/test_serverless/functional/test_suites/search/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
await expectNoPageReload();
});

it("management apps from the sidenav hide the 'stack management' root from the breadcrumbs", async () => {
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:triggersActions' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Explore', 'Alerts', 'Rules']);

await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:index_management' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Content', 'Index Management']);

await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:ingest_pipelines' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Content', 'Ingest Pipelines']);

await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management:api_keys' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Security', 'API keys']);
});

it('navigate management', async () => {
await svlCommonNavigation.sidenav.openSection('project_settings_project_nav');
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'management' });
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Management']);
await testSubjects.click('app-card-dataViews');
await svlCommonNavigation.breadcrumbs.expectBreadcrumbTexts(['Management', 'Data views']);
});

it('navigate using search', async () => {
await svlCommonNavigation.search.showSearch();
// TODO: test something search project specific instead of generic discover
Expand Down

0 comments on commit d91fe9f

Please sign in to comment.