mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
New nav (#64018)
Notably, also removes aria-hidden from many visualizations
This commit is contained in:
parent
9d88805a95
commit
35e10273d3
125 changed files with 5748 additions and 691 deletions
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppCategory](./kibana-plugin-core-public.appcategory.md) > [id](./kibana-plugin-core-public.appcategory.id.md)
|
||||
|
||||
## AppCategory.id property
|
||||
|
||||
Unique identifier for the categories
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
id: string;
|
||||
```
|
|
@ -18,6 +18,7 @@ export interface AppCategory
|
|||
| --- | --- | --- |
|
||||
| [ariaLabel](./kibana-plugin-core-public.appcategory.arialabel.md) | <code>string</code> | If the visual label isn't appropriate for screen readers, can override it here |
|
||||
| [euiIconType](./kibana-plugin-core-public.appcategory.euiicontype.md) | <code>string</code> | Define an icon to be used for the category If the category is only 1 item, and no icon is defined, will default to the product icon Defaults to initials if no icon is defined |
|
||||
| [id](./kibana-plugin-core-public.appcategory.id.md) | <code>string</code> | Unique identifier for the categories |
|
||||
| [label](./kibana-plugin-core-public.appcategory.label.md) | <code>string</code> | Label used for cateogry name. Also used as aria-label if one isn't set. |
|
||||
| [order](./kibana-plugin-core-public.appcategory.order.md) | <code>number</code> | The order that categories will be sorted in Prefer large steps between categories to allow for further editing (Default categories are in steps of 1000) |
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getNavType$](./kibana-plugin-core-public.chromestart.getnavtype_.md)
|
||||
|
||||
## ChromeStart.getNavType$() method
|
||||
|
||||
Get the navigation type TODO \#64541 Can delete
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getNavType$(): Observable<NavType>;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
`Observable<NavType>`
|
||||
|
|
@ -58,6 +58,7 @@ core.chrome.setHelpExtension(elem => {
|
|||
| [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent |
|
||||
| [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. |
|
||||
| [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. |
|
||||
| [getNavType$()](./kibana-plugin-core-public.chromestart.getnavtype_.md) | Get the navigation type TODO \#64541 Can delete |
|
||||
| [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with <code>addApplicationClass()</code>. If className is unknown it is ignored. |
|
||||
| [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title |
|
||||
| [setBadge(badge)](./kibana-plugin-core-public.chromestart.setbadge.md) | Override the current badge |
|
||||
|
|
|
@ -158,6 +158,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [IContextProvider](./kibana-plugin-core-public.icontextprovider.md) | A function that returns a context value for a specific key of given context type. |
|
||||
| [IToasts](./kibana-plugin-core-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-core-public.toastsapi.md)<!-- -->. |
|
||||
| [MountPoint](./kibana-plugin-core-public.mountpoint.md) | A function that should mount DOM content inside the provided container element and return a handler to unmount it. |
|
||||
| [NavType](./kibana-plugin-core-public.navtype.md) | |
|
||||
| [PluginInitializer](./kibana-plugin-core-public.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>public</code> directory should conform to this interface. |
|
||||
| [PluginOpaqueId](./kibana-plugin-core-public.pluginopaqueid.md) | |
|
||||
| [PublicUiSettingsParams](./kibana-plugin-core-public.publicuisettingsparams.md) | A sub-set of [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) exposed to the client-side. |
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [NavType](./kibana-plugin-core-public.navtype.md)
|
||||
|
||||
## NavType type
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type NavType = 'modern' | 'legacy';
|
||||
```
|
|
@ -68,6 +68,9 @@ into the document when displaying it.
|
|||
`metrics:max_buckets`:: The maximum numbers of buckets that a single
|
||||
data source can return. This might arise when the user selects a
|
||||
short interval (for example, 1s) for a long time period (1 year).
|
||||
`pageNavigation`:: The style of navigation menu for Kibana.
|
||||
Choices are Legacy, the legacy style where every plugin is represented in the nav,
|
||||
and Modern, a new format that bundles related plugins together in flyaway nested navigation.
|
||||
`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character
|
||||
in a query clause. Only applies when experimental query features are
|
||||
enabled in the query bar. To disallow leading wildcards in Lucene queries,
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
"@elastic/charts": "19.2.0",
|
||||
"@elastic/datemath": "5.0.3",
|
||||
"@elastic/ems-client": "7.8.0",
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"@elastic/filesaver": "1.1.2",
|
||||
"@elastic/good": "8.1.1-kibana2",
|
||||
"@elastic/numeral": "2.4.0",
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@elastic/charts": "19.2.0",
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"@kbn/i18n": "1.0.0",
|
||||
"abortcontroller-polyfill": "^1.4.0",
|
||||
"angular": "^1.7.9",
|
||||
|
|
|
@ -23,7 +23,8 @@ import {
|
|||
ChromeBreadcrumb,
|
||||
ChromeService,
|
||||
InternalChromeStart,
|
||||
} from './chrome_service';
|
||||
NavType,
|
||||
} from './';
|
||||
|
||||
const createStartContractMock = () => {
|
||||
const startContract: DeeplyMockedKeys<InternalChromeStart> = {
|
||||
|
@ -72,6 +73,7 @@ const createStartContractMock = () => {
|
|||
setHelpExtension: jest.fn(),
|
||||
setHelpSupportUrl: jest.fn(),
|
||||
getIsNavDrawerLocked$: jest.fn(),
|
||||
getNavType$: jest.fn(),
|
||||
};
|
||||
startContract.navLinks.getAll.mockReturnValue([]);
|
||||
startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as ChromeBrand));
|
||||
|
@ -81,6 +83,7 @@ const createStartContractMock = () => {
|
|||
startContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as ChromeBreadcrumb]));
|
||||
startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined));
|
||||
startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false));
|
||||
startContract.getNavType$.mockReturnValue(new BehaviorSubject('modern' as NavType));
|
||||
return startContract;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,18 +17,18 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { take, toArray } from 'rxjs/operators';
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { take, toArray } from 'rxjs/operators';
|
||||
import { App } from '../application';
|
||||
import { applicationServiceMock } from '../application/application_service.mock';
|
||||
import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
|
||||
import { httpServiceMock } from '../http/http_service.mock';
|
||||
import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock';
|
||||
import { notificationServiceMock } from '../notifications/notifications_service.mock';
|
||||
import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
|
||||
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
|
||||
import { ChromeService } from './chrome_service';
|
||||
import { App } from '../application';
|
||||
|
||||
class FakeApp implements App {
|
||||
public title = `${this.id} App`;
|
||||
|
@ -51,6 +51,7 @@ function defaultStartDeps(availableApps?: App[]) {
|
|||
http: httpServiceMock.createStartContract(),
|
||||
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
};
|
||||
|
||||
if (availableApps) {
|
||||
|
|
|
@ -17,27 +17,26 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Breadcrumb as EuiBreadcrumb, IconType } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { BehaviorSubject, Observable, ReplaySubject, combineLatest, of, merge } from 'rxjs';
|
||||
import { BehaviorSubject, combineLatest, merge, Observable, of, ReplaySubject } from 'rxjs';
|
||||
import { flatMap, map, takeUntil } from 'rxjs/operators';
|
||||
import { parse } from 'url';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IconType, Breadcrumb as EuiBreadcrumb } from '@elastic/eui';
|
||||
|
||||
import { InternalApplicationStart } from '../application';
|
||||
import { DocLinksStart } from '../doc_links';
|
||||
import { HttpStart } from '../http';
|
||||
import { InjectedMetadataStart } from '../injected_metadata';
|
||||
import { NotificationsStart } from '../notifications';
|
||||
import { InternalApplicationStart } from '../application';
|
||||
import { HttpStart } from '../http';
|
||||
|
||||
import { IUiSettingsClient } from '../ui_settings';
|
||||
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
|
||||
import { ChromeDocTitle, DocTitleService } from './doc_title';
|
||||
import { ChromeNavControls, NavControlsService } from './nav_controls';
|
||||
import { ChromeNavLinks, NavLinksService } from './nav_links';
|
||||
import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed';
|
||||
import { NavControlsService, ChromeNavControls } from './nav_controls';
|
||||
import { DocTitleService, ChromeDocTitle } from './doc_title';
|
||||
import { LoadingIndicator, Header } from './ui';
|
||||
import { DocLinksStart } from '../doc_links';
|
||||
import { Header, LoadingIndicator } from './ui';
|
||||
import { NavType } from './ui/header';
|
||||
import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu';
|
||||
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
|
||||
export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle };
|
||||
|
||||
const IS_LOCKED_KEY = 'core.chrome.isLocked';
|
||||
|
@ -84,6 +83,7 @@ interface StartDeps {
|
|||
http: HttpStart;
|
||||
injectedMetadata: InjectedMetadataStart;
|
||||
notifications: NotificationsStart;
|
||||
uiSettings: IUiSettingsClient;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -136,6 +136,7 @@ export class ChromeService {
|
|||
http,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
uiSettings,
|
||||
}: StartDeps): Promise<InternalChromeStart> {
|
||||
this.initVisibility(application);
|
||||
|
||||
|
@ -160,6 +161,10 @@ export class ChromeService {
|
|||
|
||||
const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$));
|
||||
|
||||
// TODO #64541
|
||||
// Can delete
|
||||
const getNavType$ = uiSettings.get$('pageNavigation').pipe(takeUntil(this.stop$));
|
||||
|
||||
if (!this.params.browserSupportsCsp && injectedMetadata.getCspConfig().warnLegacyBrowsers) {
|
||||
notifications.toasts.addWarning(
|
||||
i18n.translate('core.chrome.legacyBrowserWarning', {
|
||||
|
@ -197,6 +202,7 @@ export class ChromeService {
|
|||
navControlsRight$={navControls.getRight$()}
|
||||
onIsLockedUpdate={setIsNavDrawerLocked}
|
||||
isLocked$={getIsNavDrawerLocked$}
|
||||
navType$={getNavType$}
|
||||
/>
|
||||
</React.Fragment>
|
||||
),
|
||||
|
@ -257,6 +263,8 @@ export class ChromeService {
|
|||
setHelpSupportUrl: (url: string) => helpSupportUrl$.next(url),
|
||||
|
||||
getIsNavDrawerLocked$: () => getIsNavDrawerLocked$,
|
||||
|
||||
getNavType$: () => getNavType$,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -403,6 +411,13 @@ export interface ChromeStart {
|
|||
* Get an observable of the current locked state of the nav drawer.
|
||||
*/
|
||||
getIsNavDrawerLocked$(): Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Get the navigation type
|
||||
* TODO #64541
|
||||
* Can delete
|
||||
*/
|
||||
getNavType$(): Observable<NavType>;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
|
|
@ -33,6 +33,7 @@ export {
|
|||
ChromeHelpExtensionMenuDocumentationLink,
|
||||
ChromeHelpExtensionMenuGitHubLink,
|
||||
} from './ui/header/header_help_menu';
|
||||
export { NavType } from './ui';
|
||||
export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links';
|
||||
export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed';
|
||||
export { ChromeNavControl, ChromeNavControls } from './nav_controls';
|
||||
|
|
4506
src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap
generated
Normal file
4506
src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,25 +1,12 @@
|
|||
@import '@elastic/eui/src/components/header/variables';
|
||||
@import '@elastic/eui/src/components/nav_drawer/variables';
|
||||
|
||||
.chrHeaderWrapper {
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
.chrHeaderWrapper:not(.headerWrapper) {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) {
|
||||
top: $euiHeaderChildSize;
|
||||
left: $euiHeaderChildSize;
|
||||
|
||||
// HOTFIX: Temporary fix for flyouts not inside portals
|
||||
// SASSTODO: Find an actual solution
|
||||
.euiFlyout {
|
||||
top: $euiHeaderChildSize;
|
||||
height: calc(100% - #{$euiHeaderChildSize});
|
||||
}
|
||||
}
|
||||
|
||||
.chrHeaderHelpMenu__version {
|
||||
text-transform: none;
|
||||
}
|
||||
|
@ -29,19 +16,8 @@
|
|||
margin-right: $euiSize;
|
||||
}
|
||||
|
||||
// Mobile header is smaller
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
.chrHeaderWrapper ~ .app-wrapper:not(.hidden-chrome) {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xl') {
|
||||
.chrHeaderWrapper--navIsLocked {
|
||||
~ .app-wrapper:not(.hidden-chrome) {
|
||||
// Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS)
|
||||
left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important
|
||||
transition: left $euiAnimSpeedFast $euiAnimSlightResistance;
|
||||
}
|
||||
.header__toggleNavButtonSection {
|
||||
.euiBody--collapsibleNavIsDocked & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
136
src/core/public/chrome/ui/header/collapsible_nav.test.tsx
Normal file
136
src/core/public/chrome/ui/header/collapsible_nav.test.tsx
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { CollapsibleNav } from './collapsible_nav';
|
||||
import { AppCategory } from '../../../../types';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../..';
|
||||
import { StubBrowserStorage } from 'test_utils/stub_browser_storage';
|
||||
|
||||
jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({
|
||||
htmlIdGenerator: () => () => 'mockId',
|
||||
}));
|
||||
|
||||
const { kibana, observability, security, management } = DEFAULT_APP_CATEGORIES;
|
||||
|
||||
function mockLink(label: string, category?: AppCategory) {
|
||||
return {
|
||||
key: label,
|
||||
label,
|
||||
href: label,
|
||||
isActive: true,
|
||||
onClick: () => {},
|
||||
category,
|
||||
'data-test-subj': label,
|
||||
};
|
||||
}
|
||||
|
||||
function mockRecentNavLink(label: string) {
|
||||
return {
|
||||
href: label,
|
||||
label,
|
||||
title: label,
|
||||
'aria-label': label,
|
||||
};
|
||||
}
|
||||
|
||||
function mockProps() {
|
||||
return {
|
||||
id: 'collapsible-nav',
|
||||
homeHref: '/',
|
||||
isLocked: false,
|
||||
isOpen: false,
|
||||
navLinks: [],
|
||||
recentNavLinks: [],
|
||||
storage: new StubBrowserStorage(),
|
||||
onIsOpenUpdate: () => {},
|
||||
onIsLockedUpdate: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
describe('CollapsibleNav', () => {
|
||||
// this test is mostly an "EUI works as expected" sanity check
|
||||
it('renders the default nav', () => {
|
||||
const onLock = sinon.spy();
|
||||
const component = mount(<CollapsibleNav {...mockProps()} onIsLockedUpdate={onLock} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
component.setProps({ isOpen: true });
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
component.setProps({ isLocked: true });
|
||||
expect(component).toMatchSnapshot();
|
||||
|
||||
// limit the find to buttons because jest also renders data-test-subj on a JSX wrapper element
|
||||
component.find('button[data-test-subj="collapsible-nav-lock"]').simulate('click');
|
||||
expect(onLock.callCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('renders links grouped by category', () => {
|
||||
// just a test of category functionality, categories are not accurate
|
||||
const navLinks = [
|
||||
mockLink('discover', kibana),
|
||||
mockLink('siem', security),
|
||||
mockLink('metrics', observability),
|
||||
mockLink('monitoring', management),
|
||||
mockLink('visualize', kibana),
|
||||
mockLink('dashboard', kibana),
|
||||
mockLink('canvas'), // links should be able to be rendered top level as well
|
||||
mockLink('logs', observability),
|
||||
];
|
||||
const recentNavLinks = [mockRecentNavLink('recent 1'), mockRecentNavLink('recent 2')];
|
||||
const component = mount(
|
||||
<CollapsibleNav
|
||||
{...mockProps()}
|
||||
isOpen={true}
|
||||
navLinks={navLinks}
|
||||
recentNavLinks={recentNavLinks}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('remembers collapsible section state', () => {
|
||||
function expectNavLinksCount(component: ReactWrapper, count: number) {
|
||||
expect(
|
||||
component.find('.euiAccordion-isOpen a[data-test-subj="collapsibleNavAppLink"]').length
|
||||
).toEqual(count);
|
||||
}
|
||||
|
||||
const navLinks = [
|
||||
mockLink('discover', kibana),
|
||||
mockLink('siem', security),
|
||||
mockLink('metrics', observability),
|
||||
mockLink('monitoring', management),
|
||||
mockLink('visualize', kibana),
|
||||
mockLink('dashboard', kibana),
|
||||
mockLink('logs', observability),
|
||||
];
|
||||
const component = mount(<CollapsibleNav {...mockProps()} isOpen={true} navLinks={navLinks} />);
|
||||
expectNavLinksCount(component, 7);
|
||||
component.find('[data-test-subj="collapsibleNavGroup-kibana"] button').simulate('click');
|
||||
expectNavLinksCount(component, 4);
|
||||
component.setProps({ isOpen: false });
|
||||
expectNavLinksCount(component, 0); // double check the nav closed
|
||||
component.setProps({ isOpen: true });
|
||||
expectNavLinksCount(component, 4);
|
||||
});
|
||||
});
|
281
src/core/public/chrome/ui/header/collapsible_nav.tsx
Normal file
281
src/core/public/chrome/ui/header/collapsible_nav.tsx
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiCollapsibleNav,
|
||||
EuiCollapsibleNavGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiListGroup,
|
||||
EuiListGroupItem,
|
||||
EuiShowFor,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { groupBy, sortBy } from 'lodash';
|
||||
import React, { useRef } from 'react';
|
||||
import { AppCategory } from '../../../../types';
|
||||
import { OnIsLockedUpdate } from './';
|
||||
import { NavLink, RecentNavLink } from './nav_link';
|
||||
|
||||
function getAllCategories(allCategorizedLinks: Record<string, NavLink[]>) {
|
||||
const allCategories = {} as Record<string, AppCategory | undefined>;
|
||||
|
||||
for (const [key, value] of Object.entries(allCategorizedLinks)) {
|
||||
allCategories[key] = value[0].category;
|
||||
}
|
||||
|
||||
return allCategories;
|
||||
}
|
||||
|
||||
function getOrderedCategories(
|
||||
mainCategories: Record<string, NavLink[]>,
|
||||
categoryDictionary: ReturnType<typeof getAllCategories>
|
||||
) {
|
||||
return sortBy(
|
||||
Object.keys(mainCategories),
|
||||
categoryName => categoryDictionary[categoryName]?.order
|
||||
);
|
||||
}
|
||||
|
||||
function getCategoryLocalStorageKey(id: string) {
|
||||
return `core.navGroup.${id}`;
|
||||
}
|
||||
|
||||
function getIsCategoryOpen(id: string, storage: Storage) {
|
||||
const value = storage.getItem(getCategoryLocalStorageKey(id)) ?? 'true';
|
||||
|
||||
return value === 'true';
|
||||
}
|
||||
|
||||
function setIsCategoryOpen(id: string, isOpen: boolean, storage: Storage) {
|
||||
storage.setItem(getCategoryLocalStorageKey(id), `${isOpen}`);
|
||||
}
|
||||
|
||||
interface Props {
|
||||
isLocked: boolean;
|
||||
isOpen: boolean;
|
||||
navLinks: NavLink[];
|
||||
recentNavLinks: RecentNavLink[];
|
||||
homeHref: string;
|
||||
id: string;
|
||||
storage?: Storage;
|
||||
onIsLockedUpdate: OnIsLockedUpdate;
|
||||
onIsOpenUpdate: (isOpen?: boolean) => void;
|
||||
}
|
||||
|
||||
export function CollapsibleNav({
|
||||
isLocked,
|
||||
isOpen,
|
||||
navLinks,
|
||||
recentNavLinks,
|
||||
onIsLockedUpdate,
|
||||
onIsOpenUpdate,
|
||||
homeHref,
|
||||
id,
|
||||
storage = window.localStorage,
|
||||
}: Props) {
|
||||
const lockRef = useRef<HTMLButtonElement>(null);
|
||||
const groupedNavLinks = groupBy(navLinks, link => link?.category?.id);
|
||||
const { undefined: unknowns = [], ...allCategorizedLinks } = groupedNavLinks;
|
||||
const categoryDictionary = getAllCategories(allCategorizedLinks);
|
||||
const orderedCategories = getOrderedCategories(allCategorizedLinks, categoryDictionary);
|
||||
|
||||
return (
|
||||
<EuiCollapsibleNav
|
||||
data-test-subj="collapsibleNav"
|
||||
id={id}
|
||||
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
|
||||
defaultMessage: 'Primary',
|
||||
})}
|
||||
isOpen={isOpen}
|
||||
isDocked={isLocked}
|
||||
onClose={onIsOpenUpdate}
|
||||
>
|
||||
{/* Pinned items */}
|
||||
<EuiFlexItem grow={false} style={{ flexShrink: 0 }}>
|
||||
<EuiCollapsibleNavGroup
|
||||
background="light"
|
||||
className="eui-yScroll"
|
||||
style={{ maxHeight: '40vh' }}
|
||||
>
|
||||
<EuiListGroup
|
||||
aria-label={i18n.translate('core.ui.primaryNav.pinnedLinksAriaLabel', {
|
||||
defaultMessage: 'Pinned links',
|
||||
})}
|
||||
listItems={[
|
||||
{
|
||||
label: 'Home',
|
||||
iconType: 'home',
|
||||
href: homeHref,
|
||||
onClick: () => onIsOpenUpdate(false),
|
||||
},
|
||||
]}
|
||||
maxWidth="none"
|
||||
color="text"
|
||||
gutterSize="none"
|
||||
size="s"
|
||||
/>
|
||||
</EuiCollapsibleNavGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiHorizontalRule margin="none" />
|
||||
|
||||
<EuiFlexItem className="eui-yScroll">
|
||||
{/* Recently viewed */}
|
||||
<EuiCollapsibleNavGroup
|
||||
key="recentlyViewed"
|
||||
title={i18n.translate('core.ui.recentlyViewed', { defaultMessage: 'Recently viewed' })}
|
||||
isCollapsible={true}
|
||||
initialIsOpen={getIsCategoryOpen('recentlyViewed', storage)}
|
||||
onToggle={isCategoryOpen => setIsCategoryOpen('recentlyViewed', isCategoryOpen, storage)}
|
||||
>
|
||||
{recentNavLinks.length > 0 ? (
|
||||
<EuiListGroup
|
||||
aria-label={i18n.translate('core.ui.recentlyViewedAriaLabel', {
|
||||
defaultMessage: 'Recently viewed links',
|
||||
})}
|
||||
listItems={recentNavLinks.map(link => {
|
||||
// TODO #64541
|
||||
// Can remove icon from recent links completely
|
||||
const { iconType, ...linkWithoutIcon } = link;
|
||||
return linkWithoutIcon;
|
||||
})}
|
||||
maxWidth="none"
|
||||
color="subdued"
|
||||
gutterSize="none"
|
||||
size="s"
|
||||
/>
|
||||
) : (
|
||||
<EuiText size="s" color="subdued" style={{ padding: '0 8px 8px' }}>
|
||||
<p>
|
||||
{i18n.translate('core.ui.EmptyRecentlyViewed', {
|
||||
defaultMessage: 'No recently viewed items',
|
||||
})}
|
||||
</p>
|
||||
</EuiText>
|
||||
)}
|
||||
</EuiCollapsibleNavGroup>
|
||||
|
||||
{/* Kibana, Observability, Security, and Management sections */}
|
||||
{orderedCategories.map((categoryName, i) => {
|
||||
const category = categoryDictionary[categoryName]!;
|
||||
const links = allCategorizedLinks[categoryName].map(
|
||||
({ label, href, isActive, isDisabled, onClick }: NavLink) => ({
|
||||
label,
|
||||
href,
|
||||
isActive,
|
||||
isDisabled,
|
||||
'data-test-subj': 'collapsibleNavAppLink',
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
onIsOpenUpdate(false);
|
||||
onClick(e);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiCollapsibleNavGroup
|
||||
key={category.id}
|
||||
iconType={category.euiIconType}
|
||||
title={category.label}
|
||||
isCollapsible={true}
|
||||
initialIsOpen={getIsCategoryOpen(category.id, storage)}
|
||||
onToggle={isCategoryOpen => setIsCategoryOpen(category.id, isCategoryOpen, storage)}
|
||||
data-test-subj={`collapsibleNavGroup-${category.id}`}
|
||||
>
|
||||
<EuiListGroup
|
||||
aria-label={i18n.translate('core.ui.primaryNavSection.screenReaderLabel', {
|
||||
defaultMessage: 'Primary navigation links, {category}',
|
||||
values: { category: category.label },
|
||||
})}
|
||||
listItems={links}
|
||||
maxWidth="none"
|
||||
color="subdued"
|
||||
gutterSize="none"
|
||||
size="s"
|
||||
/>
|
||||
</EuiCollapsibleNavGroup>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Things with no category (largely for custom plugins) */}
|
||||
{unknowns.map(({ label, href, icon, isActive, isDisabled, onClick }, i) => (
|
||||
<EuiCollapsibleNavGroup key={i}>
|
||||
<EuiListGroup flush>
|
||||
<EuiListGroupItem
|
||||
color="text"
|
||||
size="s"
|
||||
label={label}
|
||||
href={href}
|
||||
icon={icon}
|
||||
isActive={isActive}
|
||||
isDisabled={isDisabled}
|
||||
data-test-subj="collapsibleNavAppLink"
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
onIsOpenUpdate(false);
|
||||
onClick(e);
|
||||
}}
|
||||
/>
|
||||
</EuiListGroup>
|
||||
</EuiCollapsibleNavGroup>
|
||||
))}
|
||||
|
||||
{/* Docking button only for larger screens that can support it*/}
|
||||
<EuiShowFor sizes={['l', 'xl']}>
|
||||
<EuiCollapsibleNavGroup>
|
||||
<EuiListGroup flush>
|
||||
<EuiListGroupItem
|
||||
data-test-subj="collapsible-nav-lock"
|
||||
buttonRef={lockRef}
|
||||
size="xs"
|
||||
color="subdued"
|
||||
label={
|
||||
isLocked
|
||||
? i18n.translate('core.ui.primaryNavSection.undockLabel', {
|
||||
defaultMessage: 'Undock navigation',
|
||||
})
|
||||
: i18n.translate('core.ui.primaryNavSection.dockLabel', {
|
||||
defaultMessage: 'Dock navigation',
|
||||
})
|
||||
}
|
||||
aria-label={
|
||||
isLocked
|
||||
? i18n.translate('core.ui.primaryNavSection.undockAriaLabel', {
|
||||
defaultMessage: 'Undock primary navigation',
|
||||
})
|
||||
: i18n.translate('core.ui.primaryNavSection.dockAriaLabel', {
|
||||
defaultMessage: 'Dock primary navigation',
|
||||
})
|
||||
}
|
||||
onClick={() => {
|
||||
onIsLockedUpdate(!isLocked);
|
||||
if (lockRef.current) {
|
||||
lockRef.current.focus();
|
||||
}
|
||||
}}
|
||||
iconType={isLocked ? 'lock' : 'lockOpen'}
|
||||
/>
|
||||
</EuiListGroup>
|
||||
</EuiCollapsibleNavGroup>
|
||||
</EuiShowFor>
|
||||
</EuiFlexItem>
|
||||
</EuiCollapsibleNav>
|
||||
);
|
||||
}
|
|
@ -25,8 +25,8 @@ import {
|
|||
EuiIcon,
|
||||
// @ts-ignore
|
||||
EuiNavDrawer,
|
||||
// @ts-ignore
|
||||
EuiShowFor,
|
||||
htmlIdGenerator,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Component, createRef } from 'react';
|
||||
|
@ -43,13 +43,14 @@ import { InternalApplicationStart } from '../../../application/types';
|
|||
import { HttpStart } from '../../../http';
|
||||
import { ChromeHelpExtension } from '../../chrome_service';
|
||||
import { HeaderBadge } from './header_badge';
|
||||
import { OnIsLockedUpdate } from './';
|
||||
import { NavType, OnIsLockedUpdate } from './';
|
||||
import { HeaderBreadcrumbs } from './header_breadcrumbs';
|
||||
import { HeaderHelpMenu } from './header_help_menu';
|
||||
import { HeaderNavControls } from './header_nav_controls';
|
||||
import { euiNavLink } from './nav_link';
|
||||
import { createNavLink, createRecentNavLink } from './nav_link';
|
||||
import { HeaderLogo } from './header_logo';
|
||||
import { NavDrawer } from './nav_drawer';
|
||||
import { CollapsibleNav } from './collapsible_nav';
|
||||
|
||||
export interface HeaderProps {
|
||||
kibanaVersion: string;
|
||||
|
@ -70,6 +71,7 @@ export interface HeaderProps {
|
|||
navControlsRight$: Rx.Observable<readonly ChromeNavControl[]>;
|
||||
basePath: HttpStart['basePath'];
|
||||
isLocked$: Rx.Observable<boolean>;
|
||||
navType$: Rx.Observable<NavType>;
|
||||
onIsLockedUpdate: OnIsLockedUpdate;
|
||||
}
|
||||
|
||||
|
@ -83,11 +85,14 @@ interface State {
|
|||
navControlsRight: readonly ChromeNavControl[];
|
||||
currentAppId: string | undefined;
|
||||
isLocked: boolean;
|
||||
navType: NavType;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class Header extends Component<HeaderProps, State> {
|
||||
private subscription?: Rx.Subscription;
|
||||
private navDrawerRef = createRef<EuiNavDrawer>();
|
||||
private toggleCollapsibleNavRef = createRef<HTMLButtonElement>();
|
||||
|
||||
constructor(props: HeaderProps) {
|
||||
super(props);
|
||||
|
@ -105,6 +110,8 @@ export class Header extends Component<HeaderProps, State> {
|
|||
navControlsRight: [],
|
||||
currentAppId: '',
|
||||
isLocked,
|
||||
navType: 'modern',
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -120,7 +127,8 @@ export class Header extends Component<HeaderProps, State> {
|
|||
this.props.navControlsLeft$,
|
||||
this.props.navControlsRight$,
|
||||
this.props.application.currentAppId$,
|
||||
this.props.isLocked$
|
||||
this.props.isLocked$,
|
||||
this.props.navType$
|
||||
)
|
||||
).subscribe({
|
||||
next: ([
|
||||
|
@ -129,7 +137,7 @@ export class Header extends Component<HeaderProps, State> {
|
|||
forceNavigation,
|
||||
navLinks,
|
||||
recentlyAccessed,
|
||||
[navControlsLeft, navControlsRight, currentAppId, isLocked],
|
||||
[navControlsLeft, navControlsRight, currentAppId, isLocked, navType],
|
||||
]) => {
|
||||
this.setState({
|
||||
appTitle,
|
||||
|
@ -141,6 +149,7 @@ export class Header extends Component<HeaderProps, State> {
|
|||
navControlsRight,
|
||||
currentAppId,
|
||||
isLocked,
|
||||
navType,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -176,7 +185,7 @@ export class Header extends Component<HeaderProps, State> {
|
|||
kibanaVersion,
|
||||
} = this.props;
|
||||
const navLinks = this.state.navLinks.map(link =>
|
||||
euiNavLink(
|
||||
createNavLink(
|
||||
link,
|
||||
this.props.legacyMode,
|
||||
this.state.currentAppId,
|
||||
|
@ -184,26 +193,54 @@ export class Header extends Component<HeaderProps, State> {
|
|||
this.props.application.navigateToApp
|
||||
)
|
||||
);
|
||||
const recentNavLinks = this.state.recentlyAccessed.map(link =>
|
||||
createRecentNavLink(link, this.state.navLinks, this.props.basePath)
|
||||
);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const className = classnames(
|
||||
'chrHeaderWrapper',
|
||||
'chrHeaderWrapper', // TODO #64541 - delete this
|
||||
'hide-for-sharing',
|
||||
{
|
||||
'chrHeaderWrapper--navIsLocked': this.state.isLocked,
|
||||
},
|
||||
'hide-for-sharing'
|
||||
headerWrapper: this.state.navType === 'modern',
|
||||
}
|
||||
);
|
||||
|
||||
const navId = htmlIdGenerator()();
|
||||
return (
|
||||
<header className={className} data-test-subj="headerGlobalNav">
|
||||
<EuiHeader>
|
||||
<EuiHeader position="fixed">
|
||||
<EuiHeaderSection grow={false}>
|
||||
<EuiShowFor sizes={['xs', 's']}>
|
||||
<EuiHeaderSectionItem border="right">{this.renderMenuTrigger()}</EuiHeaderSectionItem>
|
||||
</EuiShowFor>
|
||||
{this.state.navType === 'modern' ? (
|
||||
<EuiHeaderSectionItem border="right" className="header__toggleNavButtonSection">
|
||||
<EuiHeaderSectionItemButton
|
||||
data-test-subj="toggleNavButton"
|
||||
aria-label={i18n.translate('core.ui.primaryNav.toggleNavAriaLabel', {
|
||||
defaultMessage: 'Toggle primary navigation',
|
||||
})}
|
||||
onClick={() => {
|
||||
this.setState({ isOpen: !this.state.isOpen });
|
||||
}}
|
||||
aria-expanded={this.state.isOpen}
|
||||
aria-pressed={this.state.isOpen}
|
||||
aria-controls={navId}
|
||||
ref={this.toggleCollapsibleNavRef}
|
||||
>
|
||||
<EuiIcon type="menu" size="m" />
|
||||
</EuiHeaderSectionItemButton>
|
||||
</EuiHeaderSectionItem>
|
||||
) : (
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
<EuiShowFor sizes={['xs', 's']}>
|
||||
<EuiHeaderSectionItem border="right">
|
||||
{this.renderMenuTrigger()}
|
||||
</EuiHeaderSectionItem>
|
||||
</EuiShowFor>
|
||||
)}
|
||||
|
||||
<EuiHeaderSectionItem border="right">
|
||||
<HeaderLogo
|
||||
|
@ -235,15 +272,33 @@ export class Header extends Component<HeaderProps, State> {
|
|||
<HeaderNavControls side="right" navControls={navControlsRight} />
|
||||
</EuiHeaderSection>
|
||||
</EuiHeader>
|
||||
<NavDrawer
|
||||
isLocked={this.state.isLocked}
|
||||
onIsLockedUpdate={this.props.onIsLockedUpdate}
|
||||
navLinks={navLinks}
|
||||
chromeNavLinks={this.state.navLinks}
|
||||
recentlyAccessedItems={this.state.recentlyAccessed}
|
||||
basePath={this.props.basePath}
|
||||
ref={this.navDrawerRef}
|
||||
/>
|
||||
{this.state.navType === 'modern' ? (
|
||||
<CollapsibleNav
|
||||
id={navId}
|
||||
isLocked={this.state.isLocked}
|
||||
onIsLockedUpdate={this.props.onIsLockedUpdate}
|
||||
navLinks={navLinks}
|
||||
recentNavLinks={recentNavLinks}
|
||||
isOpen={this.state.isOpen}
|
||||
homeHref={this.props.homeHref}
|
||||
onIsOpenUpdate={(isOpen = !this.state.isOpen) => {
|
||||
this.setState({ isOpen });
|
||||
if (this.toggleCollapsibleNavRef.current) {
|
||||
this.toggleCollapsibleNavRef.current.focus();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
<NavDrawer
|
||||
isLocked={this.state.isLocked}
|
||||
onIsLockedUpdate={this.props.onIsLockedUpdate}
|
||||
navLinks={navLinks}
|
||||
recentNavLinks={recentNavLinks}
|
||||
ref={this.navDrawerRef}
|
||||
/>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
export { Header, HeaderProps } from './header';
|
||||
export { OnIsLockedUpdate, NavType } from './types';
|
||||
export {
|
||||
ChromeHelpExtensionMenuLink,
|
||||
ChromeHelpExtensionMenuCustomLink,
|
||||
|
@ -25,4 +26,3 @@ export {
|
|||
ChromeHelpExtensionMenuDocumentationLink,
|
||||
ChromeHelpExtensionMenuGitHubLink,
|
||||
} from './header_help_menu';
|
||||
export type OnIsLockedUpdate = (isLocked: boolean) => void;
|
||||
|
|
|
@ -22,22 +22,18 @@ import { i18n } from '@kbn/i18n';
|
|||
// @ts-ignore
|
||||
import { EuiNavDrawer, EuiHorizontalRule, EuiNavDrawerGroup } from '@elastic/eui';
|
||||
import { OnIsLockedUpdate } from './';
|
||||
import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..';
|
||||
import { HttpStart } from '../../../http';
|
||||
import { NavLink } from './nav_link';
|
||||
import { NavLink, RecentNavLink } from './nav_link';
|
||||
import { RecentLinks } from './recent_links';
|
||||
|
||||
export interface Props {
|
||||
isLocked?: boolean;
|
||||
onIsLockedUpdate?: OnIsLockedUpdate;
|
||||
navLinks: NavLink[];
|
||||
chromeNavLinks: ChromeNavLink[];
|
||||
recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[];
|
||||
basePath: HttpStart['basePath'];
|
||||
recentNavLinks: RecentNavLink[];
|
||||
}
|
||||
|
||||
function navDrawerRenderer(
|
||||
{ isLocked, onIsLockedUpdate, navLinks, chromeNavLinks, recentlyAccessedItems, basePath }: Props,
|
||||
{ isLocked, onIsLockedUpdate, navLinks, recentNavLinks }: Props,
|
||||
ref: React.Ref<HTMLElement>
|
||||
) {
|
||||
return (
|
||||
|
@ -50,11 +46,7 @@ function navDrawerRenderer(
|
|||
defaultMessage: 'Primary',
|
||||
})}
|
||||
>
|
||||
{RecentLinks({
|
||||
recentlyAccessedItems,
|
||||
navLinks: chromeNavLinks,
|
||||
basePath,
|
||||
})}
|
||||
{RecentLinks({ recentNavLinks })}
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiNavDrawerGroup
|
||||
data-test-subj="navDrawerAppsMenu"
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiImage } from '@elastic/eui';
|
||||
import { ChromeNavLink, CoreStart } from '../../../';
|
||||
import { AppCategory } from 'src/core/types';
|
||||
import { ChromeNavLink, CoreStart, ChromeRecentlyAccessedHistoryItem } from '../../../';
|
||||
import { HttpStart } from '../../../http';
|
||||
|
||||
function isModifiedEvent(event: MouseEvent) {
|
||||
function isModifiedEvent(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
||||
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||
}
|
||||
|
||||
|
@ -30,15 +32,36 @@ function LinkIcon({ url }: { url: string }) {
|
|||
return <EuiImage size="s" alt="" aria-hidden={true} url={url} />;
|
||||
}
|
||||
|
||||
export type NavLink = ReturnType<typeof euiNavLink>;
|
||||
export interface NavLink {
|
||||
key: string;
|
||||
label: string;
|
||||
href: string;
|
||||
isActive: boolean;
|
||||
onClick(event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void;
|
||||
category?: AppCategory;
|
||||
isDisabled?: boolean;
|
||||
iconType?: string;
|
||||
icon?: JSX.Element;
|
||||
order?: number;
|
||||
'data-test-subj': string;
|
||||
}
|
||||
|
||||
export function euiNavLink(
|
||||
/**
|
||||
* Create a link that's actually ready to be passed into EUI
|
||||
*
|
||||
* @param navLink
|
||||
* @param legacyMode
|
||||
* @param currentAppId
|
||||
* @param basePath
|
||||
* @param navigateToApp
|
||||
*/
|
||||
export function createNavLink(
|
||||
navLink: ChromeNavLink,
|
||||
legacyMode: boolean,
|
||||
currentAppId: string | undefined,
|
||||
basePath: HttpStart['basePath'],
|
||||
navigateToApp: CoreStart['application']['navigateToApp']
|
||||
) {
|
||||
): NavLink {
|
||||
const {
|
||||
legacy,
|
||||
url,
|
||||
|
@ -64,7 +87,7 @@ export function euiNavLink(
|
|||
key: id,
|
||||
label: tooltip ?? title,
|
||||
href, // Use href and onClick to support "open in new tab" and SPA navigation in the same link
|
||||
onClick(event: MouseEvent) {
|
||||
onClick(event) {
|
||||
if (
|
||||
!legacyMode && // ignore when in legacy mode
|
||||
!legacy && // ignore links to legacy apps
|
||||
|
@ -85,3 +108,76 @@ export function euiNavLink(
|
|||
'data-test-subj': 'navDrawerAppsMenuLink',
|
||||
};
|
||||
}
|
||||
|
||||
// Providing a buffer between the limit and the cut off index
|
||||
// protects from truncating just the last couple (6) characters
|
||||
const TRUNCATE_LIMIT: number = 64;
|
||||
const TRUNCATE_AT: number = 58;
|
||||
|
||||
function truncateRecentItemLabel(label: string): string {
|
||||
if (label.length > TRUNCATE_LIMIT) {
|
||||
label = `${label.substring(0, TRUNCATE_AT)}…`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - a relative or root relative url. If a relative path is given then the
|
||||
* absolute url returned will depend on the current page where this function is called from. For example
|
||||
* if you are on page "http://www.mysite.com/shopping/kids" and you pass this function "adults", you would get
|
||||
* back "http://www.mysite.com/shopping/adults". If you passed this function a root relative path, or one that
|
||||
* starts with a "/", for example "/account/cart", you would get back "http://www.mysite.com/account/cart".
|
||||
* @return {string} the relative url transformed into an absolute url
|
||||
*/
|
||||
function relativeToAbsolute(url: string) {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', url);
|
||||
return a.href;
|
||||
}
|
||||
|
||||
export interface RecentNavLink {
|
||||
href: string;
|
||||
label: string;
|
||||
title: string;
|
||||
'aria-label': string;
|
||||
iconType?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add saved object type info to recently links
|
||||
*
|
||||
* Recent nav links are similar to normal nav links but are missing some Kibana Platform magic and
|
||||
* because of legacy reasons have slightly different properties.
|
||||
* @param recentLink
|
||||
* @param navLinks
|
||||
* @param basePath
|
||||
*/
|
||||
export function createRecentNavLink(
|
||||
recentLink: ChromeRecentlyAccessedHistoryItem,
|
||||
navLinks: ChromeNavLink[],
|
||||
basePath: HttpStart['basePath']
|
||||
) {
|
||||
const { link, label } = recentLink;
|
||||
const href = relativeToAbsolute(basePath.prepend(link));
|
||||
const navLink = navLinks.find(nl => href.startsWith(nl.baseUrl ?? nl.subUrlBase));
|
||||
let titleAndAriaLabel = label;
|
||||
|
||||
if (navLink) {
|
||||
titleAndAriaLabel = i18n.translate('core.ui.recentLinks.linkItem.screenReaderLabel', {
|
||||
defaultMessage: '{recentlyAccessedItemLinklabel}, type: {pageType}',
|
||||
values: {
|
||||
recentlyAccessedItemLinklabel: label,
|
||||
pageType: navLink.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
href,
|
||||
label: truncateRecentItemLabel(label),
|
||||
title: titleAndAriaLabel,
|
||||
'aria-label': titleAndAriaLabel,
|
||||
iconType: navLink?.euiIconType,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -21,73 +21,13 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { EuiNavDrawerGroup } from '@elastic/eui';
|
||||
import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..';
|
||||
import { HttpStart } from '../../../http';
|
||||
|
||||
// Providing a buffer between the limit and the cut off index
|
||||
// protects from truncating just the last couple (6) characters
|
||||
const TRUNCATE_LIMIT: number = 64;
|
||||
const TRUNCATE_AT: number = 58;
|
||||
|
||||
export function truncateRecentItemLabel(label: string): string {
|
||||
if (label.length > TRUNCATE_LIMIT) {
|
||||
label = `${label.substring(0, TRUNCATE_AT)}…`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url - a relative or root relative url. If a relative path is given then the
|
||||
* absolute url returned will depend on the current page where this function is called from. For example
|
||||
* if you are on page "http://www.mysite.com/shopping/kids" and you pass this function "adults", you would get
|
||||
* back "http://www.mysite.com/shopping/adults". If you passed this function a root relative path, or one that
|
||||
* starts with a "/", for example "/account/cart", you would get back "http://www.mysite.com/account/cart".
|
||||
* @return {string} the relative url transformed into an absolute url
|
||||
*/
|
||||
function relativeToAbsolute(url: string) {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', url);
|
||||
return a.href;
|
||||
}
|
||||
|
||||
function prepareForEUI(
|
||||
recentlyAccessed: ChromeRecentlyAccessedHistoryItem[],
|
||||
navLinks: ChromeNavLink[],
|
||||
basePath: HttpStart['basePath']
|
||||
) {
|
||||
return recentlyAccessed.map(({ link, label }) => {
|
||||
const href = relativeToAbsolute(basePath.prepend(link));
|
||||
const navLink = navLinks.find(nl => href.startsWith(nl.baseUrl ?? nl.subUrlBase));
|
||||
let titleAndAriaLabel = label;
|
||||
|
||||
if (navLink) {
|
||||
titleAndAriaLabel = i18n.translate('core.ui.recentLinks.linkItem.screenReaderLabel', {
|
||||
defaultMessage: '{recentlyAccessedItemLinklabel}, type: {pageType}',
|
||||
values: {
|
||||
recentlyAccessedItemLinklabel: label,
|
||||
pageType: navLink.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
href,
|
||||
label: truncateRecentItemLabel(label),
|
||||
title: titleAndAriaLabel,
|
||||
'aria-label': titleAndAriaLabel,
|
||||
iconType: navLink?.euiIconType,
|
||||
};
|
||||
});
|
||||
}
|
||||
import { RecentNavLink } from './nav_link';
|
||||
|
||||
interface Props {
|
||||
recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[];
|
||||
navLinks: ChromeNavLink[];
|
||||
basePath: HttpStart['basePath'];
|
||||
recentNavLinks: RecentNavLink[];
|
||||
}
|
||||
|
||||
export function RecentLinks({ recentlyAccessedItems, navLinks, basePath }: Props) {
|
||||
export function RecentLinks({ recentNavLinks }: Props) {
|
||||
return (
|
||||
<EuiNavDrawerGroup
|
||||
listItems={[
|
||||
|
@ -96,12 +36,12 @@ export function RecentLinks({ recentlyAccessedItems, navLinks, basePath }: Props
|
|||
defaultMessage: 'Recently viewed',
|
||||
}),
|
||||
iconType: 'recentlyViewedApp',
|
||||
isDisabled: recentlyAccessedItems.length === 0,
|
||||
isDisabled: recentNavLinks.length === 0,
|
||||
flyoutMenu: {
|
||||
title: i18n.translate('core.ui.chrome.sideGlobalNav.viewRecentItemsFlyoutTitle', {
|
||||
defaultMessage: 'Recent items',
|
||||
}),
|
||||
listItems: prepareForEUI(recentlyAccessedItems, navLinks, basePath),
|
||||
listItems: recentNavLinks,
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
|
21
src/core/public/chrome/ui/header/types.ts
Normal file
21
src/core/public/chrome/ui/header/types.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export type OnIsLockedUpdate = (isLocked: boolean) => void;
|
||||
export type NavType = 'modern' | 'legacy';
|
|
@ -25,4 +25,5 @@ export {
|
|||
ChromeHelpExtensionMenuDiscussLink,
|
||||
ChromeHelpExtensionMenuDocumentationLink,
|
||||
ChromeHelpExtensionMenuGitHubLink,
|
||||
NavType,
|
||||
} from './header';
|
||||
|
|
|
@ -240,6 +240,7 @@ export class CoreSystem {
|
|||
http,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
uiSettings,
|
||||
});
|
||||
|
||||
application.registerMountContext(this.coreContext.coreId, 'core', () => ({
|
||||
|
|
|
@ -54,6 +54,7 @@ import {
|
|||
ChromeStart,
|
||||
ChromeRecentlyAccessed,
|
||||
ChromeRecentlyAccessedHistoryItem,
|
||||
NavType,
|
||||
} from './chrome';
|
||||
import { FatalErrorsSetup, FatalErrorsStart, FatalErrorInfo } from './fatal_errors';
|
||||
import { HttpSetup, HttpStart } from './http';
|
||||
|
@ -354,4 +355,5 @@ export {
|
|||
PluginOpaqueId,
|
||||
IUiSettingsClient,
|
||||
UiSettingsState,
|
||||
NavType,
|
||||
};
|
||||
|
|
|
@ -13,12 +13,7 @@ Array [
|
|||
Array [
|
||||
<mockConstructor>
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<MountWrapper
|
||||
className="kbnOverlayMountWrapper"
|
||||
|
@ -31,19 +26,14 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"<div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\"><div role=\\"dialog\\" class=\\"euiFlyout euiFlyout--medium\\" tabindex=\\"0\\"><button class=\\"euiButtonIcon euiButtonIcon--text euiFlyout__closeButton\\" type=\\"button\\" aria-label=\\"Closes this dialog\\" data-test-subj=\\"euiFlyoutCloseButton\\"><div data-euiicon-type=\\"cross\\" class=\\"euiButtonIcon__icon\\" aria-hidden=\\"true\\"></div></button><div class=\\"kbnOverlayMountWrapper\\"><span>Flyout content</span></div></div></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div></div>"`;
|
||||
exports[`FlyoutService openFlyout() renders a flyout to the DOM 2`] = `"<div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\"><div role=\\"dialog\\" class=\\"euiFlyout euiFlyout--medium\\" tabindex=\\"0\\"><button class=\\"euiButtonIcon euiButtonIcon--text euiFlyout__closeButton\\" type=\\"button\\" aria-label=\\"Close this dialog\\" data-test-subj=\\"euiFlyoutCloseButton\\"><div data-euiicon-type=\\"cross\\" class=\\"euiButtonIcon__icon\\" aria-hidden=\\"true\\"></div></button><div class=\\"kbnOverlayMountWrapper\\"><span>Flyout content</span></div></div></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div></div>"`;
|
||||
|
||||
exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
<mockConstructor>
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<MountWrapper
|
||||
className="kbnOverlayMountWrapper"
|
||||
|
@ -56,12 +46,7 @@ Array [
|
|||
Array [
|
||||
<mockConstructor>
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<MountWrapper
|
||||
className="kbnOverlayMountWrapper"
|
||||
|
@ -74,4 +59,4 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"<div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\"><div role=\\"dialog\\" class=\\"euiFlyout euiFlyout--medium\\" tabindex=\\"0\\"><button class=\\"euiButtonIcon euiButtonIcon--text euiFlyout__closeButton\\" type=\\"button\\" aria-label=\\"Closes this dialog\\" data-test-subj=\\"euiFlyoutCloseButton\\"><div data-euiicon-type=\\"cross\\" class=\\"euiButtonIcon__icon\\" aria-hidden=\\"true\\"></div></button><div class=\\"kbnOverlayMountWrapper\\"><span>Flyout content 2</span></div></div></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div></div>"`;
|
||||
exports[`FlyoutService openFlyout() with a currently active flyout replaces the current flyout with a new one 2`] = `"<div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-guard=\\"true\\" tabindex=\\"1\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div><div data-focus-lock-disabled=\\"false\\"><div role=\\"dialog\\" class=\\"euiFlyout euiFlyout--medium\\" tabindex=\\"0\\"><button class=\\"euiButtonIcon euiButtonIcon--text euiFlyout__closeButton\\" type=\\"button\\" aria-label=\\"Close this dialog\\" data-test-subj=\\"euiFlyoutCloseButton\\"><div data-euiicon-type=\\"cross\\" class=\\"euiButtonIcon__icon\\" aria-hidden=\\"true\\"></div></button><div class=\\"kbnOverlayMountWrapper\\"><span>Flyout content 2</span></div></div></div><div data-focus-guard=\\"true\\" tabindex=\\"0\\" style=\\"width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;\\"></div></div>"`;
|
||||
|
|
|
@ -55,6 +55,7 @@ export interface AppBase {
|
|||
export interface AppCategory {
|
||||
ariaLabel?: string;
|
||||
euiIconType?: string;
|
||||
id: string;
|
||||
label: string;
|
||||
order?: number;
|
||||
}
|
||||
|
@ -343,6 +344,7 @@ export interface ChromeStart {
|
|||
getHelpExtension$(): Observable<ChromeHelpExtension | undefined>;
|
||||
getIsNavDrawerLocked$(): Observable<boolean>;
|
||||
getIsVisible$(): Observable<boolean>;
|
||||
getNavType$(): Observable<NavType>;
|
||||
navControls: ChromeNavControls;
|
||||
navLinks: ChromeNavLinks;
|
||||
recentlyAccessed: ChromeRecentlyAccessed;
|
||||
|
@ -443,23 +445,28 @@ export function deepFreeze<T extends Freezable>(object: T): RecursiveReadonly<T>
|
|||
|
||||
// @internal (undocumented)
|
||||
export const DEFAULT_APP_CATEGORIES: Readonly<{
|
||||
analyze: {
|
||||
kibana: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
observability: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
security: {
|
||||
id: string;
|
||||
label: string;
|
||||
order: number;
|
||||
euiIconType: string;
|
||||
};
|
||||
management: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
}>;
|
||||
|
||||
|
@ -883,6 +890,11 @@ export function modifyUrl(url: string, urlModifier: (urlParts: URLMeaningfulPart
|
|||
// @public
|
||||
export type MountPoint<T extends HTMLElement = HTMLElement> = (element: T) => UnmountCallback;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "NavType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type NavType = 'modern' | 'legacy';
|
||||
|
||||
// @public (undocumented)
|
||||
export interface NotificationsSetup {
|
||||
// (undocumented)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
@import '@elastic/eui/src/components/header/variables';
|
||||
@import '@elastic/eui/src/components/nav_drawer/variables';
|
||||
|
||||
/**
|
||||
* stretch the root element of the Kibana application to set the base-size that
|
||||
* flexed children should keep. Only works when paired with root styles applied
|
||||
|
@ -9,7 +12,9 @@
|
|||
min-height: 100%;
|
||||
}
|
||||
|
||||
.app-wrapper {
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
position: absolute;
|
||||
|
@ -20,6 +25,22 @@
|
|||
z-index: 5;
|
||||
margin: 0 auto;
|
||||
|
||||
&:not(.hidden-chrome) {
|
||||
top: $euiHeaderChildSize;
|
||||
left: $euiHeaderChildSize;
|
||||
|
||||
// HOTFIX: Temporary fix for flyouts not inside portals
|
||||
// SASSTODO: Find an actual solution
|
||||
.euiFlyout {
|
||||
top: $euiHeaderChildSize;
|
||||
height: calc(100% - #{$euiHeaderChildSize});
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Dirty, but we need to override the .kbnGlobalNav-isOpen state
|
||||
* when we're looking at the log-in screen.
|
||||
|
@ -33,6 +54,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
@include euiBreakpoint('xl') {
|
||||
.chrHeaderWrapper--navIsLocked:not(.headerWrapper) {
|
||||
~ .app-wrapper:not(.hidden-chrome) {
|
||||
// Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS)
|
||||
left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important
|
||||
transition: left $euiAnimSpeedFast $euiAnimSlightResistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO #64541
|
||||
// Remove .headerWrapper and header conditionals
|
||||
.headerWrapper ~ .app-wrapper,
|
||||
:not(header) ~ .app-wrapper {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - #{$euiHeaderHeightCompensation});
|
||||
|
||||
&.hidden-chrome {
|
||||
min-height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.app-wrapper-panel {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
|
|
@ -133,6 +133,7 @@ describe('getNavLinks', () => {
|
|||
id: 'app-a',
|
||||
title: 'AppA',
|
||||
category: {
|
||||
id: 'foo',
|
||||
label: 'My Category',
|
||||
},
|
||||
order: 42,
|
||||
|
@ -151,6 +152,7 @@ describe('getNavLinks', () => {
|
|||
id: 'app-a',
|
||||
title: 'AppA',
|
||||
category: {
|
||||
id: 'foo',
|
||||
label: 'My Category',
|
||||
},
|
||||
order: 42,
|
||||
|
@ -211,6 +213,7 @@ describe('getNavLinks', () => {
|
|||
id: 'link-a',
|
||||
title: 'AppA',
|
||||
category: {
|
||||
id: 'foo',
|
||||
label: 'My Second Cat',
|
||||
},
|
||||
order: 72,
|
||||
|
@ -232,6 +235,7 @@ describe('getNavLinks', () => {
|
|||
id: 'link-a',
|
||||
title: 'AppA',
|
||||
category: {
|
||||
id: 'foo',
|
||||
label: 'My Second Cat',
|
||||
},
|
||||
order: 72,
|
||||
|
|
|
@ -700,23 +700,28 @@ export function deepFreeze<T extends Freezable>(object: T): RecursiveReadonly<T>
|
|||
|
||||
// @internal (undocumented)
|
||||
export const DEFAULT_APP_CATEGORIES: Readonly<{
|
||||
analyze: {
|
||||
kibana: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
observability: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
security: {
|
||||
id: string;
|
||||
label: string;
|
||||
order: number;
|
||||
euiIconType: string;
|
||||
};
|
||||
management: {
|
||||
id: string;
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
order: number;
|
||||
};
|
||||
}>;
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
* @public
|
||||
*/
|
||||
export interface AppCategory {
|
||||
/**
|
||||
* Unique identifier for the categories
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Label used for cateogry name.
|
||||
* Also used as aria-label if one isn't set.
|
||||
|
|
|
@ -21,13 +21,16 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
/** @internal */
|
||||
export const DEFAULT_APP_CATEGORIES = Object.freeze({
|
||||
analyze: {
|
||||
label: i18n.translate('core.ui.analyzeNavList.label', {
|
||||
defaultMessage: 'Analyze',
|
||||
kibana: {
|
||||
id: 'kibana',
|
||||
label: i18n.translate('core.ui.kibanaNavList.label', {
|
||||
defaultMessage: 'Kibana',
|
||||
}),
|
||||
euiIconType: 'logoKibana',
|
||||
order: 1000,
|
||||
},
|
||||
observability: {
|
||||
id: 'observability',
|
||||
label: i18n.translate('core.ui.observabilityNavList.label', {
|
||||
defaultMessage: 'Observability',
|
||||
}),
|
||||
|
@ -35,6 +38,7 @@ export const DEFAULT_APP_CATEGORIES = Object.freeze({
|
|||
order: 2000,
|
||||
},
|
||||
security: {
|
||||
id: 'security',
|
||||
label: i18n.translate('core.ui.securityNavList.label', {
|
||||
defaultMessage: 'Security',
|
||||
}),
|
||||
|
@ -42,9 +46,10 @@ export const DEFAULT_APP_CATEGORIES = Object.freeze({
|
|||
euiIconType: 'logoSecurity',
|
||||
},
|
||||
management: {
|
||||
id: 'management',
|
||||
label: i18n.translate('core.ui.managementNavList.label', {
|
||||
defaultMessage: 'Management',
|
||||
}),
|
||||
euiIconType: 'managementApp',
|
||||
order: 5000,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -67,33 +67,33 @@ export default function(kibana) {
|
|||
title: i18n.translate('kbn.discoverTitle', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
order: -1003,
|
||||
order: 2000,
|
||||
url: `${kbnBaseUrl}#/discover`,
|
||||
euiIconType: 'discoverApp',
|
||||
disableSubUrlTracking: true,
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
},
|
||||
{
|
||||
id: 'kibana:visualize',
|
||||
title: i18n.translate('kbn.visualizeTitle', {
|
||||
defaultMessage: 'Visualize',
|
||||
}),
|
||||
order: -1002,
|
||||
order: 7000,
|
||||
url: `${kbnBaseUrl}#/visualize`,
|
||||
euiIconType: 'visualizeApp',
|
||||
disableSubUrlTracking: true,
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
},
|
||||
{
|
||||
id: 'kibana:dashboard',
|
||||
title: i18n.translate('kbn.dashboardTitle', {
|
||||
defaultMessage: 'Dashboard',
|
||||
}),
|
||||
order: -1001,
|
||||
order: 1000,
|
||||
url: `${kbnBaseUrl}#/dashboards`,
|
||||
euiIconType: 'dashboardApp',
|
||||
disableSubUrlTracking: true,
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
},
|
||||
{
|
||||
id: 'kibana:dev_tools',
|
||||
|
@ -108,7 +108,7 @@ export default function(kibana) {
|
|||
{
|
||||
id: 'kibana:stack_management',
|
||||
title: i18n.translate('kbn.managementTitle', {
|
||||
defaultMessage: 'Management',
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
order: 9003,
|
||||
url: `${kbnBaseUrl}#/management`,
|
||||
|
|
|
@ -69,7 +69,7 @@ export function updateLandingPage(version) {
|
|||
<h1>
|
||||
<FormattedMessage
|
||||
id="kbn.management.landing.header"
|
||||
defaultMessage="Kibana {version} management"
|
||||
defaultMessage="Welcome to Stack Management {version}"
|
||||
values={{ version }}
|
||||
/>
|
||||
</h1>
|
||||
|
|
|
@ -1172,5 +1172,25 @@ export function getUiSettingDefaults() {
|
|||
category: ['accessibility'],
|
||||
requiresPageReload: true,
|
||||
},
|
||||
pageNavigation: {
|
||||
name: i18n.translate('kbn.advancedSettings.pageNavigationName', {
|
||||
defaultMessage: 'Side nav style',
|
||||
}),
|
||||
value: 'modern',
|
||||
description: i18n.translate('kbn.advancedSettings.pageNavigationDesc', {
|
||||
defaultMessage: 'Change the style of navigation',
|
||||
}),
|
||||
type: 'select',
|
||||
options: ['modern', 'legacy'],
|
||||
optionLabels: {
|
||||
modern: i18n.translate('kbn.advancedSettings.pageNavigationModern', {
|
||||
defaultMessage: 'Modern',
|
||||
}),
|
||||
legacy: i18n.translate('kbn.advancedSettings.pageNavigationLegacy', {
|
||||
defaultMessage: 'Legacy',
|
||||
}),
|
||||
},
|
||||
schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -54,11 +54,11 @@ const timelionPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPl
|
|||
uiExports: {
|
||||
app: {
|
||||
title: 'Timelion',
|
||||
order: -1000,
|
||||
order: 8000,
|
||||
icon: 'plugins/timelion/icon.svg',
|
||||
euiIconType: 'timelionApp',
|
||||
main: 'plugins/timelion/app',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
hacks: [resolve(__dirname, 'public/legacy')],
|
||||
|
|
|
@ -4,12 +4,7 @@ exports[`LabelTemplateFlyout should not render if not visible 1`] = `""`;
|
|||
|
||||
exports[`LabelTemplateFlyout should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
|
|
|
@ -4,12 +4,7 @@ exports[`UrlTemplateFlyout should not render if not visible 1`] = `""`;
|
|||
|
||||
exports[`UrlTemplateFlyout should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutBody>
|
||||
<EuiText>
|
||||
|
|
|
@ -2,13 +2,8 @@
|
|||
|
||||
exports[`ScriptingHelpFlyout should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
data-test-subj="scriptedFieldsHelpFlyout"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTabbedContent
|
||||
|
@ -53,13 +48,8 @@ exports[`ScriptingHelpFlyout should render normally 1`] = `
|
|||
|
||||
exports[`ScriptingHelpFlyout should render nothing if not visible 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
data-test-subj="scriptedFieldsHelpFlyout"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTabbedContent
|
||||
|
|
|
@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
export const MANAGEMENT_BREADCRUMB = Object.freeze({
|
||||
text: i18n.translate('common.ui.stackManagement.breadcrumb', {
|
||||
defaultMessage: 'Management',
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
href: '#/management',
|
||||
});
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@import '@elastic/eui/src/components/collapsible_nav/variables';
|
||||
|
||||
// Forms
|
||||
|
||||
// Angular form states
|
||||
|
@ -38,7 +40,9 @@ input[type='checkbox'],
|
|||
// Application Layout
|
||||
|
||||
// chrome-context
|
||||
.content {
|
||||
// TODO #64541
|
||||
// Delete this block
|
||||
.chrHeaderWrapper:not(.headerWrapper) .content {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
width: 100%;
|
||||
|
@ -119,7 +123,7 @@ input[type='checkbox'],
|
|||
}
|
||||
}
|
||||
|
||||
// A neccessary hack so that the above focus policy doesn't polute some EUI
|
||||
// A necessary hack so that the above focus policy doesn't pollute some EUI
|
||||
// entrenched inputs.
|
||||
.euiComboBox {
|
||||
// :not() specificity needed to override the above
|
||||
|
@ -128,6 +132,10 @@ input[type='checkbox'],
|
|||
}
|
||||
}
|
||||
|
||||
.euiBody--collapsibleNavIsDocked .euiBottomBar {
|
||||
margin-left: $euiCollapsibleNavWidth;
|
||||
}
|
||||
|
||||
// Utility classes
|
||||
|
||||
.fullWidth {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
@import '@elastic/eui/src/components/header/variables';
|
||||
@import '@elastic/eui/src/components/nav_drawer/variables';
|
||||
|
||||
// TODO #64541
|
||||
// Delete this whole file
|
||||
.mgtAdvancedSettingsForm__bottomBar {
|
||||
margin-left: $euiNavDrawerWidthCollapsed;
|
||||
z-index: 9; // Puts it inuder the nav drawer when expanded
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
|
@ -325,10 +326,18 @@ export class Form extends PureComponent<FormProps> {
|
|||
|
||||
renderBottomBar = () => {
|
||||
const areChangesInvalid = this.areChangesInvalid();
|
||||
const bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', {
|
||||
'mgtAdvancedSettingsForm__bottomBar--pushForNav':
|
||||
localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true',
|
||||
});
|
||||
|
||||
// TODO #64541
|
||||
// Delete these classes
|
||||
let bottomBarClasses = '';
|
||||
const pageNav = this.props.settings.general.find(setting => setting.name === 'pageNavigation');
|
||||
|
||||
if (pageNav?.value === 'legacy') {
|
||||
bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', {
|
||||
'mgtAdvancedSettingsForm__bottomBar--pushForNav':
|
||||
localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true',
|
||||
});
|
||||
}
|
||||
return (
|
||||
<EuiBottomBar className={bottomBarClasses} data-test-subj="advancedSetting-bottomBar">
|
||||
<EuiFlexGroup
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
.dshAppContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
height: 100%; // TODO #64541 - can delete this
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dshStartScreen {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { withKibana, KibanaReactContextValue } from '../../../../../kibana_react/public';
|
||||
|
@ -39,7 +39,7 @@ export interface PhraseSuggestorState {
|
|||
* aggregatable), we pull out the common logic for requesting suggestions into this component
|
||||
* which both of them extend.
|
||||
*/
|
||||
export class PhraseSuggestorUI<T extends PhraseSuggestorProps> extends Component<
|
||||
export class PhraseSuggestorUI<T extends PhraseSuggestorProps> extends React.Component<
|
||||
T,
|
||||
PhraseSuggestorState
|
||||
> {
|
||||
|
|
|
@ -180,6 +180,7 @@ export function SavedQueryManagementComponent({
|
|||
}}
|
||||
anchorPosition="downLeft"
|
||||
panelPaddingSize="none"
|
||||
buffer={-8}
|
||||
ownFocus
|
||||
>
|
||||
<div
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
exports[`render 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
data-test-subj="loadSearchForm"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={true}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
|
|
@ -61,9 +61,7 @@ export const PanelOptionsMenu: React.FC<PanelOptionsMenuProps> = ({
|
|||
);
|
||||
const ariaLabelWithoutTitle = i18n.translate(
|
||||
'embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Panel options',
|
||||
}
|
||||
{ defaultMessage: 'Panel options' }
|
||||
);
|
||||
|
||||
const button = (
|
||||
|
|
|
@ -8,6 +8,7 @@ exports[`disableMsg 1`] = `
|
|||
label="list control"
|
||||
>
|
||||
<EuiFieldText
|
||||
aria-label="Select..."
|
||||
disabled={true}
|
||||
placeholder="Select..."
|
||||
/>
|
||||
|
|
|
@ -114,6 +114,10 @@ class ListControlUi extends PureComponent<ListControlUiProps, ListControlUiState
|
|||
if (this.props.disableMsg) {
|
||||
return (
|
||||
<EuiFieldText
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'inputControl.vis.listControl.selectTextPlaceholder',
|
||||
defaultMessage: 'Select...',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'inputControl.vis.listControl.selectTextPlaceholder',
|
||||
defaultMessage: 'Select...',
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { Component, createRef } from 'react';
|
||||
import { EuiFormRow, EuiDualRange } from '@elastic/eui';
|
||||
import { EuiFormRowDisplayKeys } from '@elastic/eui/src/components/form/form_row/form_row';
|
||||
import { EuiDualRangeProps } from '@elastic/eui/src/components/form/range/dual_range';
|
||||
|
@ -35,8 +35,8 @@ interface Props extends Omit<EuiDualRangeProps, 'value' | 'onChange' | 'min' | '
|
|||
label?: string;
|
||||
formRowDisplay?: EuiFormRowDisplayKeys;
|
||||
onChange?: (val: [string, string]) => void;
|
||||
min?: ValueMember;
|
||||
max?: ValueMember;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -72,6 +72,18 @@ export class ValidatedDualRange extends Component<Props> {
|
|||
return null;
|
||||
}
|
||||
|
||||
// Can remove after eui#3412 is resolved
|
||||
componentDidMount() {
|
||||
if (this.trackRef.current) {
|
||||
const track = this.trackRef.current.querySelector('.euiRangeTrack');
|
||||
if (track) {
|
||||
track.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trackRef = createRef<HTMLDivElement>();
|
||||
|
||||
// @ts-ignore state populated by getDerivedStateFromProps
|
||||
state: State = {};
|
||||
|
||||
|
@ -103,29 +115,38 @@ export class ValidatedDualRange extends Component<Props> {
|
|||
value, // eslint-disable-line no-unused-vars
|
||||
onChange, // eslint-disable-line no-unused-vars
|
||||
allowEmptyRange, // eslint-disable-line no-unused-vars
|
||||
// @ts-ignore
|
||||
...rest // TODO: Consider alternatives for spread operator in component
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
compressed={compressed}
|
||||
fullWidth={fullWidth}
|
||||
isInvalid={!this.state.isValid}
|
||||
error={this.state.errorMessage ? [this.state.errorMessage] : []}
|
||||
label={label}
|
||||
display={formRowDisplay}
|
||||
>
|
||||
<EuiDualRange
|
||||
<div ref={this.trackRef}>
|
||||
<EuiFormRow
|
||||
compressed={compressed}
|
||||
fullWidth={fullWidth}
|
||||
value={this.state.value}
|
||||
onChange={this._onChange}
|
||||
// @ts-ignore
|
||||
focusable={false} // remove when #59039 is fixed
|
||||
{...rest}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
isInvalid={!this.state.isValid}
|
||||
error={this.state.errorMessage ? [this.state.errorMessage] : []}
|
||||
label={label}
|
||||
display={formRowDisplay}
|
||||
>
|
||||
<EuiDualRange
|
||||
compressed={compressed}
|
||||
fullWidth={fullWidth}
|
||||
value={this.state.value}
|
||||
onChange={this._onChange}
|
||||
minInputProps={{
|
||||
'aria-label': i18n.translate('kibana-react.dualRangeControl.minInputAriaLabel', {
|
||||
defaultMessage: 'Range minimum',
|
||||
}),
|
||||
}}
|
||||
maxInputProps={{
|
||||
'aria-label': i18n.translate('kibana-react.dualRangeControl.maxInputAriaLabel', {
|
||||
defaultMessage: 'Range maximum',
|
||||
}),
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export class LegacyManagementAdapter {
|
|||
'management',
|
||||
{
|
||||
display: i18n.translate('management.displayName', {
|
||||
defaultMessage: 'Management',
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
},
|
||||
capabilities
|
||||
|
|
|
@ -64,7 +64,7 @@ export class ManagementApp {
|
|||
coreStart.chrome.setBreadcrumbs([
|
||||
{
|
||||
text: i18n.translate('management.breadcrumb', {
|
||||
defaultMessage: 'Management',
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
href: '#/management',
|
||||
},
|
||||
|
|
|
@ -36,8 +36,8 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart
|
|||
) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'stack-management',
|
||||
title: i18n.translate('management.displayName', {
|
||||
defaultMessage: 'Management',
|
||||
title: i18n.translate('management.stackManagement.managementLabel', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
description: i18n.translate('management.stackManagement.managementDescription', {
|
||||
defaultMessage: 'Your center console for managing the Elastic Stack.',
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
exports[`Flyout conflicts should allow conflict resolution 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
|
@ -321,11 +317,7 @@ exports[`Flyout errors should display unsupported type errors properly 1`] = `
|
|||
|
||||
exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
|
@ -586,11 +578,7 @@ Array [
|
|||
|
||||
exports[`Flyout should render import step 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
|
|
|
@ -2,12 +2,7 @@
|
|||
|
||||
exports[`Relationships should render dashboards normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -166,12 +161,7 @@ exports[`Relationships should render dashboards normally 1`] = `
|
|||
|
||||
exports[`Relationships should render errors 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -215,12 +205,7 @@ exports[`Relationships should render errors 1`] = `
|
|||
|
||||
exports[`Relationships should render index patterns normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -384,12 +369,7 @@ exports[`Relationships should render index patterns normally 1`] = `
|
|||
|
||||
exports[`Relationships should render searches normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -553,12 +533,7 @@ exports[`Relationships should render searches normally 1`] = `
|
|||
|
||||
exports[`Relationships should render visualizations normally 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
|
|
@ -3,12 +3,9 @@
|
|||
exports[`OptInDetailsComponent renders as expected 1`] = `
|
||||
<EuiPortal>
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={true}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={true}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiTitle>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
// @ts-ignore
|
||||
import React from 'react';
|
||||
import { Action, ActionContext as Context, ActionDefinition } from './action';
|
||||
import { Presentable } from '../util/presentable';
|
||||
import { uiToReactComponent } from '../../../kibana_react/public';
|
||||
|
|
|
@ -17,7 +17,6 @@ exports[`VisualizationNoResults should render according to snapshot 1`] = `
|
|||
class="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="subdued"
|
||||
data-euiicon-type="visualizeApp"
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,6 @@ exports[`VisualizationRequestError should render according to snapshot 1`] = `
|
|||
class="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="danger"
|
||||
data-euiicon-type="alert"
|
||||
/>
|
||||
|
|
|
@ -55,7 +55,7 @@ describe('<VisualizationChart/>', () => {
|
|||
|
||||
it('should render initial html', () => {
|
||||
const wrapper = render(<VisualizationChart vis={vis} listenOnChange={true} />);
|
||||
expect(wrapper.text()).toBe('Test Visualization visualization, not yet accessible');
|
||||
expect(wrapper.text()).toBe('');
|
||||
});
|
||||
|
||||
it('should render visualization', async () => {
|
||||
|
|
|
@ -77,14 +77,7 @@ class VisualizationChart extends React.Component<VisualizationChartProps> {
|
|||
public render() {
|
||||
return (
|
||||
<div className="visChart__container kbn-resetFocusState" tabIndex={0} ref={this.containerDiv}>
|
||||
<p className="euiScreenReaderOnly">
|
||||
{this.props.vis.type.title} visualization, not yet accessible
|
||||
</p>
|
||||
<div
|
||||
aria-hidden={!this.props.vis.type.isAccessible}
|
||||
className="visChart"
|
||||
ref={this.chartDiv}
|
||||
/>
|
||||
<div className="visChart" ref={this.chartDiv} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export class VisualizationNoResults extends React.Component<VisualizationNoResul
|
|||
<div className="item top" />
|
||||
<div className="item">
|
||||
<EuiText size="xs" color="subdued">
|
||||
<EuiIcon type="visualizeApp" size="m" color="subdued" aria-hidden="true" />
|
||||
<EuiIcon type="visualizeApp" size="m" color="subdued" />
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export class VisualizationRequestError extends React.Component<VisualizationRequ
|
|||
return (
|
||||
<div className="visError" ref={this.containerDiv}>
|
||||
<EuiText size="xs" color="subdued">
|
||||
<EuiIcon type="alert" size="m" color="danger" aria-hidden="true" />
|
||||
<EuiIcon type="alert" size="m" color="danger" />
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
|
|
|
@ -288,7 +288,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -319,7 +318,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -351,7 +349,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -602,7 +599,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -633,7 +629,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -665,7 +660,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -856,7 +850,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -887,7 +880,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -919,7 +911,6 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -1304,13 +1295,11 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
@ -1373,13 +1362,11 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
@ -1442,13 +1429,11 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
@ -1811,7 +1796,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -1860,7 +1844,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -1891,7 +1874,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2108,7 +2090,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2157,7 +2138,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2188,7 +2168,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2345,7 +2324,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2394,7 +2372,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2425,7 +2402,6 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
class="euiKeyPadMenuItem__icon"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
/>
|
||||
|
@ -2712,13 +2688,11 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
@ -2828,13 +2802,11 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
@ -2897,13 +2869,11 @@ exports[`NewVisModal should render as expected 1`] = `
|
|||
>
|
||||
<VisTypeIcon>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
size="l"
|
||||
type="empty"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
color="secondary"
|
||||
data-euiicon-type="empty"
|
||||
size="l"
|
||||
|
|
|
@ -34,16 +34,8 @@ interface VisTypeIconProps {
|
|||
export const VisTypeIcon = ({ icon, image }: VisTypeIconProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{image && (
|
||||
<img
|
||||
src={image}
|
||||
aria-hidden="true"
|
||||
role="presentation"
|
||||
alt=""
|
||||
className="visNewVisDialog__typeImage"
|
||||
/>
|
||||
)}
|
||||
{!image && <EuiIcon type={icon || 'empty'} size="l" color="secondary" aria-hidden="true" />}
|
||||
{image && <img src={image} alt="" className="visNewVisDialog__typeImage" />}
|
||||
{!image && <EuiIcon type={icon || 'empty'} size="l" color="secondary" />}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -162,11 +162,5 @@ export default function({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.common.clickConfirmOnModal();
|
||||
await listingTable.searchForItemWithName('');
|
||||
});
|
||||
|
||||
// Blocked by https://github.com/elastic/kibana/issues/38980
|
||||
it.skip('Open flight dashboard', async () => {
|
||||
await testSubjects.click('dashboardListingTitleLink-[Flights]-Global-Flight-Dashboard');
|
||||
await a11y.testAppSnapshot();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import expect from '@kbn/expect';
|
|||
const dashboardName = 'Dashboard Test Time';
|
||||
|
||||
export default function({ getPageObjects, getService }) {
|
||||
const PageObjects = getPageObjects(['dashboard', 'header', 'timePicker']);
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker']);
|
||||
const browser = getService('browser');
|
||||
|
||||
describe('dashboard time', () => {
|
||||
|
@ -32,7 +32,7 @@ export default function({ getPageObjects, getService }) {
|
|||
});
|
||||
|
||||
after(async function() {
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
});
|
||||
|
||||
describe('dashboard without stored timed', () => {
|
||||
|
|
|
@ -97,12 +97,12 @@ export default function({ getService, getPageObjects }) {
|
|||
await PageObjects.discover.brushHistogram();
|
||||
|
||||
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
|
||||
expect(Math.round(newDurationHours)).to.be(25);
|
||||
expect(Math.round(newDurationHours)).to.be(24);
|
||||
const rowData = await PageObjects.discover.getDocTableField(1);
|
||||
log.debug(`The first timestamp value in doc table: ${rowData}`);
|
||||
expect(Date.parse(rowData)).to.be.within(
|
||||
Date.parse('Sep 20, 2015 @ 21:30:00.000'),
|
||||
Date.parse('Sep 20, 2015 @ 23:00:00.000')
|
||||
Date.parse('Sep 20, 2015 @ 17:30:00.000'),
|
||||
Date.parse('Sep 20, 2015 @ 23:30:00.000')
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -129,29 +129,28 @@ export default function({ getService, getPageObjects }) {
|
|||
expect(enabled).to.be(false);
|
||||
});
|
||||
|
||||
// See https://github.com/elastic/kibana/issues/13137 if this test starts failing intermittently
|
||||
it('Fit data bounds should zoom to level 3', async function() {
|
||||
const expectedPrecision2DataTable = [
|
||||
['-', 'dn', '1,429', { lat: 36, lon: -85 }],
|
||||
['-', 'dp', '1,418', { lat: 41, lon: -85 }],
|
||||
['-', '9y', '1,215', { lat: 36, lon: -96 }],
|
||||
['-', '9z', '1,099', { lat: 42, lon: -96 }],
|
||||
['-', 'dr', '1,076', { lat: 42, lon: -74 }],
|
||||
['-', 'dj', '982', { lat: 31, lon: -85 }],
|
||||
['-', '9v', '938', { lat: 31, lon: -96 }],
|
||||
['-', '9q', '722', { lat: 36, lon: -120 }],
|
||||
['-', '9w', '475', { lat: 36, lon: -107 }],
|
||||
['-', 'cb', '457', { lat: 46, lon: -96 }],
|
||||
['-', 'c2', '453', { lat: 47, lon: -120 }],
|
||||
['-', '9x', '420', { lat: 41, lon: -107 }],
|
||||
['-', 'dq', '399', { lat: 37, lon: -78 }],
|
||||
['-', '9r', '396', { lat: 41, lon: -120 }],
|
||||
['-', '9t', '274', { lat: 32, lon: -107 }],
|
||||
['-', 'c8', '271', { lat: 47, lon: -107 }],
|
||||
['-', 'dh', '214', { lat: 26, lon: -82 }],
|
||||
['-', 'b6', '207', { lat: 60, lon: -162 }],
|
||||
['-', 'bd', '206', { lat: 59, lon: -153 }],
|
||||
['-', 'b7', '167', { lat: 64, lon: -163 }],
|
||||
['-', 'dr4', '127', { lat: 40, lon: -76 }],
|
||||
['-', 'dr7', '92', { lat: 41, lon: -74 }],
|
||||
['-', '9q5', '91', { lat: 34, lon: -119 }],
|
||||
['-', '9qc', '89', { lat: 38, lon: -122 }],
|
||||
['-', 'drk', '87', { lat: 41, lon: -73 }],
|
||||
['-', 'dps', '82', { lat: 42, lon: -84 }],
|
||||
['-', 'dph', '82', { lat: 40, lon: -84 }],
|
||||
['-', 'dp3', '79', { lat: 41, lon: -88 }],
|
||||
['-', 'dpe', '78', { lat: 42, lon: -86 }],
|
||||
['-', 'dp8', '77', { lat: 43, lon: -90 }],
|
||||
['-', 'dp6', '74', { lat: 41, lon: -87 }],
|
||||
['-', 'djv', '74', { lat: 33, lon: -83 }],
|
||||
['-', '9qh', '74', { lat: 34, lon: -118 }],
|
||||
['-', 'dpq', '73', { lat: 41, lon: -81 }],
|
||||
['-', 'dpp', '73', { lat: 40, lon: -80 }],
|
||||
['-', '9y7', '73', { lat: 35, lon: -97 }],
|
||||
['-', '9vg', '73', { lat: 32, lon: -97 }],
|
||||
['-', 'drs', '71', { lat: 42, lon: -73 }],
|
||||
['-', '9ys', '71', { lat: 37, lon: -95 }],
|
||||
['-', '9yn', '71', { lat: 34, lon: -93 }],
|
||||
];
|
||||
|
||||
await PageObjects.tileMap.clickMapFitDataBounds();
|
||||
|
|
|
@ -32,13 +32,13 @@ export function HeaderPageProvider({ getService, getPageObjects }: FtrProviderCo
|
|||
|
||||
class HeaderPage {
|
||||
public async clickDiscover() {
|
||||
await appsMenu.clickLink('Discover');
|
||||
await appsMenu.clickLink('Discover', { category: 'kibana' });
|
||||
await PageObjects.common.waitForTopNavToBeVisible();
|
||||
await this.awaitGlobalLoadingIndicatorHidden();
|
||||
}
|
||||
|
||||
public async clickVisualize() {
|
||||
await appsMenu.clickLink('Visualize');
|
||||
await appsMenu.clickLink('Visualize', { category: 'kibana' });
|
||||
await this.awaitGlobalLoadingIndicatorHidden();
|
||||
await retry.waitFor('first breadcrumb to be "Visualize"', async () => {
|
||||
const firstBreadcrumb = await globalNav.getFirstBreadcrumb();
|
||||
|
@ -52,7 +52,7 @@ export function HeaderPageProvider({ getService, getPageObjects }: FtrProviderCo
|
|||
}
|
||||
|
||||
public async clickDashboard() {
|
||||
await appsMenu.clickLink('Dashboard');
|
||||
await appsMenu.clickLink('Dashboard', { category: 'kibana' });
|
||||
await retry.waitFor('dashboard app to be loaded', async () => {
|
||||
const isNavVisible = await testSubjects.exists('top-nav');
|
||||
const isLandingPageVisible = await testSubjects.exists('dashboardLandingPage');
|
||||
|
@ -62,7 +62,7 @@ export function HeaderPageProvider({ getService, getPageObjects }: FtrProviderCo
|
|||
}
|
||||
|
||||
public async clickStackManagement() {
|
||||
await appsMenu.clickLink('Management');
|
||||
await appsMenu.clickLink('Stack Management', { category: 'management' });
|
||||
await this.awaitGlobalLoadingIndicatorHidden();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,16 +22,34 @@ import { FtrProviderContext } from '../ftr_provider_context';
|
|||
export function AppsMenuProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const log = getService('log');
|
||||
const find = getService('find');
|
||||
|
||||
return new (class AppsMenu {
|
||||
/**
|
||||
* Close the collapsible nav
|
||||
* TODO #64541 can replace with a data-test-subj
|
||||
*/
|
||||
public async closeCollapsibleNav() {
|
||||
const CLOSE_BUTTON = '[data-test-subj=collapsibleNav] > button';
|
||||
if (await find.existsByCssSelector(CLOSE_BUTTON)) {
|
||||
(await find.byCssSelector(CLOSE_BUTTON)).click();
|
||||
}
|
||||
}
|
||||
|
||||
public async openCollapsibleNav() {
|
||||
if (!(await testSubjects.exists('collapsibleNav'))) {
|
||||
await testSubjects.click('toggleNavButton');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes from each of the links in the apps menu
|
||||
*/
|
||||
public async readLinks() {
|
||||
const appMenu = await testSubjects.find('navDrawer');
|
||||
await this.openCollapsibleNav();
|
||||
const appMenu = await testSubjects.find('collapsibleNav');
|
||||
const $ = await appMenu.parseDomContent();
|
||||
|
||||
const links = $.findTestSubjects('navDrawerAppsMenuLink')
|
||||
const links = $.findTestSubjects('collapsibleNavAppLink')
|
||||
.toArray()
|
||||
.map(link => {
|
||||
return {
|
||||
|
@ -41,6 +59,8 @@ export function AppsMenuProvider({ getService }: FtrProviderContext) {
|
|||
};
|
||||
});
|
||||
|
||||
await this.closeCollapsibleNav();
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
|
@ -63,14 +83,32 @@ export function AppsMenuProvider({ getService }: FtrProviderContext) {
|
|||
/**
|
||||
* Click the app link within the app menu that has the given name
|
||||
* @param name
|
||||
* @param options.closeCollapsibleNav
|
||||
* @param options.category - optional field to ensure that a link is clicked in a particular category
|
||||
* helpful when there may be a recent link with the same name as an app
|
||||
*/
|
||||
public async clickLink(name: string) {
|
||||
public async clickLink(
|
||||
name: string,
|
||||
{
|
||||
closeCollapsibleNav = true,
|
||||
category,
|
||||
}: { closeCollapsibleNav?: boolean; category?: string } = {}
|
||||
) {
|
||||
try {
|
||||
log.debug(`click "${name}" app link`);
|
||||
const container = await testSubjects.find('navDrawer');
|
||||
// Text content is not visible or selectable (0px width) so we use an attr with th same value
|
||||
const link = await container.findByCssSelector(`[aria-label='${name}']`);
|
||||
await this.openCollapsibleNav();
|
||||
let nav;
|
||||
if (typeof category === 'string') {
|
||||
nav = await testSubjects.find(`collapsibleNavGroup-${category}`);
|
||||
} else {
|
||||
nav = await testSubjects.find('collapsibleNav');
|
||||
}
|
||||
const link = await nav.findByPartialLinkText(name);
|
||||
await link.click();
|
||||
|
||||
if (closeCollapsibleNav) {
|
||||
await this.closeCollapsibleNav();
|
||||
}
|
||||
} finally {
|
||||
// Intentionally empty
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0"
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"react": "^16.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"react": "^16.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"react": "^16.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -59,19 +59,19 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
describe('when navigating to a legacy app', () => {
|
||||
it('prevents navigation if user click cancel on the alert dialog', async () => {
|
||||
await PageObjects.common.navigateToApp('appleave1');
|
||||
await appsMenu.clickLink('Core Legacy Compat');
|
||||
await appsMenu.clickLink('Core Legacy Compat', { closeCollapsibleNav: false });
|
||||
|
||||
const alert = await browser.getAlert();
|
||||
expect(alert).not.to.eql(undefined);
|
||||
expect(alert).not.to.eql(null);
|
||||
alert!.dismiss();
|
||||
expect(await browser.getCurrentUrl()).to.eql(getKibanaUrl('/app/appleave1'));
|
||||
});
|
||||
it('allows navigation if user click leave on the alert dialog', async () => {
|
||||
await PageObjects.common.navigateToApp('appleave1');
|
||||
await appsMenu.clickLink('Core Legacy Compat');
|
||||
await appsMenu.clickLink('Core Legacy Compat', { closeCollapsibleNav: false });
|
||||
|
||||
const alert = await browser.getAlert();
|
||||
expect(alert).not.to.eql(undefined);
|
||||
expect(alert).not.to.eql(null);
|
||||
alert!.accept();
|
||||
expect(await browser.getCurrentUrl()).to.eql(getKibanaUrl('/app/core_legacy_compat'));
|
||||
});
|
||||
|
|
|
@ -75,14 +75,6 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
});
|
||||
link = await appsMenu.getLink('App Status');
|
||||
expect(link).to.eql(undefined);
|
||||
|
||||
await setAppStatus({
|
||||
navLinkStatus: AppNavLinkStatus.visible,
|
||||
tooltip: 'Some tooltip',
|
||||
});
|
||||
link = await appsMenu.getLink('Some tooltip'); // the tooltip replaces the name in the selector we use.
|
||||
expect(link).not.to.eql(undefined);
|
||||
expect(link!.disabled).to.eql(false);
|
||||
});
|
||||
|
||||
it('shows an error when navigating to an inaccessible app', async () => {
|
||||
|
|
|
@ -33,11 +33,9 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
const loadingScreenNotShown = async () =>
|
||||
expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false);
|
||||
|
||||
const loadingScreenShown = () => testSubjects.existOrFail('kbnLoadingMessage');
|
||||
|
||||
const getAppWrapperWidth = async () => {
|
||||
const getAppWrapperHeight = async () => {
|
||||
const wrapper = await find.byClassName('app-wrapper');
|
||||
return (await wrapper.getSize()).width;
|
||||
return (await wrapper.getSize()).height;
|
||||
};
|
||||
|
||||
const getKibanaUrl = (pathname?: string, search?: string) =>
|
||||
|
@ -90,7 +88,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
it('navigates to app root when navlink is clicked', async () => {
|
||||
await appsMenu.clickLink('Foo');
|
||||
await waitForUrlToBe('/app/foo');
|
||||
await loadingScreenNotShown();
|
||||
// await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
});
|
||||
|
||||
|
@ -122,9 +120,9 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
await loadingScreenNotShown();
|
||||
expect(await testSubjects.exists('headerGlobalNav')).to.be(false);
|
||||
|
||||
const wrapperWidth = await getAppWrapperWidth();
|
||||
const windowWidth = (await browser.getWindowSize()).width;
|
||||
expect(wrapperWidth).to.eql(windowWidth);
|
||||
const wrapperHeight = await getAppWrapperHeight();
|
||||
const windowHeight = (await browser.getWindowSize()).height;
|
||||
expect(wrapperHeight).to.eql(windowHeight);
|
||||
});
|
||||
|
||||
it('navigating away from chromeless application shows chrome', async () => {
|
||||
|
@ -132,20 +130,18 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
await loadingScreenNotShown();
|
||||
expect(await testSubjects.exists('headerGlobalNav')).to.be(true);
|
||||
|
||||
const wrapperWidth = await getAppWrapperWidth();
|
||||
const windowWidth = (await browser.getWindowSize()).width;
|
||||
expect(wrapperWidth).to.be.below(windowWidth);
|
||||
const wrapperHeight = await getAppWrapperHeight();
|
||||
const windowHeight = (await browser.getWindowSize()).height;
|
||||
expect(wrapperHeight).to.be.below(windowHeight);
|
||||
});
|
||||
|
||||
it('can navigate from NP apps to legacy apps', async () => {
|
||||
await appsMenu.clickLink('Management');
|
||||
await loadingScreenShown();
|
||||
await appsMenu.clickLink('Stack Management');
|
||||
await testSubjects.existOrFail('managementNav');
|
||||
});
|
||||
|
||||
it('can navigate from legacy apps to NP apps', async () => {
|
||||
await appsMenu.clickLink('Foo');
|
||||
await loadingScreenShown();
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -61,7 +61,7 @@ export function dashboardMode(kibana) {
|
|||
title: i18n.translate('xpack.dashboardMode.dashboardViewer.dashboardTitle', {
|
||||
defaultMessage: 'Dashboard',
|
||||
}),
|
||||
order: -1001,
|
||||
order: 1000,
|
||||
url: `${kbnBaseUrl}#/dashboards`,
|
||||
subUrlBase: `${kbnBaseUrl}#/dashboard`,
|
||||
description: i18n.translate(
|
||||
|
@ -71,7 +71,7 @@ export function dashboardMode(kibana) {
|
|||
}
|
||||
),
|
||||
icon: 'plugins/kibana/dashboard/assets/dashboard.svg',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -34,7 +34,8 @@ export function maps(kibana) {
|
|||
main: 'plugins/maps/legacy',
|
||||
icon: 'plugins/maps/icon.svg',
|
||||
euiIconType: APP_ICON,
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
order: 4000,
|
||||
},
|
||||
injectDefaultVars(server) {
|
||||
const serverConfig = server.config();
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
"@elastic/apm-rum-react": "^1.1.1",
|
||||
"@elastic/datemath": "5.0.3",
|
||||
"@elastic/ems-client": "7.8.0",
|
||||
"@elastic/eui": "22.3.0",
|
||||
"@elastic/eui": "22.3.1",
|
||||
"@elastic/filesaver": "1.1.2",
|
||||
"@elastic/maki": "6.3.0",
|
||||
"@elastic/node-crypto": "1.1.1",
|
||||
|
|
|
@ -5,6 +5,8 @@ body.canvas-isFullscreen { // sass-lint:disable-line no-qualifying-elements
|
|||
}
|
||||
|
||||
// remove space for global nav elements
|
||||
// TODO #64541
|
||||
// Can delete this block
|
||||
.chrHeaderWrapper ~ .app-wrapper {
|
||||
// Override locked nav at all breakpoints
|
||||
left: 0 !important; // sass-lint:disable-line no-important
|
||||
|
|
|
@ -69,7 +69,7 @@ export class CanvasPlugin
|
|||
this.srcPlugin.setup(core, { canvas: canvasApi });
|
||||
|
||||
core.application.register({
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
id: 'canvas',
|
||||
title: 'Canvas',
|
||||
euiIconType: 'canvasApp',
|
||||
|
|
|
@ -33,7 +33,7 @@ storiesOf('components/FlyoutFrame', module)
|
|||
})
|
||||
.add('open in flyout', () => {
|
||||
return (
|
||||
<EuiFlyout>
|
||||
<EuiFlyout onClose={() => {}}>
|
||||
<FlyoutFrame
|
||||
title="Create drilldown"
|
||||
footer={<EuiButton>Save</EuiButton>}
|
||||
|
|
|
@ -66,10 +66,10 @@ export class GraphPlugin
|
|||
core.application.register({
|
||||
id: 'graph',
|
||||
title: 'Graph',
|
||||
order: 9000,
|
||||
order: 6000,
|
||||
appRoute: '/app/graph',
|
||||
euiIconType: 'graphApp',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const [coreStart, pluginsStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./application');
|
||||
|
|
|
@ -64,7 +64,7 @@ export class Plugin
|
|||
defaultMessage: 'Logs',
|
||||
}),
|
||||
euiIconType: 'logsApp',
|
||||
order: 8001,
|
||||
order: 8000,
|
||||
appRoute: '/app/logs',
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
|
@ -89,7 +89,7 @@ export class Plugin
|
|||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
euiIconType: 'metricsApp',
|
||||
order: 8000,
|
||||
order: 8001,
|
||||
appRoute: '/app/metrics',
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#maps-plugin {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - #{$euiHeaderChildSize});
|
||||
height: calc(100vh - #{$euiHeaderHeightCompensation});
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ exports[`RuleEditorFlyout renders the flyout after adding a condition to a rule
|
|||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
className="ml-rule-editor-flyout"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -259,12 +254,7 @@ exports[`RuleEditorFlyout renders the flyout after setting the rule to edit 1`]
|
|||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
className="ml-rule-editor-flyout"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -523,12 +513,7 @@ exports[`RuleEditorFlyout renders the flyout for creating a rule with conditions
|
|||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
className="ml-rule-editor-flyout"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -765,12 +750,7 @@ exports[`RuleEditorFlyout renders the select action component for a detector wit
|
|||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
className="ml-rule-editor-flyout"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { LicenseManagementUIPluginSetup } from '../../license_management/public'
|
|||
import { setDependencyCache } from './application/util/dependency_cache';
|
||||
import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app';
|
||||
import { registerFeature } from './register_feature';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
|
||||
import { registerEmbeddables } from './embeddables';
|
||||
import { UiActionsSetup } from '../../../../src/plugins/ui_actions/public';
|
||||
import { registerMlUiActions } from './ui_actions';
|
||||
|
@ -46,9 +47,10 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
|
|||
title: i18n.translate('xpack.ml.plugin.title', {
|
||||
defaultMessage: 'Machine Learning',
|
||||
}),
|
||||
order: 30,
|
||||
order: 5000,
|
||||
euiIconType: PLUGIN_ICON,
|
||||
appRoute: '/app/ml',
|
||||
category: DEFAULT_APP_CATEGORIES.kibana,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const [coreStart, pluginsStart] = await core.getStartServices();
|
||||
const { renderApp } = await import('./application/app');
|
||||
|
|
|
@ -3,12 +3,7 @@
|
|||
exports[`Status should render a flyout when clicking the link 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
exports[`DetailDrawer component If vertices shows basic info and no stats for if 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
|
@ -61,11 +57,7 @@ exports[`DetailDrawer component If vertices shows basic info and no stats for if
|
|||
|
||||
exports[`DetailDrawer component Plugin vertices Plugin does not have explicit ID shows basic info and stats for plugin, suggesting that user set explicit ID 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
|
@ -318,11 +310,7 @@ exports[`DetailDrawer component Plugin vertices Plugin does not have explicit ID
|
|||
|
||||
exports[`DetailDrawer component Plugin vertices Plugin has explicit ID shows basic info and stats for plugin, including explicit ID 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
|
@ -569,11 +557,7 @@ exports[`DetailDrawer component Plugin vertices Plugin has explicit ID shows bas
|
|||
|
||||
exports[`DetailDrawer component Queue vertices shows basic info and no stats for queue 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
|
@ -620,11 +604,7 @@ exports[`DetailDrawer component Queue vertices shows basic info and no stats for
|
|||
|
||||
exports[`DetailDrawer component shows vertex title 1`] = `
|
||||
<EuiFlyout
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[MockFunction]}
|
||||
ownFocus={false}
|
||||
size="s"
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
|
|
|
@ -3,12 +3,7 @@
|
|||
exports[`Flyout apm part one should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -342,12 +337,7 @@ exports[`Flyout apm part two should show instructions to migrate to metricbeat 1
|
|||
exports[`Flyout beats part one should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -723,12 +713,7 @@ exports[`Flyout beats part two should show instructions to migrate to metricbeat
|
|||
exports[`Flyout elasticsearch part one should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -1064,12 +1049,7 @@ exports[`Flyout elasticsearch part two should show instructions to migrate to me
|
|||
exports[`Flyout kibana part one should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
@ -1412,12 +1392,7 @@ exports[`Flyout kibana part two should show instructions to migrate to metricbea
|
|||
exports[`Flyout logstash part one should render normally 1`] = `
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={false}
|
||||
size="m"
|
||||
>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { NavType } from 'src/core/public';
|
||||
import { formatRequestPayload, formatJson } from '../lib/format';
|
||||
import { exampleScript } from '../constants';
|
||||
import { PayloadFormat } from '../types';
|
||||
|
@ -23,7 +24,7 @@ export const Main: React.FunctionComponent = () => {
|
|||
updatePayload,
|
||||
services: {
|
||||
http,
|
||||
chrome: { getIsNavDrawerLocked$ },
|
||||
chrome: { getIsNavDrawerLocked$, getNavType$ },
|
||||
},
|
||||
links,
|
||||
} = useAppContext();
|
||||
|
@ -43,6 +44,7 @@ export const Main: React.FunctionComponent = () => {
|
|||
};
|
||||
|
||||
const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false);
|
||||
const [isNavLegacy, setIsNavLegacy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => {
|
||||
|
@ -52,6 +54,14 @@ export const Main: React.FunctionComponent = () => {
|
|||
return () => subscription.unsubscribe();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = getNavType$().subscribe((navType: NavType) => {
|
||||
setIsNavLegacy(navType === 'legacy');
|
||||
});
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="painlessLabMainContainer">
|
||||
<EuiFlexGroup className="painlessLabPanelsContainer" responsive={false} gutterSize="none">
|
||||
|
@ -78,6 +88,7 @@ export const Main: React.FunctionComponent = () => {
|
|||
toggleRequestFlyout={toggleRequestFlyout}
|
||||
isRequestFlyoutOpen={isRequestFlyoutOpen}
|
||||
isNavDrawerLocked={isNavDrawerLocked}
|
||||
isNavLegacy={isNavLegacy}
|
||||
reset={() => updatePayload({ code: exampleScript })}
|
||||
/>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
EuiPopover,
|
||||
EuiBottomBar,
|
||||
|
@ -15,7 +14,7 @@ import {
|
|||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Links } from '../../links';
|
||||
|
||||
interface Props {
|
||||
|
@ -25,6 +24,7 @@ interface Props {
|
|||
reset: () => void;
|
||||
links: Links;
|
||||
isNavDrawerLocked: boolean;
|
||||
isNavLegacy: boolean;
|
||||
}
|
||||
|
||||
export function MainControls({
|
||||
|
@ -33,6 +33,7 @@ export function MainControls({
|
|||
reset,
|
||||
links,
|
||||
isNavDrawerLocked,
|
||||
isNavLegacy,
|
||||
}: Props) {
|
||||
const [isHelpOpen, setIsHelpOpen] = useState(false);
|
||||
|
||||
|
@ -87,9 +88,14 @@ export function MainControls({
|
|||
</EuiContextMenuItem>,
|
||||
];
|
||||
|
||||
const classes = classNames('painlessLab__bottomBar', {
|
||||
'painlessLab__bottomBar-isNavDrawerLocked': isNavDrawerLocked,
|
||||
});
|
||||
// TODO #64541
|
||||
// Can delete all this class stuff
|
||||
let classes = '';
|
||||
if (isNavLegacy) {
|
||||
classes = classNames('painlessLab__bottomBar', {
|
||||
'painlessLab__bottomBar-isNavDrawerLocked': isNavDrawerLocked,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiBottomBar paddingSize="s" className={classes}>
|
||||
|
@ -116,7 +122,7 @@ export function MainControls({
|
|||
closePopover={() => setIsHelpOpen(false)}
|
||||
panelPaddingSize="none"
|
||||
withTitle
|
||||
anchorPosition="upRight"
|
||||
anchorPosition="upLeft"
|
||||
>
|
||||
<EuiContextMenuPanel items={items} />
|
||||
</EuiPopover>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
$bottomBarHeight: calc(#{$euiSize} * 3);
|
||||
|
||||
.painlessLabBottomBarPlaceholder {
|
||||
height: $bottomBarHeight
|
||||
height: $bottomBarHeight;
|
||||
}
|
||||
|
||||
.painlessLabLeftPane {
|
||||
|
|
|
@ -28,10 +28,7 @@ exports[`ReportInfoButton opens flyout with fetch error info 1`] = `
|
|||
Array [
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
data-test-subj="reportInfoFlyout"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={true}
|
||||
size="s"
|
||||
|
@ -136,7 +133,7 @@ Array [
|
|||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
aria-label="Close this dialog"
|
||||
class="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
|
@ -196,7 +193,7 @@ Array [
|
|||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
aria-label="Close this dialog"
|
||||
class="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
|
@ -249,36 +246,41 @@ Array [
|
|||
role="dialog"
|
||||
tabIndex={0}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Closes this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
<EuiI18n
|
||||
default="Close this dialog"
|
||||
token="euiFlyout.closeAriaLabel"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
<EuiButtonIcon
|
||||
aria-label="Close this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
size="m"
|
||||
type="cross"
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
type="cross"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
</EuiI18n>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
>
|
||||
|
@ -348,36 +350,41 @@ Array [
|
|||
role="dialog"
|
||||
tabIndex={0}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Closes this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
<EuiI18n
|
||||
default="Close this dialog"
|
||||
token="euiFlyout.closeAriaLabel"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
<EuiButtonIcon
|
||||
aria-label="Close this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
size="m"
|
||||
type="cross"
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
type="cross"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
</EuiI18n>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
>
|
||||
|
@ -425,10 +432,7 @@ exports[`ReportInfoButton opens flyout with info 1`] = `
|
|||
Array [
|
||||
<EuiFlyout
|
||||
aria-labelledby="flyoutTitle"
|
||||
closeButtonAriaLabel="Closes this dialog"
|
||||
data-test-subj="reportInfoFlyout"
|
||||
hideCloseButton={false}
|
||||
maxWidth={false}
|
||||
onClose={[Function]}
|
||||
ownFocus={true}
|
||||
size="s"
|
||||
|
@ -533,7 +537,7 @@ Array [
|
|||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
aria-label="Close this dialog"
|
||||
class="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
|
@ -591,7 +595,7 @@ Array [
|
|||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
aria-label="Close this dialog"
|
||||
class="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
type="button"
|
||||
|
@ -642,36 +646,41 @@ Array [
|
|||
role="dialog"
|
||||
tabIndex={0}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Closes this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
<EuiI18n
|
||||
default="Close this dialog"
|
||||
token="euiFlyout.closeAriaLabel"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
<EuiButtonIcon
|
||||
aria-label="Close this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
size="m"
|
||||
type="cross"
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
type="cross"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
</EuiI18n>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
>
|
||||
|
@ -739,36 +748,41 @@ Array [
|
|||
role="dialog"
|
||||
tabIndex={0}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label="Closes this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
<EuiI18n
|
||||
default="Close this dialog"
|
||||
token="euiFlyout.closeAriaLabel"
|
||||
>
|
||||
<button
|
||||
aria-label="Closes this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
<EuiButtonIcon
|
||||
aria-label="Close this dialog"
|
||||
className="euiFlyout__closeButton"
|
||||
color="text"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
iconType="cross"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
size="m"
|
||||
type="cross"
|
||||
<button
|
||||
aria-label="Close this dialog"
|
||||
className="euiButtonIcon euiButtonIcon--text euiFlyout__closeButton"
|
||||
data-test-subj="euiFlyoutCloseButton"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
<EuiIcon
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
type="cross"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="euiButtonIcon__icon"
|
||||
data-euiicon-type="cross"
|
||||
size="m"
|
||||
/>
|
||||
</EuiIcon>
|
||||
</button>
|
||||
</EuiButtonIcon>
|
||||
</EuiI18n>
|
||||
<EuiFlyoutHeader
|
||||
hasBorder={true}
|
||||
>
|
||||
|
|
|
@ -151,7 +151,7 @@ export class FieldRuleEditor extends Component<Props, {}> {
|
|||
)}
|
||||
/>
|
||||
) : (
|
||||
<EuiIcon size="l" type="empty" aria-hidden={true} />
|
||||
<EuiIcon size="l" type="empty" />
|
||||
)}
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('SecurityNavControl', () => {
|
|||
const wrapper = shallowWithIntl(<SecurityNavControl {...props} />);
|
||||
const { button } = wrapper.find(EuiPopover).props();
|
||||
expect(button).toMatchInlineSnapshot(`
|
||||
<EuiHeaderSectionItemButton
|
||||
<ForwardRef
|
||||
aria-controls="headerUserMenu"
|
||||
aria-expanded={false}
|
||||
aria-haspopup="true"
|
||||
|
@ -33,7 +33,7 @@ describe('SecurityNavControl', () => {
|
|||
<EuiLoadingSpinner
|
||||
size="m"
|
||||
/>
|
||||
</EuiHeaderSectionItemButton>
|
||||
</ForwardRef>
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ describe('SecurityNavControl', () => {
|
|||
wrapper.update();
|
||||
const { button } = wrapper.find(EuiPopover).props();
|
||||
expect(button).toMatchInlineSnapshot(`
|
||||
<EuiHeaderSectionItemButton
|
||||
<ForwardRef
|
||||
aria-controls="headerUserMenu"
|
||||
aria-expanded={false}
|
||||
aria-haspopup="true"
|
||||
|
@ -61,7 +61,7 @@ describe('SecurityNavControl', () => {
|
|||
name="foo"
|
||||
size="s"
|
||||
/>
|
||||
</EuiHeaderSectionItemButton>
|
||||
</ForwardRef>
|
||||
`);
|
||||
});
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue