[Serverless] Show project name in the header (#166442)

> [!IMPORTANT]  
> I plan to merge this as an intermediate state. The next step is
changing the breadcrumbs component and make the project name as part of
it https://github.com/elastic/kibana/issues/166593


## Summary

close https://github.com/elastic/kibana/issues/166182
Shows project name in the Kibana header. 
To test locally add to the `config/serverless.yml`: 

```
xpack.cloud.serverless.project_id: "random"
xpack.cloud.serverless.project_name: "My Search Project"
```

![Screenshot 2023-09-14 at 13 01
44](4c658e19-5509-4a56-8752-a3a0f677d454)


I hardcoded 320px max-width to enable truncation for longer titles:
![Screenshot 2023-09-14 at 13 02
11](4cab9643-3eb2-4d50-a737-66bb98a46109)

In general, the header is not very flexible and has issues on smaller
screen, but this needs to be fixed separately.


The link still leads to the `/projects` page of the cloud UI
This commit is contained in:
Anton Dosov 2023-09-18 16:39:57 +02:00 committed by GitHub
parent 6369a78175
commit 05e0666d23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 40 additions and 2 deletions

View file

@ -278,6 +278,11 @@ export class ChromeService {
projectNavigation.setProjectsUrl(projectsUrl);
};
const setProjectName = (projectName: string) => {
validateChromeStyle();
projectNavigation.setProjectName(projectName);
};
const isIE = () => {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE '); // IE 10 or older
@ -371,6 +376,7 @@ export class ChromeService {
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
homeHref$={projectNavigation.getProjectHome$()}
projectsUrl$={projectNavigation.getProjectsUrl$()}
projectName$={projectNavigation.getProjectName$()}
docLinks={docLinks}
kibanaVersion={injectedMetadata.getKibanaVersion()}
prependBasePath={http.basePath.prepend}
@ -499,6 +505,7 @@ export class ChromeService {
project: {
setHome: setProjectHome,
setProjectsUrl,
setProjectName,
setNavigation: setProjectNavigation,
setSideNavComponent: setProjectSideNavComponent,
setBreadcrumbs: setProjectBreadcrumbs,

View file

@ -46,6 +46,7 @@ export class ProjectNavigationService {
}>({ current: null });
private projectHome$ = new BehaviorSubject<string | undefined>(undefined);
private projectsUrl$ = new BehaviorSubject<string | undefined>(undefined);
private projectName$ = new BehaviorSubject<string | undefined>(undefined);
private projectNavigation$ = new BehaviorSubject<ChromeProjectNavigation | undefined>(undefined);
private activeNodes$ = new BehaviorSubject<ChromeProjectNavigationNode[][]>([]);
private projectNavigationNavTreeFlattened: Record<string, ChromeProjectNavigationNode> = {};
@ -98,6 +99,12 @@ export class ProjectNavigationService {
getProjectsUrl$: () => {
return this.projectsUrl$.asObservable();
},
setProjectName: (projectName: string) => {
this.projectName$.next(projectName);
},
getProjectName$: () => {
return this.projectName$.asObservable();
},
setProjectNavigation: (projectNavigation: ChromeProjectNavigation) => {
this.projectNavigation$.next(projectNavigation);
this.projectNavigationNavTreeFlattened = flattenNav(projectNavigation.navigationTree);

View file

@ -50,6 +50,12 @@ export interface InternalChromeStart extends ChromeStart {
*/
setProjectsUrl(projectsUrl: string): void;
/**
* Sets the project name.
* @param projectName
*/
setProjectName(projectName: 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.

View file

@ -29,6 +29,7 @@ describe('Header', () => {
helpMenuLinks$: Rx.of([]),
homeHref$: Rx.of('app/home'),
projectsUrl$: Rx.of('/projects/'),
projectName$: Rx.of('My Project'),
kibanaVersion: '8.9',
loadingCount$: Rx.of(0),
navControlsLeft$: Rx.of([]),
@ -82,5 +83,6 @@ describe('Header', () => {
const projectsLink = await screen.getByTestId('projectsLink');
expect(projectsLink).toHaveAttribute('href', '/projects/');
expect(projectsLink).toHaveTextContent('My Project');
});
});

View file

@ -73,6 +73,12 @@ const getHeaderCss = ({ size }: EuiThemeComputed) => ({
padding-right: ${size.xs};
`,
},
projectName: {
link: css`
/* TODO: make header layout more flexible? */
max-width: 320px;
`,
},
});
type HeaderCss = ReturnType<typeof getHeaderCss>;
@ -107,6 +113,7 @@ export interface Props {
helpMenuLinks$: Observable<ChromeHelpMenuLink[]>;
homeHref$: Observable<string | undefined>;
projectsUrl$: Observable<string | undefined>;
projectName$: Observable<string | undefined>;
kibanaVersion: string;
application: InternalApplicationStart;
loadingCount$: ReturnType<HttpStart['getLoadingCount$']>;
@ -184,6 +191,7 @@ export const ProjectHeader = ({
const toggleCollapsibleNavRef = createRef<HTMLButtonElement & { euiAnimate: () => void }>();
const headerActionMenuMounter = useHeaderActionMenuMounter(observables.actionMenu$);
const projectsUrl = useObservable(observables.projectsUrl$);
const projectName = useObservable(observables.projectName$);
const { euiTheme } = useEuiTheme();
const headerCss = getHeaderCss(euiTheme);
const { logo: logoCss } = headerCss;
@ -246,8 +254,12 @@ export const ProjectHeader = ({
</EuiHeaderSectionItem>
<EuiHeaderSectionItem>
<EuiHeaderLink href={projectsUrl} data-test-subj={'projectsLink'}>
{headerStrings.cloud.linkToProjects}
<EuiHeaderLink
href={projectsUrl}
data-test-subj={'projectsLink'}
css={headerCss.projectName.link}
>
{projectName ?? headerStrings.cloud.linkToProjects}
</EuiHeaderLink>
</EuiHeaderSectionItem>

View file

@ -70,6 +70,7 @@ const createStartContractMock = () => {
project: {
setHome: jest.fn(),
setProjectsUrl: jest.fn(),
setProjectName: jest.fn(),
setNavigation: jest.fn(),
setSideNavComponent: jest.fn(),
setBreadcrumbs: jest.fn(),

View file

@ -68,6 +68,9 @@ export class ServerlessPlugin
if (dependencies.cloud.projectsUrl) {
project.setProjectsUrl(dependencies.cloud.projectsUrl);
}
if (dependencies.cloud.serverless.projectName) {
project.setProjectName(dependencies.cloud.serverless.projectName);
}
return {
setSideNavComponent: (sideNavigationComponent) =>