Skip to content

Commit

Permalink
[Serverless/Breadcrumbs] Use EuiBreadcrumbs instead of `EuiHeaderBr…
Browse files Browse the repository at this point in the history
…eadcrumbs` (#169838)

## Summary

fix #166593


https://github.com/elastic/kibana/assets/7784120/2569007b-92b6-47d0-a893-8747fbf17d2b

- "Projects" link removed, now it is part of breadcrumb. This also makes
the header more responsive as we get the flexibility from the
breadcrumbs.
- Added "View all projects"
- Added "Manage project" link
  • Loading branch information
Dosant committed Oct 31, 2023
1 parent 2e51c25 commit a1bde01
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 106 deletions.
Expand Up @@ -295,6 +295,11 @@ export class ChromeService {
projectNavigation.setProjectName(projectName);
};

const setProjectUrl = (projectUrl: string) => {
validateChromeStyle();
projectNavigation.setProjectUrl(projectUrl);
};

const isIE = () => {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE '); // IE 10 or older
Expand Down Expand Up @@ -387,8 +392,6 @@ export class ChromeService {
loadingCount$={http.getLoadingCount$()}
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
homeHref$={projectNavigation.getProjectHome$()}
projectsUrl$={projectNavigation.getProjectsUrl$()}
projectName$={projectNavigation.getProjectName$()}
docLinks={docLinks}
kibanaVersion={injectedMetadata.getKibanaVersion()}
prependBasePath={http.basePath.prepend}
Expand Down Expand Up @@ -521,6 +524,7 @@ export class ChromeService {
project: {
setHome: setProjectHome,
setProjectsUrl,
setProjectUrl,
setProjectName,
setNavigation: setProjectNavigation,
setSideNavComponent: setProjectSideNavComponent,
Expand Down
Expand Up @@ -6,35 +6,40 @@
* Side Public License, v 1.
*/

import { EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui';
import {
AppDeepLinkId,
ChromeProjectBreadcrumb,
ChromeProjectNavigationNode,
ChromeSetProjectBreadcrumbsParams,
ChromeBreadcrumb,
} from '@kbn/core-chrome-browser';
import { createHomeBreadcrumb } from './home_breadcrumbs';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';

export function buildBreadcrumbs({
homeHref,
projectsUrl,
projectName,
projectUrl,
projectBreadcrumbs,
activeNodes,
chromeBreadcrumbs,
}: {
homeHref: string;
projectsUrl?: string;
projectName?: string;
projectUrl?: string;
projectBreadcrumbs: {
breadcrumbs: ChromeProjectBreadcrumb[];
params: ChromeSetProjectBreadcrumbsParams;
};
chromeBreadcrumbs: ChromeBreadcrumb[];
activeNodes: ChromeProjectNavigationNode[][];
}): ChromeProjectBreadcrumb[] {
const homeBreadcrumb = createHomeBreadcrumb({
homeHref,
});
const rootCrumb = buildRootCrumb({ projectsUrl, projectName, projectUrl });

if (projectBreadcrumbs.params.absolute) {
return [homeBreadcrumb, ...projectBreadcrumbs.breadcrumbs];
return [rootCrumb, ...projectBreadcrumbs.breadcrumbs];
}

// breadcrumbs take the first active path
Expand All @@ -52,7 +57,7 @@ export function buildBreadcrumbs({

// if there are project breadcrumbs set, use them
if (projectBreadcrumbs.breadcrumbs.length !== 0) {
return [homeBreadcrumb, ...navBreadcrumbs, ...projectBreadcrumbs.breadcrumbs];
return [rootCrumb, ...navBreadcrumbs, ...projectBreadcrumbs.breadcrumbs];
}

// otherwise try to merge legacy breadcrumbs with navigational project breadcrumbs using deeplinkid
Expand All @@ -70,12 +75,50 @@ export function buildBreadcrumbs({
}

if (chromeBreadcrumbStartIndex === -1) {
return [homeBreadcrumb, ...navBreadcrumbs];
return [rootCrumb, ...navBreadcrumbs];
} else {
return [
homeBreadcrumb,
rootCrumb,
...navBreadcrumbs.slice(0, navBreadcrumbEndIndex),
...chromeBreadcrumbs.slice(chromeBreadcrumbStartIndex),
];
}
}

function buildRootCrumb({
projectsUrl,
projectName,
projectUrl,
}: {
projectsUrl?: string;
projectName?: string;
projectUrl?: string;
}): ChromeProjectBreadcrumb {
return {
text:
projectName ??
i18n.translate('core.ui.primaryNav.cloud.projectLabel', {
defaultMessage: 'Project',
}),
popoverContent: (
<EuiContextMenuPanel
size="s"
items={[
<EuiContextMenuItem key="project" href={projectUrl} icon={'gear'}>
<FormattedMessage
id="core.ui.primaryNav.cloud.linkToProject"
defaultMessage="Manage project"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem key="projects" href={projectsUrl} icon={'grid'}>
<FormattedMessage
id="core.ui.primaryNav.cloud.linkToAllProjects"
defaultMessage="View all projects"
/>
</EuiContextMenuItem>,
]}
/>
),
popoverProps: { panelPaddingSize: 'none' },
};
}

This file was deleted.

Expand Up @@ -86,12 +86,35 @@ describe('breadcrumbs', () => {
expect(breadcrumbs).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "breadcrumb-home",
"href": "/",
"text": <EuiIcon
type="home"
"popoverContent": <EuiContextMenuPanel
items={
Array [
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
}
size="s"
/>,
"title": "Home",
"popoverProps": Object {
"panelPaddingSize": "none",
},
"text": "Project",
},
Object {
"deepLinkId": "navItem1",
Expand Down Expand Up @@ -125,12 +148,35 @@ describe('breadcrumbs', () => {
expect(breadcrumbs).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "breadcrumb-home",
"href": "/",
"text": <EuiIcon
type="home"
"popoverContent": <EuiContextMenuPanel
items={
Array [
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
}
size="s"
/>,
"title": "Home",
"popoverProps": Object {
"panelPaddingSize": "none",
},
"text": "Project",
},
Object {
"href": "/custom1",
Expand Down Expand Up @@ -158,12 +204,35 @@ describe('breadcrumbs', () => {
expect(breadcrumbs).toMatchInlineSnapshot(`
Array [
Object {
"data-test-subj": "breadcrumb-home",
"href": "/",
"text": <EuiIcon
type="home"
"popoverContent": <EuiContextMenuPanel
items={
Array [
<EuiContextMenuItem
icon="gear"
>
<FormattedMessage
defaultMessage="Manage project"
id="core.ui.primaryNav.cloud.linkToProject"
values={Object {}}
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="grid"
>
<FormattedMessage
defaultMessage="View all projects"
id="core.ui.primaryNav.cloud.linkToAllProjects"
values={Object {}}
/>
</EuiContextMenuItem>,
]
}
size="s"
/>,
"title": "Home",
"popoverProps": Object {
"panelPaddingSize": "none",
},
"text": "Project",
},
Object {
"deepLinkId": "navItem1",
Expand Down
Expand Up @@ -48,6 +48,7 @@ export class ProjectNavigationService {
private projectHome$ = new BehaviorSubject<string | undefined>(undefined);
private projectsUrl$ = new BehaviorSubject<string | undefined>(undefined);
private projectName$ = new BehaviorSubject<string | undefined>(undefined);
private projectUrl$ = new BehaviorSubject<string | undefined>(undefined);
private projectNavigation$ = new BehaviorSubject<ChromeProjectNavigation | undefined>(undefined);
private activeNodes$ = new BehaviorSubject<ChromeProjectNavigationNode[][]>([]);
private projectNavigationNavTreeFlattened: Record<string, ChromeProjectNavigationNode> = {};
Expand Down Expand Up @@ -106,6 +107,9 @@ export class ProjectNavigationService {
getProjectName$: () => {
return this.projectName$.asObservable();
},
setProjectUrl: (projectUrl: string) => {
this.projectUrl$.next(projectUrl);
},
setProjectNavigation: (projectNavigation: ChromeProjectNavigation) => {
this.projectNavigation$.next(projectNavigation);
this.projectNavigationNavTreeFlattened = flattenNav(projectNavigation.navigationTree);
Expand Down Expand Up @@ -136,17 +140,30 @@ export class ProjectNavigationService {
return combineLatest([
this.projectBreadcrumbs$,
this.activeNodes$,
this.projectHome$.pipe(map((homeHref) => homeHref ?? '/')),
chromeBreadcrumbs$,
this.projectsUrl$,
this.projectUrl$,
this.projectName$,
]).pipe(
map(([projectBreadcrumbs, activeNodes, homeHref, chromeBreadcrumbs]) => {
return buildBreadcrumbs({
homeHref: this.http?.basePath.prepend?.(homeHref) ?? homeHref,
map(
([
projectBreadcrumbs,
activeNodes,
chromeBreadcrumbs,
});
})
projectsUrl,
projectUrl,
projectName,
]) => {
return buildBreadcrumbs({
projectUrl,
projectName,
projectsUrl,
projectBreadcrumbs,
activeNodes,
chromeBreadcrumbs,
});
}
)
);
},
};
Expand Down
Expand Up @@ -56,6 +56,12 @@ export interface InternalChromeStart extends ChromeStart {
*/
setProjectName(projectName: string): void;

/**
* Sets the project url.
* @param projectUrl
*/
setProjectUrl(projectUrl: string): void;

/**
* Sets the project navigation config to be used for rendering project navigation.
* It is used for default project sidenav, project breadcrumbs, tracking active deep link.
Expand Down

0 comments on commit a1bde01

Please sign in to comment.