diff --git a/docs/development/core/public/kibana-plugin-public.appbase.category.md b/docs/development/core/public/kibana-plugin-public.appbase.category.md new file mode 100644 index 0000000000000..215ebbbd0e186 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appbase.category.md @@ -0,0 +1,13 @@ + + +[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 + +Signature: + +```typescript +category?: AppCategory; +``` diff --git a/docs/development/core/public/kibana-plugin-public.appbase.md b/docs/development/core/public/kibana-plugin-public.appbase.md index eb6d91cb92488..6f547450b6a12 100644 --- a/docs/development/core/public/kibana-plugin-public.appbase.md +++ b/docs/development/core/public/kibana-plugin-public.appbase.md @@ -16,6 +16,7 @@ export interface AppBase | Property | Type | Description | | --- | --- | --- | | [capabilities](./kibana-plugin-public.appbase.capabilities.md) | Partial<Capabilities> | Custom capabilities defined by the app. | +| [category](./kibana-plugin-public.appbase.category.md) | AppCategory | 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) | boolean | Hide the UI chrome when the application is mounted. Defaults to false. Takes precedence over chrome service visibility settings. | | [euiIconType](./kibana-plugin-public.appbase.euiicontype.md) | string | A EUI iconType that will be used for the app's icon. This icon takes precendence over the icon property. | | [icon](./kibana-plugin-public.appbase.icon.md) | string | A URL to an image file used as an icon. Used as a fallback if euiIconType is not provided. | diff --git a/docs/development/core/public/kibana-plugin-public.appcategory.arialabel.md b/docs/development/core/public/kibana-plugin-public.appcategory.arialabel.md new file mode 100644 index 0000000000000..0245b548ae74f --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appcategory.arialabel.md @@ -0,0 +1,13 @@ + + +[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 + +Signature: + +```typescript +ariaLabel?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-public.appcategory.euiicontype.md b/docs/development/core/public/kibana-plugin-public.appcategory.euiicontype.md new file mode 100644 index 0000000000000..90133735a0082 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appcategory.euiicontype.md @@ -0,0 +1,13 @@ + + +[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 + +Signature: + +```typescript +euiIconType?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-public.appcategory.label.md b/docs/development/core/public/kibana-plugin-public.appcategory.label.md new file mode 100644 index 0000000000000..171b1627f9ef8 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appcategory.label.md @@ -0,0 +1,13 @@ + + +[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. + +Signature: + +```typescript +label: string; +``` diff --git a/docs/development/core/public/kibana-plugin-public.appcategory.md b/docs/development/core/public/kibana-plugin-public.appcategory.md new file mode 100644 index 0000000000000..f1085e7325272 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appcategory.md @@ -0,0 +1,23 @@ + + +[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 + +Signature: + +```typescript +export interface AppCategory +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ariaLabel](./kibana-plugin-public.appcategory.arialabel.md) | string | If the visual label isn't appropriate for screen readers, can override it here | +| [euiIconType](./kibana-plugin-public.appcategory.euiicontype.md) | string | 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) | string | Label used for cateogry name. Also used as aria-label if one isn't set. | +| [order](./kibana-plugin-public.appcategory.order.md) | number | 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) | + diff --git a/docs/development/core/public/kibana-plugin-public.appcategory.order.md b/docs/development/core/public/kibana-plugin-public.appcategory.order.md new file mode 100644 index 0000000000000..ef17ac04b78d6 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.appcategory.order.md @@ -0,0 +1,13 @@ + + +[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) + +Signature: + +```typescript +order?: number; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromenavlink.category.md b/docs/development/core/public/kibana-plugin-public.chromenavlink.category.md new file mode 100644 index 0000000000000..19d5a43a29307 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.chromenavlink.category.md @@ -0,0 +1,13 @@ + + +[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 + +Signature: + +```typescript +readonly category?: AppCategory; +``` diff --git a/docs/development/core/public/kibana-plugin-public.chromenavlink.md b/docs/development/core/public/kibana-plugin-public.chromenavlink.md index 4cb9080222ac5..2afd6ce2d58c4 100644 --- a/docs/development/core/public/kibana-plugin-public.chromenavlink.md +++ b/docs/development/core/public/kibana-plugin-public.chromenavlink.md @@ -17,6 +17,7 @@ export interface ChromeNavLink | --- | --- | --- | | [active](./kibana-plugin-public.chromenavlink.active.md) | boolean | Indicates whether or not this app is currently on the screen. | | [baseUrl](./kibana-plugin-public.chromenavlink.baseurl.md) | string | The base route used to open the root of an application. | +| [category](./kibana-plugin-public.chromenavlink.category.md) | AppCategory | The category the app lives in | | [disabled](./kibana-plugin-public.chromenavlink.disabled.md) | boolean | Disables a link from being clickable. | | [euiIconType](./kibana-plugin-public.chromenavlink.euiicontype.md) | string | A EUI iconType that will be used for the app's icon. This icon takes precendence over the icon property. | | [hidden](./kibana-plugin-public.chromenavlink.hidden.md) | boolean | Hides a link from the navigation. | diff --git a/docs/development/core/public/kibana-plugin-public.legacynavlink.category.md b/docs/development/core/public/kibana-plugin-public.legacynavlink.category.md new file mode 100644 index 0000000000000..7026e9b519cc0 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-public.legacynavlink.category.md @@ -0,0 +1,11 @@ + + +[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 + +Signature: + +```typescript +category?: AppCategory; +``` diff --git a/docs/development/core/public/kibana-plugin-public.legacynavlink.md b/docs/development/core/public/kibana-plugin-public.legacynavlink.md index fc0c445f517b3..e112110dd10f8 100644 --- a/docs/development/core/public/kibana-plugin-public.legacynavlink.md +++ b/docs/development/core/public/kibana-plugin-public.legacynavlink.md @@ -15,6 +15,7 @@ export interface LegacyNavLink | Property | Type | Description | | --- | --- | --- | +| [category](./kibana-plugin-public.legacynavlink.category.md) | AppCategory | | | [euiIconType](./kibana-plugin-public.legacynavlink.euiicontype.md) | string | | | [icon](./kibana-plugin-public.legacynavlink.icon.md) | string | | | [id](./kibana-plugin-public.legacynavlink.id.md) | string | | diff --git a/docs/development/core/public/kibana-plugin-public.md b/docs/development/core/public/kibana-plugin-public.md index 27037d46926c1..52aca7501e64d 100644 --- a/docs/development/core/public/kibana-plugin-public.md +++ b/docs/development/core/public/kibana-plugin-public.md @@ -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) | | diff --git a/packages/kbn-i18n/src/loader.ts b/packages/kbn-i18n/src/loader.ts index 2d68079735c03..21f540f588f46 100644 --- a/packages/kbn-i18n/src/loader.ts +++ b/packages/kbn-i18n/src/loader.ts @@ -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 { - 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')); } /** diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 0d955482d2226..63e542b0127ed 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -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` diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index abd04722a49f2..9018b21973634 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -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) { diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 09ea1afe35766..6ab9fe158742a 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -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 { this.initVisibility(application); @@ -173,7 +175,6 @@ export class ChromeService { getHeaderComponent: () => ( -
), diff --git a/src/core/public/chrome/nav_links/nav_link.ts b/src/core/public/chrome/nav_links/nav_link.ts index 3b16c030ddcc9..4d3a1e9ecd199 100644 --- a/src/core/public/chrome/nav_links/nav_link.ts +++ b/src/core/public/chrome/nav_links/nav_link.ts @@ -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. */ diff --git a/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap new file mode 100644 index 0000000000000..0ebc44ba67862 --- /dev/null +++ b/src/core/public/chrome/ui/header/__snapshots__/nav_drawer.test.tsx.snap @@ -0,0 +1,5224 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NavDrawer Advanced setting set to grouped renders grouped items 1`] = ` + + + + + + + +`; + +exports[`NavDrawer Advanced setting set to grouped renders individual items if there are less than 7 1`] = ` + + + + + + + +`; + +exports[`NavDrawer Advanced setting set to grouped renders individual items if there is only 1 category 1`] = ` + + + + + + + +`; + +exports[`NavDrawer Advanced setting set to individual renders individual items 1`] = ` + + + + + + + +`; diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index d05a6bb53405c..c3cefd180b16f 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -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'; - -// 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>; +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'; -interface Props { +export interface HeaderProps { kibanaVersion: string; application: InternalApplicationStart; appTitle$: Rx.Observable; @@ -168,28 +67,29 @@ interface Props { legacyMode: boolean; navControlsLeft$: Rx.Observable; navControlsRight$: Rx.Observable; - intl: InjectedIntl; basePath: HttpStart['basePath']; isLocked?: boolean; - onIsLockedUpdate?: (isLocked: boolean) => void; + navSetting$: Rx.Observable; + onIsLockedUpdate?: OnIsLockedUpdate; } interface State { appTitle: string; - currentAppId?: string; isVisible: boolean; - navLinks: ReadonlyArray>; - recentlyAccessed: ReadonlyArray>; + navLinks: ChromeNavLink[]; + recentlyAccessed: ChromeRecentlyAccessedHistoryItem[]; forceNavigation: boolean; navControlsLeft: readonly ChromeNavControl[]; navControlsRight: readonly ChromeNavControl[]; + navSetting: NavSetting; + currentAppId: string | undefined; } -class HeaderUI extends Component { +export class Header extends Component { private subscription?: Rx.Subscription; private navDrawerRef = createRef(); - constructor(props: Props) { + constructor(props: HeaderProps) { super(props); this.state = { @@ -200,6 +100,8 @@ class HeaderUI extends Component { forceNavigation: false, navControlsLeft: [], navControlsRight: [], + navSetting: 'grouped', + currentAppId: '', }; } @@ -214,7 +116,8 @@ class HeaderUI extends Component { 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 { 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 { } } - public renderLogo() { - const { homeHref, intl } = this.props; - return ( - - ); - } - public renderMenuTrigger() { return ( this.navDrawerRef.current.toggleOpen()} > @@ -275,98 +163,29 @@ class HeaderUI extends Component { } 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 ? ( - - ) : ( - 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 (
@@ -375,7 +194,13 @@ class HeaderUI extends Component { {this.renderMenuTrigger()} - {this.renderLogo()} + + + @@ -399,75 +224,17 @@ class HeaderUI extends Component { - - - - - - + />
); } - - private onNavClick = (event: React.MouseEvent) => { - 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); diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx new file mode 100644 index 0000000000000..793b8646dabf7 --- /dev/null +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -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, + 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 ( + onClick(e, forceNavigation, navLinks)} + href={href} + aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', { + defaultMessage: 'Go to home page', + })} + /> + ); +} diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts index 6d59fc6d9433b..b396c94b3f2a3 100644 --- a/src/core/public/chrome/ui/header/index.ts +++ b/src/core/public/chrome/ui/header/index.ts @@ -26,3 +26,5 @@ export { ChromeHelpExtensionMenuDocumentationLink, ChromeHelpExtensionMenuGitHubLink, } from './header_help_menu'; +export type NavSetting = 'grouped' | 'individual'; +export type OnIsLockedUpdate = (isLocked: boolean) => void; diff --git a/src/core/public/chrome/ui/header/nav_drawer.test.tsx b/src/core/public/chrome/ui/header/nav_drawer.test.tsx new file mode 100644 index 0000000000000..7272935b93a52 --- /dev/null +++ b/src/core/public/chrome/ui/header/nav_drawer.test.tsx @@ -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( + + ); + 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(); + 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(); + expect(component).toMatchSnapshot(); + }); + it('renders grouped items', () => { + const component = mount(); + expect(component).toMatchSnapshot(); + }); + }); +}); diff --git a/src/core/public/chrome/ui/header/nav_drawer.tsx b/src/core/public/chrome/ui/header/nav_drawer.tsx new file mode 100644 index 0000000000000..dbb68d5dd3901 --- /dev/null +++ b/src/core/public/chrome/ui/header/nav_drawer.tsx @@ -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) { + const allCategories = {} as Record; + + for (const [key, value] of Object.entries(allCategorizedLinks)) { + allCategories[key] = value[0].category; + } + + return allCategories; +} + +function getOrderedCategories( + mainCategories: Record, + categoryDictionary: ReturnType +) { + 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 +) { + 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 ( + + {RecentLinks({ + recentlyAccessedItems, + navLinks: chromeNavLinks, + basePath, + })} + + {showUngroupedNav ? ( + + ) : ( + <> + { + 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'), + ]} + /> + + { + link['data-test-subj'] = 'navDrawerFlyoutLink'; + return link; + }), + }, + }, + ]} + /> + + )} + + ); +} + +export const NavDrawer = React.forwardRef(navDrawerRenderer); diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx new file mode 100644 index 0000000000000..52b59c53b658c --- /dev/null +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -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 ; +} + +export type NavLink = ReturnType; + +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 ? : undefined, + order, + 'data-test-subj': 'navDrawerAppsMenuLink', + }; +} diff --git a/src/core/public/chrome/ui/header/recent_links.tsx b/src/core/public/chrome/ui/header/recent_links.tsx new file mode 100644 index 0000000000000..a947ab1c45056 --- /dev/null +++ b/src/core/public/chrome/ui/header/recent_links.tsx @@ -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 ( + + ); +} diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index 97bd49416f705..b9439a0f96e7e 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -246,6 +246,7 @@ export class CoreSystem { http, injectedMetadata, notifications, + uiSettings, }); application.registerMountContext(this.coreContext.coreId, 'core', () => ({ diff --git a/src/core/public/index.ts b/src/core/public/index.ts index a3faddecec47a..67b31c28d2478 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -78,7 +78,8 @@ import { /** @interal */ export { CoreContext, CoreSystem } from './core_system'; -export { RecursiveReadonly } from '../utils'; +export { RecursiveReadonly, DEFAULT_APP_CATEGORIES } from '../utils'; +export { AppCategory } from '../types'; export { ApplicationSetup, diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 0bde1b68e1876..1075a7741ee32 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -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; user?: Record; diff --git a/src/core/public/legacy/legacy_service.ts b/src/core/public/legacy/legacy_service.ts index f906aff1759e2..cc3210771eecc 100644 --- a/src/core/public/legacy/legacy_service.ts +++ b/src/core/public/legacy/legacy_service.ts @@ -74,6 +74,7 @@ export class LegacyPlatformService { appUrl: navLink.url, subUrlBase: navLink.subUrlBase, linkToLastSubUrl: navLink.linkToLastSubUrl, + category: navLink.category, }) ); diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index f7b260c68ee96..0da6e0d422f2d 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -26,6 +26,7 @@ export interface App extends AppBase { // @public (undocumented) export interface AppBase { capabilities?: Partial; + category?: AppCategory; chromeless?: boolean; euiIconType?: string; icon?: string; @@ -40,6 +41,14 @@ export interface AppBase { updater$?: Observable; } +// @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) diff --git a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts index 9867274d224bd..a19133c30659b 100644 --- a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts +++ b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts @@ -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(spec => ({ id: spec.id, + category: spec.category, title: spec.title, order: typeof spec.order === 'number' ? spec.order : 0, url: spec.url, diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts index 40b8244a31890..d51058ca561c6 100644 --- a/src/core/server/legacy/types.ts +++ b/src/core/server/legacy/types.ts @@ -139,7 +139,7 @@ export type LegacyNavLinkSpec = Record & 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 }; /** diff --git a/src/core/types/app_category.ts b/src/core/types/app_category.ts new file mode 100644 index 0000000000000..83a3693f009b6 --- /dev/null +++ b/src/core/types/app_category.ts @@ -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; +} diff --git a/src/core/types/index.ts b/src/core/types/index.ts index d01b514c770a7..7ddb6b0d8dfbb 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -23,3 +23,4 @@ */ export * from './core_service'; export * from './capabilities'; +export * from './app_category'; diff --git a/src/core/utils/default_app_categories.ts b/src/core/utils/default_app_categories.ts new file mode 100644 index 0000000000000..3e3cc2fef2a22 --- /dev/null +++ b/src/core/utils/default_app_categories.ts @@ -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', + }, +}); diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index 7c8ed481c0a7d..7317c222d3bc3 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -28,3 +28,4 @@ export * from './pick'; export * from './promise'; export * from './url'; export * from './unset'; +export * from './default_app_categories'; diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index 0366d8b27f211..55bd852050218 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -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, }, ], diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx index d2dda32f318fe..1aad7e953b8de 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx @@ -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`; diff --git a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js index be2ceb66f69d0..27d4f1a8b1c1f 100644 --- a/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js +++ b/src/legacy/core_plugins/kibana/public/home/np_ready/components/home.test.js @@ -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', diff --git a/src/legacy/core_plugins/kibana/public/management/index.js b/src/legacy/core_plugins/kibana/public/management/index.js index d62770956b88e..1305310b6f615 100644 --- a/src/legacy/core_plugins/kibana/public/management/index.js +++ b/src/legacy/core_plugins/kibana/public/management/index.js @@ -74,7 +74,7 @@ export function updateLandingPage(version) {

@@ -93,7 +93,7 @@ export function updateLandingPage(version) {

@@ -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', diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index 2c2861b8e0a8e..1a9d8f2b40e98 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -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', + }), + }, + }, }; } diff --git a/src/legacy/core_plugins/management/index.ts b/src/legacy/core_plugins/management/index.ts index 65601b5371815..4962c948f842f 100644 --- a/src/legacy/core_plugins/management/index.ts +++ b/src/legacy/core_plugins/management/index.ts @@ -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({ diff --git a/src/legacy/core_plugins/telemetry/common/constants.ts b/src/legacy/core_plugins/telemetry/common/constants.ts index cb4ff79969a32..cf2c9c883871b 100644 --- a/src/legacy/core_plugins/telemetry/common/constants.ts +++ b/src/legacy/core_plugins/telemetry/common/constants.ts @@ -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'; diff --git a/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts b/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts index f45cf7fc6bb33..44926b644ced5 100644 --- a/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts +++ b/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts @@ -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), }); diff --git a/src/legacy/core_plugins/timelion/index.ts b/src/legacy/core_plugins/timelion/index.ts index ec121647f4e47..d725327e2365b 100644 --- a/src/legacy/core_plugins/timelion/index.ts +++ b/src/legacy/core_plugins/timelion/index.ts @@ -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')], diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts index fe886b9d17811..9425003eae874 100644 --- a/src/legacy/plugin_discovery/types.ts +++ b/src/legacy/plugin_discovery/types.ts @@ -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; diff --git a/src/legacy/ui/public/management/breadcrumbs.ts b/src/legacy/ui/public/management/breadcrumbs.ts index fe53bcfde9e1f..936e99caff565 100644 --- a/src/legacy/ui/public/management/breadcrumbs.ts +++ b/src/legacy/ui/public/management/breadcrumbs.ts @@ -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', }); diff --git a/src/legacy/ui/ui_apps/ui_app.js b/src/legacy/ui/ui_apps/ui_app.js index 9c82ff2abedb5..1cfd54588b516 100644 --- a/src/legacy/ui/ui_apps/ui_app.js +++ b/src/legacy/ui/ui_apps/ui_app.js @@ -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, }; } } diff --git a/src/legacy/ui/ui_exports/ui_export_types/ui_apps.js b/src/legacy/ui/ui_exports/ui_export_types/ui_apps.js index d7ac49d9d49a3..639a5a7c58e18 100644 --- a/src/legacy/ui/ui_exports/ui_export_types/ui_apps.js +++ b/src/legacy/ui/ui_exports/ui_export_types/ui_apps.js @@ -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, }; } diff --git a/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js b/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js index 37e023127ed41..543fe05b13e43 100644 --- a/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js +++ b/src/legacy/ui/ui_nav_links/__tests__/ui_nav_link.js @@ -45,6 +45,7 @@ describe('UiNavLink', () => { euiIconType: spec.euiIconType, hidden: spec.hidden, disabled: spec.disabled, + category: undefined, // defaults linkToLastSubUrl: true, diff --git a/src/legacy/ui/ui_nav_links/ui_nav_link.js b/src/legacy/ui/ui_nav_links/ui_nav_link.js index 7537a60adbcf2..5888c21a53c95 100644 --- a/src/legacy/ui/ui_nav_links/ui_nav_link.js +++ b/src/legacy/ui/ui_nav_links/ui_nav_link.js @@ -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, }; } } diff --git a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx index cb0b82d0f0bde..69ba813d2347e 100644 --- a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx +++ b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx @@ -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 ( <>

{i18n.translate('management.nav.label', { - defaultMessage: 'Management', + // todo + defaultMessage: 'Stack Management', })}

diff --git a/src/plugins/management/public/legacy/sections_register.js b/src/plugins/management/public/legacy/sections_register.js index 63d919377f89e..ca35db56c340b 100644 --- a/src/plugins/management/public/legacy/sections_register.js +++ b/src/plugins/management/public/legacy/sections_register.js @@ -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, diff --git a/src/plugins/management/public/management_app.tsx b/src/plugins/management/public/management_app.tsx index 705d98eaaf2ff..02b3ea306c23d 100644 --- a/src/plugins/management/public/management_app.tsx +++ b/src/plugins/management/public/management_app.tsx @@ -64,7 +64,8 @@ export class ManagementApp { coreStart.chrome.setBreadcrumbs([ { text: i18n.translate('management.breadcrumb', { - defaultMessage: 'Management', + // todo + defaultMessage: 'Stack Management', }), href: '#/management', }, diff --git a/test/functional/apps/dashboard/create_and_add_embeddables.js b/test/functional/apps/dashboard/create_and_add_embeddables.js index 90f02c36b3b7f..0b628100a98bd 100644 --- a/test/functional/apps/dashboard/create_and_add_embeddables.js +++ b/test/functional/apps/dashboard/create_and_add_embeddables.js @@ -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(); diff --git a/test/functional/apps/management/_index_pattern_filter.js b/test/functional/apps/management/_index_pattern_filter.js index a32024adb5ec7..e685c43e9ce98 100644 --- a/test/functional/apps/management/_index_pattern_filter.js +++ b/test/functional/apps/management/_index_pattern_filter.js @@ -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(); }); diff --git a/test/functional/apps/visualize/_lab_mode.js b/test/functional/apps/visualize/_lab_mode.js index 3ee806af8165d..b082480d95a2e 100644 --- a/test/functional/apps/visualize/_lab_mode.js +++ b/test/functional/apps/visualize/_lab_mode.js @@ -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'); }); diff --git a/test/functional/page_objects/header_page.js b/test/functional/page_objects/header_page.js index f82e4e4387e27..05edd64545a56 100644 --- a/test/functional/page_objects/header_page.js +++ b/test/functional/page_objects/header_page.js @@ -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(); } diff --git a/test/functional/page_objects/settings_page.js b/test/functional/page_objects/settings_page.ts similarity index 90% rename from test/functional/page_objects/settings_page.js rename to test/functional/page_objects/settings_page.ts index a4ae361b12ed8..e92780143f09a 100644 --- a/test/functional/page_objects/settings_page.js +++ b/test/functional/page_objects/settings_page.ts @@ -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) { 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, + 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(); diff --git a/test/plugin_functional/test_suites/core_plugins/application_status.ts b/test/plugin_functional/test_suites/core_plugins/application_status.ts index b6d13a5604011..0cc64277efe11 100644 --- a/test/plugin_functional/test_suites/core_plugins/application_status.ts +++ b/test/plugin_functional/test_suites/core_plugins/application_status.ts @@ -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'); }); diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts index f50d460532556..6567837f65309 100644 --- a/test/plugin_functional/test_suites/core_plugins/applications.ts +++ b/test/plugin_functional/test_suites/core_plugins/applications.ts @@ -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'); }); diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts index 0934cb0019f44..c52e6742ddae5 100644 --- a/x-pack/legacy/plugins/apm/index.ts +++ b/x-pack/legacy/plugins/apm/index.ts @@ -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'], diff --git a/x-pack/legacy/plugins/canvas/index.js b/x-pack/legacy/plugins/canvas/index.js index 8e742de6de944..ebd4f35db8175 100644 --- a/x-pack/legacy/plugins/canvas/index.js +++ b/x-pack/legacy/plugins/canvas/index.js @@ -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', diff --git a/x-pack/legacy/plugins/dashboard_mode/index.js b/x-pack/legacy/plugins/dashboard_mode/index.js index 4a04249844322..94655adf981b4 100644 --- a/x-pack/legacy/plugins/dashboard_mode/index.js +++ b/x-pack/legacy/plugins/dashboard_mode/index.js @@ -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, }, ], }, diff --git a/x-pack/legacy/plugins/graph/index.ts b/x-pack/legacy/plugins/graph/index.ts index 601a239574e6b..f798fa5e9f39d 100644 --- a/x-pack/legacy/plugins/graph/index.ts +++ b/x-pack/legacy/plugins/graph/index.ts @@ -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, diff --git a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx index f34b82d6bb1a3..d1fcbea2ff5b7 100644 --- a/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx +++ b/x-pack/legacy/plugins/graph/public/components/guidance_panel/guidance_panel.tsx @@ -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' diff --git a/x-pack/legacy/plugins/infra/index.ts b/x-pack/legacy/plugins/infra/index.ts index 196950b51be3a..d9abadcb5125c 100644 --- a/x-pack/legacy/plugins/infra/index.ts +++ b/x-pack/legacy/plugins/infra/index.ts @@ -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, diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index 8179a15e441ac..52045ee218825 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -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({ @@ -30,6 +31,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(); diff --git a/x-pack/legacy/plugins/ml/index.ts b/x-pack/legacy/plugins/ml/index.ts index c4289389b0d56..fc1cec7c16208 100755 --- a/x-pack/legacy/plugins/ml/index.ts +++ b/x-pack/legacy/plugins/ml/index.ts @@ -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'], diff --git a/x-pack/legacy/plugins/monitoring/ui_exports.js b/x-pack/legacy/plugins/monitoring/ui_exports.js index ba659aa74f10c..2b5ea21a2bb45 100644 --- a/x-pack/legacy/plugins/monitoring/ui_exports.js +++ b/x-pack/legacy/plugins/monitoring/ui_exports.js @@ -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(); diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts index edbb62feb580f..f6f2ead2d64fa 100644 --- a/x-pack/legacy/plugins/siem/index.ts +++ b/x-pack/legacy/plugins/siem/index.ts @@ -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: { diff --git a/x-pack/legacy/plugins/uptime/index.ts b/x-pack/legacy/plugins/uptime/index.ts index e090a2c85e136..cf7332f97d466 100644 --- a/x-pack/legacy/plugins/uptime/index.ts +++ b/x-pack/legacy/plugins/uptime/index.ts @@ -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'], }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 56b751e3ad42f..dc8a12c83c4ef 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -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 サーバーが実行されていて、ご使用のブラウザの接続が正常に動作していることを確認するか、システム管理者にお問い合わせください。", @@ -1898,8 +1897,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 オブジェクトを削除しますか?", @@ -13256,4 +13253,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} +} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index bc95191196fbd..1f3517cf5514e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -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 服务器是否正在运行以及您的浏览器是否具有有效的连接,或请联系您的系统管理员。", @@ -1898,8 +1897,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 对象?", @@ -13255,4 +13252,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} +} \ No newline at end of file diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts index ad4f81777e780..2649c5d26309d 100644 --- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts +++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts index ee58be76928b3..79bb10e0bded1 100644 --- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts +++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_spaces.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts index e2d5efac4644c..7c9c9f9c8c155 100644 --- a/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts +++ b/x-pack/test/functional/apps/apm/feature_controls/apm_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts b/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts index 1ac1784e0e05d..474240b201fac 100644 --- a/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts +++ b/x-pack/test/functional/apps/apm/feature_controls/apm_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts index d0e37ec8e3f35..71c10bd8248be 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts index 28b572401892b..5395f125bbd22 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index d25fae3c4894c..6a6e2f23785e3 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts index ebe08a60c2563..002ae627c488d 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js index bab798dacc453..1189fe909ca32 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js @@ -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'); } }); diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts index 494fd71ea6f34..9db9a913e9a4b 100644 --- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts +++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts @@ -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', () => { diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts index 4184d223a9686..f917792eea027 100644 --- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts +++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 1912b16d96f36..1796858165a2b 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts index e6b6f28f8b92f..c38dda536f253 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index a2b062e6ef84f..37de93a0a7e91 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts index a0b0d5bef9668..d0d0232b5a8b1 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts index 30cdc95b38e62..ed25816e68712 100644 --- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts +++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts index 6a2b77de17f45..75020d6eab7e4 100644 --- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts +++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_spaces.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts index dd2a479c740c3..2a694f73d486e 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_security.ts @@ -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', () => { diff --git a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts index 170bb28e91419..66f38ee349074 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/infrastructure_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts index bb358ff1d0dba..164376d20712c 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts @@ -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', () => { diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts index 3251dc08e72dd..234cfca63b09a 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/logs_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts index 8fb6f21c778d3..c25c1bfe4b731 100644 --- a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts +++ b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts index fc94688e98811..c633852a2da0a 100644 --- a/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts +++ b/x-pack/test/functional/apps/machine_learning/feature_controls/ml_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts index 804ad5725edfd..ece162cbd96cc 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts +++ b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts @@ -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 () => { diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts index d985da42ab5ed..130aefb3cae2a 100644 --- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts +++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts index 7459b53ca4a32..0465cbcf54541 100644 --- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts +++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts @@ -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'); }); diff --git a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts index 1e79c76bf83e5..d71d197a6ea19 100644 --- a/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts +++ b/x-pack/test/functional/apps/spaces/feature_controls/spaces_security.ts @@ -16,6 +16,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { describe('security feature controls', () => { before(async () => { await esArchiver.load('empty_kibana'); + await PageObjects.settings.setNavType('individual'); }); after(async () => { @@ -56,7 +57,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.contain('Management'); + expect(navLinks).to.contain('Stack Management'); }); it(`displays Spaces management section`, async () => { @@ -130,7 +131,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.contain('Management'); + expect(navLinks).to.contain('Stack Management'); }); it(`doesn't display Spaces management section`, async () => { diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts index dea45f161e451..62483a10552e3 100644 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts +++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts @@ -60,7 +60,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.eql(['Timelion', 'Management']); + expect(navLinks).to.eql(['Timelion', 'Stack Management']); }); it(`allows a timelion sheet to be created`, async () => { @@ -112,7 +112,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.eql(['Timelion', 'Management']); + expect(navLinks).to.eql(['Timelion', 'Stack Management']); }); it(`does not allow a timelion sheet to be created`, async () => { diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts index fb203a23359bd..7e0fe731301a6 100644 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts +++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_spaces.ts @@ -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', 'timelion', 'security', 'spaceSelector']); + const PageObjects = getPageObjects([ + 'common', + 'timelion', + 'security', + 'spaceSelector', + 'settings', + ]); const appsMenu = getService('appsMenu'); describe('timelion', () => { @@ -38,6 +44,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('Timelion'); }); diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts index a004f8db66823..4ff82484db91c 100644 --- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts +++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_security.ts @@ -64,7 +64,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows uptime navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map(link => link.text)).to.eql(['Uptime', 'Management']); + expect(navLinks.map(link => link.text)).to.eql(['Uptime', 'Stack Management']); }); it('can navigate to Uptime app', async () => { @@ -115,7 +115,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows uptime navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.eql(['Uptime', 'Management']); + expect(navLinks).to.eql(['Uptime', 'Stack Management']); }); it('can navigate to Uptime app', async () => { diff --git a/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts b/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts index 77c5b323340bf..c3dcb1b27771f 100644 --- a/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts +++ b/x-pack/test/functional/apps/uptime/feature_controls/uptime_spaces.ts @@ -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('Uptime'); }); diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index d55076cb0ab43..767dbd7165567 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -75,7 +75,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.eql(['Visualize', 'Management']); + expect(navLinks).to.eql(['Visualize', 'Stack Management']); }); it(`landing page shows "Create new Visualization" button`, async () => { @@ -189,7 +189,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map(link => link.text); - expect(navLinks).to.eql(['Visualize', 'Management']); + expect(navLinks).to.eql(['Visualize', 'Stack Management']); }); it(`landing page shows "Create new Visualization" button`, async () => { diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts index 9193862d2ba9e..066042896c122 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts @@ -10,7 +10,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', 'visualize', 'security', 'spaceSelector']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'security', + 'spaceSelector', + 'settings', + ]); const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); @@ -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('Visualize'); }); diff --git a/x-pack/test/ui_capabilities/common/nav_links_builder.ts b/x-pack/test/ui_capabilities/common/nav_links_builder.ts index 8b7741469362e..aaeb22852bcc0 100644 --- a/x-pack/test/ui_capabilities/common/nav_links_builder.ts +++ b/x-pack/test/ui_capabilities/common/nav_links_builder.ts @@ -13,7 +13,7 @@ export class NavLinksBuilder { ...features, // management isn't a first-class "feature", but it makes our life easier here to pretend like it is management: { - navLinkId: 'kibana:management', + navLinkId: 'kibana:stack_management', }, }; } diff --git a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts index 4af7d81e5a7b4..5c13e6b0eb51e 100644 --- a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts +++ b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts @@ -68,7 +68,7 @@ export class UICapabilitiesService { : {}; const response = await this.axios.post( `${spaceUrlPrefix}/api/core/capabilities`, - { applications: [...applications, 'kibana:management'] }, + { applications: [...applications, 'kibana:stack_management'] }, { headers: requestHeaders, }