diff --git a/docs/management/upgrade-assistant.asciidoc b/docs/management/upgrade-assistant.asciidoc index a44afa4474a7b..ed9093ded2846 100644 --- a/docs/management/upgrade-assistant.asciidoc +++ b/docs/management/upgrade-assistant.asciidoc @@ -26,4 +26,4 @@ The Upgrade assistant pulls information about deprecations from the following so * Elasticsearch deprecation logs * Kibana deprecations API -For more information about the API's the Upgraed assistant provides, refer to <>. +For more information about Upgrade Assistant APIs, refer to <>. diff --git a/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template.test.tsx.snap b/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template.test.tsx.snap index c57c90acbdcf7..09421e4cb5dd7 100644 --- a/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template.test.tsx.snap +++ b/packages/shared-ux/page/kibana_template/impl/src/__snapshots__/page_template.test.tsx.snap @@ -3,7 +3,7 @@ exports[`KibanaPageTemplate render basic template 1`] = `
<_EuiPageHeader diff --git a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx index f9b9dcd247de6..5da29ba797041 100644 --- a/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx +++ b/packages/shared-ux/page/kibana_template/impl/src/page_template_inner.tsx @@ -68,7 +68,8 @@ export const KibanaPageTemplateInner: FC = ({ // the following props can be removed to allow the template to auto-handle // the fixed header and banner heights. offset={0} - minHeight={0} + minHeight={header ? 'calc(100vh - var(--euiFixedHeadersOffset, 0))' : 0} + grow={header ? false : undefined} {...rest} > {sideBar} diff --git a/src/dev/build/tasks/fleet/download_elastic_gpg_key.ts b/src/dev/build/tasks/fleet/download_elastic_gpg_key.ts index 483a342ba300e..6cd0b351c4d31 100644 --- a/src/dev/build/tasks/fleet/download_elastic_gpg_key.ts +++ b/src/dev/build/tasks/fleet/download_elastic_gpg_key.ts @@ -13,9 +13,9 @@ import { ToolingLog } from '@kbn/tooling-log'; import { downloadToDisk } from '../../lib'; const ARTIFACTS_URL = 'https://artifacts.elastic.co/'; -const GPG_KEY_NAME = 'GPG-KEY-elasticsearch'; +const GPG_KEY_NAME = 'GPG-KEY-elasticsearch.sha1'; const GPG_KEY_SHA512 = - '62a567354286deb02baf5fc6b82ddf6c7067898723463da9ae65b132b8c6d6f064b2874e390885682376228eed166c1c82fe7f11f6c9a69f0c157029c548fa3d'; + '84ee193cc337344d9a7da9021daf3f5ede83f5f1ab049d169f3634921529dcd096abf7a91eec7f26f3a6913e5e38f88f69a5e2ce79ad155d46edc75705a648c6'; export async function downloadElasticGpgKey(pkgDir: string, log: ToolingLog) { const gpgKeyUrl = ARTIFACTS_URL + GPG_KEY_NAME; diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap index bd4e658b04a99..2cd4388680751 100644 --- a/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap +++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/tutorial.test.js.snap @@ -1,160 +1,166 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`isCloudEnabled is false should not render instruction toggle when ON_PREM_ELASTIC_CLOUD instructions are not provided 1`] = ` - -
- - - +
+ -
- + "prepend": [Function], + } + } + description="tutorial used to drive jest tests" + notices={null} + title="jest test tutorial" + /> + + +
+ + `; exports[`isCloudEnabled is false should render ON_PREM instructions with instruction toggle 1`] = ` - -
- +
+ - - - + + + + + + - - - -
- +
+ + `; exports[`should render ELASTIC_CLOUD instructions when isCloudEnabled is true 1`] = ` - -
- - - +
+ -
- + "prepend": [Function], + } + } + description="tutorial used to drive jest tests" + iconType="logoApache" + notices={null} + title="jest test tutorial" + /> + + +
+ + `; diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.js b/src/plugins/home/public/application/components/tutorial/tutorial.js index de222dbe6155d..620da42169c35 100644 --- a/src/plugins/home/public/application/components/tutorial/tutorial.js +++ b/src/plugins/home/public/application/components/tutorial/tutorial.js @@ -18,7 +18,7 @@ import * as StatusCheckStates from './status_check_states'; import { injectI18n, FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { getServices } from '../../kibana_services'; -import { KibanaPageTemplate } from '@kbn/kibana-react-plugin/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; const INSTRUCTIONS_TYPE = { ELASTIC_CLOUD: 'elasticCloud', @@ -360,7 +360,7 @@ class TutorialUi extends React.Component { render() { let content; if (this.state.notFound) { - content = ( + return ( {content}; + return ( + + {content} + + ); } } diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js index cde8b86f5df22..c5a4c1b27fbab 100644 --- a/src/plugins/home/public/application/components/tutorial_directory.js +++ b/src/plugins/home/public/application/components/tutorial_directory.js @@ -16,7 +16,7 @@ import { SampleDataTab } from '@kbn/home-sample-data-tab'; import { i18n } from '@kbn/i18n'; import { Synopsis } from './synopsis'; import { getServices } from '../kibana_services'; -import { KibanaPageTemplate } from '@kbn/kibana-react-plugin/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { getTutorials } from '../load_tutorials'; const SAMPLE_DATA_TAB_ID = 'sampleData'; @@ -257,7 +257,7 @@ class TutorialDirectoryUi extends React.Component { rightSideItems: headerLinks ? [headerLinks] : [], }} > - {this.renderTabContent()} + {this.renderTabContent()}
); } diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index f92739d045439..7954559206bb7 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -54,17 +54,13 @@ export { POSITIONS, WEIGHTS, TOOLBAR_BUTTON_SIZES, ToolbarButton } from './toolb export { reactRouterNavigate, reactRouterOnClickHandler } from './react_router_navigate'; export type { - KibanaPageTemplateProps, NoDataPageActions, NoDataPageActionsProps, NoDataPageProps, ElasticAgentCardProps, } from './page_template'; export { - KibanaPageTemplate, KibanaPageTemplateSolutionNavAvatar, - NO_DATA_PAGE_MAX_WIDTH, - NO_DATA_PAGE_TEMPLATE_PROPS, NO_DATA_RECOMMENDED, NoDataPage, ElasticAgentCard, diff --git a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap b/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap deleted file mode 100644 index 4dea9549670f3..0000000000000 --- a/src/plugins/kibana_react/public/page_template/__snapshots__/page_template.test.tsx.snap +++ /dev/null @@ -1,440 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`KibanaPageTemplate render basic template 1`] = ` -
-
-
-
-
-
-
-

- test -

-
-
-
-
-
- test -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`KibanaPageTemplate render custom empty prompt only 1`] = ` - - - custom test - - } - /> - -`; - -exports[`KibanaPageTemplate render custom empty prompt with page header 1`] = ` - - - custom test - - } - /> - -`; - -exports[`KibanaPageTemplate render default empty prompt 1`] = ` - -`; - -exports[`KibanaPageTemplate render noDataContent 1`] = ` - -`; - -exports[`KibanaPageTemplate render solutionNav 1`] = ` -
-
- - -
-
-
-
-
-
-
-
-

- test -

-
-
-
-
-
- test -
-
-
-
-
-
-
-
-
-
-
-
-`; diff --git a/src/plugins/kibana_react/public/page_template/index.ts b/src/plugins/kibana_react/public/page_template/index.ts index fda644a284797..65a5db433593a 100644 --- a/src/plugins/kibana_react/public/page_template/index.ts +++ b/src/plugins/kibana_react/public/page_template/index.ts @@ -6,9 +6,5 @@ * Side Public License, v 1. */ -export type { KibanaPageTemplateProps } from './page_template'; -export { KibanaPageTemplate } from './page_template'; export { KibanaPageTemplateSolutionNavAvatar, KibanaPageTemplateSolutionNav } from './solution_nav'; export * from './no_data_page'; -export { withSolutionNav } from './with_solution_nav'; -export { NO_DATA_PAGE_MAX_WIDTH, NO_DATA_PAGE_TEMPLATE_PROPS } from './util'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/index.ts b/src/plugins/kibana_react/public/page_template/no_data_page/index.ts index b5a11722dd397..55661ad6f14f7 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/index.ts +++ b/src/plugins/kibana_react/public/page_template/no_data_page/index.ts @@ -8,4 +8,3 @@ export * from './no_data_page'; export * from './no_data_card'; -export * from './no_data_config_page'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/index.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/index.tsx deleted file mode 100644 index 0bdde40021398..0000000000000 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/index.tsx +++ /dev/null @@ -1,9 +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. - */ - -export { NoDataConfigPage, NoDataConfigPageWithSolutionNavBar } from './no_data_config_page'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx deleted file mode 100644 index cae591f571c79..0000000000000 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx +++ /dev/null @@ -1,38 +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 { EuiPageTemplate_Deprecated as EuiPageTemplate } from '@elastic/eui'; -import React from 'react'; -import { NoDataPage } from '../no_data_page'; -import { withSolutionNav } from '../../with_solution_nav'; -import { KibanaPageTemplateProps } from '../../page_template'; -import { getClasses, NO_DATA_PAGE_TEMPLATE_PROPS } from '../../util'; - -export const NoDataConfigPage = (props: KibanaPageTemplateProps) => { - const { className, noDataConfig, ...rest } = props; - - if (!noDataConfig) { - return null; - } - - const template = NO_DATA_PAGE_TEMPLATE_PROPS.template; - const classes = getClasses(template, className); - - return ( - - - - ); -}; - -export const NoDataConfigPageWithSolutionNavBar = withSolutionNav(NoDataConfigPage); diff --git a/src/plugins/kibana_react/public/page_template/page_template.scss b/src/plugins/kibana_react/public/page_template/page_template.scss deleted file mode 100644 index d94daec56235f..0000000000000 --- a/src/plugins/kibana_react/public/page_template/page_template.scss +++ /dev/null @@ -1,22 +0,0 @@ -.kbnPageTemplate__pageSideBar { - overflow: hidden; - // Temporary hack till the sizing is changed directly in EUI - min-width: 248px; - - @include euiCanAnimate { - transition: min-width $euiAnimSpeedFast $euiAnimSlightResistance; - } - - &.kbnPageTemplate__pageSideBar--shrink { - min-width: $euiSizeXXL; - } - - .kbnPageTemplate--centeredBody & { - border-bottom: $euiBorderThin; - - @include euiBreakpoint('m', 'l', 'xl') { - border-bottom: none; - border-right: $euiBorderThin; - } - } -} diff --git a/src/plugins/kibana_react/public/page_template/page_template.test.tsx b/src/plugins/kibana_react/public/page_template/page_template.test.tsx deleted file mode 100644 index aff6082902a34..0000000000000 --- a/src/plugins/kibana_react/public/page_template/page_template.test.tsx +++ /dev/null @@ -1,173 +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 from 'react'; -import { shallow, render } from 'enzyme'; -import { KibanaPageTemplate, KibanaPageTemplateProps } from './page_template'; -import { EuiEmptyPrompt } from '@elastic/eui'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; - -const navItems: KibanaPageTemplateSolutionNavProps['items'] = [ - { - name: 'Ingest', - id: '1', - items: [ - { - name: 'Ingest Node Pipelines', - id: '1.1', - }, - { - name: 'Logstash Pipelines', - id: '1.2', - }, - { - name: 'Beats Central Management', - id: '1.3', - }, - ], - }, - { - name: 'Data', - id: '2', - items: [ - { - name: 'Index Management', - id: '2.1', - }, - { - name: 'Index Lifecycle Policies', - id: '2.2', - }, - { - name: 'Snapshot and Restore', - id: '2.3', - }, - ], - }, -]; - -const noDataConfig: KibanaPageTemplateProps['noDataConfig'] = { - solution: 'Elastic', - actions: { - elasticAgent: {}, - beats: {}, - custom: {}, - }, - docsLink: 'test', -}; - -describe('KibanaPageTemplate', () => { - test('render default empty prompt', () => { - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - }); - - test('render custom empty prompt only', () => { - const component = shallow( - - custom test} /> - - ); - expect(component).toMatchSnapshot(); - }); - - test('render custom empty prompt with page header', () => { - const component = shallow( - - custom test} /> - - ); - expect(component).toMatchSnapshot(); - }); - - test('render basic template', () => { - const component = render( - - ); - expect(component).toMatchSnapshot(); - }); - - test('render solutionNav', () => { - const component = render( - - ); - expect(component).toMatchSnapshot(); - expect(component.find('div.kbnPageTemplate__pageSideBar').length).toBe(1); - }); - - test('render noDataContent', () => { - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - }); - - test('render sidebar classes', () => { - const component = shallow( - - ); - expect(component.html().includes('kbnPageTemplate__pageSideBar customClass')).toBe(true); - }); -}); diff --git a/src/plugins/kibana_react/public/page_template/page_template.tsx b/src/plugins/kibana_react/public/page_template/page_template.tsx deleted file mode 100644 index 42ba9d1873587..0000000000000 --- a/src/plugins/kibana_react/public/page_template/page_template.tsx +++ /dev/null @@ -1,98 +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 './page_template.scss'; - -import React, { FunctionComponent } from 'react'; -import { EuiPageTemplateProps_Deprecated } from '@elastic/eui'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; - -import { - NoDataPageProps, - NoDataConfigPage, - NoDataConfigPageWithSolutionNavBar, -} from './no_data_page'; -import { KibanaPageTemplateInner, KibanaPageTemplateWithSolutionNav } from './page_template_inner'; - -/** - * A thin wrapper around EuiPageTemplate with a few Kibana specific additions - * @deprecated Use `KibanaPageTemplateProps` from `@kbn/shared-ux-page-kibana-template-types`. - */ -export type KibanaPageTemplateProps = EuiPageTemplateProps_Deprecated & { - /** - * Changes the template type depending on other props provided. - * With `pageHeader` only: Uses `centeredBody` and fills an EuiEmptyPrompt with `pageHeader` info. - * With `children` only: Uses `centeredBody` - * With `pageHeader` and `children`: Uses `centeredContent` - */ - isEmptyState?: boolean; - /** - * Quick creation of EuiSideNav. Hooks up mobile instance too - */ - solutionNav?: KibanaPageTemplateSolutionNavProps; - /** - * Accepts a configuration object, that when provided, ignores pageHeader and children and instead - * displays Agent, Beats, and custom cards to direct users to the right ingest location - */ - noDataConfig?: NoDataPageProps; -}; - -/** @deprecated Use `KibanaPageTemplate` from `@kbn/shared-ux-page-kibana-template`. */ -export const KibanaPageTemplate: FunctionComponent = ({ - template, - className, - children, - solutionNav, - noDataConfig, - ...rest -}) => { - /** - * If passing the custom template of `noDataConfig` - */ - if (noDataConfig && solutionNav) { - return ( - - ); - } - - if (noDataConfig) { - return ( - - ); - } - - if (solutionNav) { - return ( - - ); - } - - return ( - - ); -}; diff --git a/src/plugins/kibana_react/public/page_template/page_template_inner.tsx b/src/plugins/kibana_react/public/page_template/page_template_inner.tsx deleted file mode 100644 index 001cea5c26a23..0000000000000 --- a/src/plugins/kibana_react/public/page_template/page_template_inner.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, { FunctionComponent } from 'react'; - -import { EuiEmptyPrompt, EuiPageTemplate_Deprecated as EuiPageTemplate } from '@elastic/eui'; -import { withSolutionNav } from './with_solution_nav'; -import { KibanaPageTemplateProps } from './page_template'; -import { getClasses } from './util'; - -type Props = KibanaPageTemplateProps; - -/** - * A thin wrapper around EuiPageTemplate with a few Kibana specific additions - */ -export const KibanaPageTemplateInner: FunctionComponent = ({ - template, - className, - pageHeader, - children, - isEmptyState, - ...rest -}) => { - /** - * An easy way to create the right content for empty pages - */ - const emptyStateDefaultTemplate = 'centeredBody'; - if (isEmptyState && pageHeader && !children) { - template = template ?? emptyStateDefaultTemplate; - const { iconType, pageTitle, description, rightSideItems } = pageHeader; - pageHeader = undefined; - children = ( - {pageTitle} : undefined} - body={description ?

{description}

: undefined} - actions={rightSideItems} - /> - ); - } else if (isEmptyState && pageHeader && children) { - template = template ?? 'centeredContent'; - } else if (isEmptyState && !pageHeader) { - template = template ?? emptyStateDefaultTemplate; - } - - const classes = getClasses(template, className); - - return ( - - {children} - - ); -}; - -export const KibanaPageTemplateWithSolutionNav = withSolutionNav(KibanaPageTemplateInner); diff --git a/src/plugins/kibana_react/public/page_template/util/constants.ts b/src/plugins/kibana_react/public/page_template/util/constants.ts deleted file mode 100644 index 159a6d0d8d4c1..0000000000000 --- a/src/plugins/kibana_react/public/page_template/util/constants.ts +++ /dev/null @@ -1,20 +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 { KibanaPageTemplateProps } from '../page_template'; - -export const NO_DATA_PAGE_MAX_WIDTH = 950; - -export const NO_DATA_PAGE_TEMPLATE_PROPS: KibanaPageTemplateProps = { - restrictWidth: NO_DATA_PAGE_MAX_WIDTH, - template: 'centeredBody', - pageContentProps: { - hasShadow: false, - color: 'transparent', - }, -}; diff --git a/src/plugins/kibana_react/public/page_template/util/index.ts b/src/plugins/kibana_react/public/page_template/util/index.ts index adfefdf834566..06edc43d70d57 100644 --- a/src/plugins/kibana_react/public/page_template/util/index.ts +++ b/src/plugins/kibana_react/public/page_template/util/index.ts @@ -7,4 +7,3 @@ */ export { getClasses } from './presentation'; -export * from './constants'; diff --git a/src/plugins/kibana_react/public/page_template/with_solution_nav.tsx b/src/plugins/kibana_react/public/page_template/with_solution_nav.tsx deleted file mode 100644 index 842573b9d8de4..0000000000000 --- a/src/plugins/kibana_react/public/page_template/with_solution_nav.tsx +++ /dev/null @@ -1,77 +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, { ComponentType, useState } from 'react'; -import classNames from 'classnames'; -import { useIsWithinBreakpoints } from '@elastic/eui'; -import { EuiPageSideBarProps_Deprecated as EuiPageSideBarProps } from '@elastic/eui/src/components/page/page_side_bar'; -import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; -import { KibanaPageTemplateProps } from '.'; - -// https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging -function getDisplayName(Component: ComponentType) { - return Component.displayName || Component.name || 'UnnamedComponent'; -} - -type SolutionNavProps = KibanaPageTemplateProps & { - solutionNav: KibanaPageTemplateSolutionNavProps; -}; - -const SOLUTION_NAV_COLLAPSED_KEY = 'solutionNavIsCollapsed'; - -export const withSolutionNav = (WrappedComponent: ComponentType) => { - const WithSolutionNav = (props: SolutionNavProps) => { - const isMediumBreakpoint = useIsWithinBreakpoints(['m']); - const isLargerBreakpoint = useIsWithinBreakpoints(['l', 'xl']); - const [isSideNavOpenOnDesktop, setisSideNavOpenOnDesktop] = useState( - !JSON.parse(String(localStorage.getItem(SOLUTION_NAV_COLLAPSED_KEY))) - ); - const { solutionNav, ...propagatedProps } = props; - const { children, isEmptyState, template } = propagatedProps; - const toggleOpenOnDesktop = () => { - setisSideNavOpenOnDesktop(!isSideNavOpenOnDesktop); - // Have to store it as the opposite of the default we want - localStorage.setItem(SOLUTION_NAV_COLLAPSED_KEY, JSON.stringify(isSideNavOpenOnDesktop)); - }; - const sideBarClasses = classNames( - 'kbnPageTemplate__pageSideBar', - { - 'kbnPageTemplate__pageSideBar--shrink': - isMediumBreakpoint || (isLargerBreakpoint && !isSideNavOpenOnDesktop), - }, - props.pageSideBarProps?.className - ); - - const templateToUse = isEmptyState && !template ? 'centeredContent' : template; - - const pageSideBar = ( - - ); - const pageSideBarProps = { - paddingSize: 'none', - ...props.pageSideBarProps, - className: sideBarClasses, - } as EuiPageSideBarProps; // needed because for some reason 'none' is not recognized as a valid value for paddingSize - return ( - - {children} - - ); - }; - WithSolutionNav.displayName = `WithSolutionNavBar(${getDisplayName(WrappedComponent)})`; - return WithSolutionNav; -}; diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx index 27ae07cbf768d..08b89dad773ce 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx @@ -15,6 +15,8 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public'; import { MaintenanceWindowCallout } from '@kbn/alerts-ui-shared'; +import { rulesLocatorID } from '../../../common'; +import { RulesParams } from '../../locators/rules'; import { useKibana } from '../../utils/kibana_react'; import { useHasData } from '../../hooks/use_has_data'; import { usePluginContext } from '../../hooks/use_plugin_context'; @@ -53,6 +55,9 @@ function InternalAlertsPage() { }, http, notifications: { toasts }, + share: { + url: { locators }, + }, triggersActionsUi: { alertsTableConfigurationRegistry, getAlertsSearchBar: AlertsSearchBar, @@ -179,7 +184,12 @@ function InternalAlertsPage() { pageTitle: ( <>{i18n.translate('xpack.observability.alertsTitle', { defaultMessage: 'Alerts' })} ), - rightSideItems: renderRuleStats(ruleStats, manageRulesHref, ruleStatsLoading), + rightSideItems: renderRuleStats( + ruleStats, + manageRulesHref, + ruleStatsLoading, + locators.get(rulesLocatorID) + ), }} > diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.test.tsx index aaeab5d7855f7..758df7224be2d 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.test.tsx @@ -6,7 +6,9 @@ */ import { renderRuleStats } from './rule_stats'; -import { render, screen } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { LocatorPublic } from '@kbn/share-plugin/common'; +import { RulesParams } from '../../../locators/rules'; const RULES_PAGE_LINK = '/app/observability/alerts/rules'; const STAT_CLASS = 'euiStat'; @@ -14,6 +16,14 @@ const STAT_TITLE_PRIMARY_SELECTOR = '[class*="euiStat__title-primary"]'; const STAT_BUTTON_CLASS = 'euiButtonEmpty'; describe('Rule stats', () => { + const mockedLocator = { + navigate: jest.fn(), + } as any as LocatorPublic; + + beforeEach(() => { + jest.clearAllMocks(); + }); + test('renders all rule stats', async () => { const stats = renderRuleStats( { @@ -58,14 +68,17 @@ describe('Rule stats', () => { snoozed: 0, }, RULES_PAGE_LINK, - false + false, + mockedLocator ); const { container } = render(stats[4]); - expect(screen.getByText('Disabled').closest('a')).toHaveAttribute( - 'href', - `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(disabled))` - ); + fireEvent.click(screen.getByText('Disabled')); + + expect(mockedLocator.navigate).toHaveBeenCalledWith( + { status: ['disabled'] }, + { replace: false } + ); expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); }); @@ -115,14 +128,18 @@ describe('Rule stats', () => { snoozed: 1, }, RULES_PAGE_LINK, - false + false, + mockedLocator ); const { container } = render(stats[3]); - expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); - expect(screen.getByText('Snoozed').closest('a')).toHaveAttribute( - 'href', - `${RULES_PAGE_LINK}?_a=(lastResponse:!(),status:!(snoozed))` + + fireEvent.click(screen.getByText('Snoozed')); + + expect(mockedLocator.navigate).toHaveBeenCalledWith( + { status: ['snoozed'] }, + { replace: false } ); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); }); test('snoozed stat count is link-colored, when there are snoozed rules', async () => { @@ -171,14 +188,18 @@ describe('Rule stats', () => { snoozed: 0, }, RULES_PAGE_LINK, - false + false, + mockedLocator ); const { container } = render(stats[2]); - expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); - expect(screen.getByText('Errors').closest('a')).toHaveAttribute( - 'href', - `${RULES_PAGE_LINK}?_a=(lastResponse:!(error),status:!())` + + fireEvent.click(screen.getByText('Errors')); + + expect(mockedLocator.navigate).toHaveBeenCalledWith( + { lastResponse: ['failed'] }, + { replace: false } ); + expect(container.getElementsByClassName(STAT_BUTTON_CLASS).length).toBe(1); }); test('errors stat count is link-colored, when there are error rules', () => { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.tsx b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.tsx index f85a6a766b17c..005ba7ccaec82 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/rule_stats.tsx @@ -8,8 +8,10 @@ import React from 'react'; import { EuiButtonEmpty, EuiStat } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { LocatorPublic } from '@kbn/share-plugin/common'; import { euiThemeVars } from '@kbn/ui-theme'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { RulesParams } from '../../../locators/rules'; export interface RuleStatsState { total: number; @@ -18,7 +20,7 @@ export interface RuleStatsState { error: number; snoozed: number; } -type StatType = 'disabled' | 'snoozed' | 'error'; +type Status = 'disabled' | 'snoozed' | 'error'; const Divider = euiStyled.div` border-right: 1px solid ${euiThemeVars.euiColorLightShade}; @@ -41,33 +43,32 @@ const ConditionalWrap = ({ children: JSX.Element; }): JSX.Element => (condition ? wrap(children) : children); -const getStatCount = (stats: RuleStatsState, statType: StatType) => { - if (statType === 'snoozed') return stats.snoozed + stats.muted; - return stats[statType]; +const getStatCount = (stats: RuleStatsState, status: Status) => { + if (status === 'snoozed') return stats.snoozed + stats.muted; + return stats[status]; }; export const renderRuleStats = ( ruleStats: RuleStatsState, manageRulesHref: string, - ruleStatsLoading: boolean + ruleStatsLoading: boolean, + rulesLocator?: LocatorPublic ) => { - const createRuleStatsLink = (stats: RuleStatsState, statType: StatType) => { - const count = getStatCount(stats, statType); - let statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!())`; + const handleNavigateToRules = async (stats: RuleStatsState, status: Status) => { + const count = getStatCount(stats, status); if (count > 0) { - switch (statType) { + switch (status) { case 'error': - statsLink = `${manageRulesHref}?_a=(lastResponse:!(error),status:!())`; + await rulesLocator?.navigate({ lastResponse: ['failed'] }, { replace: false }); break; case 'snoozed': case 'disabled': - statsLink = `${manageRulesHref}?_a=(lastResponse:!(),status:!(${statType}))`; + await rulesLocator?.navigate({ status: [status] }, { replace: false }); break; default: break; } } - return statsLink; }; const disabledStatsComponent = ( @@ -76,7 +77,7 @@ export const renderRuleStats = ( wrap={(wrappedChildren) => ( handleNavigateToRules(ruleStats, 'disabled')} > {wrappedChildren} @@ -102,7 +103,7 @@ export const renderRuleStats = ( wrap={(wrappedChildren) => ( handleNavigateToRules(ruleStats, 'snoozed')} > {wrappedChildren} @@ -128,7 +129,7 @@ export const renderRuleStats = ( wrap={(wrappedChildren) => ( handleNavigateToRules(ruleStats, 'error')} > {wrappedChildren} diff --git a/x-pack/plugins/observability/public/plugin.mock.tsx b/x-pack/plugins/observability/public/plugin.mock.tsx index be663d15e444d..9732f67aac35f 100644 --- a/x-pack/plugins/observability/public/plugin.mock.tsx +++ b/x-pack/plugins/observability/public/plugin.mock.tsx @@ -4,11 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React from 'react'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; const triggersActionsUiStartMock = { @@ -97,12 +99,13 @@ export const observabilityPublicPluginsStartMock = { cases: mockCasesContract(), charts: chartPluginMock.createStartContract(), contentManagement: contentManagementMock.createStartContract(), - triggersActionsUi: triggersActionsUiStartMock.createStart(), data: dataPluginMock.createStartContract(), - dataViews: dataViews.createStart(), dataViewEditor: dataViewEditor.createStart(), - lens: null, + dataViews: dataViews.createStart(), discover: null, + lens: null, + share: sharePluginMock.createStartContract(), + triggersActionsUi: triggersActionsUiStartMock.createStart(), unifiedSearch: unifiedSearchPluginMock.createStartContract(), }; }, diff --git a/x-pack/plugins/osquery/public/components/empty_state.tsx b/x-pack/plugins/osquery/public/components/empty_state.tsx index 099e9e22b1f64..18f5afe8eaf5f 100644 --- a/x-pack/plugins/osquery/public/components/empty_state.tsx +++ b/x-pack/plugins/osquery/public/components/empty_state.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton } from '@elastic/eui'; -import { KibanaPageTemplate } from '@kbn/kibana-react-plugin/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { INTEGRATIONS_PLUGIN_ID } from '@kbn/fleet-plugin/common'; import { pagePathGetters } from '@kbn/fleet-plugin/public'; import { isModifiedEvent, isLeftClickEvent, useKibana } from '../common/lib/kibana'; diff --git a/x-pack/plugins/osquery/tsconfig.json b/x-pack/plugins/osquery/tsconfig.json index 6cd1086b8a850..d2344a2581df8 100644 --- a/x-pack/plugins/osquery/tsconfig.json +++ b/x-pack/plugins/osquery/tsconfig.json @@ -77,5 +77,6 @@ "@kbn/core-saved-objects-server", "@kbn/monaco", "@kbn/io-ts-utils", + "@kbn/shared-ux-page-kibana-template", ] } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts index ddc90ec10e9b6..02a41b6de7afa 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts @@ -26,8 +26,7 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F const esTestIndexTool = new ESTestIndexTool(es, retry); const supertestWithoutAuth = getService('supertestWithoutAuth'); - // FLAKY: https://github.com/elastic/kibana/issues/140973 - describe.skip('telemetry', () => { + describe('telemetry', () => { const objectRemover = new ObjectRemover(supertest); const alwaysFiringRuleId: { [key: string]: string } = {};