mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Stateful sidenav] Custom branding logo (#184035)
This commit is contained in:
parent
9bafd068e0
commit
e75440335f
4 changed files with 89 additions and 30 deletions
|
@ -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$}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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(
|
||||
<ProjectHeader {...mockProps} customBranding$={Rx.of({ logo: 'foo.jpg' })}>
|
||||
<EuiHeader>Hello, world!</EuiHeader>
|
||||
</ProjectHeader>
|
||||
);
|
||||
|
||||
expect(queryByTestId(/customLogo/)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<MountPoint | undefined>;
|
||||
docLinks: DocLinksStart;
|
||||
children: React.ReactNode;
|
||||
customBranding$: Observable<CustomBranding>;
|
||||
globalHelpExtensionMenuLinks$: Observable<ChromeGlobalHelpExtensionMenuLink[]>;
|
||||
helpExtension$: Observable<ChromeHelpExtension | undefined>;
|
||||
helpSupportUrl$: Observable<string>;
|
||||
|
@ -130,46 +134,77 @@ export interface Props {
|
|||
|
||||
const LOADING_DEBOUNCE_TIME = 80;
|
||||
|
||||
type LogoProps = Pick<Props, 'application' | 'homeHref$' | 'loadingCount$' | 'prependBasePath'> & {
|
||||
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 (
|
||||
<a href={fullHref} onClick={navigateHome} data-test-subj="globalLoadingIndicator-hidden">
|
||||
<EuiImage
|
||||
src={logo}
|
||||
css={logoCss}
|
||||
data-test-subj="globalLoadingIndicator-hidden customLogo"
|
||||
size={24}
|
||||
alt="logo"
|
||||
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.customLogoAriaLabel', {
|
||||
defaultMessage: 'User logo',
|
||||
})}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiHeaderLogo
|
||||
iconType="logoElastic"
|
||||
onClick={navigateHome}
|
||||
href={fullHref}
|
||||
css={logoCss}
|
||||
data-test-subj="globalLoadingIndicator-hidden"
|
||||
aria-label={headerStrings.logo.ariaLabel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<span css={props.logoCss.container} data-test-subj="nav-header-logo">
|
||||
<span css={logoCss.container} data-test-subj="nav-header-logo">
|
||||
{loadingCount === 0 ? (
|
||||
<EuiHeaderLogo
|
||||
iconType="logoElastic"
|
||||
onClick={navigateHome}
|
||||
href={fullHref}
|
||||
css={props.logoCss}
|
||||
data-test-subj="globalLoadingIndicator-hidden"
|
||||
aria-label={headerStrings.logo.ariaLabel}
|
||||
/>
|
||||
renderLogo()
|
||||
) : (
|
||||
<a onClick={navigateHome} href={fullHref} css={props.logoCss.spinner}>
|
||||
<a onClick={navigateHome} href={fullHref} css={logoCss.spinner}>
|
||||
<EuiLoadingSpinner
|
||||
size="l"
|
||||
aria-hidden={false}
|
||||
|
@ -189,6 +224,7 @@ export const ProjectHeader = ({
|
|||
prependBasePath,
|
||||
docLinks,
|
||||
toggleSideNav,
|
||||
customBranding$,
|
||||
...observables
|
||||
}: Props) => {
|
||||
const headerActionMenuMounter = useHeaderActionMenuMounter(observables.actionMenu$);
|
||||
|
@ -200,7 +236,7 @@ export const ProjectHeader = ({
|
|||
<>
|
||||
<ScreenReaderRouteAnnouncements
|
||||
breadcrumbs$={observables.breadcrumbs$}
|
||||
customBranding$={of()}
|
||||
customBranding$={customBranding$}
|
||||
appId$={application.currentAppId$}
|
||||
/>
|
||||
<SkipToMainContent />
|
||||
|
@ -220,6 +256,7 @@ export const ProjectHeader = ({
|
|||
application={application}
|
||||
homeHref$={observables.homeHref$}
|
||||
loadingCount$={observables.loadingCount$}
|
||||
customBranding$={customBranding$}
|
||||
logoCss={logoCss}
|
||||
/>
|
||||
</EuiHeaderSectionItem>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue