Skip to content

Commit

Permalink
Merge branch 'main' into fix-case-flyout-z-index
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykkopycinski authored May 2, 2023
2 parents 75bc5bc + 589ea3a commit ab3ddb6
Show file tree
Hide file tree
Showing 212 changed files with 4,932 additions and 1,615 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
import React from 'react';
import * as Rx from 'rxjs';
import { toArray } from 'rxjs/operators';
Expand All @@ -19,6 +19,7 @@ import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks';
import { getAppInfo } from '@kbn/core-application-browser-internal';
import { findTestSubject } from '@kbn/test-jest-helpers';
import { ChromeService } from './chrome_service';

class FakeApp implements App {
Expand Down Expand Up @@ -176,6 +177,41 @@ describe('start', () => {
// Don't capture the snapshot because it's 600+ lines long.
expect(shallow(React.createElement(() => chrome.getHeaderComponent()))).toBeDefined();
});

it('renders the default project side navigation', async () => {
const { chrome } = await start();

chrome.setChromeStyle('project');

const component = mount(chrome.getHeaderComponent());

const projectHeader = findTestSubject(component, 'kibanaProjectHeader');
expect(projectHeader.length).toBe(1);

const defaultProjectSideNav = findTestSubject(component, 'defaultProjectSideNav');
expect(defaultProjectSideNav.length).toBe(1);
});

it('renders the custom project side navigation', async () => {
const { chrome } = await start();

const MyNav = function MyNav() {
return <div data-test-subj="customProjectSideNav">HELLO</div>;
};
chrome.setChromeStyle('project');
chrome.project.setSideNavComponent(MyNav);

const component = mount(chrome.getHeaderComponent());

const projectHeader = findTestSubject(component, 'kibanaProjectHeader');
expect(projectHeader.length).toBe(1);

const defaultProjectSideNav = findTestSubject(component, 'defaultProjectSideNav');
expect(defaultProjectSideNav.length).toBe(0); // Default side nav not mounted

const customProjectSideNav = findTestSubject(component, 'customProjectSideNav');
expect(customProjectSideNav.text()).toBe('HELLO');
});
});

describe('visibility', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { BehaviorSubject, combineLatest, merge, type Observable, of, ReplaySubje
import { flatMap, map, takeUntil } from 'rxjs/operators';
import { parse } from 'url';
import { EuiLink } from '@elastic/eui';
import useObservable from 'react-use/lib/useObservable';
import type { InternalInjectedMetadataStart } from '@kbn/core-injected-metadata-browser-internal';
import type { DocLinksStart } from '@kbn/core-doc-links-browser';
import type { HttpStart } from '@kbn/core-http-browser';
Expand All @@ -27,14 +28,17 @@ import type {
ChromeHelpExtension,
ChromeUserBanner,
ChromeStyle,
ChromeProjectNavigation,
} from '@kbn/core-chrome-browser';
import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser';
import type { SideNavComponent as ISideNavComponent } from '@kbn/core-chrome-browser';
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
import { DocTitleService } from './doc_title';
import { NavControlsService } from './nav_controls';
import { NavLinksService } from './nav_links';
import { ProjectNavigationService } from './project_navigation';
import { RecentlyAccessedService } from './recently_accessed';
import { Header, ProjectHeader } from './ui';
import { Header, ProjectHeader, ProjectSideNavigation } from './ui';
import type { InternalChromeStart } from './types';

const IS_LOCKED_KEY = 'core.chrome.isLocked';
Expand Down Expand Up @@ -63,6 +67,7 @@ export class ChromeService {
private readonly navLinks = new NavLinksService();
private readonly recentlyAccessed = new RecentlyAccessedService();
private readonly docTitle = new DocTitleService();
private readonly projectNavigation = new ProjectNavigationService();

constructor(private readonly params: ConstructorParams) {}

Expand Down Expand Up @@ -147,6 +152,7 @@ export class ChromeService {

const navControls = this.navControls.start();
const navLinks = this.navLinks.start({ application, http });
const projectNavigation = this.projectNavigation.start({ application, navLinks });
const recentlyAccessed = await this.recentlyAccessed.start({ http });
const docTitle = this.docTitle.start({ document: window.document });
const { customBranding$ } = customBranding;
Expand All @@ -170,6 +176,14 @@ export class ChromeService {
chromeStyle$.next(style);
};

const setProjectSideNavComponent = (component: ISideNavComponent | null) => {
projectNavigation.setProjectSideNavComponent(component);
};

const setProjectNavigation = (config: ChromeProjectNavigation) => {
projectNavigation.setProjectNavigation(config);
};

const isIE = () => {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE '); // IE 10 or older
Expand Down Expand Up @@ -211,8 +225,28 @@ export class ChromeService {
}

const getHeaderComponent = () => {
const Component = ({ style$ }: { style$: typeof chromeStyle$ }) => {
if (style$.getValue() === 'project') {
if (chromeStyle$.getValue() === 'project') {
// const projectNavigationConfig = projectNavigation.getProjectNavigation$();
// TODO: Uncommented when we support the project navigation config
// if (!projectNavigationConfig) {
// throw new Erorr(`Project navigation config must be provided for project.`);
// }

const projectNavigationComponent$ = projectNavigation.getProjectSideNavComponent$();

const ProjectHeaderWithNavigation = () => {
const CustomSideNavComponent = useObservable(projectNavigationComponent$, undefined);

let SideNavComponent: ISideNavComponent = () => null;

if (CustomSideNavComponent !== undefined) {
// We have the state from the Observable
SideNavComponent =
CustomSideNavComponent.current !== null
? CustomSideNavComponent.current
: ProjectSideNavigation;
}

return (
<ProjectHeader
{...{
Expand All @@ -226,41 +260,45 @@ export class ChromeService {
navControlsRight$={navControls.getRight$()}
kibanaDocLink={docLinks.links.kibana.guide}
kibanaVersion={injectedMetadata.getKibanaVersion()}
/>
>
{/* TODO: pass down the SideNavCompProps once they are defined */}
<SideNavComponent />
</ProjectHeader>
);
}

return (
<Header
loadingCount$={http.getLoadingCount$()}
application={application}
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
badge$={badge$.pipe(takeUntil(this.stop$))}
basePath={http.basePath}
breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$.pipe(takeUntil(this.stop$))}
customNavLink$={customNavLink$.pipe(takeUntil(this.stop$))}
kibanaDocLink={docLinks.links.kibana.guide}
forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()}
globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))}
homeHref={http.basePath.prepend('/app/home')}
isVisible$={this.isVisible$}
kibanaVersion={injectedMetadata.getKibanaVersion()}
navLinks$={navLinks.getNavLinks$()}
recentlyAccessed$={recentlyAccessed.get$()}
navControlsLeft$={navControls.getLeft$()}
navControlsCenter$={navControls.getCenter$()}
navControlsRight$={navControls.getRight$()}
navControlsExtension$={navControls.getExtension$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
customBranding$={customBranding$}
/>
);
};
return <Component {...{ style$: chromeStyle$ }} />;
};

return <ProjectHeaderWithNavigation />;
}

return (
<Header
loadingCount$={http.getLoadingCount$()}
application={application}
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
badge$={badge$.pipe(takeUntil(this.stop$))}
basePath={http.basePath}
breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$.pipe(takeUntil(this.stop$))}
customNavLink$={customNavLink$.pipe(takeUntil(this.stop$))}
kibanaDocLink={docLinks.links.kibana.guide}
forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()}
globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))}
homeHref={http.basePath.prepend('/app/home')}
isVisible$={this.isVisible$}
kibanaVersion={injectedMetadata.getKibanaVersion()}
navLinks$={navLinks.getNavLinks$()}
recentlyAccessed$={recentlyAccessed.get$()}
navControlsLeft$={navControls.getLeft$()}
navControlsCenter$={navControls.getCenter$()}
navControlsRight$={navControls.getRight$()}
navControlsExtension$={navControls.getExtension$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
customBranding$={customBranding$}
/>
);
};

return {
Expand Down Expand Up @@ -335,6 +373,10 @@ export class ChromeService {
getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)),
setChromeStyle,
getChromeStyle$: () => chromeStyle$.pipe(takeUntil(this.stop$)),
project: {
setNavigation: setProjectNavigation,
setSideNavComponent: setProjectSideNavComponent,
},
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { ProjectNavigationService } from './project_navigation_service';
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import {
ChromeNavLinks,
ChromeProjectNavigation,
SideNavComponent,
} from '@kbn/core-chrome-browser';
import { BehaviorSubject } from 'rxjs';

interface StartDeps {
application: InternalApplicationStart;
navLinks: ChromeNavLinks;
}

export class ProjectNavigationService {
private customProjectSideNavComponent$ = new BehaviorSubject<{
current: SideNavComponent | null;
}>({ current: null });
private projectNavigation$ = new BehaviorSubject<ChromeProjectNavigation | undefined>(undefined);

public start({ application, navLinks }: StartDeps) {
// TODO: use application, navLink and projectNavigation$ to:
// 1. validate projectNavigation$ against navLinks,
// 2. filter disabled/missing links from projectNavigation
// 3. keep track of currently active link / path (path will be used to highlight the link in the sidenav and display part of the breadcrumbs)

return {
setProjectNavigation: (projectNavigation: ChromeProjectNavigation) => {
this.projectNavigation$.next(projectNavigation);
},
getProjectNavigation$: () => {
return this.projectNavigation$.asObservable();
},
setProjectSideNavComponent: (component: SideNavComponent | null) => {
this.customProjectSideNavComponent$.next({ current: component });
},
getProjectSideNavComponent$: () => {
return this.customProjectSideNavComponent$.asObservable();
},
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
*/

export { Header } from './header';
export { ProjectHeader } from './project';
export { ProjectHeader, SideNavigation as ProjectSideNavigation } from './project';
export { LoadingIndicator } from './loading_indicator';
export type { NavType } from './header';
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ interface Props {
kibanaVersion: string;
application: InternalApplicationStart;
navControlsRight$: Observable<ChromeNavControl[]>;
children: React.ReactNode;
}

export const ProjectHeader = ({
application,
kibanaDocLink,
kibanaVersion,
children,
...observables
}: Props) => {
const renderLogo = () => (
Expand All @@ -53,7 +55,7 @@ export const ProjectHeader = ({

return (
<>
<EuiHeader position="fixed">
<EuiHeader position="fixed" data-test-subj="kibanaProjectHeader">
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem border="right">{renderLogo()}</EuiHeaderSectionItem>
<EuiHeaderSectionItem>
Expand Down Expand Up @@ -81,9 +83,7 @@ export const ProjectHeader = ({
</EuiHeaderSection>
</EuiHeader>
<Router history={application.history}>
<ProjectNavigation>
<span />
</ProjectNavigation>
<ProjectNavigation>{children}</ProjectNavigation>
</Router>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
*/

export { ProjectHeader } from './header';
export { SideNavigation } from './side_navigation';
Loading

0 comments on commit ab3ddb6

Please sign in to comment.