mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Grouped Kibana nav (#53545)
Adds concept of `category` to nav links, grouping them by this in the side nav
This commit is contained in:
parent
01fe8afb98
commit
da54657b91
109 changed files with 6419 additions and 444 deletions
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppBase](./kibana-plugin-public.appbase.md) > [category](./kibana-plugin-public.appbase.category.md)
|
||||
|
||||
## AppBase.category property
|
||||
|
||||
The category definition of the product See [AppCategory](./kibana-plugin-public.appcategory.md) See DEFAULT\_APP\_CATEGORIES for more reference
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
category?: AppCategory;
|
||||
```
|
|
@ -16,6 +16,7 @@ export interface AppBase
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [capabilities](./kibana-plugin-public.appbase.capabilities.md) | <code>Partial<Capabilities></code> | Custom capabilities defined by the app. |
|
||||
| [category](./kibana-plugin-public.appbase.category.md) | <code>AppCategory</code> | The category definition of the product See [AppCategory](./kibana-plugin-public.appcategory.md) See DEFAULT\_APP\_CATEGORIES for more reference |
|
||||
| [chromeless](./kibana-plugin-public.appbase.chromeless.md) | <code>boolean</code> | Hide the UI chrome when the application is mounted. Defaults to <code>false</code>. Takes precedence over chrome service visibility settings. |
|
||||
| [euiIconType](./kibana-plugin-public.appbase.euiicontype.md) | <code>string</code> | A EUI iconType that will be used for the app's icon. This icon takes precendence over the <code>icon</code> property. |
|
||||
| [icon](./kibana-plugin-public.appbase.icon.md) | <code>string</code> | A URL to an image file used as an icon. Used as a fallback if <code>euiIconType</code> is not provided. |
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppCategory](./kibana-plugin-public.appcategory.md) > [ariaLabel](./kibana-plugin-public.appcategory.arialabel.md)
|
||||
|
||||
## AppCategory.ariaLabel property
|
||||
|
||||
If the visual label isn't appropriate for screen readers, can override it here
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
ariaLabel?: string;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppCategory](./kibana-plugin-public.appcategory.md) > [euiIconType](./kibana-plugin-public.appcategory.euiicontype.md)
|
||||
|
||||
## AppCategory.euiIconType property
|
||||
|
||||
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
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
euiIconType?: string;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppCategory](./kibana-plugin-public.appcategory.md) > [label](./kibana-plugin-public.appcategory.label.md)
|
||||
|
||||
## AppCategory.label property
|
||||
|
||||
Label used for cateogry name. Also used as aria-label if one isn't set.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
label: string;
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppCategory](./kibana-plugin-public.appcategory.md)
|
||||
|
||||
## AppCategory interface
|
||||
|
||||
A category definition for nav links to know where to sort them in the left hand nav
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface AppCategory
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [ariaLabel](./kibana-plugin-public.appcategory.arialabel.md) | <code>string</code> | If the visual label isn't appropriate for screen readers, can override it here |
|
||||
| [euiIconType](./kibana-plugin-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 |
|
||||
| [label](./kibana-plugin-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-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,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [AppCategory](./kibana-plugin-public.appcategory.md) > [order](./kibana-plugin-public.appcategory.order.md)
|
||||
|
||||
## AppCategory.order property
|
||||
|
||||
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)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
order?: number;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [ChromeNavLink](./kibana-plugin-public.chromenavlink.md) > [category](./kibana-plugin-public.chromenavlink.category.md)
|
||||
|
||||
## ChromeNavLink.category property
|
||||
|
||||
The category the app lives in
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly category?: AppCategory;
|
||||
```
|
|
@ -17,6 +17,7 @@ export interface ChromeNavLink
|
|||
| --- | --- | --- |
|
||||
| [active](./kibana-plugin-public.chromenavlink.active.md) | <code>boolean</code> | Indicates whether or not this app is currently on the screen. |
|
||||
| [baseUrl](./kibana-plugin-public.chromenavlink.baseurl.md) | <code>string</code> | The base route used to open the root of an application. |
|
||||
| [category](./kibana-plugin-public.chromenavlink.category.md) | <code>AppCategory</code> | The category the app lives in |
|
||||
| [disabled](./kibana-plugin-public.chromenavlink.disabled.md) | <code>boolean</code> | Disables a link from being clickable. |
|
||||
| [euiIconType](./kibana-plugin-public.chromenavlink.euiicontype.md) | <code>string</code> | A EUI iconType that will be used for the app's icon. This icon takes precendence over the <code>icon</code> property. |
|
||||
| [hidden](./kibana-plugin-public.chromenavlink.hidden.md) | <code>boolean</code> | Hides a link from the navigation. |
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-public](./kibana-plugin-public.md) > [LegacyNavLink](./kibana-plugin-public.legacynavlink.md) > [category](./kibana-plugin-public.legacynavlink.category.md)
|
||||
|
||||
## LegacyNavLink.category property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
category?: AppCategory;
|
||||
```
|
|
@ -15,6 +15,7 @@ export interface LegacyNavLink
|
|||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [category](./kibana-plugin-public.legacynavlink.category.md) | <code>AppCategory</code> | |
|
||||
| [euiIconType](./kibana-plugin-public.legacynavlink.euiicontype.md) | <code>string</code> | |
|
||||
| [icon](./kibana-plugin-public.legacynavlink.icon.md) | <code>string</code> | |
|
||||
| [id](./kibana-plugin-public.legacynavlink.id.md) | <code>string</code> | |
|
||||
|
|
|
@ -32,6 +32,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| --- | --- |
|
||||
| [App](./kibana-plugin-public.app.md) | Extension of [common app properties](./kibana-plugin-public.appbase.md) with the mount function. |
|
||||
| [AppBase](./kibana-plugin-public.appbase.md) | |
|
||||
| [AppCategory](./kibana-plugin-public.appcategory.md) | A category definition for nav links to know where to sort them in the left hand nav |
|
||||
| [AppLeaveConfirmAction](./kibana-plugin-public.appleaveconfirmaction.md) | Action to return from a [AppLeaveHandler](./kibana-plugin-public.appleavehandler.md) to show a confirmation message when trying to leave an application.<!-- -->See |
|
||||
| [AppLeaveDefaultAction](./kibana-plugin-public.appleavedefaultaction.md) | Action to return from a [AppLeaveHandler](./kibana-plugin-public.appleavehandler.md) to execute the default behaviour when leaving the application.<!-- -->See |
|
||||
| [ApplicationSetup](./kibana-plugin-public.applicationsetup.md) | |
|
||||
|
|
|
@ -17,15 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { unique } from './core/helper';
|
||||
import { Translation } from './translation';
|
||||
|
||||
const asyncReadFile = promisify(readFile);
|
||||
|
||||
const TRANSLATION_FILE_EXTENSION = '.json';
|
||||
|
||||
/**
|
||||
|
@ -69,7 +67,8 @@ function getLocaleFromFileName(fullFileName: string) {
|
|||
* @returns
|
||||
*/
|
||||
async function loadFile(pathToFile: string): Promise<Translation> {
|
||||
return JSON.parse(await asyncReadFile(pathToFile, 'utf8'));
|
||||
// doing this at the moment because fs is mocked in a lot of places where this would otherwise fail
|
||||
return JSON.parse(await promisify(fs.readFile)(pathToFile, 'utf8'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import { PluginOpaqueId } from '../plugins';
|
|||
import { IUiSettingsClient } from '../ui_settings';
|
||||
import { RecursiveReadonly } from '../../utils';
|
||||
import { SavedObjectsStart } from '../saved_objects';
|
||||
import { AppCategory } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export interface AppBase {
|
||||
|
@ -44,6 +45,13 @@ export interface AppBase {
|
|||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* The category definition of the product
|
||||
* See {@link AppCategory}
|
||||
* See DEFAULT_APP_CATEGORIES for more reference
|
||||
*/
|
||||
category?: AppCategory;
|
||||
|
||||
/**
|
||||
* The initial status of the application.
|
||||
* Defaulting to `accessible`
|
||||
|
|
|
@ -29,6 +29,7 @@ import { notificationServiceMock } from '../notifications/notifications_service.
|
|||
import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
|
||||
import { ChromeService } from './chrome_service';
|
||||
import { App } from '../application';
|
||||
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
|
||||
|
||||
class FakeApp implements App {
|
||||
public title = `${this.id} App`;
|
||||
|
@ -51,6 +52,7 @@ function defaultStartDeps(availableApps?: App[]) {
|
|||
http: httpServiceMock.createStartContract(),
|
||||
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
};
|
||||
|
||||
if (availableApps) {
|
||||
|
|
|
@ -38,7 +38,7 @@ import { LoadingIndicator, HeaderWrapper as Header } from './ui';
|
|||
import { DocLinksStart } from '../doc_links';
|
||||
import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu';
|
||||
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
|
||||
|
||||
import { IUiSettingsClient } from '../ui_settings';
|
||||
export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle };
|
||||
|
||||
const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed';
|
||||
|
@ -85,6 +85,7 @@ interface StartDeps {
|
|||
http: HttpStart;
|
||||
injectedMetadata: InjectedMetadataStart;
|
||||
notifications: NotificationsStart;
|
||||
uiSettings: IUiSettingsClient;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -139,6 +140,7 @@ export class ChromeService {
|
|||
http,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
uiSettings,
|
||||
}: StartDeps): Promise<InternalChromeStart> {
|
||||
this.initVisibility(application);
|
||||
|
||||
|
@ -173,7 +175,6 @@ export class ChromeService {
|
|||
getHeaderComponent: () => (
|
||||
<React.Fragment>
|
||||
<LoadingIndicator loadingCount$={http.getLoadingCount$()} />
|
||||
|
||||
<Header
|
||||
application={application}
|
||||
appTitle$={appTitle$.pipe(takeUntil(this.stop$))}
|
||||
|
@ -192,6 +193,7 @@ export class ChromeService {
|
|||
recentlyAccessed$={recentlyAccessed.get$()}
|
||||
navControlsLeft$={navControls.getLeft$()}
|
||||
navControlsRight$={navControls.getRight$()}
|
||||
navSetting$={uiSettings.get$('pageNavigation')}
|
||||
/>
|
||||
</React.Fragment>
|
||||
),
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { pick } from '../../../utils';
|
||||
import { AppCategory } from '../../';
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -33,6 +34,11 @@ export interface ChromeNavLink {
|
|||
*/
|
||||
readonly title: string;
|
||||
|
||||
/**
|
||||
* The category the app lives in
|
||||
*/
|
||||
readonly category?: AppCategory;
|
||||
|
||||
/**
|
||||
* The base route used to open the root of an application.
|
||||
*/
|
||||
|
|
5224
src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap
generated
Normal file
5224
src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -17,141 +17,40 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import Url from 'url';
|
||||
|
||||
import React, { Component, createRef } from 'react';
|
||||
import * as Rx from 'rxjs';
|
||||
|
||||
import {
|
||||
// TODO: add type annotations
|
||||
EuiHeader,
|
||||
EuiHeaderLogo,
|
||||
EuiHeaderSection,
|
||||
EuiHeaderSectionItem,
|
||||
EuiHeaderSectionItemButton,
|
||||
EuiHorizontalRule,
|
||||
EuiIcon,
|
||||
EuiImage,
|
||||
// @ts-ignore
|
||||
EuiNavDrawer,
|
||||
// @ts-ignore
|
||||
EuiNavDrawerGroup,
|
||||
// @ts-ignore
|
||||
EuiShowFor,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
import { HeaderBadge } from './header_badge';
|
||||
import { HeaderBreadcrumbs } from './header_breadcrumbs';
|
||||
import { HeaderHelpMenu } from './header_help_menu';
|
||||
import { HeaderNavControls } from './header_nav_controls';
|
||||
|
||||
import React, { Component, createRef } from 'react';
|
||||
import * as Rx from 'rxjs';
|
||||
import {
|
||||
ChromeBadge,
|
||||
ChromeBreadcrumb,
|
||||
ChromeNavControl,
|
||||
ChromeNavLink,
|
||||
ChromeRecentlyAccessedHistoryItem,
|
||||
ChromeNavControl,
|
||||
} from '../..';
|
||||
import { InternalApplicationStart } from '../../../application/types';
|
||||
import { HttpStart } from '../../../http';
|
||||
import { ChromeHelpExtension } from '../../chrome_service';
|
||||
import { InternalApplicationStart } from '../../../application/types';
|
||||
import { HeaderBadge } from './header_badge';
|
||||
import { NavSetting, 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 { HeaderLogo } from './header_logo';
|
||||
import { NavDrawer } from './nav_drawer';
|
||||
|
||||
// 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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) {
|
||||
// convert all link urls to absolute urls
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', url);
|
||||
return a.href;
|
||||
}
|
||||
|
||||
function extendRecentlyAccessedHistoryItem(
|
||||
navLinks: ChromeNavLink[],
|
||||
recentlyAccessed: ChromeRecentlyAccessedHistoryItem,
|
||||
basePath: HttpStart['basePath']
|
||||
) {
|
||||
const href = relativeToAbsolute(basePath.prepend(recentlyAccessed.link));
|
||||
const navLink = navLinks.find(nl => href.startsWith(nl.subUrlBase || nl.baseUrl));
|
||||
|
||||
let titleAndAriaLabel = recentlyAccessed.label;
|
||||
if (navLink) {
|
||||
const objectTypeForAriaAppendix = navLink.title;
|
||||
titleAndAriaLabel = i18n.translate('core.ui.recentLinks.linkItem.screenReaderLabel', {
|
||||
defaultMessage: '{recentlyAccessedItemLinklabel}, type: {pageType}',
|
||||
values: {
|
||||
recentlyAccessedItemLinklabel: recentlyAccessed.label,
|
||||
pageType: objectTypeForAriaAppendix,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...recentlyAccessed,
|
||||
href,
|
||||
euiIconType: navLink ? navLink.euiIconType : undefined,
|
||||
title: titleAndAriaLabel,
|
||||
};
|
||||
}
|
||||
|
||||
function extendNavLink(navLink: ChromeNavLink) {
|
||||
if (navLink.legacy) {
|
||||
return {
|
||||
...navLink,
|
||||
href: navLink.url && !navLink.active ? navLink.url : navLink.baseUrl,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...navLink,
|
||||
href: navLink.baseUrl,
|
||||
};
|
||||
}
|
||||
|
||||
function isModifiedEvent(event: MouseEvent) {
|
||||
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||
}
|
||||
|
||||
function findClosestAnchor(element: HTMLElement): HTMLAnchorElement | void {
|
||||
let current = element;
|
||||
while (current) {
|
||||
if (current.tagName === 'A') {
|
||||
return current as HTMLAnchorElement;
|
||||
}
|
||||
|
||||
if (!current.parentElement || current.parentElement === document.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
current = current.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
function truncateRecentItemLabel(label: string): string {
|
||||
if (label.length > TRUNCATE_LIMIT) {
|
||||
label = `${label.substring(0, TRUNCATE_AT)}…`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
export type HeaderProps = Pick<Props, Exclude<keyof Props, 'intl'>>;
|
||||
|
||||
interface Props {
|
||||
export interface HeaderProps {
|
||||
kibanaVersion: string;
|
||||
application: InternalApplicationStart;
|
||||
appTitle$: Rx.Observable<string>;
|
||||
|
@ -168,28 +67,29 @@ interface Props {
|
|||
legacyMode: boolean;
|
||||
navControlsLeft$: Rx.Observable<readonly ChromeNavControl[]>;
|
||||
navControlsRight$: Rx.Observable<readonly ChromeNavControl[]>;
|
||||
intl: InjectedIntl;
|
||||
basePath: HttpStart['basePath'];
|
||||
isLocked?: boolean;
|
||||
onIsLockedUpdate?: (isLocked: boolean) => void;
|
||||
navSetting$: Rx.Observable<NavSetting>;
|
||||
onIsLockedUpdate?: OnIsLockedUpdate;
|
||||
}
|
||||
|
||||
interface State {
|
||||
appTitle: string;
|
||||
currentAppId?: string;
|
||||
isVisible: boolean;
|
||||
navLinks: ReadonlyArray<ReturnType<typeof extendNavLink>>;
|
||||
recentlyAccessed: ReadonlyArray<ReturnType<typeof extendRecentlyAccessedHistoryItem>>;
|
||||
navLinks: ChromeNavLink[];
|
||||
recentlyAccessed: ChromeRecentlyAccessedHistoryItem[];
|
||||
forceNavigation: boolean;
|
||||
navControlsLeft: readonly ChromeNavControl[];
|
||||
navControlsRight: readonly ChromeNavControl[];
|
||||
navSetting: NavSetting;
|
||||
currentAppId: string | undefined;
|
||||
}
|
||||
|
||||
class HeaderUI extends Component<Props, State> {
|
||||
export class Header extends Component<HeaderProps, State> {
|
||||
private subscription?: Rx.Subscription;
|
||||
private navDrawerRef = createRef<EuiNavDrawer>();
|
||||
|
||||
constructor(props: Props) {
|
||||
constructor(props: HeaderProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -200,6 +100,8 @@ class HeaderUI extends Component<Props, State> {
|
|||
forceNavigation: false,
|
||||
navControlsLeft: [],
|
||||
navControlsRight: [],
|
||||
navSetting: 'grouped',
|
||||
currentAppId: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -214,7 +116,8 @@ class HeaderUI extends Component<Props, State> {
|
|||
Rx.combineLatest(
|
||||
this.props.navControlsLeft$,
|
||||
this.props.navControlsRight$,
|
||||
this.props.application.currentAppId$
|
||||
this.props.application.currentAppId$,
|
||||
this.props.navSetting$
|
||||
)
|
||||
).subscribe({
|
||||
next: ([
|
||||
|
@ -223,18 +126,17 @@ class HeaderUI extends Component<Props, State> {
|
|||
forceNavigation,
|
||||
navLinks,
|
||||
recentlyAccessed,
|
||||
[navControlsLeft, navControlsRight, currentAppId],
|
||||
[navControlsLeft, navControlsRight, currentAppId, navSetting],
|
||||
]) => {
|
||||
this.setState({
|
||||
appTitle,
|
||||
isVisible,
|
||||
forceNavigation,
|
||||
navLinks: navLinks.map(extendNavLink),
|
||||
recentlyAccessed: recentlyAccessed.map(ra =>
|
||||
extendRecentlyAccessedHistoryItem(navLinks, ra, this.props.basePath)
|
||||
),
|
||||
navLinks: navLinks.filter(navLink => !navLink.hidden),
|
||||
recentlyAccessed,
|
||||
navControlsLeft,
|
||||
navControlsRight,
|
||||
navSetting,
|
||||
currentAppId,
|
||||
});
|
||||
},
|
||||
|
@ -247,26 +149,12 @@ class HeaderUI extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public renderLogo() {
|
||||
const { homeHref, intl } = this.props;
|
||||
return (
|
||||
<EuiHeaderLogo
|
||||
data-test-subj="logo"
|
||||
iconType="logoKibana"
|
||||
onClick={this.onNavClick}
|
||||
href={homeHref}
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel',
|
||||
defaultMessage: 'Go to home page',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
public renderMenuTrigger() {
|
||||
return (
|
||||
<EuiHeaderSectionItemButton
|
||||
aria-label="Toggle side navigation"
|
||||
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.toggleSideNavAriaLabel', {
|
||||
defaultMessage: 'Toggle side navigation',
|
||||
})}
|
||||
onClick={() => this.navDrawerRef.current.toggleOpen()}
|
||||
>
|
||||
<EuiIcon type="apps" size="m" />
|
||||
|
@ -275,98 +163,29 @@ class HeaderUI extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { appTitle, isVisible, navControlsLeft, navControlsRight } = this.state;
|
||||
const {
|
||||
application,
|
||||
badge$,
|
||||
basePath,
|
||||
breadcrumbs$,
|
||||
helpExtension$,
|
||||
helpSupportUrl$,
|
||||
intl,
|
||||
isLocked,
|
||||
kibanaDocLink,
|
||||
kibanaVersion,
|
||||
onIsLockedUpdate,
|
||||
legacyMode,
|
||||
} = this.props;
|
||||
const {
|
||||
appTitle,
|
||||
currentAppId,
|
||||
isVisible,
|
||||
navControlsLeft,
|
||||
navControlsRight,
|
||||
navLinks,
|
||||
recentlyAccessed,
|
||||
} = this.state;
|
||||
const navLinks = this.state.navLinks.map(link =>
|
||||
euiNavLink(
|
||||
link,
|
||||
this.props.legacyMode,
|
||||
this.state.currentAppId,
|
||||
this.props.basePath,
|
||||
this.props.application.navigateToApp
|
||||
)
|
||||
);
|
||||
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const navLinksArray = navLinks
|
||||
.filter(navLink => !navLink.hidden)
|
||||
.map(navLink => ({
|
||||
key: navLink.id,
|
||||
label: navLink.tooltip ?? navLink.title,
|
||||
|
||||
// Use href and onClick to support "open in new tab" and SPA navigation in the same link
|
||||
href: navLink.href,
|
||||
onClick: (event: MouseEvent) => {
|
||||
if (
|
||||
!legacyMode && // ignore when in legacy mode
|
||||
!navLink.legacy && // ignore links to legacy apps
|
||||
!event.defaultPrevented && // onClick prevented default
|
||||
event.button === 0 && // ignore everything but left clicks
|
||||
!isModifiedEvent(event) // ignore clicks with modifier keys
|
||||
) {
|
||||
event.preventDefault();
|
||||
application.navigateToApp(navLink.id);
|
||||
}
|
||||
},
|
||||
|
||||
// Legacy apps use `active` property, NP apps should match the current app
|
||||
isActive: navLink.active || currentAppId === navLink.id,
|
||||
isDisabled: navLink.disabled,
|
||||
|
||||
iconType: navLink.euiIconType,
|
||||
icon:
|
||||
!navLink.euiIconType && navLink.icon ? (
|
||||
<EuiImage
|
||||
size="s"
|
||||
alt=""
|
||||
aria-hidden={true}
|
||||
url={basePath.prepend(`/${navLink.icon}`)}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
),
|
||||
'data-test-subj': 'navDrawerAppsMenuLink',
|
||||
}));
|
||||
|
||||
const recentLinksArray = [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: 'core.ui.chrome.sideGlobalNav.viewRecentItemsLabel',
|
||||
defaultMessage: 'Recently viewed',
|
||||
}),
|
||||
iconType: 'clock',
|
||||
isDisabled: recentlyAccessed.length > 0 ? false : true,
|
||||
flyoutMenu: {
|
||||
title: intl.formatMessage({
|
||||
id: 'core.ui.chrome.sideGlobalNav.viewRecentItemsFlyoutTitle',
|
||||
defaultMessage: 'Recent items',
|
||||
}),
|
||||
listItems: recentlyAccessed.map(item => ({
|
||||
label: truncateRecentItemLabel(item.label),
|
||||
title: item.title,
|
||||
'aria-label': item.title,
|
||||
href: item.href,
|
||||
iconType: item.euiIconType,
|
||||
})),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<header>
|
||||
<EuiHeader>
|
||||
|
@ -375,7 +194,13 @@ class HeaderUI extends Component<Props, State> {
|
|||
<EuiHeaderSectionItem border="right">{this.renderMenuTrigger()}</EuiHeaderSectionItem>
|
||||
</EuiShowFor>
|
||||
|
||||
<EuiHeaderSectionItem border="right">{this.renderLogo()}</EuiHeaderSectionItem>
|
||||
<EuiHeaderSectionItem border="right">
|
||||
<HeaderLogo
|
||||
href={this.props.homeHref}
|
||||
forceNavigation={this.state.forceNavigation}
|
||||
navLinks={navLinks}
|
||||
/>
|
||||
</EuiHeaderSectionItem>
|
||||
|
||||
<HeaderNavControls side="left" navControls={navControlsLeft} />
|
||||
</EuiHeaderSection>
|
||||
|
@ -399,75 +224,17 @@ class HeaderUI extends Component<Props, State> {
|
|||
<HeaderNavControls side="right" navControls={navControlsRight} />
|
||||
</EuiHeaderSection>
|
||||
</EuiHeader>
|
||||
|
||||
<EuiNavDrawer
|
||||
<NavDrawer
|
||||
navSetting={this.state.navSetting}
|
||||
isLocked={this.props.isLocked}
|
||||
onIsLockedUpdate={this.props.onIsLockedUpdate}
|
||||
navLinks={navLinks}
|
||||
chromeNavLinks={this.state.navLinks}
|
||||
recentlyAccessedItems={this.state.recentlyAccessed}
|
||||
basePath={this.props.basePath}
|
||||
ref={this.navDrawerRef}
|
||||
data-test-subj="navDrawer"
|
||||
isLocked={isLocked}
|
||||
onIsLockedUpdate={onIsLockedUpdate}
|
||||
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
|
||||
defaultMessage: 'Primary',
|
||||
})}
|
||||
>
|
||||
<EuiNavDrawerGroup
|
||||
listItems={recentLinksArray}
|
||||
aria-label={i18n.translate('core.ui.recentLinks.screenReaderLabel', {
|
||||
defaultMessage: 'Recently viewed links, navigation',
|
||||
})}
|
||||
/>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiNavDrawerGroup
|
||||
data-test-subj="navDrawerAppsMenu"
|
||||
listItems={navLinksArray}
|
||||
aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
|
||||
defaultMessage: 'Primary navigation links',
|
||||
})}
|
||||
/>
|
||||
</EuiNavDrawer>
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
private onNavClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
const anchor = findClosestAnchor((event as any).nativeEvent.target);
|
||||
if (!anchor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const navLink = this.state.navLinks.find(item => item.href === anchor.href);
|
||||
if (navLink && navLink.disabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.state.forceNavigation ||
|
||||
event.isDefaultPrevented() ||
|
||||
event.altKey ||
|
||||
event.metaKey ||
|
||||
event.ctrlKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toParsed = Url.parse(anchor.href);
|
||||
const fromParsed = Url.parse(document.location.href);
|
||||
const sameProto = toParsed.protocol === fromParsed.protocol;
|
||||
const sameHost = toParsed.host === fromParsed.host;
|
||||
const samePath = toParsed.path === fromParsed.path;
|
||||
|
||||
if (sameProto && sameHost && samePath) {
|
||||
if (toParsed.hash) {
|
||||
document.location.reload();
|
||||
}
|
||||
|
||||
// event.preventDefault() keeps the browser from seeing the new url as an update
|
||||
// and even setting window.location does not mimic that behavior, so instead
|
||||
// we use stopPropagation() to prevent angular from seeing the click and
|
||||
// starting a digest cycle/attempting to handle it in the router.
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const Header = injectI18n(HeaderUI);
|
||||
|
|
104
src/core/public/chrome/ui/header/header_logo.tsx
Normal file
104
src/core/public/chrome/ui/header/header_logo.tsx
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 Url from 'url';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiHeaderLogo } from '@elastic/eui';
|
||||
import { NavLink } from './nav_link';
|
||||
|
||||
function findClosestAnchor(element: HTMLElement): HTMLAnchorElement | void {
|
||||
let current = element;
|
||||
while (current) {
|
||||
if (current.tagName === 'A') {
|
||||
return current as HTMLAnchorElement;
|
||||
}
|
||||
|
||||
if (!current.parentElement || current.parentElement === document.body) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
current = current.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
function onClick(
|
||||
event: React.MouseEvent<HTMLAnchorElement>,
|
||||
forceNavigation: boolean,
|
||||
navLinks: NavLink[]
|
||||
) {
|
||||
const anchor = findClosestAnchor((event as any).nativeEvent.target);
|
||||
if (!anchor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const navLink = navLinks.find(item => item.href === anchor.href);
|
||||
if (navLink && navLink.isDisabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!forceNavigation ||
|
||||
event.isDefaultPrevented() ||
|
||||
event.altKey ||
|
||||
event.metaKey ||
|
||||
event.ctrlKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toParsed = Url.parse(anchor.href);
|
||||
const fromParsed = Url.parse(document.location.href);
|
||||
const sameProto = toParsed.protocol === fromParsed.protocol;
|
||||
const sameHost = toParsed.host === fromParsed.host;
|
||||
const samePath = toParsed.path === fromParsed.path;
|
||||
|
||||
if (sameProto && sameHost && samePath) {
|
||||
if (toParsed.hash) {
|
||||
document.location.reload();
|
||||
}
|
||||
|
||||
// event.preventDefault() keeps the browser from seeing the new url as an update
|
||||
// and even setting window.location does not mimic that behavior, so instead
|
||||
// we use stopPropagation() to prevent angular from seeing the click and
|
||||
// starting a digest cycle/attempting to handle it in the router.
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
href: string;
|
||||
navLinks: NavLink[];
|
||||
forceNavigation: boolean;
|
||||
}
|
||||
|
||||
export function HeaderLogo({ href, forceNavigation, navLinks }: Props) {
|
||||
return (
|
||||
<EuiHeaderLogo
|
||||
data-test-subj="logo"
|
||||
iconType="logoKibana"
|
||||
onClick={e => onClick(e, forceNavigation, navLinks)}
|
||||
href={href}
|
||||
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', {
|
||||
defaultMessage: 'Go to home page',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -26,3 +26,5 @@ export {
|
|||
ChromeHelpExtensionMenuDocumentationLink,
|
||||
ChromeHelpExtensionMenuGitHubLink,
|
||||
} from './header_help_menu';
|
||||
export type NavSetting = 'grouped' | 'individual';
|
||||
export type OnIsLockedUpdate = (isLocked: boolean) => void;
|
||||
|
|
103
src/core/public/chrome/ui/header/nav_drawer.test.tsx
Normal file
103
src/core/public/chrome/ui/header/nav_drawer.test.tsx
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 { cloneDeep } from 'lodash';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { NavSetting } from './';
|
||||
import { ChromeNavLink } from '../../../';
|
||||
import { AppCategory } from 'src/core/types';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../utils';
|
||||
import { NavDrawer } from './nav_drawer';
|
||||
import { euiNavLink } from './nav_link';
|
||||
|
||||
const { analyze, management, observability, security } = DEFAULT_APP_CATEGORIES;
|
||||
const mockIBasePath = {
|
||||
get: () => '/app',
|
||||
prepend: () => '/app',
|
||||
remove: () => '/app',
|
||||
};
|
||||
|
||||
const getMockProps = (chromeNavLinks: ChromeNavLink[], navSetting: NavSetting = 'grouped') => ({
|
||||
navSetting,
|
||||
navLinks: chromeNavLinks.map(link =>
|
||||
euiNavLink(link, true, undefined, mockIBasePath, () => Promise.resolve())
|
||||
),
|
||||
chromeNavLinks,
|
||||
recentlyAccessedItems: [],
|
||||
basePath: mockIBasePath,
|
||||
});
|
||||
|
||||
const makeLink = (id: string, order: number, category?: AppCategory) => ({
|
||||
id,
|
||||
category,
|
||||
order,
|
||||
title: id,
|
||||
baseUrl: `http://localhost:5601/app/${id}`,
|
||||
legacy: true,
|
||||
});
|
||||
|
||||
const getMockChromeNavLink = () =>
|
||||
cloneDeep([
|
||||
makeLink('discover', 100, analyze),
|
||||
makeLink('siem', 500, security),
|
||||
makeLink('metrics', 600, observability),
|
||||
makeLink('monitoring', 800, management),
|
||||
makeLink('visualize', 200, analyze),
|
||||
makeLink('dashboard', 300, analyze),
|
||||
makeLink('canvas', 400, { label: 'customCategory' }),
|
||||
makeLink('logs', 700, observability),
|
||||
]);
|
||||
|
||||
describe('NavDrawer', () => {
|
||||
describe('Advanced setting set to individual', () => {
|
||||
it('renders individual items', () => {
|
||||
const component = mount(
|
||||
<NavDrawer {...getMockProps(getMockChromeNavLink(), 'individual')} />
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('Advanced setting set to grouped', () => {
|
||||
it('renders individual items if there are less than 7', () => {
|
||||
const links = getMockChromeNavLink().slice(0, 5);
|
||||
const component = mount(<NavDrawer {...getMockProps(links)} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it('renders individual items if there is only 1 category', () => {
|
||||
// management doesn't count as a category
|
||||
const navLinks = [
|
||||
makeLink('discover', 100, analyze),
|
||||
makeLink('siem', 500, analyze),
|
||||
makeLink('metrics', 600, analyze),
|
||||
makeLink('monitoring', 800, analyze),
|
||||
makeLink('visualize', 200, analyze),
|
||||
makeLink('dashboard', 300, management),
|
||||
makeLink('canvas', 400, management),
|
||||
makeLink('logs', 700, management),
|
||||
];
|
||||
const component = mount(<NavDrawer {...getMockProps(navLinks)} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it('renders grouped items', () => {
|
||||
const component = mount(<NavDrawer {...getMockProps(getMockChromeNavLink())} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
170
src/core/public/chrome/ui/header/nav_drawer.tsx
Normal file
170
src/core/public/chrome/ui/header/nav_drawer.tsx
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { groupBy, sortBy } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
// @ts-ignore
|
||||
import { EuiNavDrawer, EuiHorizontalRule, EuiNavDrawerGroup } from '@elastic/eui';
|
||||
import { NavSetting, OnIsLockedUpdate } from './';
|
||||
import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../../..';
|
||||
import { AppCategory } from '../../../../types';
|
||||
import { HttpStart } from '../../../http';
|
||||
import { NavLink } from './nav_link';
|
||||
import { RecentLinks } from './recent_links';
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
navSetting: NavSetting;
|
||||
isLocked?: boolean;
|
||||
onIsLockedUpdate?: OnIsLockedUpdate;
|
||||
navLinks: NavLink[];
|
||||
chromeNavLinks: ChromeNavLink[];
|
||||
recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[];
|
||||
basePath: HttpStart['basePath'];
|
||||
}
|
||||
|
||||
function navDrawerRenderer(
|
||||
{
|
||||
navSetting,
|
||||
isLocked,
|
||||
onIsLockedUpdate,
|
||||
navLinks,
|
||||
chromeNavLinks,
|
||||
recentlyAccessedItems,
|
||||
basePath,
|
||||
}: Props,
|
||||
ref: React.Ref<HTMLElement>
|
||||
) {
|
||||
const disableGroupedNavSetting = navSetting === 'individual';
|
||||
const groupedNavLinks = groupBy(navLinks, link => link?.category?.label);
|
||||
const { undefined: unknowns, ...allCategorizedLinks } = groupedNavLinks;
|
||||
const { Management: management, ...mainCategories } = allCategorizedLinks;
|
||||
const categoryDictionary = getAllCategories(allCategorizedLinks);
|
||||
const orderedCategories = getOrderedCategories(mainCategories, categoryDictionary);
|
||||
const showUngroupedNav =
|
||||
disableGroupedNavSetting || navLinks.length < 7 || Object.keys(mainCategories).length === 1;
|
||||
|
||||
return (
|
||||
<EuiNavDrawer
|
||||
ref={ref}
|
||||
data-test-subj="navDrawer"
|
||||
isLocked={isLocked}
|
||||
onIsLockedUpdate={onIsLockedUpdate}
|
||||
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
|
||||
defaultMessage: 'Primary',
|
||||
})}
|
||||
>
|
||||
{RecentLinks({
|
||||
recentlyAccessedItems,
|
||||
navLinks: chromeNavLinks,
|
||||
basePath,
|
||||
})}
|
||||
<EuiHorizontalRule margin="none" />
|
||||
{showUngroupedNav ? (
|
||||
<EuiNavDrawerGroup
|
||||
data-test-subj="navDrawerAppsMenu"
|
||||
listItems={navLinks}
|
||||
aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
|
||||
defaultMessage: 'Primary navigation links',
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<EuiNavDrawerGroup
|
||||
data-test-subj="navDrawerAppsMenu"
|
||||
aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
|
||||
defaultMessage: 'Primary navigation links',
|
||||
})}
|
||||
listItems={[
|
||||
...orderedCategories.map(categoryName => {
|
||||
const category = categoryDictionary[categoryName]!;
|
||||
const links = mainCategories[categoryName];
|
||||
|
||||
if (links.length === 1) {
|
||||
return {
|
||||
...links[0],
|
||||
label: category.label,
|
||||
iconType: category.euiIconType || links[0].iconType,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
'data-test-subj': 'navDrawerCategory',
|
||||
iconType: category.euiIconType,
|
||||
label: category.label,
|
||||
flyoutMenu: {
|
||||
title: category.label,
|
||||
listItems: sortBy(links, 'order').map(link => {
|
||||
link['data-test-subj'] = 'navDrawerFlyoutLink';
|
||||
return link;
|
||||
}),
|
||||
},
|
||||
};
|
||||
}),
|
||||
...sortBy(unknowns, 'order'),
|
||||
]}
|
||||
/>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiNavDrawerGroup
|
||||
data-test-subj="navDrawerManagementMenu"
|
||||
aria-label={i18n.translate('core.ui.managementNavList.screenReaderLabel', {
|
||||
defaultMessage: 'Management navigation links',
|
||||
})}
|
||||
listItems={[
|
||||
{
|
||||
label: categoryDictionary.Management!.label,
|
||||
iconType: categoryDictionary.Management!.euiIconType,
|
||||
'data-test-subj': 'navDrawerCategory',
|
||||
flyoutMenu: {
|
||||
title: categoryDictionary.Management!.label,
|
||||
listItems: sortBy(management, 'order').map(link => {
|
||||
link['data-test-subj'] = 'navDrawerFlyoutLink';
|
||||
return link;
|
||||
}),
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</EuiNavDrawer>
|
||||
);
|
||||
}
|
||||
|
||||
export const NavDrawer = React.forwardRef(navDrawerRenderer);
|
87
src/core/public/chrome/ui/header/nav_link.tsx
Normal file
87
src/core/public/chrome/ui/header/nav_link.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiImage } from '@elastic/eui';
|
||||
import { ChromeNavLink, CoreStart } from '../../../';
|
||||
import { HttpStart } from '../../../http';
|
||||
|
||||
function isModifiedEvent(event: MouseEvent) {
|
||||
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||
}
|
||||
|
||||
function LinkIcon({ url }: { url: string }) {
|
||||
return <EuiImage size="s" alt="" aria-hidden={true} url={url} />;
|
||||
}
|
||||
|
||||
export type NavLink = ReturnType<typeof euiNavLink>;
|
||||
|
||||
export function euiNavLink(
|
||||
navLink: ChromeNavLink,
|
||||
legacyMode: boolean,
|
||||
currentAppId: string | undefined,
|
||||
basePath: HttpStart['basePath'],
|
||||
navigateToApp: CoreStart['application']['navigateToApp']
|
||||
) {
|
||||
const {
|
||||
legacy,
|
||||
url,
|
||||
active,
|
||||
baseUrl,
|
||||
id,
|
||||
title,
|
||||
disabled,
|
||||
euiIconType,
|
||||
icon,
|
||||
category,
|
||||
order,
|
||||
tooltip,
|
||||
} = navLink;
|
||||
let href = navLink.baseUrl;
|
||||
|
||||
if (legacy) {
|
||||
href = url && !active ? url : baseUrl;
|
||||
}
|
||||
|
||||
return {
|
||||
category,
|
||||
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) {
|
||||
if (
|
||||
!legacyMode && // ignore when in legacy mode
|
||||
!legacy && // ignore links to legacy apps
|
||||
!event.defaultPrevented && // onClick prevented default
|
||||
event.button === 0 && // ignore everything but left clicks
|
||||
!isModifiedEvent(event) // ignore clicks with modifier keys
|
||||
) {
|
||||
event.preventDefault();
|
||||
navigateToApp(navLink.id);
|
||||
}
|
||||
},
|
||||
// Legacy apps use `active` property, NP apps should match the current app
|
||||
isActive: active || currentAppId === id,
|
||||
isDisabled: disabled,
|
||||
iconType: euiIconType,
|
||||
icon: !euiIconType && icon ? <LinkIcon url={basePath.prepend(`/${icon}`)} /> : undefined,
|
||||
order,
|
||||
'data-test-subj': 'navDrawerAppsMenuLink',
|
||||
};
|
||||
}
|
113
src/core/public/chrome/ui/header/recent_links.tsx
Normal file
113
src/core/public/chrome/ui/header/recent_links.tsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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 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,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
interface Props {
|
||||
recentlyAccessedItems: ChromeRecentlyAccessedHistoryItem[];
|
||||
navLinks: ChromeNavLink[];
|
||||
basePath: HttpStart['basePath'];
|
||||
}
|
||||
|
||||
export function RecentLinks({ recentlyAccessedItems, navLinks, basePath }: Props) {
|
||||
return (
|
||||
<EuiNavDrawerGroup
|
||||
listItems={[
|
||||
{
|
||||
label: i18n.translate('core.ui.chrome.sideGlobalNav.viewRecentItemsLabel', {
|
||||
defaultMessage: 'Recently viewed',
|
||||
}),
|
||||
iconType: 'clock',
|
||||
isDisabled: recentlyAccessedItems.length === 0,
|
||||
flyoutMenu: {
|
||||
title: i18n.translate('core.ui.chrome.sideGlobalNav.viewRecentItemsFlyoutTitle', {
|
||||
defaultMessage: 'Recent items',
|
||||
}),
|
||||
listItems: prepareForEUI(recentlyAccessedItems, navLinks, basePath),
|
||||
},
|
||||
},
|
||||
]}
|
||||
aria-label={i18n.translate('core.ui.recentLinks.screenReaderLabel', {
|
||||
defaultMessage: 'Recently viewed links, navigation',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -239,6 +239,7 @@ export class CoreSystem {
|
|||
http,
|
||||
injectedMetadata,
|
||||
notifications,
|
||||
uiSettings,
|
||||
});
|
||||
|
||||
application.registerMountContext(this.coreContext.coreId, 'core', () => ({
|
||||
|
|
|
@ -77,7 +77,8 @@ import {
|
|||
} from './context';
|
||||
|
||||
export { CoreContext, CoreSystem } from './core_system';
|
||||
export { RecursiveReadonly } from '../utils';
|
||||
export { RecursiveReadonly, DEFAULT_APP_CATEGORIES } from '../utils';
|
||||
export { AppCategory } from '../types';
|
||||
|
||||
export {
|
||||
ApplicationSetup,
|
||||
|
|
|
@ -26,10 +26,12 @@ import {
|
|||
UserProvidedValues,
|
||||
} from '../../server/types';
|
||||
import { deepFreeze } from '../../utils/';
|
||||
import { AppCategory } from '../';
|
||||
|
||||
/** @public */
|
||||
export interface LegacyNavLink {
|
||||
id: string;
|
||||
category?: AppCategory;
|
||||
title: string;
|
||||
order: number;
|
||||
url: string;
|
||||
|
@ -52,6 +54,7 @@ export interface InjectedMetadataParams {
|
|||
buildNumber: number;
|
||||
branch: string;
|
||||
basePath: string;
|
||||
category?: AppCategory;
|
||||
csp: {
|
||||
warnLegacyBrowsers: boolean;
|
||||
};
|
||||
|
@ -75,6 +78,7 @@ export interface InjectedMetadataParams {
|
|||
basePath: string;
|
||||
serverName: string;
|
||||
devMode: boolean;
|
||||
category?: AppCategory;
|
||||
uiSettings: {
|
||||
defaults: Record<string, UiSettingsParams>;
|
||||
user?: Record<string, UserProvidedValues>;
|
||||
|
|
|
@ -74,6 +74,7 @@ export class LegacyPlatformService {
|
|||
appUrl: navLink.url,
|
||||
subUrlBase: navLink.subUrlBase,
|
||||
linkToLastSubUrl: navLink.linkToLastSubUrl,
|
||||
category: navLink.category,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface App extends AppBase {
|
|||
// @public (undocumented)
|
||||
export interface AppBase {
|
||||
capabilities?: Partial<Capabilities>;
|
||||
category?: AppCategory;
|
||||
chromeless?: boolean;
|
||||
euiIconType?: string;
|
||||
icon?: string;
|
||||
|
@ -40,6 +41,14 @@ export interface AppBase {
|
|||
updater$?: Observable<AppUpdater>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppCategory {
|
||||
ariaLabel?: string;
|
||||
euiIconType?: string;
|
||||
label: string;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AppLeaveAction = AppLeaveDefaultAction | AppLeaveConfirmAction;
|
||||
|
||||
|
@ -251,6 +260,7 @@ export interface ChromeNavLink {
|
|||
// @deprecated
|
||||
readonly active?: boolean;
|
||||
readonly baseUrl: string;
|
||||
readonly category?: AppCategory;
|
||||
// @deprecated
|
||||
readonly disabled?: boolean;
|
||||
readonly euiIconType?: string;
|
||||
|
@ -410,6 +420,26 @@ export class CoreSystem {
|
|||
stop(): void;
|
||||
}
|
||||
|
||||
// @internal (undocumented)
|
||||
export const DEFAULT_APP_CATEGORIES: Readonly<{
|
||||
analyze: {
|
||||
label: string;
|
||||
order: number;
|
||||
};
|
||||
observability: {
|
||||
label: string;
|
||||
order: number;
|
||||
};
|
||||
security: {
|
||||
label: string;
|
||||
order: number;
|
||||
};
|
||||
management: {
|
||||
label: string;
|
||||
euiIconType: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface DocLinksStart {
|
||||
// (undocumented)
|
||||
|
@ -746,6 +776,8 @@ export interface LegacyCoreStart extends CoreStart {
|
|||
|
||||
// @public (undocumented)
|
||||
export interface LegacyNavLink {
|
||||
// (undocumented)
|
||||
category?: AppCategory;
|
||||
// (undocumented)
|
||||
euiIconType?: string;
|
||||
// (undocumented)
|
||||
|
|
|
@ -65,6 +65,7 @@ function getUiAppsNavLinks({ uiAppSpecs = [] }: LegacyUiExports, pluginSpecs: Le
|
|||
|
||||
return {
|
||||
id,
|
||||
category: spec.category,
|
||||
title: spec.title,
|
||||
order: typeof spec.order === 'number' ? spec.order : 0,
|
||||
icon: spec.icon,
|
||||
|
@ -79,6 +80,7 @@ function getNavLinks(uiExports: LegacyUiExports, pluginSpecs: LegacyPluginSpec[]
|
|||
return (uiExports.navLinkSpecs || [])
|
||||
.map<LegacyNavLink>(spec => ({
|
||||
id: spec.id,
|
||||
category: spec.category,
|
||||
title: spec.title,
|
||||
order: typeof spec.order === 'number' ? spec.order : 0,
|
||||
url: spec.url,
|
||||
|
|
|
@ -139,7 +139,7 @@ export type LegacyNavLinkSpec = Record<string, unknown> & ChromeNavLink;
|
|||
*/
|
||||
export type LegacyAppSpec = Pick<
|
||||
ChromeNavLink,
|
||||
'title' | 'order' | 'icon' | 'euiIconType' | 'url' | 'linkToLastSubUrl' | 'hidden'
|
||||
'title' | 'order' | 'icon' | 'euiIconType' | 'url' | 'linkToLastSubUrl' | 'hidden' | 'category'
|
||||
> & { pluginId?: string; id?: string; listed?: boolean };
|
||||
|
||||
/**
|
||||
|
|
52
src/core/types/app_category.ts
Normal file
52
src/core/types/app_category.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** @public */
|
||||
|
||||
/**
|
||||
* A category definition for nav links to know where to sort them in the left hand nav
|
||||
* @public
|
||||
*/
|
||||
export interface AppCategory {
|
||||
/**
|
||||
* Label used for cateogry name.
|
||||
* Also used as aria-label if one isn't set.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* If the visual label isn't appropriate for screen readers,
|
||||
* can override it here
|
||||
*/
|
||||
ariaLabel?: string;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
order?: number;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
euiIconType?: string;
|
||||
}
|
|
@ -23,3 +23,4 @@
|
|||
*/
|
||||
export * from './core_service';
|
||||
export * from './capabilities';
|
||||
export * from './app_category';
|
||||
|
|
48
src/core/utils/default_app_categories.ts
Normal file
48
src/core/utils/default_app_categories.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
/** @internal */
|
||||
export const DEFAULT_APP_CATEGORIES = Object.freeze({
|
||||
analyze: {
|
||||
label: i18n.translate('core.ui.analyzeNavList.label', {
|
||||
defaultMessage: 'Analyze',
|
||||
}),
|
||||
order: 1000,
|
||||
},
|
||||
observability: {
|
||||
label: i18n.translate('core.ui.observabilityNavList.label', {
|
||||
defaultMessage: 'Observability',
|
||||
}),
|
||||
order: 2000,
|
||||
},
|
||||
security: {
|
||||
label: i18n.translate('core.ui.securityNavList.label', {
|
||||
defaultMessage: 'Security',
|
||||
}),
|
||||
order: 3000,
|
||||
},
|
||||
management: {
|
||||
label: i18n.translate('core.ui.managementNavList.label', {
|
||||
defaultMessage: 'Management',
|
||||
}),
|
||||
euiIconType: 'managementApp',
|
||||
},
|
||||
});
|
|
@ -28,3 +28,4 @@ export * from './pick';
|
|||
export * from './promise';
|
||||
export * from './url';
|
||||
export * from './unset';
|
||||
export * from './default_app_categories';
|
||||
|
|
|
@ -34,6 +34,7 @@ import { getUiSettingDefaults } from './ui_setting_defaults';
|
|||
import { registerCspCollector } from './server/lib/csp_usage_collector';
|
||||
import { injectVars } from './inject_vars';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
const mkdirAsync = promisify(Fs.mkdir);
|
||||
|
||||
|
@ -83,6 +84,7 @@ export default function(kibana) {
|
|||
order: -1003,
|
||||
url: `${kbnBaseUrl}#/discover`,
|
||||
euiIconType: 'discoverApp',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
{
|
||||
id: 'kibana:visualize',
|
||||
|
@ -92,6 +94,7 @@ export default function(kibana) {
|
|||
order: -1002,
|
||||
url: `${kbnBaseUrl}#/visualize`,
|
||||
euiIconType: 'visualizeApp',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
{
|
||||
id: 'kibana:dashboard',
|
||||
|
@ -107,6 +110,7 @@ export default function(kibana) {
|
|||
// to determine what url to use for the app link.
|
||||
subUrlBase: `${kbnBaseUrl}#/dashboard`,
|
||||
euiIconType: 'dashboardApp',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
{
|
||||
id: 'kibana:dev_tools',
|
||||
|
@ -116,16 +120,18 @@ export default function(kibana) {
|
|||
order: 9001,
|
||||
url: '/app/kibana#/dev_tools',
|
||||
euiIconType: 'devToolsApp',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
},
|
||||
{
|
||||
id: 'kibana:management',
|
||||
id: 'kibana:stack_management',
|
||||
title: i18n.translate('kbn.managementTitle', {
|
||||
defaultMessage: 'Management',
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
order: 9003,
|
||||
url: `${kbnBaseUrl}#/management`,
|
||||
euiIconType: 'managementApp',
|
||||
linkToLastSubUrl: false,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
},
|
||||
],
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ const DiscoverFetchError = ({ fetchError }: Props) => {
|
|||
|
||||
if (fetchError.lang === 'painless') {
|
||||
const { chrome } = getServices();
|
||||
const mangagementUrlObj = chrome.navLinks.get('kibana:management');
|
||||
const mangagementUrlObj = chrome.navLinks.get('kibana:stack_management');
|
||||
const managementUrl = mangagementUrlObj ? mangagementUrlObj.url : '';
|
||||
const url = `${managementUrl}/kibana/index_patterns`;
|
||||
|
||||
|
|
|
@ -129,8 +129,8 @@ describe('home', () => {
|
|||
|
||||
test('should not render directory entry when showOnHomePage is false', async () => {
|
||||
const directoryEntry = {
|
||||
id: 'management',
|
||||
title: 'Management',
|
||||
id: 'stack-management',
|
||||
title: 'Stack Management',
|
||||
description: 'Your center console for managing the Elastic Stack.',
|
||||
icon: 'managementApp',
|
||||
path: 'management_landing_page',
|
||||
|
|
|
@ -74,7 +74,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>
|
||||
|
@ -93,7 +93,7 @@ export function updateLandingPage(version) {
|
|||
<p>
|
||||
<FormattedMessage
|
||||
id="kbn.management.landing.text"
|
||||
defaultMessage="A full list of tools can be found in the left menu"
|
||||
defaultMessage="A complete list of apps is in the menu on the left."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
|
@ -173,11 +173,11 @@ uiModules.get('apps/management').directive('kbnManagementLanding', function(kbnV
|
|||
|
||||
FeatureCatalogueRegistryProvider.register(() => {
|
||||
return {
|
||||
id: 'management',
|
||||
title: i18n.translate('kbn.management.managementLabel', {
|
||||
defaultMessage: 'Management',
|
||||
id: 'stack-management',
|
||||
title: i18n.translate('kbn.stackManagement.managementLabel', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
description: i18n.translate('kbn.management.managementDescription', {
|
||||
description: i18n.translate('kbn.stackManagement.managementDescription', {
|
||||
defaultMessage: 'Your center console for managing the Elastic Stack.',
|
||||
}),
|
||||
icon: 'managementApp',
|
||||
|
|
|
@ -1170,5 +1170,24 @@ export function getUiSettingDefaults() {
|
|||
category: ['accessibility'],
|
||||
requiresPageReload: true,
|
||||
},
|
||||
pageNavigation: {
|
||||
name: i18n.translate('kbn.advancedSettings.pageNavigationName', {
|
||||
defaultMessage: 'Side nav style',
|
||||
}),
|
||||
value: 'grouped',
|
||||
description: i18n.translate('kbn.advancedSettings.pageNavigationDesc', {
|
||||
defaultMessage: 'Change the style of navigation',
|
||||
}),
|
||||
type: 'select',
|
||||
options: ['grouped', 'individual'],
|
||||
optionLabels: {
|
||||
grouped: i18n.translate('kbn.advancedSettings.pageNavigationGrouped', {
|
||||
defaultMessage: 'Grouped',
|
||||
}),
|
||||
individual: i18n.translate('kbn.advancedSettings.pageNavigationIndividual', {
|
||||
defaultMessage: 'Individual',
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Legacy } from '../../../../kibana';
|
|||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ManagementPlugin(kibana: any) {
|
||||
const config: Legacy.PluginSpecOptions = {
|
||||
id: 'management',
|
||||
id: 'stack-management',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
config: (Joi: any) => {
|
||||
return Joi.object({
|
||||
|
|
|
@ -80,4 +80,4 @@ export const PATH_TO_ADVANCED_SETTINGS = 'kibana#/management/kibana/settings';
|
|||
* The type name used within the Monitoring index to publish management stats.
|
||||
* @type {string}
|
||||
*/
|
||||
export const KIBANA_MANAGEMENT_STATS_TYPE = 'management';
|
||||
export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management';
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import { Server } from 'hapi';
|
||||
import { size } from 'lodash';
|
||||
import { KIBANA_MANAGEMENT_STATS_TYPE } from '../../../common/constants';
|
||||
import { KIBANA_STACK_MANAGEMENT_STATS_TYPE } from '../../../common/constants';
|
||||
import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
|
||||
import { SavedObjectsClient } from '../../../../../../core/server';
|
||||
|
||||
|
@ -54,7 +54,7 @@ export function registerManagementUsageCollector(
|
|||
server: any
|
||||
) {
|
||||
const collector = usageCollection.makeUsageCollector({
|
||||
type: KIBANA_MANAGEMENT_STATS_TYPE,
|
||||
type: KIBANA_STACK_MANAGEMENT_STATS_TYPE,
|
||||
isReady: () => true,
|
||||
fetch: createCollectorFetch(server),
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { Legacy } from 'kibana';
|
||||
import { LegacyPluginApi, LegacyPluginInitializer } from 'src/legacy/plugin_discovery/types';
|
||||
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../core/utils';
|
||||
import { plugin } from './server';
|
||||
import { CustomCoreSetup } from './server/plugin';
|
||||
|
||||
|
@ -60,6 +61,7 @@ const timelionPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPl
|
|||
icon: 'plugins/timelion/icon.svg',
|
||||
euiIconType: 'timelionApp',
|
||||
main: 'plugins/timelion/app',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
hacks: [resolve(__dirname, 'public/legacy')],
|
||||
|
|
|
@ -24,6 +24,7 @@ import { Capabilities } from '../../core/server';
|
|||
import { SavedObjectsSchemaDefinition } from '../../core/server/saved_objects/schema';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { SavedObjectsManagementDefinition } from '../../core/server/saved_objects/management';
|
||||
import { AppCategory } from '../../core/types';
|
||||
|
||||
/**
|
||||
* Usage
|
||||
|
@ -53,6 +54,7 @@ export interface LegacyPluginOptions {
|
|||
uiExports: Partial<{
|
||||
app: Partial<{
|
||||
title: string;
|
||||
category?: AppCategory;
|
||||
description: string;
|
||||
main: string;
|
||||
icon: string;
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const MANAGEMENT_BREADCRUMB = Object.freeze({
|
||||
text: i18n.translate('common.ui.management.breadcrumb', {
|
||||
defaultMessage: 'Management',
|
||||
text: i18n.translate('common.ui.stackManagement.breadcrumb', {
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
href: '#/management',
|
||||
});
|
||||
|
|
|
@ -32,6 +32,7 @@ export class UiApp {
|
|||
hidden,
|
||||
linkToLastSubUrl,
|
||||
listed,
|
||||
category,
|
||||
url = `/app/${id}`,
|
||||
} = spec;
|
||||
|
||||
|
@ -46,6 +47,7 @@ export class UiApp {
|
|||
this._icon = icon;
|
||||
this._euiIconType = euiIconType;
|
||||
this._linkToLastSubUrl = linkToLastSubUrl;
|
||||
this._category = category;
|
||||
this._hidden = hidden;
|
||||
this._listed = listed;
|
||||
this._url = url;
|
||||
|
@ -68,6 +70,7 @@ export class UiApp {
|
|||
euiIconType: this._euiIconType,
|
||||
url: this._url,
|
||||
linkToLastSubUrl: this._linkToLastSubUrl,
|
||||
category: this._category,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +118,7 @@ export class UiApp {
|
|||
main: this._main,
|
||||
navLink: this._navLink,
|
||||
linkToLastSubUrl: this._linkToLastSubUrl,
|
||||
category: this._category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ function applySpecDefaults(spec, type, pluginSpec) {
|
|||
linkToLastSubUrl = true,
|
||||
listed = !hidden,
|
||||
url = `/app/${id}`,
|
||||
category,
|
||||
} = spec;
|
||||
|
||||
if (spec.injectVars) {
|
||||
|
@ -61,6 +62,7 @@ function applySpecDefaults(spec, type, pluginSpec) {
|
|||
linkToLastSubUrl,
|
||||
listed,
|
||||
url,
|
||||
category,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ describe('UiNavLink', () => {
|
|||
euiIconType: spec.euiIconType,
|
||||
hidden: spec.hidden,
|
||||
disabled: spec.disabled,
|
||||
category: undefined,
|
||||
|
||||
// defaults
|
||||
linkToLastSubUrl: true,
|
||||
|
|
|
@ -31,6 +31,7 @@ export class UiNavLink {
|
|||
hidden = false,
|
||||
disabled = false,
|
||||
tooltip = '',
|
||||
category,
|
||||
} = spec;
|
||||
|
||||
this._id = id;
|
||||
|
@ -44,6 +45,7 @@ export class UiNavLink {
|
|||
this._hidden = hidden;
|
||||
this._disabled = disabled;
|
||||
this._tooltip = tooltip;
|
||||
this._category = category;
|
||||
}
|
||||
|
||||
getOrder() {
|
||||
|
@ -63,6 +65,7 @@ export class UiNavLink {
|
|||
hidden: this._hidden,
|
||||
disabled: this._disabled,
|
||||
tooltip: this._tooltip,
|
||||
category: this._category,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,14 +161,15 @@ export class ManagementSidebarNav extends React.Component<
|
|||
}
|
||||
|
||||
public render() {
|
||||
const HEADER_ID = 'management-nav-header';
|
||||
const HEADER_ID = 'stack-management-nav-header';
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiScreenReaderOnly>
|
||||
<h2 id={HEADER_ID}>
|
||||
{i18n.translate('management.nav.label', {
|
||||
defaultMessage: 'Management',
|
||||
// todo
|
||||
defaultMessage: 'Stack Management',
|
||||
})}
|
||||
</h2>
|
||||
</EuiScreenReaderOnly>
|
||||
|
|
|
@ -27,7 +27,8 @@ export class LegacyManagementAdapter {
|
|||
'management',
|
||||
{
|
||||
display: i18n.translate('management.displayName', {
|
||||
defaultMessage: 'Management',
|
||||
// todo
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
},
|
||||
capabilities
|
||||
|
@ -35,6 +36,7 @@ export class LegacyManagementAdapter {
|
|||
|
||||
this.main.register('data', {
|
||||
display: i18n.translate('management.connectDataDisplayName', {
|
||||
// todo
|
||||
defaultMessage: 'Connect Data',
|
||||
}),
|
||||
order: 0,
|
||||
|
|
|
@ -64,7 +64,8 @@ export class ManagementApp {
|
|||
coreStart.chrome.setBreadcrumbs([
|
||||
{
|
||||
text: i18n.translate('management.breadcrumb', {
|
||||
defaultMessage: 'Management',
|
||||
// todo
|
||||
defaultMessage: 'Stack Management',
|
||||
}),
|
||||
href: '#/management',
|
||||
},
|
||||
|
|
|
@ -34,6 +34,7 @@ export default function({ getService, getPageObjects }) {
|
|||
await esArchiver.load('dashboard/current/kibana');
|
||||
await kibanaServer.uiSettings.replace({
|
||||
defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
|
||||
pageNavigation: 'individual',
|
||||
});
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.preserveCrossAppState();
|
||||
|
@ -83,7 +84,7 @@ export default function({ getService, getPageObjects }) {
|
|||
|
||||
describe('is false', () => {
|
||||
before(async () => {
|
||||
await PageObjects.header.clickManagement();
|
||||
await PageObjects.header.clickStackManagement();
|
||||
await PageObjects.settings.clickKibanaSettings();
|
||||
await PageObjects.settings.toggleAdvancedSettingCheckbox('visualize:enableLabs');
|
||||
});
|
||||
|
@ -98,7 +99,7 @@ export default function({ getService, getPageObjects }) {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.header.clickManagement();
|
||||
await PageObjects.header.clickStackManagement();
|
||||
await PageObjects.settings.clickKibanaSettings();
|
||||
await PageObjects.settings.clearAdvancedSettings('visualize:enableLabs');
|
||||
await PageObjects.header.clickDashboard();
|
||||
|
|
|
@ -27,7 +27,7 @@ export default function({ getService, getPageObjects }) {
|
|||
describe('index pattern filter', function describeIndexTests() {
|
||||
before(async function() {
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
await kibanaServer.uiSettings.replace({});
|
||||
await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaIndexPatterns();
|
||||
});
|
||||
|
|
|
@ -23,7 +23,6 @@ export default function({ getService, getPageObjects }) {
|
|||
const log = getService('log');
|
||||
const PageObjects = getPageObjects(['common', 'header', 'discover', 'settings']);
|
||||
|
||||
// Flaky: https://github.com/elastic/kibana/issues/19743
|
||||
describe('visualize lab mode', () => {
|
||||
it('disabling does not break loading saved searches', async () => {
|
||||
await PageObjects.common.navigateToUrl('discover', '');
|
||||
|
@ -36,7 +35,7 @@ export default function({ getService, getPageObjects }) {
|
|||
log.info('found saved search before toggling enableLabs mode');
|
||||
|
||||
// Navigate to advanced setting and disable lab mode
|
||||
await PageObjects.header.clickManagement();
|
||||
await PageObjects.header.clickStackManagement();
|
||||
await PageObjects.settings.clickKibanaSettings();
|
||||
await PageObjects.settings.toggleAdvancedSettingCheckbox('visualize:enableLabs');
|
||||
|
||||
|
@ -50,7 +49,7 @@ export default function({ getService, getPageObjects }) {
|
|||
|
||||
after(async () => {
|
||||
await PageObjects.discover.closeLoadSaveSearchPanel();
|
||||
await PageObjects.header.clickManagement();
|
||||
await PageObjects.header.clickStackManagement();
|
||||
await PageObjects.settings.clickKibanaSettings();
|
||||
await PageObjects.settings.clearAdvancedSettings('visualize:enableLabs');
|
||||
});
|
||||
|
|
|
@ -59,8 +59,8 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
|
|||
await this.awaitGlobalLoadingIndicatorHidden();
|
||||
}
|
||||
|
||||
async clickManagement() {
|
||||
await appsMenu.clickLink('Management');
|
||||
async clickStackManagement() {
|
||||
await appsMenu.clickLink('Stack Management');
|
||||
await this.awaitGlobalLoadingIndicatorHidden();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
|
||||
import { map as mapAsync } from 'bluebird';
|
||||
import expect from '@kbn/expect';
|
||||
import { NavSetting } from '../../../src/core/public/chrome/ui/header/';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function SettingsPageProvider({ getService, getPageObjects }) {
|
||||
export function SettingsPageProvider({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
const browser = getService('browser');
|
||||
|
@ -34,7 +36,8 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
async clickNavigation() {
|
||||
find.clickDisplayedByCssSelector('.app-link:nth-child(5) a');
|
||||
}
|
||||
async clickLinkText(text) {
|
||||
|
||||
async clickLinkText(text: string) {
|
||||
await find.clickByDisplayedLinkText(text);
|
||||
}
|
||||
async clickKibanaSettings() {
|
||||
|
@ -55,6 +58,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
// check for the index pattern info flyout that covers the
|
||||
// create index pattern button on smaller screens
|
||||
// @ts-ignore
|
||||
await retry.waitFor('index pattern info flyout', async () => {
|
||||
if (await testSubjects.exists('CreateIndexPatternPrompt')) {
|
||||
await testSubjects.click('CreateIndexPatternPrompt > euiFlyoutCloseButton');
|
||||
|
@ -62,18 +66,18 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
});
|
||||
}
|
||||
|
||||
async getAdvancedSettings(propertyName) {
|
||||
async getAdvancedSettings(propertyName: string) {
|
||||
log.debug('in getAdvancedSettings');
|
||||
const setting = await testSubjects.find(`advancedSetting-editField-${propertyName}`);
|
||||
return await setting.getAttribute('value');
|
||||
}
|
||||
|
||||
async expectDisabledAdvancedSetting(propertyName) {
|
||||
async expectDisabledAdvancedSetting(propertyName: string) {
|
||||
const setting = await testSubjects.find(`advancedSetting-editField-${propertyName}`);
|
||||
expect(setting.getAttribute('disabled')).to.eql('');
|
||||
}
|
||||
|
||||
async getAdvancedSettingCheckbox(propertyName) {
|
||||
async getAdvancedSettingCheckbox(propertyName: string) {
|
||||
log.debug('in getAdvancedSettingCheckbox');
|
||||
return await testSubjects.getAttribute(
|
||||
`advancedSetting-editField-${propertyName}`,
|
||||
|
@ -81,12 +85,12 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
);
|
||||
}
|
||||
|
||||
async clearAdvancedSettings(propertyName) {
|
||||
async clearAdvancedSettings(propertyName: string) {
|
||||
await testSubjects.click(`advancedSetting-resetField-${propertyName}`);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async setAdvancedSettingsSelect(propertyName, propertyValue) {
|
||||
async setAdvancedSettingsSelect(propertyName: string, propertyValue: string) {
|
||||
await find.clickByCssSelector(
|
||||
`[data-test-subj="advancedSetting-editField-${propertyName}"] option[value="${propertyValue}"]`
|
||||
);
|
||||
|
@ -95,7 +99,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async setAdvancedSettingsInput(propertyName, propertyValue) {
|
||||
async setAdvancedSettingsInput(propertyName: string, propertyValue: string) {
|
||||
const input = await testSubjects.find(`advancedSetting-editField-${propertyName}`);
|
||||
await input.clearValue();
|
||||
await input.type(propertyValue);
|
||||
|
@ -103,7 +107,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async toggleAdvancedSettingCheckbox(propertyName) {
|
||||
async toggleAdvancedSettingCheckbox(propertyName: string) {
|
||||
testSubjects.click(`advancedSetting-editField-${propertyName}`);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.click(`advancedSetting-saveEditField-${propertyName}`);
|
||||
|
@ -126,7 +130,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
return await testSubjects.find('createIndexPatternTimeFieldSelect');
|
||||
}
|
||||
|
||||
async selectTimeFieldOption(selection) {
|
||||
async selectTimeFieldOption(selection: string) {
|
||||
// open dropdown
|
||||
await this.clickTimeFieldNameField();
|
||||
// close dropdown, keep focus
|
||||
|
@ -141,7 +145,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
});
|
||||
}
|
||||
|
||||
async getTimeFieldOption(selection) {
|
||||
async getTimeFieldOption(selection: string) {
|
||||
return await find.displayedByCssSelector('option[value="' + selection + '"]');
|
||||
}
|
||||
|
||||
|
@ -174,9 +178,9 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
return await find.allByCssSelector('table.euiTable thead tr th');
|
||||
}
|
||||
|
||||
async sortBy(columnName) {
|
||||
async sortBy(columnName: string) {
|
||||
const chartTypes = await find.allByCssSelector('table.euiTable thead tr th button');
|
||||
async function getChartType(chart) {
|
||||
async function getChartType(chart: Record<string, any>) {
|
||||
const chartString = await chart.getVisibleText();
|
||||
if (chartString === columnName) {
|
||||
await chart.click();
|
||||
|
@ -187,7 +191,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
return Promise.all(getChartTypesPromises);
|
||||
}
|
||||
|
||||
async getTableRow(rowNumber, colNumber) {
|
||||
async getTableRow(rowNumber: number, colNumber: number) {
|
||||
// passing in zero-based index, but adding 1 for css 1-based indexes
|
||||
return await find.byCssSelector(
|
||||
'table.euiTable tbody tr:nth-child(' +
|
||||
|
@ -234,13 +238,13 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
});
|
||||
}
|
||||
|
||||
async setFieldTypeFilter(type) {
|
||||
async setFieldTypeFilter(type: string) {
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="indexedFieldTypeFilterDropdown"] > option[label="' + type + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setScriptedFieldLanguageFilter(language) {
|
||||
async setScriptedFieldLanguageFilter(language: string) {
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[label="' +
|
||||
language +
|
||||
|
@ -248,13 +252,13 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
);
|
||||
}
|
||||
|
||||
async filterField(name) {
|
||||
async filterField(name: string) {
|
||||
const input = await testSubjects.find('indexPatternFieldFilter');
|
||||
await input.clearValue();
|
||||
await input.type(name);
|
||||
}
|
||||
|
||||
async openControlsByName(name) {
|
||||
async openControlsByName(name: string) {
|
||||
await this.filterField(name);
|
||||
const tableFields = await (
|
||||
await find.byCssSelector(
|
||||
|
@ -312,7 +316,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
}
|
||||
|
||||
async createIndexPattern(
|
||||
indexPatternName,
|
||||
indexPatternName: string,
|
||||
timefield = '@timestamp',
|
||||
isStandardIndexPattern = true
|
||||
) {
|
||||
|
@ -364,7 +368,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
async getIndexPatternIdFromUrl() {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const indexPatternId = currentUrl.match(/.*\/(.*)/)[1];
|
||||
const indexPatternId = currentUrl.match(/.*\/(.*)/)![1];
|
||||
|
||||
log.debug('index pattern ID: ', indexPatternId);
|
||||
|
||||
|
@ -423,12 +427,19 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await testSubjects.click('tab-sourceFilters');
|
||||
}
|
||||
|
||||
async editScriptedField(name) {
|
||||
async editScriptedField(name: string) {
|
||||
await this.filterField(name);
|
||||
await find.clickByCssSelector('.euiTableRowCell--hasActions button:first-child');
|
||||
}
|
||||
|
||||
async addScriptedField(name, language, type, format, popularity, script) {
|
||||
async addScriptedField(
|
||||
name: string,
|
||||
language: string,
|
||||
type: string,
|
||||
format: Record<string, any>,
|
||||
popularity: string,
|
||||
script: string
|
||||
) {
|
||||
await this.clickAddScriptedField();
|
||||
await this.setScriptedFieldName(name);
|
||||
if (language) await this.setScriptedFieldLanguage(language);
|
||||
|
@ -469,42 +480,42 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
|
||||
async setScriptedFieldName(name) {
|
||||
async setScriptedFieldName(name: string) {
|
||||
log.debug('set scripted field name = ' + name);
|
||||
const field = await testSubjects.find('editorFieldName');
|
||||
await field.clearValue();
|
||||
await field.type(name);
|
||||
}
|
||||
|
||||
async setScriptedFieldLanguage(language) {
|
||||
async setScriptedFieldLanguage(language: string) {
|
||||
log.debug('set scripted field language = ' + language);
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="editorFieldLang"] > option[value="' + language + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setScriptedFieldType(type) {
|
||||
async setScriptedFieldType(type: string) {
|
||||
log.debug('set scripted field type = ' + type);
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="editorFieldType"] > option[value="' + type + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setFieldFormat(format) {
|
||||
async setFieldFormat(format: string) {
|
||||
log.debug('set scripted field format = ' + format);
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="editorSelectedFormatId"] > option[value="' + format + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setScriptedFieldUrlType(type) {
|
||||
async setScriptedFieldUrlType(type: string) {
|
||||
log.debug('set scripted field Url type = ' + type);
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="urlEditorType"] > option[value="' + type + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setScriptedFieldUrlTemplate(template) {
|
||||
async setScriptedFieldUrlTemplate(template: string) {
|
||||
log.debug('set scripted field Url Template = ' + template);
|
||||
const urlTemplateField = await find.byCssSelector(
|
||||
'input[data-test-subj="urlEditorUrlTemplate"]'
|
||||
|
@ -512,7 +523,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await urlTemplateField.type(template);
|
||||
}
|
||||
|
||||
async setScriptedFieldUrlLabelTemplate(labelTemplate) {
|
||||
async setScriptedFieldUrlLabelTemplate(labelTemplate: string) {
|
||||
log.debug('set scripted field Url Label Template = ' + labelTemplate);
|
||||
const urlEditorLabelTemplate = await find.byCssSelector(
|
||||
'input[data-test-subj="urlEditorLabelTemplate"]'
|
||||
|
@ -520,7 +531,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await urlEditorLabelTemplate.type(labelTemplate);
|
||||
}
|
||||
|
||||
async setScriptedFieldDatePattern(datePattern) {
|
||||
async setScriptedFieldDatePattern(datePattern: string) {
|
||||
log.debug('set scripted field Date Pattern = ' + datePattern);
|
||||
const datePatternField = await find.byCssSelector(
|
||||
'input[data-test-subj="dateEditorPattern"]'
|
||||
|
@ -531,21 +542,21 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await datePatternField.type(datePattern);
|
||||
}
|
||||
|
||||
async setScriptedFieldStringTransform(stringTransform) {
|
||||
async setScriptedFieldStringTransform(stringTransform: string) {
|
||||
log.debug('set scripted field string Transform = ' + stringTransform);
|
||||
await find.clickByCssSelector(
|
||||
'select[data-test-subj="stringEditorTransform"] > option[value="' + stringTransform + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
async setScriptedFieldPopularity(popularity) {
|
||||
async setScriptedFieldPopularity(popularity: string) {
|
||||
log.debug('set scripted field popularity = ' + popularity);
|
||||
const field = await testSubjects.find('editorFieldCount');
|
||||
await field.clearValue();
|
||||
await field.type(popularity);
|
||||
}
|
||||
|
||||
async setScriptedFieldScript(script) {
|
||||
async setScriptedFieldScript(script: string) {
|
||||
log.debug('set scripted field script = ' + script);
|
||||
const aceEditorCssSelector = '[data-test-subj="editorFieldScript"] .ace_editor';
|
||||
await find.clickByCssSelector(aceEditorCssSelector);
|
||||
|
@ -555,7 +566,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await browser.pressKeys(...script.split(''));
|
||||
}
|
||||
|
||||
async openScriptedFieldHelp(activeTab) {
|
||||
async openScriptedFieldHelp(activeTab: string) {
|
||||
log.debug('open Scripted Fields help');
|
||||
let isOpen = await testSubjects.exists('scriptedFieldsHelpFlyout');
|
||||
if (!isOpen) {
|
||||
|
@ -577,7 +588,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await flyout.ensureClosed('scriptedFieldsHelpFlyout');
|
||||
}
|
||||
|
||||
async executeScriptedField(script, additionalField) {
|
||||
async executeScriptedField(script: string, additionalField: string) {
|
||||
log.debug('execute Scripted Fields help');
|
||||
await this.closeScriptedFieldHelp(); // ensure script help is closed so script input is not blocked
|
||||
await this.setScriptedFieldScript(script);
|
||||
|
@ -595,7 +606,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
return scriptResults;
|
||||
}
|
||||
|
||||
async importFile(path, overwriteAll = true) {
|
||||
async importFile(path: string, overwriteAll = true) {
|
||||
log.debug(`importFile(${path})`);
|
||||
|
||||
log.debug(`Clicking importObjects`);
|
||||
|
@ -645,7 +656,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await testSubjects.click('importSavedObjectsConfirmBtn');
|
||||
}
|
||||
|
||||
async associateIndexPattern(oldIndexPatternId, newIndexPatternTitle) {
|
||||
async associateIndexPattern(oldIndexPatternId: string, newIndexPatternTitle: string) {
|
||||
await find.clickByCssSelector(
|
||||
`select[data-test-subj="managementChangeIndexSelection-${oldIndexPatternId}"] >
|
||||
[data-test-subj="indexPatternOption-${newIndexPatternTitle}"]`
|
||||
|
@ -710,7 +721,7 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
return await deleteButton.isEnabled();
|
||||
}
|
||||
|
||||
async canSavedObjectBeDeleted(id) {
|
||||
async canSavedObjectBeDeleted(id: string) {
|
||||
const allCheckBoxes = await testSubjects.findAll('checkboxSelectRow*');
|
||||
for (const checkBox of allCheckBoxes) {
|
||||
if (await checkBox.isSelected()) {
|
||||
|
@ -722,6 +733,12 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
|
|||
await checkBox.click();
|
||||
return await this.canSavedObjectsBeDeleted();
|
||||
}
|
||||
|
||||
async setNavType(navType: NavSetting) {
|
||||
await PageObjects.common.navigateToApp('settings');
|
||||
await this.clickKibanaSettings();
|
||||
await this.setAdvancedSettingsSelect('pageNavigation', navType);
|
||||
}
|
||||
}
|
||||
|
||||
return new SettingsPage();
|
|
@ -28,7 +28,7 @@ import '../../plugins/core_app_status/public/types';
|
|||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) {
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
const PageObjects = getPageObjects(['common', 'settings']);
|
||||
const browser = getService('browser');
|
||||
const appsMenu = getService('appsMenu');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
@ -48,6 +48,10 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
};
|
||||
|
||||
describe('application status management', () => {
|
||||
before(async () => {
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await PageObjects.common.navigateToApp('app_status_start');
|
||||
});
|
||||
|
|
|
@ -122,7 +122,7 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
|
|||
});
|
||||
|
||||
it('can navigate from NP apps to legacy apps', async () => {
|
||||
await appsMenu.clickLink('Management');
|
||||
await appsMenu.clickLink('Stack Management');
|
||||
await loadingScreenShown();
|
||||
await testSubjects.existOrFail('managementNav');
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Server } from 'hapi';
|
|||
import { resolve } from 'path';
|
||||
import { APMPluginContract } from '../../../plugins/apm/server';
|
||||
import { LegacyPluginInitializer } from '../../../../src/legacy/types';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
import mappings from './mappings.json';
|
||||
import { makeApmUsageCollector } from './server/lib/apm_telemetry';
|
||||
|
||||
|
@ -18,7 +19,6 @@ export const apm: LegacyPluginInitializer = kibana => {
|
|||
id: 'apm',
|
||||
configPrefix: 'xpack.apm',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
|
||||
uiExports: {
|
||||
app: {
|
||||
title: 'APM',
|
||||
|
@ -28,7 +28,8 @@ export const apm: LegacyPluginInitializer = kibana => {
|
|||
main: 'plugins/apm/index',
|
||||
icon: 'plugins/apm/icon.svg',
|
||||
euiIconType: 'apmApp',
|
||||
order: 8100
|
||||
order: 8100,
|
||||
category: DEFAULT_APP_CATEGORIES.observability
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
home: ['plugins/apm/legacy_register_feature'],
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
import { init } from './init';
|
||||
import { mappings } from './server/mappings';
|
||||
import { CANVAS_APP, CANVAS_TYPE, CUSTOM_ELEMENT_TYPE } from './common/lib';
|
||||
|
@ -23,6 +24,7 @@ export function canvas(kibana) {
|
|||
icon: 'plugins/canvas/icon.svg',
|
||||
euiIconType: 'canvasApp',
|
||||
main: 'plugins/canvas/legacy_start',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
interpreter: [
|
||||
'plugins/canvas/browser_functions',
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
import { CONFIG_DASHBOARD_ONLY_MODE_ROLES } from './common';
|
||||
|
||||
import { createDashboardModeRequestInterceptor } from './server';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// Copied largely from plugins/kibana/index.js. The dashboard viewer includes just the dashboard section of
|
||||
// the standard kibana plugin. We don't want to include code for the other links (visualize, dev tools, etc)
|
||||
// the standard kibana plugin. We don't want to include code for the other links (visualize, dev tools, etc)
|
||||
// since it's view only, but we want the urls to be the same, so we are using largely the same setup.
|
||||
export function dashboardMode(kibana) {
|
||||
const kbnBaseUrl = '/app/kibana';
|
||||
|
@ -64,6 +62,7 @@ export function dashboardMode(kibana) {
|
|||
}
|
||||
),
|
||||
icon: 'plugins/kibana/dashboard/assets/dashboard.svg',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import migrations from './migrations';
|
||||
import mappings from './mappings.json';
|
||||
import { LegacyPluginInitializer } from '../../../../src/legacy/plugin_discovery/types';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
export const graph: LegacyPluginInitializer = kibana => {
|
||||
return new kibana.Plugin({
|
||||
|
@ -25,6 +26,7 @@ export const graph: LegacyPluginInitializer = kibana => {
|
|||
icon: 'plugins/graph/icon.png',
|
||||
euiIconType: 'graphApp',
|
||||
main: 'plugins/graph/index',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
mappings,
|
||||
|
|
|
@ -146,7 +146,7 @@ function GuidancePanelComponent(props: GuidancePanelProps) {
|
|||
);
|
||||
|
||||
if (noIndexPatterns) {
|
||||
const managementUrl = chrome.navLinks.get('kibana:management')!.url;
|
||||
const managementUrl = chrome.navLinks.get('kibana:stack_management')!.url;
|
||||
const indexPatternUrl = `${managementUrl}/kibana/index_patterns`;
|
||||
const sampleDataUrl = `${application.getUrlForApp(
|
||||
'kibana'
|
||||
|
|
|
@ -18,6 +18,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../plugins/fea
|
|||
import { SpacesPluginSetup } from '../../../plugins/spaces/server';
|
||||
import { VisTypeTimeseriesSetup } from '../../../../src/plugins/vis_type_timeseries/server';
|
||||
import { APMPluginContract } from '../../../plugins/apm/server';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
export const APP_ID = 'infra';
|
||||
|
||||
|
@ -55,6 +56,7 @@ export function infra(kibana: any) {
|
|||
defaultMessage: 'Metrics',
|
||||
}),
|
||||
url: `/app/${APP_ID}#/infrastructure`,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
},
|
||||
{
|
||||
description: i18n.translate('xpack.infra.linkLogsDescription', {
|
||||
|
@ -68,6 +70,7 @@ export function infra(kibana: any) {
|
|||
defaultMessage: 'Logs',
|
||||
}),
|
||||
url: `/app/${APP_ID}#/logs`,
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
},
|
||||
],
|
||||
mappings: savedObjectMappings,
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import mappings from './mappings.json';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { resolve } from 'path';
|
||||
import mappings from './mappings.json';
|
||||
import { migrations } from './migrations';
|
||||
import { initTelemetryCollection } from './server/maps_telemetry';
|
||||
import { getAppTitle } from './common/i18n_getters';
|
||||
import _ from 'lodash';
|
||||
import { MapPlugin } from './server/plugin';
|
||||
import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common/constants';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
export function maps(kibana) {
|
||||
return new kibana.Plugin({
|
||||
|
@ -29,6 +30,7 @@ export function maps(kibana) {
|
|||
main: 'plugins/maps/legacy',
|
||||
icon: 'plugins/maps/icon.svg',
|
||||
euiIconType: APP_ICON,
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
injectDefaultVars(server) {
|
||||
const serverConfig = server.config();
|
||||
|
|
|
@ -10,7 +10,7 @@ import KbnServer, { Server } from 'src/legacy/server/kbn_server';
|
|||
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
import { plugin } from './server/new_platform';
|
||||
import { CloudSetup } from '../../../plugins/cloud/server';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
import {
|
||||
MlInitializerContext,
|
||||
MlCoreSetup,
|
||||
|
@ -42,6 +42,7 @@ export const ml = (kibana: any) => {
|
|||
icon: 'plugins/ml/application/ml.svg',
|
||||
euiIconType: 'machineLearningApp',
|
||||
main: 'plugins/ml/legacy',
|
||||
category: DEFAULT_APP_CATEGORIES.analyze,
|
||||
},
|
||||
styleSheetPaths: resolve(__dirname, 'public/application/index.scss'),
|
||||
hacks: ['plugins/ml/application/hacks/toggle_app_link_in_nav'],
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { resolve } from 'path';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
/**
|
||||
* Configuration of dependency objects for the UI, which are needed for the
|
||||
|
@ -26,6 +27,7 @@ export const getUiExports = () => ({
|
|||
euiIconType: 'monitoringApp',
|
||||
linkToLastSubUrl: false,
|
||||
main: 'plugins/monitoring/monitoring',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
},
|
||||
injectDefaultVars(server) {
|
||||
const config = server.config();
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
} from './common/constants';
|
||||
import { defaultIndexPattern } from './default_index_pattern';
|
||||
import { initServerWithKibana } from './server/kibana.index';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const siem = (kibana: any) => {
|
||||
|
@ -62,6 +63,7 @@ export const siem = (kibana: any) => {
|
|||
order: 9000,
|
||||
title: APP_NAME,
|
||||
url: `/app/${APP_ID}`,
|
||||
category: DEFAULT_APP_CATEGORIES.security,
|
||||
},
|
||||
],
|
||||
uiSettingDefaults: {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { resolve } from 'path';
|
|||
import { PluginInitializerContext } from 'src/core/server';
|
||||
import { PLUGIN } from './common/constants';
|
||||
import { KibanaServer, plugin } from './server';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
export const uptime = (kibana: any) =>
|
||||
new kibana.Plugin({
|
||||
|
@ -30,6 +31,7 @@ export const uptime = (kibana: any) =>
|
|||
main: 'plugins/uptime/app',
|
||||
order: 8900,
|
||||
url: '/app/uptime#/',
|
||||
category: DEFAULT_APP_CATEGORIES.observability,
|
||||
},
|
||||
home: ['plugins/uptime/register_feature'],
|
||||
},
|
||||
|
|
|
@ -450,7 +450,6 @@
|
|||
"common.ui.flotCharts.thuLabel": "木",
|
||||
"common.ui.flotCharts.tueLabel": "火",
|
||||
"common.ui.flotCharts.wedLabel": "水",
|
||||
"common.ui.management.breadcrumb": "管理",
|
||||
"common.ui.modals.cancelButtonLabel": "キャンセル",
|
||||
"common.ui.notify.fatalError.errorStatusMessage": "エラー {errStatus} {errStatusText}: {errMessage}",
|
||||
"common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP リクエストが接続に失敗しました。Kibana サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。",
|
||||
|
@ -1887,8 +1886,6 @@
|
|||
"kbn.management.landing.header": "Kibana {version} 管理",
|
||||
"kbn.management.landing.subhead": "インデックス、インデックスパターン、保存されたオブジェクト、Kibana の設定、その他を管理します。",
|
||||
"kbn.management.landing.text": "すべてのツールの一覧は、左のメニューにあります。",
|
||||
"kbn.management.managementDescription": "Elastic Stack の管理を行うセンターコンソールです。",
|
||||
"kbn.management.managementLabel": "管理",
|
||||
"kbn.management.objects.confirmModalOptions.deleteButtonLabel": "削除",
|
||||
"kbn.management.objects.confirmModalOptions.modalDescription": "削除されたオブジェクトは復元できません",
|
||||
"kbn.management.objects.confirmModalOptions.modalTitle": "保存された Kibana オブジェクトを削除しますか?",
|
||||
|
@ -13219,4 +13216,4 @@
|
|||
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。",
|
||||
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -450,7 +450,6 @@
|
|||
"common.ui.flotCharts.thuLabel": "周四",
|
||||
"common.ui.flotCharts.tueLabel": "周二",
|
||||
"common.ui.flotCharts.wedLabel": "周三",
|
||||
"common.ui.management.breadcrumb": "管理",
|
||||
"common.ui.modals.cancelButtonLabel": "取消",
|
||||
"common.ui.notify.fatalError.errorStatusMessage": "错误 {errStatus} {errStatusText}:{errMessage}",
|
||||
"common.ui.notify.fatalError.unavailableServerErrorMessage": "HTTP 请求无法连接。请检查 Kibana 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。",
|
||||
|
@ -1887,8 +1886,6 @@
|
|||
"kbn.management.landing.header": "Kibana {version} 管理",
|
||||
"kbn.management.landing.subhead": "管理您的索引、索引模式、已保存对象、Kibana 设置等等。",
|
||||
"kbn.management.landing.text": "在左侧菜单中可找到完整工具列表",
|
||||
"kbn.management.managementDescription": "您用于管理 Elastic Stack 的中心控制台。",
|
||||
"kbn.management.managementLabel": "管理",
|
||||
"kbn.management.objects.confirmModalOptions.deleteButtonLabel": "删除",
|
||||
"kbn.management.objects.confirmModalOptions.modalDescription": "您无法恢复删除的对象",
|
||||
"kbn.management.objects.confirmModalOptions.modalTitle": "删除已保存 Kibana 对象?",
|
||||
|
@ -13218,4 +13215,4 @@
|
|||
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。",
|
||||
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
expectSpaceSelector: false,
|
||||
}
|
||||
);
|
||||
await kibanaServer.uiSettings.replace({});
|
||||
await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
|
||||
await PageObjects.settings.navigateTo();
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Management']);
|
||||
expect(navLinks).to.eql(['Stack Management']);
|
||||
});
|
||||
|
||||
it(`allows settings to be changed`, async () => {
|
||||
|
@ -124,7 +124,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows Management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Management']);
|
||||
expect(navLinks).to.eql(['Stack Management']);
|
||||
});
|
||||
|
||||
it(`does not allow settings to be changed`, async () => {
|
||||
|
@ -175,7 +175,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows Management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Discover', 'Management']);
|
||||
expect(navLinks).to.eql(['Discover', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`does not allow navigation to advanced settings; redirects to Kibana home`, async () => {
|
||||
|
|
|
@ -40,8 +40,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Management');
|
||||
expect(navLinks).to.contain('Stack Management');
|
||||
});
|
||||
|
||||
it(`allows settings to be changed`, async () => {
|
||||
|
|
|
@ -60,7 +60,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows apm navlink', async () => {
|
||||
const navLinks = await appsMenu.readLinks();
|
||||
expect(navLinks.map(link => link.text)).to.eql(['APM', 'Management']);
|
||||
expect(navLinks.map(link => link.text)).to.eql(['APM', 'Stack Management']);
|
||||
});
|
||||
|
||||
it('can navigate to APM app', async () => {
|
||||
|
@ -109,7 +109,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows apm navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['APM', 'Management']);
|
||||
expect(navLinks).to.eql(['APM', 'Stack Management']);
|
||||
});
|
||||
|
||||
it('can navigate to APM app', async () => {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
|
||||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security']);
|
||||
const PageObjects = getPageObjects(['common', 'error', 'timePicker', 'security', 'settings']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const appsMenu = getService('appsMenu');
|
||||
|
||||
|
@ -30,6 +30,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('APM');
|
||||
});
|
||||
|
|
|
@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows canvas navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Canvas', 'Management']);
|
||||
expect(navLinks).to.eql(['Canvas', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`landing page shows "Create new workpad" button`, async () => {
|
||||
|
@ -142,7 +142,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows canvas navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Canvas', 'Management']);
|
||||
expect(navLinks).to.eql(['Canvas', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`landing page shows disabled "Create new workpad" button`, async () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'canvas', 'security', 'spaceSelector']);
|
||||
const PageObjects = getPageObjects(['common', 'canvas', 'security', 'spaceSelector', 'settings']);
|
||||
const appsMenu = getService('appsMenu');
|
||||
|
||||
describe('spaces feature controls', function() {
|
||||
|
@ -40,6 +40,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Canvas');
|
||||
});
|
||||
|
|
|
@ -75,7 +75,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows dashboard navlink', async () => {
|
||||
const navLinks = await appsMenu.readLinks();
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Dashboard', 'Management']);
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Dashboard', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`landing page shows "Create new Dashboard" button`, async () => {
|
||||
|
@ -253,7 +253,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows dashboard navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Dashboard', 'Management']);
|
||||
expect(navLinks).to.eql(['Dashboard', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`landing page doesn't show "Create new Dashboard" button`, async () => {
|
||||
|
|
|
@ -13,7 +13,13 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'spaceSelector']);
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'dashboard',
|
||||
'security',
|
||||
'spaceSelector',
|
||||
'settings',
|
||||
]);
|
||||
const appsMenu = getService('appsMenu');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
|
@ -43,6 +49,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Dashboard');
|
||||
});
|
||||
|
|
|
@ -39,6 +39,7 @@ export default function({ getService, getPageObjects }) {
|
|||
await esArchiver.load('dashboard_view_mode');
|
||||
await kibanaServer.uiSettings.replace({
|
||||
defaultIndex: 'logstash-*',
|
||||
pageNavigation: 'individual',
|
||||
});
|
||||
await browser.setWindowSize(1600, 1000);
|
||||
|
||||
|
@ -199,7 +200,7 @@ export default function({ getService, getPageObjects }) {
|
|||
await PageObjects.security.forceLogout();
|
||||
await PageObjects.security.login('mixeduser', '123456');
|
||||
|
||||
if (await appsMenu.linkExists('Management')) {
|
||||
if (await appsMenu.linkExists('Stack Management')) {
|
||||
throw new Error('Expected management nav link to not be shown');
|
||||
}
|
||||
});
|
||||
|
@ -208,7 +209,7 @@ export default function({ getService, getPageObjects }) {
|
|||
await PageObjects.security.forceLogout();
|
||||
await PageObjects.security.login('mysuperuser', '123456');
|
||||
|
||||
if (!(await appsMenu.linkExists('Management'))) {
|
||||
if (!(await appsMenu.linkExists('Stack Management'))) {
|
||||
throw new Error('Expected management nav link to be shown');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -63,7 +63,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows Dev Tools navlink', async () => {
|
||||
const navLinks = await appsMenu.readLinks();
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Dev Tools', 'Management']);
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Dev Tools', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('console', () => {
|
||||
|
@ -144,7 +144,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it(`shows 'Dev Tools' navlink`, async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Dev Tools', 'Management']);
|
||||
expect(navLinks).to.eql(['Dev Tools', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('console', () => {
|
||||
|
|
|
@ -9,7 +9,13 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'spaceSelector']);
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'dashboard',
|
||||
'security',
|
||||
'spaceSelector',
|
||||
'settings',
|
||||
]);
|
||||
const appsMenu = getService('appsMenu');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const grokDebugger = getService('grokDebugger');
|
||||
|
@ -40,6 +46,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Dev Tools');
|
||||
});
|
||||
|
|
|
@ -81,7 +81,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows discover navlink', async () => {
|
||||
const navLinks = await appsMenu.readLinks();
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Discover', 'Management']);
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Discover', 'Stack Management']);
|
||||
});
|
||||
|
||||
it('shows save button', async () => {
|
||||
|
@ -168,7 +168,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows discover navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Discover', 'Management']);
|
||||
expect(navLinks).to.eql(['Discover', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`doesn't show save button`, async () => {
|
||||
|
|
|
@ -15,6 +15,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
'timePicker',
|
||||
'security',
|
||||
'spaceSelector',
|
||||
'settings',
|
||||
]);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const appsMenu = getService('appsMenu');
|
||||
|
@ -49,6 +50,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Discover');
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows graph navlink', async () => {
|
||||
const navLinks = await appsMenu.readLinks();
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Graph', 'Management']);
|
||||
expect(navLinks.map(link => link.text)).to.eql(['Graph', 'Stack Management']);
|
||||
});
|
||||
|
||||
it('landing page shows "Create new graph" button', async () => {
|
||||
|
@ -127,7 +127,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows graph navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Graph', 'Management']);
|
||||
expect(navLinks).to.eql(['Graph', 'Stack Management']);
|
||||
});
|
||||
|
||||
it('does not show a "Create new Workspace" button', async () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'graph', 'security', 'error']);
|
||||
const PageObjects = getPageObjects(['common', 'graph', 'security', 'error', 'settings']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const appsMenu = getService('appsMenu');
|
||||
|
||||
|
@ -34,6 +34,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Graph');
|
||||
});
|
||||
|
|
|
@ -70,7 +70,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Management']);
|
||||
expect(navLinks).to.eql(['Stack Management']);
|
||||
});
|
||||
|
||||
it(`index pattern listing shows create button`, async () => {
|
||||
|
@ -113,7 +113,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
}
|
||||
);
|
||||
|
||||
await kibanaServer.uiSettings.replace({});
|
||||
await kibanaServer.uiSettings.replace({ pageNavigation: 'individual' });
|
||||
await PageObjects.settings.navigateTo();
|
||||
});
|
||||
|
||||
|
@ -124,7 +124,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Management']);
|
||||
expect(navLinks).to.eql(['Stack Management']);
|
||||
});
|
||||
|
||||
it(`index pattern listing doesn't show create button`, async () => {
|
||||
|
@ -176,7 +176,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows Management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Discover', 'Management']);
|
||||
expect(navLinks).to.eql(['Discover', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`doesn't show Index Patterns in management side-nav`, async () => {
|
||||
|
|
|
@ -40,8 +40,9 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Management');
|
||||
expect(navLinks).to.contain('Stack Management');
|
||||
});
|
||||
|
||||
it(`index pattern listing shows create button`, async () => {
|
||||
|
|
|
@ -61,7 +61,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows metrics navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Metrics', 'Management']);
|
||||
expect(navLinks).to.eql(['Metrics', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('infrastructure landing page without data', () => {
|
||||
|
@ -174,7 +174,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows metrics navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Metrics', 'Management']);
|
||||
expect(navLinks).to.eql(['Metrics', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('infrastructure landing page without data', () => {
|
||||
|
|
|
@ -12,7 +12,13 @@ const DATE_WITH_DATA = DATES.metricsAndLogs.hosts.withData;
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'infraHome', 'security', 'spaceSelector']);
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'infraHome',
|
||||
'security',
|
||||
'spaceSelector',
|
||||
'settings',
|
||||
]);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const appsMenu = getService('appsMenu');
|
||||
const retry = getService('retry');
|
||||
|
@ -31,7 +37,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
// we need to load the following in every situation as deleting
|
||||
// a space deletes all of the associated saved objects
|
||||
await esArchiver.load('empty_kibana');
|
||||
|
||||
await spacesService.create({
|
||||
id: 'custom_space',
|
||||
name: 'custom_space',
|
||||
|
@ -48,6 +53,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Metrics');
|
||||
});
|
||||
|
|
|
@ -58,7 +58,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows logs navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Logs', 'Management']);
|
||||
expect(navLinks).to.eql(['Logs', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('logs landing page without data', () => {
|
||||
|
@ -121,7 +121,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows logs navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Logs', 'Management']);
|
||||
expect(navLinks).to.eql(['Logs', 'Stack Management']);
|
||||
});
|
||||
|
||||
describe('logs landing page without data', () => {
|
||||
|
|
|
@ -9,7 +9,13 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'infraHome', 'security', 'spaceSelector']);
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'infraHome',
|
||||
'security',
|
||||
'spaceSelector',
|
||||
'settings',
|
||||
]);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const appsMenu = getService('appsMenu');
|
||||
|
||||
|
@ -36,6 +42,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Logs');
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
const esArchiver = getService('esArchiver');
|
||||
const security = getService('security');
|
||||
const appsMenu = getService('appsMenu');
|
||||
const PageObjects = getPageObjects(['common', 'security']);
|
||||
const PageObjects = getPageObjects(['common', 'security', 'settings']);
|
||||
|
||||
describe('security', () => {
|
||||
before(async () => {
|
||||
|
@ -94,6 +94,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
await PageObjects.security.login('machine_learning_user', 'machine_learning_user-password');
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error']);
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error', 'settings']);
|
||||
const appsMenu = getService('appsMenu');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
|
@ -39,6 +39,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Machine Learning');
|
||||
});
|
||||
|
|
|
@ -66,7 +66,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows maps navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Maps', 'Management']);
|
||||
expect(navLinks).to.eql(['Maps', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`allows a map to be created`, async () => {
|
||||
|
@ -153,7 +153,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('shows Maps navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Maps', 'Management']);
|
||||
expect(navLinks).to.eql(['Maps', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`does not show create new button`, async () => {
|
||||
|
@ -248,7 +248,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('does not show Maps navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.eql(['Discover', 'Management']);
|
||||
expect(navLinks).to.eql(['Discover', 'Stack Management']);
|
||||
});
|
||||
|
||||
it(`returns a 404`, async () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
const esArchiver = getService('esArchiver');
|
||||
const security = getService('security');
|
||||
const appsMenu = getService('appsMenu');
|
||||
const PageObjects = getPageObjects(['common', 'security']);
|
||||
const PageObjects = getPageObjects(['common', 'security', 'settings']);
|
||||
|
||||
describe('security', () => {
|
||||
before(async () => {
|
||||
|
@ -97,6 +97,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('shows monitoring navlink', async () => {
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Stack Monitoring');
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
export default function({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const spacesService = getService('spaces');
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error']);
|
||||
const PageObjects = getPageObjects(['common', 'dashboard', 'security', 'error', 'settings']);
|
||||
const appsMenu = getService('appsMenu');
|
||||
const find = getService('find');
|
||||
|
||||
|
@ -37,10 +37,11 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
|
|||
await spacesService.delete('custom_space');
|
||||
});
|
||||
|
||||
it('shows Stack Monitoring navlink', async () => {
|
||||
it('shows Stack Monitoring navlink fail', async () => {
|
||||
await PageObjects.common.navigateToApp('home', {
|
||||
basePath: '/s/custom_space',
|
||||
});
|
||||
await PageObjects.settings.setNavType('individual');
|
||||
const navLinks = (await appsMenu.readLinks()).map(link => link.text);
|
||||
expect(navLinks).to.contain('Stack Monitoring');
|
||||
});
|
||||
|
|
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