[Search Sessions] Fix search session indicator in Amsterdam theme (#90153)

This commit is contained in:
Anton Dosov 2021-02-03 20:32:41 +01:00 committed by GitHub
parent 87e83860ce
commit c1ec73bea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 100 deletions

View file

@ -4271,55 +4271,6 @@ exports[`Header renders 1`] = `
"thrownError": null, "thrownError": null,
} }
} }
breadcrumbsAppendExtension$={
BehaviorSubject {
"_isScalar": false,
"_value": undefined,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": Array [
Subscriber {
"_parentOrParents": null,
"_subscriptions": Array [
SubjectSubscription {
"_parentOrParents": [Circular],
"_subscriptions": null,
"closed": false,
"subject": [Circular],
"subscriber": [Circular],
},
],
"closed": false,
"destination": SafeSubscriber {
"_complete": undefined,
"_context": [Circular],
"_error": undefined,
"_next": [Function],
"_parentOrParents": null,
"_parentSubscriber": [Circular],
"_subscriptions": null,
"closed": false,
"destination": Object {
"closed": true,
"complete": [Function],
"error": [Function],
"next": [Function],
},
"isStopped": false,
"syncErrorThrowable": false,
"syncErrorThrown": false,
"syncErrorValue": null,
},
"isStopped": false,
"syncErrorThrowable": true,
"syncErrorThrown": false,
"syncErrorValue": null,
},
],
"thrownError": null,
}
}
> >
<EuiHeaderBreadcrumbs <EuiHeaderBreadcrumbs
breadcrumbs={ breadcrumbs={

View file

@ -28,3 +28,18 @@
display: none; display: none;
} }
} }
.header__breadcrumbsWithExtensionContainer {
overflow: hidden; // enables text-ellipsis in the last breadcrumb
.euiHeaderBreadcrumbs {
// stop breadcrumbs from growing.
// this makes the extension appear right next to the last breadcrumb
flex-grow: 0;
margin-right: 0;
overflow: hidden; // enables text-ellipsis in the last breadcrumb
}
}
.header__breadcrumbsAppendExtension {
flex-grow: 1;
}

View file

@ -13,6 +13,7 @@ import { StubBrowserStorage, mountWithIntl } from '@kbn/test/jest';
import { httpServiceMock } from '../../../http/http_service.mock'; import { httpServiceMock } from '../../../http/http_service.mock';
import { applicationServiceMock } from '../../../mocks'; import { applicationServiceMock } from '../../../mocks';
import { Header } from './header'; import { Header } from './header';
import { ChromeBreadcrumbsAppendExtension } from '../../chrome_service';
jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({
htmlIdGenerator: () => () => 'mockId', htmlIdGenerator: () => () => 'mockId',
@ -71,6 +72,9 @@ describe('Header', () => {
const recentlyAccessed$ = new BehaviorSubject([ const recentlyAccessed$ = new BehaviorSubject([
{ link: '', label: 'dashboard', id: 'dashboard' }, { link: '', label: 'dashboard', id: 'dashboard' },
]); ]);
const breadcrumbsAppendExtension$ = new BehaviorSubject<
undefined | ChromeBreadcrumbsAppendExtension
>(undefined);
const component = mountWithIntl( const component = mountWithIntl(
<Header <Header
{...mockProps()} {...mockProps()}
@ -80,6 +84,7 @@ describe('Header', () => {
recentlyAccessed$={recentlyAccessed$} recentlyAccessed$={recentlyAccessed$}
isLocked$={isLocked$} isLocked$={isLocked$}
customNavLink$={customNavLink$} customNavLink$={customNavLink$}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$}
/> />
); );
expect(component.find('EuiHeader').exists()).toBeFalsy(); expect(component.find('EuiHeader').exists()).toBeFalsy();
@ -93,5 +98,19 @@ describe('Header', () => {
component.update(); component.update();
expect(component.find('nav[aria-label="Primary"]').exists()).toBeTruthy(); expect(component.find('nav[aria-label="Primary"]').exists()).toBeTruthy();
expect(component).toMatchSnapshot(); expect(component).toMatchSnapshot();
act(() =>
breadcrumbsAppendExtension$.next({
content: (root: HTMLDivElement) => {
root.innerHTML = '<div class="my-extension">__render__</div>';
return () => (root.innerHTML = '');
},
})
);
component.update();
expect(component.find('HeaderExtension').exists()).toBeTruthy();
expect(
component.find('HeaderExtension').getDOMNode().querySelector('.my-extension')
).toBeTruthy();
}); });
}); });

View file

@ -7,6 +7,7 @@
*/ */
import { import {
EuiFlexGroup,
EuiHeader, EuiHeader,
EuiHeaderSection, EuiHeaderSection,
EuiHeaderSectionItem, EuiHeaderSectionItem,
@ -40,6 +41,7 @@ import { HeaderHelpMenu } from './header_help_menu';
import { HeaderLogo } from './header_logo'; import { HeaderLogo } from './header_logo';
import { HeaderNavControls } from './header_nav_controls'; import { HeaderNavControls } from './header_nav_controls';
import { HeaderActionMenu } from './header_action_menu'; import { HeaderActionMenu } from './header_action_menu';
import { HeaderExtension } from './header_extension';
export interface HeaderProps { export interface HeaderProps {
kibanaVersion: string; kibanaVersion: string;
@ -73,11 +75,13 @@ export function Header({
basePath, basePath,
onIsLockedUpdate, onIsLockedUpdate,
homeHref, homeHref,
breadcrumbsAppendExtension$,
...observables ...observables
}: HeaderProps) { }: HeaderProps) {
const isVisible = useObservable(observables.isVisible$, false); const isVisible = useObservable(observables.isVisible$, false);
const isLocked = useObservable(observables.isLocked$, false); const isLocked = useObservable(observables.isLocked$, false);
const [isNavOpen, setIsNavOpen] = useState(false); const [isNavOpen, setIsNavOpen] = useState(false);
const breadcrumbsAppendExtension = useObservable(breadcrumbsAppendExtension$);
if (!isVisible) { if (!isVisible) {
return <LoadingIndicator loadingCount$={observables.loadingCount$} showAsBar />; return <LoadingIndicator loadingCount$={observables.loadingCount$} showAsBar />;
@ -87,6 +91,10 @@ export function Header({
const navId = htmlIdGenerator()(); const navId = htmlIdGenerator()();
const className = classnames('hide-for-sharing', 'headerGlobalNav'); const className = classnames('hide-for-sharing', 'headerGlobalNav');
const Breadcrumbs = (
<HeaderBreadcrumbs appTitle$={observables.appTitle$} breadcrumbs$={observables.breadcrumbs$} />
);
return ( return (
<> <>
<header className={className} data-test-subj="headerGlobalNav"> <header className={className} data-test-subj="headerGlobalNav">
@ -157,11 +165,23 @@ export function Header({
<HeaderNavControls side="left" navControls$={observables.navControlsLeft$} /> <HeaderNavControls side="left" navControls$={observables.navControlsLeft$} />
</EuiHeaderSection> </EuiHeaderSection>
<HeaderBreadcrumbs {!breadcrumbsAppendExtension ? (
appTitle$={observables.appTitle$} Breadcrumbs
breadcrumbs$={observables.breadcrumbs$} ) : (
breadcrumbsAppendExtension$={observables.breadcrumbsAppendExtension$} <EuiFlexGroup
/> responsive={false}
wrap={false}
alignItems={'center'}
className={'header__breadcrumbsWithExtensionContainer'}
gutterSize={'none'}
>
{Breadcrumbs}
<HeaderExtension
extension={breadcrumbsAppendExtension.content}
containerClassName={'header__breadcrumbsAppendExtension'}
/>
</EuiFlexGroup>
)}
<HeaderBadge badge$={observables.badge$} /> <HeaderBadge badge$={observables.badge$} />

View file

@ -11,17 +11,12 @@ import React from 'react';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { HeaderBreadcrumbs } from './header_breadcrumbs'; import { HeaderBreadcrumbs } from './header_breadcrumbs';
import { ChromeBreadcrumbsAppendExtension } from '../../chrome_service';
describe('HeaderBreadcrumbs', () => { describe('HeaderBreadcrumbs', () => {
it('renders updates to the breadcrumbs$ observable', () => { it('renders updates to the breadcrumbs$ observable', () => {
const breadcrumbs$ = new BehaviorSubject([{ text: 'First' }]); const breadcrumbs$ = new BehaviorSubject([{ text: 'First' }]);
const wrapper = mount( const wrapper = mount(
<HeaderBreadcrumbs <HeaderBreadcrumbs appTitle$={new BehaviorSubject('')} breadcrumbs$={breadcrumbs$} />
appTitle$={new BehaviorSubject('')}
breadcrumbs$={breadcrumbs$}
breadcrumbsAppendExtension$={new BehaviorSubject(undefined)}
/>
); );
expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot(); expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot();
@ -33,29 +28,4 @@ describe('HeaderBreadcrumbs', () => {
wrapper.update(); wrapper.update();
expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot(); expect(wrapper.find('.euiBreadcrumb')).toMatchSnapshot();
}); });
it('renders breadcrumbs extension', () => {
const breadcrumbs$ = new BehaviorSubject([{ text: 'First' }]);
const breadcrumbsAppendExtension$ = new BehaviorSubject<
undefined | ChromeBreadcrumbsAppendExtension
>({
content: (root: HTMLDivElement) => {
root.innerHTML = '<div class="my-extension">__render__</div>';
return () => (root.innerHTML = '');
},
});
const wrapper = mount(
<HeaderBreadcrumbs
appTitle$={new BehaviorSubject('')}
breadcrumbs$={breadcrumbs$}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$}
/>
);
expect(wrapper.find('.euiBreadcrumb').getDOMNode().querySelector('my-extension')).toBeDefined();
act(() => breadcrumbsAppendExtension$.next(undefined));
wrapper.update();
expect(wrapper.find('.euiBreadcrumb').getDOMNode().querySelector('my-extension')).toBeNull();
});
}); });

View file

@ -6,24 +6,21 @@
* Public License, v 1. * Public License, v 1.
*/ */
import { EuiFlexGroup, EuiHeaderBreadcrumbs } from '@elastic/eui'; import { EuiHeaderBreadcrumbs } from '@elastic/eui';
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react'; import React from 'react';
import useObservable from 'react-use/lib/useObservable'; import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ChromeBreadcrumb, ChromeBreadcrumbsAppendExtension } from '../../chrome_service'; import { ChromeBreadcrumb } from '../../chrome_service';
import { HeaderExtension } from './header_extension';
interface Props { interface Props {
appTitle$: Observable<string>; appTitle$: Observable<string>;
breadcrumbs$: Observable<ChromeBreadcrumb[]>; breadcrumbs$: Observable<ChromeBreadcrumb[]>;
breadcrumbsAppendExtension$: Observable<ChromeBreadcrumbsAppendExtension | undefined>;
} }
export function HeaderBreadcrumbs({ appTitle$, breadcrumbs$, breadcrumbsAppendExtension$ }: Props) { export function HeaderBreadcrumbs({ appTitle$, breadcrumbs$ }: Props) {
const appTitle = useObservable(appTitle$, 'Kibana'); const appTitle = useObservable(appTitle$, 'Kibana');
const breadcrumbs = useObservable(breadcrumbs$, []); const breadcrumbs = useObservable(breadcrumbs$, []);
const breadcrumbsAppendExtension = useObservable(breadcrumbsAppendExtension$);
let crumbs = breadcrumbs; let crumbs = breadcrumbs;
if (breadcrumbs.length === 0 && appTitle) { if (breadcrumbs.length === 0 && appTitle) {
@ -40,14 +37,5 @@ export function HeaderBreadcrumbs({ appTitle$, breadcrumbs$, breadcrumbsAppendEx
), ),
})); }));
if (breadcrumbsAppendExtension && crumbs[crumbs.length - 1]) {
const lastCrumb = crumbs[crumbs.length - 1];
lastCrumb.text = (
<EuiFlexGroup responsive={false} gutterSize={'none'} alignItems={'baseline'}>
<div className="eui-textTruncate">{lastCrumb.text}</div>
<HeaderExtension extension={breadcrumbsAppendExtension.content} />
</EuiFlexGroup>
);
}
return <EuiHeaderBreadcrumbs breadcrumbs={crumbs} max={10} data-test-subj="breadcrumbs" />; return <EuiHeaderBreadcrumbs breadcrumbs={crumbs} max={10} data-test-subj="breadcrumbs" />;
} }

View file

@ -12,6 +12,7 @@ import { MountPoint } from '../../../types';
interface Props { interface Props {
extension?: MountPoint<HTMLDivElement>; extension?: MountPoint<HTMLDivElement>;
display?: 'block' | 'inlineBlock'; display?: 'block' | 'inlineBlock';
containerClassName?: string;
} }
export class HeaderExtension extends React.Component<Props> { export class HeaderExtension extends React.Component<Props> {
@ -39,6 +40,7 @@ export class HeaderExtension extends React.Component<Props> {
return ( return (
<div <div
ref={this.ref} ref={this.ref}
className={this.props.containerClassName}
style={{ display: this.props.display === 'inlineBlock' ? 'inline-block' : undefined }} style={{ display: this.props.display === 'inlineBlock' ? 'inline-block' : undefined }}
/> />
); );