diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index cf0fb81f7407e7..3cc1c30877a893 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -398,6 +398,7 @@ export class ChromeService { globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$} actionMenu$={application.currentActionMenu$} breadcrumbs$={currentProjectBreadcrumbs$} + customBranding$={customBranding$} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))} helpMenuLinks$={helpMenuLinks$} diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts index f07ac64de38564..03e40d7678f026 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts @@ -114,9 +114,7 @@ export class ProjectNavigationService { ); return { - setProjectHome: (homeHref: string) => { - this.projectHome$.next(homeHref); - }, + setProjectHome: this.setProjectHome.bind(this), getProjectHome$: () => { return this.projectHome$.asObservable(); }, @@ -408,14 +406,25 @@ export class ProjectNavigationService { return; } - const { sideNavComponent } = definition; + const { sideNavComponent, homePage = '' } = definition; + const homePageLink = this.navLinksService?.get(homePage); + if (sideNavComponent) { this.setSideNavComponent(sideNavComponent); } + + if (homePageLink) { + this.setProjectHome(homePageLink.href); + } + this.initNavigation(nextId, definition.navigationTree$); }); } + private setProjectHome(homeHref: string) { + this.projectHome$.next(homeHref); + } + private goToSolutionHome(id: string) { const definitions = this.solutionNavDefinitions$.getValue(); const definition = definitions[id]; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx index 04e5773e176bdd..10a6c75d709beb 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx @@ -33,6 +33,7 @@ describe('Header', () => { navControlsLeft$: Rx.of([]), navControlsCenter$: Rx.of([]), navControlsRight$: Rx.of([]), + customBranding$: Rx.of({}), prependBasePath: (str) => `hello/world/${str}`, toggleSideNav: jest.fn(), }; @@ -46,5 +47,16 @@ describe('Header', () => { expect(await screen.findByTestId('euiCollapsibleNavButton')).toBeVisible(); expect(await screen.findByText('Hello, world!')).toBeVisible(); + expect(screen.queryByTestId(/customLogo/)).toBeNull(); + }); + + it('renders custom branding logo', async () => { + const { queryByTestId } = render( + + Hello, world! + + ); + + expect(queryByTestId(/customLogo/)).not.toBeNull(); }); }); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 77aef1053a8a24..637e87773cf7a8 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -14,6 +14,7 @@ import { EuiLoadingSpinner, useEuiTheme, EuiThemeComputed, + EuiImage, } from '@elastic/eui'; import { css } from '@emotion/react'; import type { InternalApplicationStart } from '@kbn/core-application-browser-internal'; @@ -33,7 +34,9 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Router } from '@kbn/shared-ux-router'; import React, { useCallback } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { debounceTime, Observable, of } from 'rxjs'; +import { debounceTime, Observable } from 'rxjs'; +import type { CustomBranding } from '@kbn/core-custom-branding-common'; + import { useHeaderActionMenuMounter } from '../header/header_action_menu'; import { Breadcrumbs } from './breadcrumbs'; import { HeaderHelpMenu } from '../header/header_help_menu'; @@ -46,11 +49,11 @@ import { ProjectNavigation } from './navigation'; const getHeaderCss = ({ size, colors }: EuiThemeComputed) => ({ logo: { container: css` - display: inline-block; + display: flex; + align-items: center; + justify-content: center; min-width: 56px; /* 56 = 40 + 8 + 8 */ - padding: 0 ${size.s}; cursor: pointer; - margin-left: -${size.s}; // to get equal spacing between .euiCollapsibleNavButtonWrapper, logo and breadcrumbs `, logo: css` min-width: 0; /* overrides min-width: 40px */ @@ -113,6 +116,7 @@ export interface Props { actionMenu$: Observable; docLinks: DocLinksStart; children: React.ReactNode; + customBranding$: Observable; globalHelpExtensionMenuLinks$: Observable; helpExtension$: Observable; helpSupportUrl$: Observable; @@ -130,46 +134,77 @@ export interface Props { const LOADING_DEBOUNCE_TIME = 80; -type LogoProps = Pick & { +type LogoProps = Pick< + Props, + 'application' | 'homeHref$' | 'loadingCount$' | 'prependBasePath' | 'customBranding$' +> & { logoCss: HeaderCss['logo']; }; -const Logo = (props: LogoProps) => { - const loadingCount = useObservable( - props.loadingCount$.pipe(debounceTime(LOADING_DEBOUNCE_TIME)), - 0 - ); - - const homeHref = useObservable(props.homeHref$, '/app/home'); +const Logo = ({ + loadingCount$, + homeHref$, + prependBasePath, + application, + logoCss, + customBranding$, +}: LogoProps) => { + const loadingCount = useObservable(loadingCount$.pipe(debounceTime(LOADING_DEBOUNCE_TIME)), 0); + const homeHref = useObservable(homeHref$, '/app/home'); + const customBranding = useObservable(customBranding$, {}); + const { logo } = customBranding; let fullHref: string | undefined; if (homeHref) { - fullHref = props.prependBasePath(homeHref); + fullHref = prependBasePath(homeHref); } const navigateHome = useCallback( (event: React.MouseEvent) => { if (fullHref) { - props.application.navigateToUrl(fullHref); + application.navigateToUrl(fullHref); } event.preventDefault(); }, - [fullHref, props.application] + [fullHref, application] ); + const renderLogo = () => { + if (logo) { + return ( + + + + ); + } + + return ( + + ); + }; + return ( - + {loadingCount === 0 ? ( - + renderLogo() ) : ( - + { const headerActionMenuMounter = useHeaderActionMenuMounter(observables.actionMenu$); @@ -200,7 +236,7 @@ export const ProjectHeader = ({ <> @@ -220,6 +256,7 @@ export const ProjectHeader = ({ application={application} homeHref$={observables.homeHref$} loadingCount$={observables.loadingCount$} + customBranding$={customBranding$} logoCss={logoCss} />