From 2e904ffa5e5dd9e7be3aca0613d20875cb2fde3b Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 6 Jun 2023 11:34:12 -0700 Subject: [PATCH 01/16] [Serverless Chrome] Polish of home logo and project switcher --- .../src/chrome_service.tsx | 31 ++-- .../project_navigation_service.ts | 7 + .../core-chrome-browser-internal/src/types.ts | 22 ++- .../src/ui/project/header.tsx | 149 ++++++++++++++---- .../src/chrome_service.mock.ts | 1 + .../src/project_navigation.ts | 4 - .../project_switcher/src/header_button.tsx | 30 ++-- .../serverless/project_switcher/tsconfig.json | 1 + packages/shared-ux/chrome/navigation/index.ts | 4 +- .../chrome/navigation/mocks/src/jest.ts | 2 - .../chrome/navigation/mocks/src/storybook.ts | 4 - .../chrome/navigation/src/services.tsx | 1 - .../src/ui/components/cloud_link.tsx | 64 -------- .../navigation/src/ui/components/index.ts | 1 - .../src/ui/components/navigation.test.tsx | 55 ++----- .../src/ui/components/navigation.tsx | 18 +-- .../src/ui/components/navigation_header.tsx | 61 ------- .../src/ui/components/navigation_ui.tsx | 18 +-- .../src/ui/default_navigation.test.tsx | 51 +----- .../navigation/src/ui/default_navigation.tsx | 18 +-- .../chrome/navigation/src/ui/i18n_strings.ts | 18 --- .../chrome/navigation/src/ui/index.ts | 1 - .../navigation/src/ui/navigation.stories.tsx | 28 +--- .../chrome/navigation/src/ui/types.ts | 24 +-- .../chrome/navigation/types/index.ts | 17 +- x-pack/plugins/serverless/public/mocks.ts | 3 +- x-pack/plugins/serverless/public/plugin.tsx | 15 +- x-pack/plugins/serverless/public/types.ts | 11 +- .../components/side_navigation/index.tsx | 7 +- .../serverless_observability/public/plugin.ts | 1 + .../serverless_search/public/layout/nav.tsx | 7 +- .../serverless_search/public/plugin.ts | 1 + 32 files changed, 232 insertions(+), 443 deletions(-) delete mode 100644 packages/shared-ux/chrome/navigation/src/ui/components/cloud_link.tsx delete mode 100644 packages/shared-ux/chrome/navigation/src/ui/components/navigation_header.tsx diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 22f32f7960a37..efc927bff8c27 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { BehaviorSubject, combineLatest, merge, type Observable, of, ReplaySubject } from 'rxjs'; -import { flatMap, map, takeUntil } from 'rxjs/operators'; +import { mergeMap, map, takeUntil } from 'rxjs/operators'; import { parse } from 'url'; import { EuiLink } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; @@ -89,7 +89,7 @@ export class ChromeService { // in the sense that the chrome UI should not be displayed until a non-chromeless app is mounting or mounted of(true), application.currentAppId$.pipe( - flatMap((appId) => + mergeMap((appId) => application.applications$.pipe( map((applications) => { return !!appId && applications.has(appId) && !!applications.get(appId)!.chromeless; @@ -178,24 +178,22 @@ export class ChromeService { }; const setProjectSideNavComponent = (component: ISideNavComponent | null) => { - const chromeStyle = chromeStyle$.getValue(); - if (chromeStyle !== 'project') { - // Helps ensure callers go through the serverless plugin to get here. - throw new Error( - `Invalid ChromeStyle value of "${chromeStyle}". setProjectSideNavComponent requires ChromeStyle set to "project".` - ); - } + validateChromeStyle(); projectNavigation.setProjectSideNavComponent(component); }; - const setProjectNavigation = (config: ChromeProjectNavigation) => { + const validateChromeStyle = () => { const chromeStyle = chromeStyle$.getValue(); if (chromeStyle !== 'project') { // Helps ensure callers go through the serverless plugin to get here. throw new Error( - `Invalid ChromeStyle value of "${chromeStyle}". setProjectNavigation requires ChromeStyle set to "project".` + `Invalid ChromeStyle value of "${chromeStyle}". This method requires ChromeStyle set to "project".` ); } + }; + + const setProjectNavigation = (config: ChromeProjectNavigation) => { + validateChromeStyle(); projectNavigation.setProjectNavigation(config); }; @@ -206,6 +204,11 @@ export class ChromeService { projectNavigation.setProjectBreadcrumbs(breadcrumbs, params); }; + const setProjectHome = (homeHref: string) => { + validateChromeStyle(); + projectNavigation.setProjectHome(homeHref); + }; + const isIE = () => { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); // IE 10 or older @@ -288,9 +291,14 @@ export class ChromeService { breadcrumbs$={projectBreadcrumbs$.pipe(takeUntil(this.stop$))} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))} + navControlsLeft$={navControls.getLeft$()} + navControlsCenter$={navControls.getCenter$()} navControlsRight$={navControls.getRight$()} + loadingCount$={http.getLoadingCount$()} + homeHref$={projectNavigation.getProjectHome$()} kibanaDocLink={docLinks.links.kibana.guide} kibanaVersion={injectedMetadata.getKibanaVersion()} + prependBasePath={http.basePath.prepend} > {/* TODO: pass down the SideNavCompProps once they are defined */} @@ -405,6 +413,7 @@ export class ChromeService { setChromeStyle, getChromeStyle$: () => chromeStyle$.pipe(takeUntil(this.stop$)), project: { + setHome: setProjectHome, setNavigation: setProjectNavigation, setSideNavComponent: setProjectSideNavComponent, setBreadcrumbs: setProjectBreadcrumbs, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts index 5398d09d132f3..ac6bf79e0ead9 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.ts @@ -26,6 +26,7 @@ export class ProjectNavigationService { private customProjectSideNavComponent$ = new BehaviorSubject<{ current: SideNavComponent | null; }>({ current: null }); + private projectHome$ = new BehaviorSubject(undefined); private projectNavigation$ = new BehaviorSubject(undefined); private projectBreadcrumbs$ = new BehaviorSubject<{ @@ -40,6 +41,12 @@ export class ProjectNavigationService { // 3. keep track of currently active link / path (path will be used to highlight the link in the sidenav and display part of the breadcrumbs) return { + setProjectHome: (homeHref: string) => { + this.projectHome$.next(homeHref); + }, + getProjectHome$: () => { + return this.projectHome$.asObservable(); + }, setProjectNavigation: (projectNavigation: ChromeProjectNavigation) => { this.projectNavigation$.next(projectNavigation); }, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/types.ts b/packages/core/chrome/core-chrome-browser-internal/src/types.ts index 98f5ab672df94..427fb2c90d7e0 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/types.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/types.ts @@ -32,34 +32,42 @@ export interface InternalChromeStart extends ChromeStart { /** * Used only by the serverless plugin to customize project-style chrome. - * Use {@link ServerlessPluginStart.setSideNavComponent} to set serverless navigation. + * @internal */ project: { + /** + * Sets the project home href string. + * @param homeHref + * + * Use {@link ServerlessPluginStart.setProjectHome} to set project home. + */ + setHome(homeHref: string): void; + /** * Sets the project navigation config to be used for rendering project navigation. * It is used for default project sidenav, project breadcrumbs, tracking active deep link. * @param projectNavigation The project navigation config * - * @remarks Has no effect if the chrome style is not `project`. + * Use {@link ServerlessPluginStart.setNavigation} to set project navigation config. */ setNavigation(projectNavigation: ChromeProjectNavigation): void; /** * Set custom project sidenav component to be used instead of the default project sidenav. - * @param getter A function returning a CustomNavigationComponent. - * This component will receive Chrome navigation state as props (not yet implemented) + * @param component A getter function returning a CustomNavigationComponent. + * + * @remarks This component will receive Chrome navigation state as props (not yet implemented) * - * @remarks Has no effect if the chrome style is not `project`. + * Use {@link ServerlessPluginStart.setSideNavComponent} to set custom project navigation. */ setSideNavComponent(component: SideNavComponent | null): void; /** * Set project breadcrumbs - * * @param breadcrumbs * @param params.absolute If true, If true, the breadcrumbs will replace the defaults, otherwise they will be appended to the default ones. false by default. * - * @remarks Has no effect if the chrome style is not `project` or if setNavigation was not called + * Use {@link ServerlessPluginStart.setBreadcrumbs} to set project breadcrumbs. */ setBreadcrumbs( breadcrumbs: ChromeProjectBreadcrumb[] | ChromeProjectBreadcrumb, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 9b7ad144055e7..66a33bfb4fb52 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import React, { createRef, useState } from 'react'; -import { Router } from 'react-router-dom'; import { EuiHeader, EuiHeaderLogo, @@ -15,67 +13,146 @@ import { EuiHeaderSectionItem, EuiHeaderSectionItemButton, EuiIcon, + EuiLoadingSpinner, htmlIdGenerator, } from '@elastic/eui'; +import { css } from '@emotion/react'; +import type { InternalApplicationStart } from '@kbn/core-application-browser-internal'; import { ChromeBreadcrumb, ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, ChromeNavControl, } from '@kbn/core-chrome-browser/src'; -import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { i18n } from '@kbn/i18n'; -import { Observable } from 'rxjs'; +import type { HttpStart } from '@kbn/core-http-browser'; import { MountPoint } from '@kbn/core-mount-utils-browser'; -import { InternalApplicationStart } from '@kbn/core-application-browser-internal'; -import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; +import { i18n } from '@kbn/i18n'; +import React, { createRef, useState } from 'react'; +import { Router } from 'react-router-dom'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import useObservable from 'react-use/lib/useObservable'; +import type { Observable } from 'rxjs'; import { HeaderActionMenu, useHeaderActionMenuMounter } from '../header/header_action_menu'; +import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; import { HeaderHelpMenu } from '../header/header_help_menu'; import { HeaderNavControls } from '../header/header_nav_controls'; import { ProjectNavigation } from './navigation'; +const headerCss = { + logo: { + container: css` + display: inline-block; + min-width: 56px; /* 56 = 40 + 8 + 8 */ + padding: 0 8px; + cursor: pointer; + `, + logo: css` + min-width: 0; /* overrides min-width: 40px */ + padding: 0; + `, + spinner: css` + position: relative; + left: 4px; + top: 2px; + `, + }, + nav: { + toggleNavButton: css` + border-right: 1px solid #d3dae6; + margin-left: -1px; + `, + }, +}; + +const headerStrings = { + logo: { + ariaLabel: i18n.translate('core.ui.primaryNav.goToHome.ariaLabel', { + defaultMessage: 'Go to home page', + }), + }, + nav: { + closeNavAriaLabel: i18n.translate('core.ui.primaryNav.toggleNavAriaLabel', { + defaultMessage: 'Toggle primary navigation', + }), + }, +}; + interface Props { breadcrumbs$: Observable; actionMenu$: Observable; kibanaDocLink: string; + children: React.ReactNode; globalHelpExtensionMenuLinks$: Observable; helpExtension$: Observable; helpSupportUrl$: Observable; + homeHref$: Observable; kibanaVersion: string; application: InternalApplicationStart; + loadingCount$: ReturnType; + navControlsLeft$: Observable; + navControlsCenter$: Observable; navControlsRight$: Observable; - children: React.ReactNode; + prependBasePath: (url: string) => string; } const LOCAL_STORAGE_IS_OPEN_KEY = 'PROJECT_NAVIGATION_OPEN' as const; +const Logo = ( + props: Pick +) => { + const loadingCount = useObservable(props.loadingCount$, 0); + const homeHref = useObservable(props.homeHref$, '/app/home'); + const { logo } = headerCss; + + const navigateHome = (event: React.MouseEvent) => { + if (homeHref) { + props.application.navigateToUrl(props.prependBasePath(homeHref)); + } + event.preventDefault(); + }; + + return ( + + {loadingCount === 0 ? ( + + ) : ( + + + + )} + + ); +}; + export const ProjectHeader = ({ application, kibanaDocLink, kibanaVersion, children, + prependBasePath, ...observables }: Props) => { const [navId] = useState(htmlIdGenerator()()); const [isOpen, setIsOpen] = useLocalStorage(LOCAL_STORAGE_IS_OPEN_KEY, true); const toggleCollapsibleNavRef = createRef void }>(); - - const renderLogo = () => ( - e.preventDefault()} - aria-label="Go to home page" - /> - ); - const headerActionMenuMounter = useHeaderActionMenuMounter(observables.actionMenu$); return ( <> - + setIsOpen(!isOpen)} aria-expanded={isOpen!} aria-pressed={isOpen!} @@ -105,12 +180,31 @@ export const ProjectHeader = ({ - {renderLogo()} + + + + + + + + + + + + + + + - - - - + {headerActionMenuMounter.mount && ( - {/* TODO: This puts a group of nav menu items on the right edge of the screen, - but it should be possible for apps customize the layout in a grid and use spacers between items. - https://github.com/elastic/kibana/issues/158034 */} - diff --git a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts index 4c2461510c669..68554c646cd99 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts +++ b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts @@ -64,6 +64,7 @@ const createStartContractMock = () => { getChromeStyle$: jest.fn(), setChromeStyle: jest.fn(), project: { + setHome: jest.fn(), setNavigation: jest.fn(), setSideNavComponent: jest.fn(), setBreadcrumbs: jest.fn(), diff --git a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts index 7ef86b1e34a98..b17cdf558b10b 100644 --- a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts +++ b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts @@ -50,10 +50,6 @@ export interface ChromeProjectNavigationNode { /** @public */ export interface ChromeProjectNavigation { - /** - * The URL href for the home link - */ - homeRef: string; /** * The navigation tree representation of the side bar navigation. */ diff --git a/packages/serverless/project_switcher/src/header_button.tsx b/packages/serverless/project_switcher/src/header_button.tsx index ee1bd0acc5888..f90daacc05173 100644 --- a/packages/serverless/project_switcher/src/header_button.tsx +++ b/packages/serverless/project_switcher/src/header_button.tsx @@ -6,26 +6,28 @@ * Side Public License, v 1. */ +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiHeaderLink, EuiHeaderSectionItem } from '@elastic/eui'; import React, { MouseEventHandler } from 'react'; -import { EuiHeaderSectionItemButton, EuiIcon } from '@elastic/eui'; - -import { ProjectType } from '@kbn/serverless-types'; - -import { icons } from './constants'; export const TEST_ID = 'projectSwitcherButton'; export interface Props { onClick: MouseEventHandler; - currentProjectType: ProjectType; } -export const HeaderButton = ({ onClick, currentProjectType }: Props) => ( - - - +export const HeaderButton = (props: Props) => ( + + + + + ); diff --git a/packages/serverless/project_switcher/tsconfig.json b/packages/serverless/project_switcher/tsconfig.json index 8fd6b54236754..a6bc3fb6473c1 100644 --- a/packages/serverless/project_switcher/tsconfig.json +++ b/packages/serverless/project_switcher/tsconfig.json @@ -19,5 +19,6 @@ "kbn_references": [ "@kbn/shared-ux-storybook-mock", "@kbn/serverless-types", + "@kbn/i18n-react", ] } diff --git a/packages/shared-ux/chrome/navigation/index.ts b/packages/shared-ux/chrome/navigation/index.ts index 13034398065a5..e38f23b2db8f5 100644 --- a/packages/shared-ux/chrome/navigation/index.ts +++ b/packages/shared-ux/chrome/navigation/index.ts @@ -23,8 +23,8 @@ export type { export type { ChromeNavigation, - ChromeNavigationViewModel, - NavigationServices, ChromeNavigationNode, ChromeNavigationNodeViewModel, + ChromeNavigationViewModel, + NavigationServices, } from './types'; diff --git a/packages/shared-ux/chrome/navigation/mocks/src/jest.ts b/packages/shared-ux/chrome/navigation/mocks/src/jest.ts index be3e72d0c8b39..684ef0e3d72af 100644 --- a/packages/shared-ux/chrome/navigation/mocks/src/jest.ts +++ b/packages/shared-ux/chrome/navigation/mocks/src/jest.ts @@ -12,13 +12,11 @@ import { NavigationServices, ChromeNavigationNodeViewModel } from '../../types'; export const getServicesMock = (): NavigationServices => { const navigateToUrl = jest.fn().mockResolvedValue(undefined); const basePath = { prepend: jest.fn((path: string) => `/base${path}`) }; - const loadingCount$ = new BehaviorSubject(0); const recentlyAccessed$ = new BehaviorSubject([]); const navLinks$ = new BehaviorSubject([]); return { basePath, - loadingCount$, recentlyAccessed$, navLinks$, navIsOpen: true, diff --git a/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts b/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts index 9d660d923033b..28be60ca54538 100644 --- a/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts +++ b/packages/shared-ux/chrome/navigation/mocks/src/storybook.ts @@ -15,7 +15,6 @@ type Arguments = ChromeNavigationViewModel & NavigationServices; export type Params = Pick< Arguments, | 'activeNavItemId' - | 'loadingCount$' | 'navIsOpen' | 'navigationTree' | 'platformConfig' @@ -51,7 +50,6 @@ export class StorybookMock extends AbstractStorybookMock< ...params, basePath: { prepend: (suffix: string) => `/basepath${suffix}` }, navigateToUrl, - loadingCount$: params.loadingCount$ ?? new BehaviorSubject(0), recentlyAccessed$: params.recentlyAccessed$ ?? new BehaviorSubject([]), navLinks$: params.navLinks$ ?? new BehaviorSubject([]), onProjectNavigationChange: params.onProjectNavigationChange ?? (() => undefined), @@ -61,8 +59,6 @@ export class StorybookMock extends AbstractStorybookMock< getProps(params: Params): ChromeNavigationViewModel { return { ...params, - homeHref: '#', - linkToCloud: 'projects', recentlyAccessedFilter: params.recentlyAccessedFilter, }; } diff --git a/packages/shared-ux/chrome/navigation/src/services.tsx b/packages/shared-ux/chrome/navigation/src/services.tsx index cd20965f6e3c8..655d37fc63c2c 100644 --- a/packages/shared-ux/chrome/navigation/src/services.tsx +++ b/packages/shared-ux/chrome/navigation/src/services.tsx @@ -32,7 +32,6 @@ export const NavigationKibanaProvider: FC = ({ const value: NavigationServices = { basePath, - loadingCount$: http.getLoadingCount$(), recentlyAccessed$: chrome.recentlyAccessed.get$(), navLinks$: chrome.navLinks.getNavLinks$(), navigateToUrl, diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/cloud_link.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/cloud_link.tsx deleted file mode 100644 index b71b949e47603..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/components/cloud_link.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -import { EuiCollapsibleNavGroup, EuiLink } from '@elastic/eui'; -import React, { FC } from 'react'; -import { getI18nStrings } from '../i18n_strings'; - -const i18nTexts = getI18nStrings(); - -const presets = { - projects: { - href: 'https://cloud.elastic.co/projects', - icon: 'spaces', - title: i18nTexts.linkToCloudProjects, - dataTestSubj: 'nav-header-link-to-projects', - }, - deployments: { - href: 'https://cloud.elastic.co/deployments', - icon: 'spaces', - title: i18nTexts.linkToCloudDeployments, - dataTestSubj: 'nav-header-link-to-deployments', - }, -}; - -export interface Props { - /** Use one of the cloud link presets */ - preset?: 'projects' | 'deployments' | null; - /** Optional. If "preset" is not provided it is required */ - href?: string; - /** Optional. If "preset" is not provided it is required */ - icon?: string; - /** Optional. If "preset" is not provided it is required */ - title?: string; -} - -export const CloudLink: FC = ({ preset, href: _href, icon: _icon, title: _title }) => { - if (preset === null) { - return null; - } - - if (!preset && (!_href || !_icon || !_title)) { - throw new Error(`Navigation.CloudLink requires href, icon, and title`); - } - - const { href, icon, title, dataTestSubj } = - preset && presets[preset] - ? presets[preset]! - : { - href: _href, - icon: _icon, - title: _title, - dataTestSubj: 'nav-header-link-to-cloud', - }; - - return ( - - - - ); -}; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/index.ts b/packages/shared-ux/chrome/navigation/src/ui/components/index.ts index bb7aeacdfabb7..2174b35603525 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/index.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/components/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export type { Props as CloudLinkProps } from './cloud_link'; export { Navigation } from './navigation'; export type { Props as RecentlyAccessedProps } from './recently_accessed'; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx index e5bf85341345e..32a9736e30b66 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx @@ -6,11 +6,10 @@ * Side Public License, v 1. */ -import React from 'react'; -import { render } from '@testing-library/react'; -import { type Observable, of } from 'rxjs'; import type { ChromeNavLink } from '@kbn/core-chrome-browser'; - +import { render } from '@testing-library/react'; +import React from 'react'; +import { of, type Observable } from 'rxjs'; import { defaultAnalyticsNavGroup, defaultDevtoolsNavGroup, @@ -30,7 +29,7 @@ describe('', () => { const { findByTestId } = render( - + @@ -62,7 +61,6 @@ describe('', () => { const [navTree] = lastCall; expect(navTree).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'group1', @@ -128,7 +126,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + {/* Title from deeplink */} @@ -148,7 +146,6 @@ describe('', () => { const [navTree] = lastCall; expect(navTree).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -221,7 +218,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + {/* Title from deeplink */} @@ -243,7 +240,6 @@ describe('', () => { const [navTree] = lastCall; expect(navTree).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -294,7 +290,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + @@ -311,9 +307,7 @@ describe('', () => { expect(await findByTestId('my-custom-element')).toBeVisible(); expect(await findByTestId('my-other-custom-element')).toBeVisible(); - expect(await (await findByTestId('my-other-custom-element')).textContent).toBe( - 'Children prop' - ); + expect((await findByTestId('my-other-custom-element')).textContent).toBe('Children prop'); expect(onProjectNavigationChange).toHaveBeenCalled(); const lastCall = @@ -321,7 +315,6 @@ describe('', () => { const [navTree] = lastCall; expect(navTree).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -365,7 +358,7 @@ describe('', () => { render( - + @@ -380,7 +373,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: expect.any(Array), }); @@ -397,31 +389,6 @@ describe('', () => { expect(navTreeGenerated.navigationTree[3]).toEqual(defaultManagementNavGroup); }); - test('should render cloud link', async () => { - const onProjectNavigationChange = jest.fn(); - - const { findByTestId } = render( - - - - - - - - - - - - ); - - expect(await findByTestId('nav-header-link-to-projects')).toBeVisible(); - expect(await findByTestId('nav-header-link-to-deployments')).toBeVisible(); - expect(await findByTestId('nav-header-link-to-cloud')).toBeVisible(); - expect(await (await findByTestId('nav-header-link-to-cloud')).textContent).toBe( - 'Custom link' - ); - }); - test('should render recently accessed items', async () => { const recentlyAccessed$ = of([ { label: 'This is an example', link: '/app/example/39859', id: '39850' }, @@ -430,7 +397,7 @@ describe('', () => { const { findByTestId } = render( - + @@ -441,7 +408,7 @@ describe('', () => { ); expect(await findByTestId('nav-bucket-recentlyAccessed')).toBeVisible(); - expect(await (await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( + expect((await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( 'RecentThis is an exampleAnother example' ); }); diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx index 6825c8a8084bf..073318df6f838 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx @@ -20,7 +20,6 @@ import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; import { useNavigation as useNavigationServices } from '../../services'; import { RegisterFunction, UnRegisterFunction } from '../types'; -import { CloudLink } from './cloud_link'; import { NavigationFooter } from './navigation_footer'; import { NavigationGroup } from './navigation_group'; import { NavigationItem } from './navigation_item'; @@ -44,10 +43,6 @@ const NavigationContext = createContext({ interface Props { children: ReactNode; - /** - * Href to the home page - */ - homeRef: string; /** * Flag to indicate if the Navigation should not be styled with EUI components. * If set to true, the children will be rendered as is. @@ -56,7 +51,7 @@ interface Props { dataTestSubj?: string; } -export function Navigation({ children, homeRef, unstyled = false, dataTestSubj }: Props) { +export function Navigation({ children, unstyled = false, dataTestSubj }: Props) { const { onProjectNavigationChange } = useNavigationServices(); // We keep a reference of the order of the children that register themselves when mounting. @@ -109,23 +104,17 @@ export function Navigation({ children, homeRef, unstyled = false, dataTestSubj } useEffect(() => { // This will update the navigation tree in the Chrome service (calling the serverless.setNavigation()) onProjectNavigationChange({ - homeRef, navigationTree: Object.values(navigationItems).sort((a, b) => { const aOrder = orderChildrenRef.current[a.id]; const bOrder = orderChildrenRef.current[b.id]; return aOrder - bOrder; }), }); - }, [navigationItems, onProjectNavigationChange, homeRef]); + }, [navigationItems, onProjectNavigationChange]); return ( - + {children} @@ -143,5 +132,4 @@ export function useNavigation() { Navigation.Group = NavigationGroup; Navigation.Item = NavigationItem; Navigation.Footer = NavigationFooter; -Navigation.CloudLink = CloudLink; Navigation.RecentlyAccessed = RecentlyAccessed; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_header.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_header.tsx deleted file mode 100644 index 7895ca62e7a4b..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_header.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { FC } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiHeaderLogo, EuiLoadingSpinner } from '@elastic/eui'; -import useObservable from 'react-use/lib/useObservable'; -import { useNavigation as useServices } from '../../services'; -import { ElasticMark } from '../elastic_mark'; -import { getI18nStrings } from '../i18n_strings'; - -import '../header_logo.scss'; - -interface Props { - homeHref: string; -} - -export const NavHeader: FC = ({ homeHref }) => { - const strings = getI18nStrings(); - const { basePath, navigateToUrl, loadingCount$ } = useServices(); - const loadingCount = useObservable(loadingCount$, 0); - const homeUrl = basePath.prepend(homeHref); - - const navigateHome = (event: React.MouseEvent) => { - event.preventDefault(); - navigateToUrl(homeUrl); - }; - - const logo = - loadingCount === 0 ? ( - - ) : ( - - - - ); - - return ( - - {logo} - - - - - ); -}; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_ui.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_ui.tsx index a0f48aef8a112..898a3b6829821 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_ui.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_ui.tsx @@ -6,32 +6,18 @@ * Side Public License, v 1. */ -import { EuiCollapsibleNavGroup, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { FC } from 'react'; -import { NavHeader } from './navigation_header'; interface Props { - homeRef: string; unstyled?: boolean; footerChildren?: React.ReactNode; dataTestSubj?: string; } -export const NavigationUI: FC = ({ - children, - unstyled, - footerChildren, - homeRef, - dataTestSubj, -}) => { - const { euiTheme } = useEuiTheme(); - +export const NavigationUI: FC = ({ children, unstyled, footerChildren, dataTestSubj }) => { return ( <> - - - - {unstyled ? ( <>{children} ) : ( diff --git a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx index 323d6056a73c1..547cb96ba710c 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx @@ -22,10 +22,6 @@ import { defaultMlNavGroup, } from '../../mocks/src/default_navigation.test.helpers'; -const defaultProps = { - homeRef: 'https://elastic.co', -}; - describe('', () => { const services = getServicesMock(); @@ -73,7 +69,6 @@ describe('', () => { const { findByTestId } = render( ', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'group1', @@ -193,7 +187,6 @@ describe('', () => { onProjectNavigationChange={onProjectNavigationChange} > ', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -251,43 +243,6 @@ describe('', () => { }); }); - test('should render cloud link', async () => { - const navigationBody: RootNavigationItemDefinition[] = [ - { - type: 'cloudLink', - preset: 'deployments', - }, - { - type: 'cloudLink', - preset: 'projects', - }, - { - type: 'cloudLink', - href: 'https://foo.com', - icon: 'myIcon', - title: 'Custom link', - }, - ]; - - const { findByTestId } = render( - - - - ); - - expect(await findByTestId('nav-header-link-to-projects')).toBeVisible(); - expect(await findByTestId('nav-header-link-to-deployments')).toBeVisible(); - expect(await findByTestId('nav-header-link-to-cloud')).toBeVisible(); - expect(await (await findByTestId('nav-header-link-to-cloud')).textContent).toBe( - 'Custom link' - ); - }); - test('should render recently accessed items', async () => { const recentlyAccessed$ = of([ { label: 'This is an example', link: '/app/example/39859', id: '39850' }, @@ -303,7 +258,6 @@ describe('', () => { const { findByTestId } = render( ', () => { ); expect(await findByTestId('nav-bucket-recentlyAccessed')).toBeVisible(); - expect(await (await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( + expect((await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( 'RecentThis is an exampleAnother example' ); }); @@ -365,7 +319,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + ); @@ -375,7 +329,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: expect.any(Array), }); diff --git a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx index ec2b921e01e82..508f5624ccf26 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.tsx @@ -18,7 +18,6 @@ import type { ProjectNavigationTreeDefinition, RootNavigationItemDefinition, } from './types'; -import { CloudLink } from './components/cloud_link'; import { RecentlyAccessed } from './components/recently_accessed'; import { NavigationFooter } from './components/navigation_footer'; import { getPresets } from './nav_tree_presets'; @@ -37,10 +36,6 @@ const getDefaultNavigationTree = ( ): NavigationTreeDefinition => { return { body: [ - { - type: 'cloudLink', - preset: 'deployments', - }, { type: 'recentlyAccessed', }, @@ -70,7 +65,6 @@ const getDefaultNavigationTree = ( let idCounter = 0; export const DefaultNavigation: FC = ({ - homeRef, projectNavigationTree, navigationTree, dataTestSubj, @@ -90,14 +84,8 @@ export const DefaultNavigation: FC { return items.map((item) => { const isRootNavigationItem = isRootNavigationItemDefinition(item); - if (isRootNavigationItem) { - if (item.type === 'cloudLink') { - return ; - } - - if (item.type === 'recentlyAccessed') { - return ; - } + if (isRootNavigationItem && item.type === 'recentlyAccessed') { + return ; } if (item.preset) { @@ -131,7 +119,7 @@ export const DefaultNavigation: FC + <> {renderItems(navigationDefinition.body)} {navigationDefinition.footer && ( diff --git a/packages/shared-ux/chrome/navigation/src/ui/i18n_strings.ts b/packages/shared-ux/chrome/navigation/src/ui/i18n_strings.ts index c268e7a42de10..0167ffd0ea7ee 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/i18n_strings.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/i18n_strings.ts @@ -9,24 +9,6 @@ import { i18n } from '@kbn/i18n'; export const getI18nStrings = () => ({ - headerLogoAriaLabel: i18n.translate( - 'sharedUXPackages.chrome.sideNavigation.headerLogo.ariaLabel', - { - defaultMessage: 'Go to home page', - } - ), - linkToCloudProjects: i18n.translate( - 'sharedUXPackages.chrome.sideNavigation.linkToCloud.projects', - { - defaultMessage: 'My projects', - } - ), - linkToCloudDeployments: i18n.translate( - 'sharedUXPackages.chrome.sideNavigation.linkToCloud.deployments', - { - defaultMessage: 'My deployments', - } - ), recentlyAccessed: i18n.translate( 'sharedUXPackages.chrome.sideNavigation.recentlyAccessed.title', { diff --git a/packages/shared-ux/chrome/navigation/src/ui/index.ts b/packages/shared-ux/chrome/navigation/src/ui/index.ts index f0c63fda8e27f..4f039cf49cd54 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/index.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/index.ts @@ -13,7 +13,6 @@ export { DefaultNavigation } from './default_navigation'; export { getPresets } from './nav_tree_presets'; export type { - CloudLinkDefinition, GroupDefinition, NavigationGroupPreset, NavigationTreeDefinition, diff --git a/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx b/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx index cc6bd2978a9c5..6e696677f46b4 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/navigation.stories.tsx @@ -115,7 +115,6 @@ const deepLinks: ChromeNavLink[] = [ ]; const simpleNavigationDefinition: ProjectNavigationDefinition = { - homeRef: 'https://elastic.co', projectNavigationTree: [ { id: 'example_projet', @@ -186,13 +185,8 @@ export const SimpleObjectDefinition = (args: ChromeNavigationViewModel & Navigat }; const navigationDefinition: ProjectNavigationDefinition = { - homeRef: 'https://elastic.co', navigationTree: { body: [ - { - type: 'cloudLink', - preset: 'deployments', - }, // My custom project { type: 'navGroup', @@ -321,9 +315,7 @@ export const WithUIComponents = (args: ChromeNavigationViewModel & NavigationSer return ( - - - + { +export const MinimalUI = (args: ChromeNavigationViewModel & NavigationServices) => { const services = storybookMock.getServices({ ...args, navLinks$: of(deepLinks), @@ -386,13 +376,7 @@ export const MinimalUIAndCustomCloudLink = ( return ( - - - + - + - - diff --git a/packages/shared-ux/chrome/navigation/src/ui/types.ts b/packages/shared-ux/chrome/navigation/src/ui/types.ts index 96c283d508f87..e23891d8ca763 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/types.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/types.ts @@ -12,7 +12,7 @@ import type { ChromeProjectNavigationNode, } from '@kbn/core-chrome-browser'; -import type { CloudLinkProps, RecentlyAccessedProps } from './components'; +import type { RecentlyAccessedProps } from './components'; /** * @public @@ -89,15 +89,6 @@ export interface RecentlyAccessedDefinition extends RecentlyAccessedProps { type: 'recentlyAccessed'; } -/** - * @public - * - * A cloud link root item definition. Use it to add one or more links to the Cloud console - */ -export interface CloudLinkDefinition extends CloudLinkProps { - type: 'cloudLink'; -} - /** * @public * @@ -116,10 +107,7 @@ export interface GroupDefinition extends NodeDefinition { * * The navigation definition for a root item in the side navigation. */ -export type RootNavigationItemDefinition = - | RecentlyAccessedDefinition - | CloudLinkDefinition - | GroupDefinition; +export type RootNavigationItemDefinition = RecentlyAccessedDefinition | GroupDefinition; export type ProjectNavigationTreeDefinition = Array>; @@ -130,12 +118,12 @@ export type ProjectNavigationTreeDefinition = Array; recentlyAccessed$: Observable; navLinks$: Observable>; navIsOpen: boolean; @@ -55,7 +54,7 @@ export interface NavigationKibanaDependencies { export type ChromeNavigationLink = string; /** - * Chrome navigatioin node definition. + * Chrome navigation node definition. * * @public */ @@ -97,14 +96,6 @@ export interface ChromeNavigationNodeViewModel extends Omit { - /** - * Target for the logo icon - */ - homeHref: string; + extends Pick { /** * The navigation tree definition */ diff --git a/x-pack/plugins/serverless/public/mocks.ts b/x-pack/plugins/serverless/public/mocks.ts index d23bc823c0182..ffe61c833e6dc 100644 --- a/x-pack/plugins/serverless/public/mocks.ts +++ b/x-pack/plugins/serverless/public/mocks.ts @@ -8,9 +8,10 @@ import { ServerlessPluginStart } from './types'; const startMock = (): ServerlessPluginStart => ({ - setSideNavComponent: jest.fn(), setNavigation: jest.fn(), setBreadcrumbs: jest.fn(), + setProjectHome: jest.fn(), + setSideNavComponent: jest.fn(), }); export const serverlessMock = { diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index e015633a2f99e..c4fd761c6543c 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -53,7 +53,7 @@ export class ServerlessPlugin if (developer && developer.projectSwitcher && developer.projectSwitcher.enabled) { const { currentType } = developer.projectSwitcher; - core.chrome.navControls.registerRight({ + core.chrome.navControls.registerLeft({ mount: (target) => this.mountProjectSwitcher(target, core, currentType), }); } @@ -61,14 +61,15 @@ export class ServerlessPlugin core.chrome.setChromeStyle('project'); management.setIsSidebarEnabled(false); + // Casting the "chrome.projects" service to an "internal" type: this is intentional to obscure the property from Typescript. + const { project } = core.chrome as InternalChromeStart; + return { - // Casting the "chrome.projects" service to an "internal" type: this is intentional to obscure the property from Typescript. setSideNavComponent: (sideNavigationComponent) => - (core.chrome as InternalChromeStart).project.setSideNavComponent(sideNavigationComponent), - setNavigation: (projectNavigation) => - (core.chrome as InternalChromeStart).project.setNavigation(projectNavigation), - setBreadcrumbs: (breadcrumbs, params) => - (core.chrome as InternalChromeStart).project.setBreadcrumbs(breadcrumbs, params), + project.setSideNavComponent(sideNavigationComponent), + setNavigation: (projectNavigation) => project.setNavigation(projectNavigation), + setBreadcrumbs: (breadcrumbs, params) => project.setBreadcrumbs(breadcrumbs, params), + setProjectHome: (homeHref: string) => project.setHome(homeHref), }; } diff --git a/x-pack/plugins/serverless/public/types.ts b/x-pack/plugins/serverless/public/types.ts index 8efad28215d49..8e9e8672f7e69 100644 --- a/x-pack/plugins/serverless/public/types.ts +++ b/x-pack/plugins/serverless/public/types.ts @@ -5,24 +5,25 @@ * 2.0. */ -import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; import type { - SideNavComponent, - ChromeProjectNavigation, ChromeProjectBreadcrumb, + ChromeProjectNavigation, ChromeSetProjectBreadcrumbsParams, + SideNavComponent, } from '@kbn/core-chrome-browser'; +import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessPluginSetup {} export interface ServerlessPluginStart { - setSideNavComponent: (navigation: SideNavComponent) => void; - setNavigation(projectNavigation: ChromeProjectNavigation): void; setBreadcrumbs: ( breadcrumbs: ChromeProjectBreadcrumb | ChromeProjectBreadcrumb[], params?: Partial ) => void; + setNavigation(projectNavigation: ChromeProjectNavigation): void; + setProjectHome(homeHref: string): void; + setSideNavComponent: (navigation: SideNavComponent) => void; } export interface ServerlessPluginSetupDependencies { diff --git a/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx b/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx index 7dadce3cdf1d4..3e8435ad716b5 100644 --- a/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx +++ b/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx @@ -17,7 +17,6 @@ import React from 'react'; const navigationTree: NavigationTreeDefinition = { body: [ - { type: 'cloudLink', preset: 'projects' }, { type: 'recentlyAccessed' }, { type: 'navGroup', @@ -124,11 +123,7 @@ export const getObservabilitySideNavComponent = () => { return ( - + ); }; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts index 56304cfdb623d..d07b936bdbdde 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -30,6 +30,7 @@ export class ServerlessObservabilityPlugin ): ServerlessObservabilityPluginStart { const { observabilityShared, serverless } = setupDeps; observabilityShared.setIsSidebarEnabled(false); + serverless.setProjectHome('/app/enterprise_search/content/setup_guide'); serverless.setSideNavComponent(getObservabilitySideNavComponent(core, { serverless })); return {}; } diff --git a/x-pack/plugins/serverless_search/public/layout/nav.tsx b/x-pack/plugins/serverless_search/public/layout/nav.tsx index 409c46ba68490..77dec3a5dad8a 100644 --- a/x-pack/plugins/serverless_search/public/layout/nav.tsx +++ b/x-pack/plugins/serverless_search/public/layout/nav.tsx @@ -20,7 +20,6 @@ const devTools = getPresets('devtools'); const navigationTree: NavigationTreeDefinition = { body: [ - { type: 'cloudLink', preset: 'projects' }, { type: 'recentlyAccessed' }, { type: 'navGroup', @@ -129,11 +128,7 @@ export const createServerlessSearchSideNavComponent = () => { return ( - + ); }; diff --git a/x-pack/plugins/serverless_search/public/plugin.ts b/x-pack/plugins/serverless_search/public/plugin.ts index 18644b72f426c..69de4a01ecbdd 100644 --- a/x-pack/plugins/serverless_search/public/plugin.ts +++ b/x-pack/plugins/serverless_search/public/plugin.ts @@ -50,6 +50,7 @@ export class ServerlessSearchPlugin core: CoreStart, { serverless }: ServerlessSearchPluginStartDependencies ): ServerlessSearchPluginStart { + serverless.setProjectHome('/app/elasticsearch'); serverless.setSideNavComponent(createComponent(core, { serverless })); return {}; } From 8f785d1a45df773849d7cf1c12e0defe65ace2a8 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 6 Jun 2023 12:14:33 -0700 Subject: [PATCH 02/16] declare validateChromeStyle before it is used --- .../src/chrome_service.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index efc927bff8c27..ede90c7098b5f 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -177,11 +177,6 @@ export class ChromeService { chromeStyle$.next(style); }; - const setProjectSideNavComponent = (component: ISideNavComponent | null) => { - validateChromeStyle(); - projectNavigation.setProjectSideNavComponent(component); - }; - const validateChromeStyle = () => { const chromeStyle = chromeStyle$.getValue(); if (chromeStyle !== 'project') { @@ -192,6 +187,11 @@ export class ChromeService { } }; + const setProjectSideNavComponent = (component: ISideNavComponent | null) => { + validateChromeStyle(); + projectNavigation.setProjectSideNavComponent(component); + }; + const setProjectNavigation = (config: ChromeProjectNavigation) => { validateChromeStyle(); projectNavigation.setProjectNavigation(config); From f7f64f276299a30ea659c2c034dc898d60b0fa48 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 6 Jun 2023 12:26:17 -0700 Subject: [PATCH 03/16] fix i18n in project switcher --- x-pack/plugins/serverless/public/plugin.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index c4fd761c6543c..3d4f06aa6a7c0 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -7,6 +7,7 @@ import { InternalChromeStart } from '@kbn/core-chrome-browser-internal'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import { I18nProvider } from '@kbn/i18n-react'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { ProjectSwitcher, ProjectSwitcherKibanaProvider } from '@kbn/serverless-project-switcher'; import { ProjectType } from '@kbn/serverless-types'; @@ -81,11 +82,13 @@ export class ServerlessPlugin currentProjectType: ProjectType ) { ReactDOM.render( - - - - - , + + + + + + + , targetDomElement ); From d3106b254c5744e1d75839dd99fdacdc1262a952 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:31:58 +0000 Subject: [PATCH 04/16] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/serverless/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/serverless/tsconfig.json b/x-pack/plugins/serverless/tsconfig.json index 52919a26a652f..c4cf602f928c7 100644 --- a/x-pack/plugins/serverless/tsconfig.json +++ b/x-pack/plugins/serverless/tsconfig.json @@ -23,5 +23,6 @@ "@kbn/utils", "@kbn/core-chrome-browser", "@kbn/core-chrome-browser-internal", + "@kbn/i18n-react", ] } From e320e0577a6449499dee6b73e3fe534d47e4acf5 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 6 Jun 2023 15:47:40 -0700 Subject: [PATCH 05/16] fix ts --- packages/shared-ux/chrome/navigation/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared-ux/chrome/navigation/index.ts b/packages/shared-ux/chrome/navigation/index.ts index e38f23b2db8f5..9a145c7d6af78 100644 --- a/packages/shared-ux/chrome/navigation/index.ts +++ b/packages/shared-ux/chrome/navigation/index.ts @@ -11,7 +11,6 @@ export { NavigationKibanaProvider, NavigationProvider } from './src/services'; export { DefaultNavigation, getPresets, Navigation } from './src/ui'; export type { - CloudLinkDefinition, GroupDefinition, NavigationGroupPreset, NavigationTreeDefinition, From 33282558e92dfc2c0655d19ee0eadfe42fc8a618 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 9 Jun 2023 15:06:34 -0700 Subject: [PATCH 06/16] set security project home --- x-pack/plugins/serverless_security/public/plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.ts index 9b22f39833201..9e77dd6883b41 100644 --- a/x-pack/plugins/serverless_security/public/plugin.ts +++ b/x-pack/plugins/serverless_security/public/plugin.ts @@ -48,6 +48,7 @@ export class ServerlessSecurityPlugin securitySolution.setIsSidebarEnabled(false); securitySolution.setGetStartedPage(getSecurityGetStartedComponent(core, startDeps)); + serverless.setProjectHome('/app/security'); serverless.setSideNavComponent(getSecuritySideNavComponent(core, startDeps)); return {}; From 61834f6c057ca7780bd1abfeaefaa7f971a91d25 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 9 Jun 2023 15:10:09 -0700 Subject: [PATCH 07/16] ensure logo links have basepath and can be opened in new tab --- .../src/ui/project/header.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 66a33bfb4fb52..2d6eaafb55efb 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -104,9 +104,14 @@ const Logo = ( const homeHref = useObservable(props.homeHref$, '/app/home'); const { logo } = headerCss; + let fullHref: string | undefined; + if (homeHref) { + fullHref = props.prependBasePath(homeHref); + } + const navigateHome = (event: React.MouseEvent) => { - if (homeHref) { - props.application.navigateToUrl(props.prependBasePath(homeHref)); + if (fullHref) { + props.application.navigateToUrl(fullHref); } event.preventDefault(); }; @@ -117,12 +122,13 @@ const Logo = ( ) : ( - + Date: Fri, 9 Jun 2023 15:38:24 -0700 Subject: [PATCH 08/16] debounce updates to the home icon/loading spinner --- .../core-chrome-browser-internal/src/ui/project/header.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 2d6eaafb55efb..d4a9a0c5733a4 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -31,7 +31,7 @@ import React, { createRef, useState } from 'react'; import { Router } from 'react-router-dom'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import useObservable from 'react-use/lib/useObservable'; -import type { Observable } from 'rxjs'; +import { Observable, debounceTime } from 'rxjs'; import { HeaderActionMenu, useHeaderActionMenuMounter } from '../header/header_action_menu'; import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; import { HeaderHelpMenu } from '../header/header_help_menu'; @@ -100,7 +100,8 @@ const LOCAL_STORAGE_IS_OPEN_KEY = 'PROJECT_NAVIGATION_OPEN' as const; const Logo = ( props: Pick ) => { - const loadingCount = useObservable(props.loadingCount$, 0); + const loadingCount = useObservable(props.loadingCount$.pipe(debounceTime(50)), 0); + const homeHref = useObservable(props.homeHref$, '/app/home'); const { logo } = headerCss; From 20263e7ea5f2012911a26760a999d8e9734a9c35 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 12 Jun 2023 13:06:49 -0700 Subject: [PATCH 09/16] update loading spinner debounce time --- .../core-chrome-browser-internal/src/ui/project/header.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index d4a9a0c5733a4..bb2516f8f4aa1 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -96,11 +96,15 @@ interface Props { } const LOCAL_STORAGE_IS_OPEN_KEY = 'PROJECT_NAVIGATION_OPEN' as const; +const LOADING_DEBOUNCE_TIME = 80; const Logo = ( props: Pick ) => { - const loadingCount = useObservable(props.loadingCount$.pipe(debounceTime(50)), 0); + const loadingCount = useObservable( + props.loadingCount$.pipe(debounceTime(LOADING_DEBOUNCE_TIME)), + 0 + ); const homeHref = useObservable(props.homeHref$, '/app/home'); const { logo } = headerCss; From 2fac0aad45e70fd4eaa6d027d0f01876eb02587e Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 12 Jun 2023 18:58:44 -0700 Subject: [PATCH 10/16] fix misalignment of action menu --- .../core-chrome-browser-internal/src/ui/project/header.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index bb2516f8f4aa1..39ee90d15aafc 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -231,6 +231,7 @@ export const ProjectHeader = ({ {headerActionMenuMounter.mount && ( + From 2fba2ac2a756aa58439218f52c903f07c7eb5bd8 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 12 Jun 2023 21:24:50 -0700 Subject: [PATCH 11/16] correction to observability landing page --- x-pack/plugins/serverless_observability/public/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts index d07b936bdbdde..ee774980f7c29 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -30,7 +30,7 @@ export class ServerlessObservabilityPlugin ): ServerlessObservabilityPluginStart { const { observabilityShared, serverless } = setupDeps; observabilityShared.setIsSidebarEnabled(false); - serverless.setProjectHome('/app/enterprise_search/content/setup_guide'); + serverless.setProjectHome('/app/observability/landing'); serverless.setSideNavComponent(getObservabilitySideNavComponent(core, { serverless })); return {}; } From 4da4059ac80f1eba1eb0a4a5aad6a71895a566d5 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 13 Jun 2023 09:55:52 -0700 Subject: [PATCH 12/16] Revert project switcher --- .../project_switcher/src/header_button.tsx | 30 +++++++++---------- .../serverless/project_switcher/tsconfig.json | 1 - x-pack/plugins/serverless/public/plugin.tsx | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/serverless/project_switcher/src/header_button.tsx b/packages/serverless/project_switcher/src/header_button.tsx index f90daacc05173..ee1bd0acc5888 100644 --- a/packages/serverless/project_switcher/src/header_button.tsx +++ b/packages/serverless/project_switcher/src/header_button.tsx @@ -6,28 +6,26 @@ * Side Public License, v 1. */ -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiHeaderLink, EuiHeaderSectionItem } from '@elastic/eui'; import React, { MouseEventHandler } from 'react'; +import { EuiHeaderSectionItemButton, EuiIcon } from '@elastic/eui'; + +import { ProjectType } from '@kbn/serverless-types'; + +import { icons } from './constants'; export const TEST_ID = 'projectSwitcherButton'; export interface Props { onClick: MouseEventHandler; + currentProjectType: ProjectType; } -export const HeaderButton = (props: Props) => ( - - - - - +export const HeaderButton = ({ onClick, currentProjectType }: Props) => ( + + + ); diff --git a/packages/serverless/project_switcher/tsconfig.json b/packages/serverless/project_switcher/tsconfig.json index a6bc3fb6473c1..8fd6b54236754 100644 --- a/packages/serverless/project_switcher/tsconfig.json +++ b/packages/serverless/project_switcher/tsconfig.json @@ -19,6 +19,5 @@ "kbn_references": [ "@kbn/shared-ux-storybook-mock", "@kbn/serverless-types", - "@kbn/i18n-react", ] } diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index 3d4f06aa6a7c0..73f199eb3c468 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -54,7 +54,7 @@ export class ServerlessPlugin if (developer && developer.projectSwitcher && developer.projectSwitcher.enabled) { const { currentType } = developer.projectSwitcher; - core.chrome.navControls.registerLeft({ + core.chrome.navControls.registerRight({ mount: (target) => this.mountProjectSwitcher(target, core, currentType), }); } From c808a4bc7415863c42431a13dc2216fe6448d1be Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 13 Jun 2023 10:13:54 -0700 Subject: [PATCH 13/16] restore cloud link --- .../src/ui/project/header.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 39ee90d15aafc..195222cfbcbd6 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -8,6 +8,7 @@ import { EuiHeader, + EuiHeaderLink, EuiHeaderLogo, EuiHeaderSection, EuiHeaderSectionItem, @@ -70,6 +71,11 @@ const headerStrings = { defaultMessage: 'Go to home page', }), }, + cloud: { + linkToDeployments: i18n.translate('core.ui.primaryNav.cloud.linkToDeployments', { + defaultMessage: 'My deployments', + }), + }, nav: { closeNavAriaLabel: i18n.translate('core.ui.primaryNav.toggleNavAriaLabel', { defaultMessage: 'Toggle primary navigation', @@ -205,6 +211,12 @@ export const ProjectHeader = ({ + + + {headerStrings.cloud.linkToDeployments} + + + From 2d61cca302160213b748f341d67604252322902b Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 13 Jun 2023 10:27:55 -0700 Subject: [PATCH 14/16] fix ts for removed homeRef prop --- .../navigation/src/ui/components/navigation.test.tsx | 10 +++++----- packages/shared-ux/chrome/navigation/src/ui/types.ts | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx index 47ce421ee3d9e..3961dce34abae 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx @@ -293,7 +293,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + id="item1" link="notRegistered" /> @@ -306,8 +306,8 @@ describe('', () => { ); - expect(await queryByTestId('nav-group-root.group1')).toBeNull(); - expect(await queryByTestId('nav-item-root.group2.item1')).toBeVisible(); + expect(queryByTestId('nav-group-root.group1')).toBeNull(); + expect(queryByTestId('nav-item-root.group2.item1')).toBeVisible(); expect(onProjectNavigationChange).toHaveBeenCalled(); const lastCall = @@ -500,7 +500,7 @@ describe('', () => { render( - + @@ -546,7 +546,7 @@ describe('', () => { const expectToThrow = () => { render( - + diff --git a/packages/shared-ux/chrome/navigation/src/ui/types.ts b/packages/shared-ux/chrome/navigation/src/ui/types.ts index 3a20dfaf2fd8b..91f37e8d3c63d 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/types.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/types.ts @@ -133,10 +133,6 @@ export interface NavigationTreeDefinition { * or when calling `setNavigation()` on the serverless plugin. */ export interface ProjectNavigationDefinition { - /** - * The URL href for the home link - */ - homeRef: string; /** * A navigation tree structure with object items containing labels, links, and sub-items * for a project. Use it if you only need to configure your project navigation and leave From 3cdb41a7456fdda4c6199e28d702b2e0b8ab5963 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 13 Jun 2023 11:15:50 -0700 Subject: [PATCH 15/16] update test fixture --- .../chrome/navigation/src/ui/components/navigation.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx index 3961dce34abae..9860b8b231289 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx @@ -315,7 +315,6 @@ describe('', () => { const [navTree] = lastCall; expect(navTree).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -514,7 +513,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'group1', From cb5763ffb853d8c12ede0a5bbc59dbc732b7c024 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Tue, 13 Jun 2023 18:22:23 -0700 Subject: [PATCH 16/16] update test fixture II --- .../src/ui/default_navigation.test.tsx | 45 +++---------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx index c878fec34742c..4ad3dc75a9d93 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx @@ -23,10 +23,6 @@ import { } from '../../mocks/src/default_navigation.test.helpers'; import { navLinksMock } from '../../mocks/src/navlinks'; -const defaultProps = { - homeRef: 'https://elastic.co', -}; - describe('', () => { const services = getServicesMock(); @@ -77,12 +73,7 @@ describe('', () => { const { findByTestId } = render( - + ); @@ -103,7 +94,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'group1', @@ -202,12 +192,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + ); @@ -217,7 +202,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -285,12 +269,7 @@ describe('', () => { render( - + ); @@ -300,7 +279,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: [ { id: 'root', @@ -358,12 +336,7 @@ describe('', () => { const expectToThrow = () => { render( - + ); }; @@ -388,12 +361,7 @@ describe('', () => { const { findByTestId } = render( - + ); @@ -452,7 +420,7 @@ describe('', () => { navLinks$={navLinks$} onProjectNavigationChange={onProjectNavigationChange} > - + ); @@ -462,7 +430,6 @@ describe('', () => { const [navTreeGenerated] = lastCall; expect(navTreeGenerated).toEqual({ - homeRef: 'https://elastic.co', navigationTree: expect.any(Array), });