From 6ac52fb9ec71b4957e8f6408b91a867f2f2b6d89 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 20 Jun 2023 13:56:00 +0200 Subject: [PATCH 1/8] [ML] Update the Overview page (#159609) ## Summary Resolves https://github.com/elastic/kibana/issues/154294 and updates the UI of the Overview page - Updates panels layout - Stores expand/collapsed state of the panels in the local storage - Update empty states text and layout image ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- x-pack/plugins/ml/common/types/storage.ts | 11 ++ .../collapsible_panel/collapsible_panel.tsx | 137 ++++++++++++++++++ .../components/collapsible_panel/index.ts | 8 + .../components/empty_prompt/empty_prompt.tsx | 87 ++--------- .../analytics_service/get_analytics.test.ts | 2 +- .../analytics_service/get_analytics.ts | 10 +- .../anomaly_detection_empty_state.tsx | 51 ++----- .../nodes_overview/nodes_list.tsx | 23 +-- .../ml_nodes_check/check_ml_nodes.ts | 4 + .../analytics_panel/analytics_panel.tsx | 137 +++++++++--------- .../anomaly_detection_panel.tsx | 94 +++++++++--- .../anomaly_detection_panel/table.tsx | 71 ++------- .../anomaly_detection_panel/utils.ts | 36 +++-- .../application/overview/overview_page.tsx | 57 +++++++- .../application/routing/use_resolver.tsx | 1 - .../translations/translations/fr-FR.json | 5 - .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 18 files changed, 441 insertions(+), 303 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx create mode 100644 x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts diff --git a/x-pack/plugins/ml/common/types/storage.ts b/x-pack/plugins/ml/common/types/storage.ts index cb80b17bda583..a74bbea0e3aff 100644 --- a/x-pack/plugins/ml/common/types/storage.ts +++ b/x-pack/plugins/ml/common/types/storage.ts @@ -14,6 +14,7 @@ export const ML_GETTING_STARTED_CALLOUT_DISMISSED = 'ml.gettingStarted.isDismiss export const ML_FROZEN_TIER_PREFERENCE = 'ml.frozenDataTierPreference'; export const ML_ANOMALY_EXPLORER_PANELS = 'ml.anomalyExplorerPanels'; export const ML_NOTIFICATIONS_LAST_CHECKED_AT = 'ml.notificationsLastCheckedAt'; +export const ML_OVERVIEW_PANELS = 'ml.overviewPanels'; export type PartitionFieldConfig = | { @@ -52,6 +53,12 @@ export interface AnomalyExplorerPanelsState { mainPage: { size: number }; } +export interface OverviewPanelsState { + nodes: boolean; + adJobs: boolean; + dfaJobs: boolean; +} + export interface MlStorageRecord { [key: string]: unknown; [ML_ENTITY_FIELDS_CONFIG]: PartitionFieldsConfig; @@ -60,6 +67,7 @@ export interface MlStorageRecord { [ML_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; [ML_ANOMALY_EXPLORER_PANELS]: AnomalyExplorerPanelsState | undefined; [ML_NOTIFICATIONS_LAST_CHECKED_AT]: number | undefined; + [ML_OVERVIEW_PANELS]: OverviewPanelsState; } export type MlStorage = Partial | null; @@ -78,6 +86,8 @@ export type TMlStorageMapped = T extends typeof ML_ENTIT ? AnomalyExplorerPanelsState | undefined : T extends typeof ML_NOTIFICATIONS_LAST_CHECKED_AT ? number | undefined + : T extends typeof ML_OVERVIEW_PANELS + ? OverviewPanelsState | undefined : null; export const ML_STORAGE_KEYS = [ @@ -87,4 +97,5 @@ export const ML_STORAGE_KEYS = [ ML_FROZEN_TIER_PREFERENCE, ML_ANOMALY_EXPLORER_PANELS, ML_NOTIFICATIONS_LAST_CHECKED_AT, + ML_OVERVIEW_PANELS, ] as const; diff --git a/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx b/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx new file mode 100644 index 0000000000000..53ed046423c2f --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx @@ -0,0 +1,137 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiBadge, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiSplitPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React, { type FC } from 'react'; +import { css } from '@emotion/react/dist/emotion-react.cjs'; +import { useCurrentThemeVars } from '../../contexts/kibana'; + +export interface CollapsiblePanelProps { + isOpen: boolean; + onToggle: (isOpen: boolean) => void; + + header: React.ReactElement; + headerItems?: React.ReactElement[]; +} + +export const CollapsiblePanel: FC = ({ + isOpen, + onToggle, + children, + header, + headerItems, +}) => { + const { euiTheme } = useCurrentThemeVars(); + + return ( + + + + + + + { + onToggle(!isOpen); + }} + /> + + + +

{header}

+
+
+
+
+ {headerItems ? ( + + + {headerItems.map((item, i) => { + return ( + +
+ {item} +
+
+ ); + })} +
+
+ ) : null} +
+
+ {isOpen ? ( + + {children} + + ) : null} +
+ ); +}; + +export interface StatEntry { + label: string; + value: number; + 'data-test-subj'?: string; +} + +export interface OverviewStatsBarProps { + inputStats: StatEntry[]; + dataTestSub?: string; +} + +export const OverviewStatsBar: FC = ({ inputStats, dataTestSub }) => { + return ( + + {inputStats.map(({ value, label, 'data-test-subj': dataTestSubjValue }) => { + return ( + + + + {label}: + + + {value} + + + + ); + })} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts b/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts new file mode 100644 index 0000000000000..d45a251f69ca9 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { CollapsiblePanel } from './collapsible_panel'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx index 23ce92dce1b91..14745812e3045 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx @@ -6,16 +6,7 @@ */ import React, { FC } from 'react'; -import { - EuiButton, - EuiCallOut, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiLink, - EuiTitle, -} from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import dfaImage from './data_frame_analytics_kibana.png'; @@ -26,10 +17,7 @@ import { usePermissionCheck } from '../../../../../capabilities/check_capabiliti export const AnalyticsEmptyPrompt: FC = () => { const { - services: { - docLinks, - http: { basePath }, - }, + services: { docLinks }, } = useMlKibana(); const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([ @@ -40,7 +28,6 @@ export const AnalyticsEmptyPrompt: FC = () => { const disabled = !mlNodesAvailable() || !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics; - const transformsLink = `${basePath.get()}/app/management/data/transform`; const navigateToPath = useNavigateToPath(); const navigateToSourceSelection = async () => { @@ -57,16 +44,15 @@ export const AnalyticsEmptyPrompt: FC = () => { size="fullWidth" src={dfaImage} alt={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', { - defaultMessage: 'Create your first data frame analytics job', + defaultMessage: 'Analyze your data with data frame analytics', })} /> } - color="subdued" title={

} @@ -78,39 +64,6 @@ export const AnalyticsEmptyPrompt: FC = () => { defaultMessage="Train outlier detection, regression, or classification machine learning models using data frame analytics." />

- - - - ), - sourcedata: ( - - - - ), - }} - /> - } - iconType="iInCircle" - /> } actions={[ @@ -118,37 +71,19 @@ export const AnalyticsEmptyPrompt: FC = () => { onClick={navigateToSourceSelection} isDisabled={disabled} color="primary" - iconType="plusInCircle" - fill data-test-subj="mlAnalyticsCreateFirstButton" > {i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptButtonText', { - defaultMessage: 'Create job', + defaultMessage: 'Create data frame analytics job', })} , + + + , ]} - footer={ - - - -

- -

-
-
- - - - - -
- } data-test-subj="mlNoDataFrameAnalyticsFound" /> ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts index 613f9034e2ff4..949c8a47deb32 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts @@ -67,7 +67,7 @@ describe('get_analytics', () => { // act and assert expect(getAnalyticsJobsStats(mockResponse)).toEqual({ total: { - label: 'Total analytics jobs', + label: 'Total', value: 2, show: true, }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts index 170a90b1fcba0..9a3ea0c9bef90 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts @@ -47,7 +47,7 @@ export function getInitialAnalyticsStats(): AnalyticStatsBarStats { return { total: { label: i18n.translate('xpack.ml.overview.statsBar.totalAnalyticsLabel', { - defaultMessage: 'Total analytics jobs', + defaultMessage: 'Total', }), value: 0, show: true, @@ -97,12 +97,18 @@ export function getAnalyticsJobsStats( ); resultStats.failed.show = resultStats.failed.value > 0; resultStats.total.value = analyticsStats.count; + + if (resultStats.total.value === 0) { + resultStats.started.show = false; + resultStats.stopped.show = false; + } + return resultStats; } export const getAnalyticsFactory = ( setAnalytics: React.Dispatch>, - setAnalyticsStats: React.Dispatch>, + setAnalyticsStats: (update: AnalyticStatsBarStats | undefined) => void, setErrorMessage: React.Dispatch< React.SetStateAction >, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx index b8dee1a7e6f60..171320dd7b781 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx @@ -7,15 +7,7 @@ import React, { FC } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButton, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiLink, - EuiTitle, -} from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui'; import adImage from './anomaly_detection_kibana.png'; import { ML_PAGES } from '../../../../../../common/constants/locator'; import { useMlKibana, useMlLocator, useNavigateToPath } from '../../../../contexts/kibana'; @@ -47,12 +39,11 @@ export const AnomalyDetectionEmptyState: FC = () => { hasBorder={false} hasShadow={false} icon={} - color="subdued" title={

} @@ -66,43 +57,25 @@ export const AnomalyDetectionEmptyState: FC = () => {

} - actions={ + actions={[ - - } - footer={ - - - -

- -

-
-
- - - - - -
- } + , + + + , + ]} data-test-subj="mlAnomalyDetectionEmptyState" /> ); diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx index a615e40c9e3ea..a8e5848aaef1a 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx @@ -200,15 +200,20 @@ export const NodesList: FC = ({ compactView = false }) => { return (
- - - {nodesStats && ( - - - - )} - - + {nodesStats && !compactView ? ( + <> + + + {nodesStats && ( + + + + )} + + + + ) : null} +
allowNeutralSort={false} diff --git a/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts b/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts index ea981a25d7ecb..c44c391a2fcfd 100644 --- a/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts +++ b/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts @@ -43,3 +43,7 @@ export function lazyMlNodesAvailable() { export function permissionToViewMlNodeCount() { return userHasPermissionToViewMlNodeCount; } + +export function getMlNodesCount(): number { + return mlNodeCount; +} diff --git a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx index b53860d9a3be6..41e9732461bb3 100644 --- a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx @@ -5,28 +5,32 @@ * 2.0. */ -import React, { FC, useEffect, useState } from 'react'; -import { - EuiButton, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiPanel, - EuiSpacer, - EuiText, -} from '@elastic/eui'; +import React, { FC, useCallback, useEffect, useState } from 'react'; +import { EuiCallOut, EuiLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useStorage } from '@kbn/ml-local-storage'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { type AnalyticStatsBarStats } from '../../../components/stats_bar'; +import { + OverviewStatsBar, + type StatEntry, +} from '../../../components/collapsible_panel/collapsible_panel'; +import { + ML_OVERVIEW_PANELS, + MlStorageKey, + TMlStorageMapped, +} from '../../../../../common/types/storage'; import { AnalyticsTable } from './table'; import { getAnalyticsFactory } from '../../../data_frame_analytics/pages/analytics_management/services/analytics_service'; import { DataFrameAnalyticsListRow } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; -import { AnalyticStatsBarStats, StatsBar } from '../../../components/stats_bar'; import { useMlLink } from '../../../contexts/kibana'; import { ML_PAGES } from '../../../../../common/constants/locator'; import { useRefresh } from '../../../routing/use_refresh'; import type { GetDataFrameAnalyticsStatsResponseError } from '../../../services/ml_api_service/data_frame_analytics'; import { AnalyticsEmptyPrompt } from '../../../data_frame_analytics/pages/analytics_management/components/empty_prompt'; +import { overviewPanelDefaultState } from '../../overview_page'; +import { CollapsiblePanel } from '../../../components/collapsible_panel'; interface Props { setLazyJobCount: React.Dispatch>; @@ -35,9 +39,7 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { const refresh = useRefresh(); const [analytics, setAnalytics] = useState([]); - const [analyticsStats, setAnalyticsStats] = useState( - undefined - ); + const [analyticsStats, setAnalyticsStats] = useState(undefined); const [errorMessage, setErrorMessage] = useState(); const [isInitialized, setIsInitialized] = useState(false); @@ -45,9 +47,24 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE, }); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + + const setAnalyticsStatsCustom = useCallback((stats: AnalyticStatsBarStats | undefined) => { + if (!stats) return; + + const result = Object.entries(stats) + .filter(([k, v]) => v.show) + .map(([k, v]) => v); + + setAnalyticsStats(result); + }, []); + const getAnalytics = getAnalyticsFactory( setAnalytics, - setAnalyticsStats, + setAnalyticsStatsCustom, setErrorMessage, setIsInitialized, setLazyJobCount, @@ -78,58 +95,40 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { const noDFAJobs = errorMessage === undefined && isInitialized === true && analytics.length === 0; return ( - <> - {noDFAJobs ? ( - - ) : ( - - {typeof errorMessage !== 'undefined' ? errorDisplay : null} - {isInitialized === false && ( - - )} - - {isInitialized === true && analytics.length > 0 && ( - <> - - - -

- {i18n.translate('xpack.ml.overview.analyticsList.PanelTitle', { - defaultMessage: 'Analytics', - })} -

-
-
- - - {analyticsStats !== undefined ? ( - - - - ) : null} - - - {i18n.translate('xpack.ml.overview.analyticsList.manageJobsButtonText', { - defaultMessage: 'Manage jobs', - })} - - - - -
- - - - )} -
- )} - + { + setPanelsState({ ...panelsState, dfaJobs: update }); + }} + header={ + + } + headerItems={[ + ...(analyticsStats + ? [ + , + ] + : []), + + {i18n.translate('xpack.ml.overview.analyticsList.manageJobsButtonText', { + defaultMessage: 'Manage jobs', + })} + , + ]} + > + {noDFAJobs ? : null} + + {typeof errorMessage !== 'undefined' ? errorDisplay : null} + + {isInitialized === false && } + + {isInitialized === true && analytics.length > 0 ? : null} + ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx index 1773531cb9aa2..30aa0f6d22dfb 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx @@ -6,10 +6,20 @@ */ import React, { FC, Fragment, useEffect, useState } from 'react'; -import { EuiCallOut, EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; +import { EuiCallOut, EuiLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { zipObject } from 'lodash'; -import { useMlKibana } from '../../../contexts/kibana'; +import { zipObject, groupBy } from 'lodash'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useStorage } from '@kbn/ml-local-storage'; +import { + ML_OVERVIEW_PANELS, + MlStorageKey, + TMlStorageMapped, +} from '../../../../../common/types/storage'; +import { ML_PAGES } from '../../../../../common/constants/locator'; +import { OverviewStatsBar } from '../../../components/collapsible_panel/collapsible_panel'; +import { CollapsiblePanel } from '../../../components/collapsible_panel'; +import { useMlKibana, useMlLink } from '../../../contexts/kibana'; import { AnomalyDetectionTable } from './table'; import { ml } from '../../../services/ml_api_service'; import { getGroupsFromJobs, getStatsBarData } from './utils'; @@ -19,8 +29,8 @@ import { useRefresh } from '../../../routing/use_refresh'; import { useToastNotificationService } from '../../../services/toast_notification_service'; import { AnomalyTimelineService } from '../../../services/anomaly_timeline_service'; import type { OverallSwimlaneData } from '../../../explorer/explorer_utils'; -import { JobStatsBarStats } from '../../../components/stats_bar'; import { AnomalyDetectionEmptyState } from '../../../jobs/jobs_list/components/anomaly_detection_empty_state'; +import { overviewPanelDefaultState } from '../../overview_page'; export type GroupsDictionary = Dictionary; @@ -50,10 +60,21 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa const refresh = useRefresh(); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + + const manageJobsLink = useMlLink({ + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + }); + const [isLoading, setIsLoading] = useState(false); const [groups, setGroups] = useState({}); const [groupsCount, setGroupsCount] = useState(0); - const [statsBarData, setStatsBarData] = useState(); + const [statsBarData, setStatsBarData] = useState>(); + const [restStatsBarData, setRestStatsBarData] = + useState>(); const [errorMessage, setErrorMessage] = useState(); const loadJobs = async () => { @@ -71,9 +92,20 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa }); const { groups: jobsGroups, count } = getGroupsFromJobs(jobsSummaryList); const stats = getStatsBarData(jobsSummaryList); + + const statGroups = groupBy( + Object.entries(stats) + .filter(([k, v]) => v.show) + .map(([k, v]) => v), + 'group' + ); + setIsLoading(false); setErrorMessage(undefined); - setStatsBarData(stats); + + setStatsBarData(statGroups[0]); + setRestStatsBarData(statGroups[1]); + setGroupsCount(count); setGroups(jobsGroups); loadOverallSwimLanes(jobsGroups); @@ -138,30 +170,52 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa ); - const panelClass = isLoading ? 'mlOverviewPanel__isLoading' : 'mlOverviewPanel'; - const noAdJobs = !errorMessage && isLoading === false && typeof errorMessage === 'undefined' && groupsCount === 0; - if (noAdJobs) { - return ; - } - return ( - + { + setPanelsState({ ...panelsState, adJobs: update }); + }} + header={ + + } + headerItems={[ + ...(statsBarData + ? [] + : []), + ...(restStatsBarData + ? [ + , + ] + : []), + + {i18n.translate('xpack.ml.overview.anomalyDetection.manageJobsButtonText', { + defaultMessage: 'Manage jobs', + })} + , + ]} + > + {noAdJobs ? : null} + {typeof errorMessage !== 'undefined' && errorDisplay} - {isLoading && } + + {isLoading ? : null} {isLoading === false && typeof errorMessage === 'undefined' && groupsCount > 0 ? ( - + ) : null} - + ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx index 016261be7997e..de4a05c638ce2 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx @@ -9,13 +9,8 @@ import React, { FC, useState } from 'react'; import { Direction, EuiBasicTableColumn, - EuiButton, - EuiFlexGroup, - EuiFlexItem, EuiIcon, EuiInMemoryTable, - EuiSpacer, - EuiText, EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -24,13 +19,10 @@ import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { formatHumanReadableDateTime } from '@kbn/ml-date-utils'; import { useGroupActions } from './actions'; import { Group, GroupsDictionary } from './anomaly_detection_panel'; -import { JobStatsBarStats, StatsBar } from '../../../components/stats_bar'; import { JobSelectorBadge } from '../../../components/job_selector/job_selector_badge'; import { toLocaleString } from '../../../util/string_utils'; import { SwimlaneContainer } from '../../../explorer/swimlane_container'; import { useTimeBuckets } from '../../../components/custom_hooks/use_time_buckets'; -import { ML_PAGES } from '../../../../../common/constants/locator'; -import { useMlLink } from '../../../contexts/kibana'; export enum AnomalyDetectionListColumns { id = 'id', @@ -44,11 +36,10 @@ export enum AnomalyDetectionListColumns { interface Props { items: GroupsDictionary; - statsBarData: JobStatsBarStats; chartsService: ChartsPluginStart; } -export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsService }) => { +export const AnomalyDetectionTable: FC = ({ items, chartsService }) => { const groupsList = Object.values(items); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -58,10 +49,6 @@ export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsSe const timeBuckets = useTimeBuckets(); - const manageJobsLink = useMlLink({ - page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, - }); - const columns: Array> = [ { field: AnomalyDetectionListColumns.id, @@ -195,47 +182,19 @@ export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsSe }; return ( - <> - - - -

- {i18n.translate('xpack.ml.overview.anomalyDetection.panelTitle', { - defaultMessage: 'Anomaly Detection', - })} -

-
-
- - - - - - - - {i18n.translate('xpack.ml.overview.anomalyDetection.manageJobsButtonText', { - defaultMessage: 'Manage jobs', - })} - - - - -
- - - allowNeutralSort={false} - className="mlAnomalyDetectionTable" - columns={columns} - hasActions={true} - isExpandable={false} - isSelectable={false} - items={groupsList} - itemId={AnomalyDetectionListColumns.id} - onTableChange={onTableChange} - pagination={pagination} - sorting={sorting} - data-test-subj="mlOverviewTableAnomalyDetection" - /> - + + allowNeutralSort={false} + className="mlAnomalyDetectionTable" + columns={columns} + hasActions={true} + isExpandable={false} + isSelectable={false} + items={groupsList} + itemId={AnomalyDetectionListColumns.id} + onTableChange={onTableChange} + pagination={pagination} + sorting={sorting} + data-test-subj="mlOverviewTableAnomalyDetection" + /> ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts index fa2e83151b80f..a5a864e139eae 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts @@ -76,40 +76,45 @@ export function getGroupsFromJobs(jobs: MlSummaryJobs): { export function getStatsBarData(jobsList: any) { const jobStats = { - activeNodes: { - label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML nodes', - }), - value: 0, - show: true, - }, total: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.totalJobsLabel', { - defaultMessage: 'Total jobs', + defaultMessage: 'Total', }), value: 0, show: true, + group: 0, }, open: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.openJobsLabel', { - defaultMessage: 'Open jobs', + defaultMessage: 'Open', }), value: 0, show: true, + group: 0, }, closed: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.closedJobsLabel', { - defaultMessage: 'Closed jobs', + defaultMessage: 'Closed', }), value: 0, show: true, + group: 0, }, failed: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.failedJobsLabel', { - defaultMessage: 'Failed jobs', + defaultMessage: 'Failed', }), value: 0, show: false, + group: 0, + }, + activeNodes: { + label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { + defaultMessage: 'Active ML nodes', + }), + value: 0, + show: true, + group: 1, }, activeDatafeeds: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeDatafeedsLabel', { @@ -117,6 +122,7 @@ export function getStatsBarData(jobsList: any) { }), value: 0, show: true, + group: 1, }, }; @@ -158,5 +164,13 @@ export function getStatsBarData(jobsList: any) { jobStats.activeNodes.value = Object.keys(mlNodes).length; + if (jobStats.total.value === 0) { + for (const [statKey, val] of Object.entries(jobStats)) { + if (statKey !== 'total') { + val.show = false; + } + } + } + return jobStats; } diff --git a/x-pack/plugins/ml/public/application/overview/overview_page.tsx b/x-pack/plugins/ml/public/application/overview/overview_page.tsx index 4f7244c37f298..6772125bb9532 100644 --- a/x-pack/plugins/ml/public/application/overview/overview_page.tsx +++ b/x-pack/plugins/ml/public/application/overview/overview_page.tsx @@ -6,9 +6,15 @@ */ import React, { FC, useState } from 'react'; -import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker'; +import { useStorage } from '@kbn/ml-local-storage'; +import { OverviewStatsBar } from '../components/collapsible_panel/collapsible_panel'; +import { ML_PAGES } from '../../../common/constants/locator'; +import { ML_OVERVIEW_PANELS, MlStorageKey, TMlStorageMapped } from '../../../common/types/storage'; +import { CollapsiblePanel } from '../components/collapsible_panel'; import { usePermissionCheck } from '../capabilities/check_capabilities'; import { mlNodesAvailable } from '../ml_nodes_check'; import { OverviewContent } from './components/content'; @@ -17,11 +23,18 @@ import { JobsAwaitingNodeWarning } from '../components/jobs_awaiting_node_warnin import { SavedObjectsWarning } from '../components/saved_objects_warning'; import { UpgradeWarning } from '../components/upgrade'; import { HelpMenu } from '../components/help_menu'; -import { useMlKibana } from '../contexts/kibana'; +import { useMlKibana, useMlLink } from '../contexts/kibana'; import { NodesList } from '../memory_usage/nodes_overview'; import { MlPageHeader } from '../components/page_header'; import { PageTitle } from '../components/page_title'; import { useIsServerless } from '../contexts/kibana/use_is_serverless'; +import { getMlNodesCount } from '../ml_nodes_check/check_ml_nodes'; + +export const overviewPanelDefaultState = Object.freeze({ + nodes: true, + adJobs: true, + dfaJobs: true, +}); export const OverviewPage: FC = () => { const serverless = useIsServerless(); @@ -33,11 +46,20 @@ export const OverviewPage: FC = () => { } = useMlKibana(); const helpLink = docLinks.links.ml.guide; + const viewNodesLink = useMlLink({ + page: ML_PAGES.MEMORY_USAGE, + }); + const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true }); const [adLazyJobCount, setAdLazyJobCount] = useState(0); const [dfaLazyJobCount, setDfaLazyJobCount] = useState(0); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + return (
@@ -63,9 +85,36 @@ export const OverviewPage: FC = () => { {canViewMlNodes && serverless === false ? ( <> - + { + setPanelsState({ ...panelsState, nodes: update }); + }} + header={ + + } + headerItems={[ + , + + {i18n.translate('xpack.ml.overview.nodesPanel.viewNodeLink', { + defaultMessage: 'View nodes', + })} + , + ]} + > - + ) : null} diff --git a/x-pack/plugins/ml/public/application/routing/use_resolver.tsx b/x-pack/plugins/ml/public/application/routing/use_resolver.tsx index eb3586c15c05f..5c67ed9769426 100644 --- a/x-pack/plugins/ml/public/application/routing/use_resolver.tsx +++ b/x-pack/plugins/ml/public/application/routing/use_resolver.tsx @@ -35,7 +35,6 @@ export const useRouteResolver = ( ): { context: RouteResolverContext; results: ResolverResults; - component?: React.Component; } => { const requiredCapabilitiesRef = useRef(requiredCapabilities); const customResolversRef = useRef(customResolvers); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 84582899a6d4b..493218761bd72 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -22521,7 +22521,6 @@ "xpack.ml.notifications.newNotificationsMessage": "Il y a eu {newNotificationsCount, plural, one {# notification} many {# notifications} other {# notifications}} depuis {sinceDate}. Actualisez la page pour afficher les mises à jour.", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "Il y a eu {count, plural, one {# notification} many {# notifications} other {# notifications}} avec un niveau d'avertissement ou d'erreur depuis {lastCheckedAt}", "xpack.ml.notificationsIndicator.unreadLabel": "Vous avez des notifications non lues depuis {lastCheckedAt}", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "Avant de créer une tâche d'analyse du cadre de données, utilisez des {transforms} pour créer une {sourcedata}.", "xpack.ml.previewAlert.otherValuesLabel": "et {count, plural, one {# autre} many {# autres} other {# autres}}", "xpack.ml.previewAlert.previewMessage": "{alertsCount, plural, one {# anomalie a été trouvée} many {# anomalies ont été trouvées} other {# anomalies ont été trouvées}} au cours des dernières {interval}.", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message} Veuillez contacter votre administrateur.", @@ -22898,7 +22897,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "couloir d'anomalie ajouté", "xpack.ml.changePointDetection.pageHeader": "Modifier la détection du point", "xpack.ml.chrome.help.appName": "Machine Learning", - "xpack.ml.common.learnMoreQuestion": "Envie d'en savoir plus ?", "xpack.ml.common.readDocumentationLink": "Lire la documentation", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "Bleu", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "Vert – Rouge", @@ -24525,7 +24523,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "Aucune anomalie n'a été trouvée", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "Résultat introuvable", "xpack.ml.overview.anomalyDetection.overallScore": "Score général", - "xpack.ml.overview.anomalyDetection.panelTitle": "Détection des anomalies", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "Afficher les tâches", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "Afficher dans l’Explorateur d'anomalies", "xpack.ml.overview.anomalyDetection.tableActionLabel": "Actions", @@ -24539,8 +24536,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "Valeurs typiques dans les résultats d'enregistrement des anomalies.", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "Afficher les tâches", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "Afficher dans l’Explorateur d'anomalies", - "xpack.ml.overview.gettingStartedSectionSourceData": "ensemble de données source centré sur les entités", - "xpack.ml.overview.gettingStartedSectionTransforms": "transformations", "xpack.ml.overview.notificationsLabel": "Notifications", "xpack.ml.overview.overviewLabel": "Aperçu", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "Échoué", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3eece16c9098d..e4f5c1867869e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22512,7 +22512,6 @@ "xpack.ml.notifications.newNotificationsMessage": "{sinceDate}以降に{newNotificationsCount, plural, other {#件の通知があります}}。更新を表示するには、ページを更新してください。", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "{lastCheckedAt}以降にエラーまたは警告レベルの{count, plural, other {#件の通知があります}}", "xpack.ml.notificationsIndicator.unreadLabel": "{lastCheckedAt}以降に未読の通知があります", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "データフレーム分析ジョブを構築する前に、{transforms}を使用して{sourcedata}を作成してください。", "xpack.ml.previewAlert.otherValuesLabel": "および{count, plural, other {#個のその他}}", "xpack.ml.previewAlert.previewMessage": "過去{interval}に{alertsCount, plural, other {#個の異常}}が見つかりました。", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message} 管理者にお問い合わせください。", @@ -22884,7 +22883,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "追加された異常スイムレーン", "xpack.ml.changePointDetection.pageHeader": "変化点検出", "xpack.ml.chrome.help.appName": "機械学習", - "xpack.ml.common.learnMoreQuestion": "詳細について", "xpack.ml.common.readDocumentationLink": "ドキュメンテーションを表示", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "青", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "緑 - 赤", @@ -24511,7 +24509,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "異常値が見つかりませんでした", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "結果が見つかりませんでした", "xpack.ml.overview.anomalyDetection.overallScore": "全体スコア", - "xpack.ml.overview.anomalyDetection.panelTitle": "異常検知", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "ジョブを表示", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "異常エクスプローラーで表示", "xpack.ml.overview.anomalyDetection.tableActionLabel": "アクション", @@ -24525,8 +24522,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "異常レコード結果の標準的な値。", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "ジョブを表示", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "異常エクスプローラーで表示", - "xpack.ml.overview.gettingStartedSectionSourceData": "エンティティ中心のソースデータセット", - "xpack.ml.overview.gettingStartedSectionTransforms": "トランスフォーム", "xpack.ml.overview.notificationsLabel": "通知", "xpack.ml.overview.overviewLabel": "概要", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失敗", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 41d6320f76e0a..ecd9de2def365 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22511,7 +22511,6 @@ "xpack.ml.notifications.newNotificationsMessage": "自 {sinceDate}以来有 {newNotificationsCount, plural, other {# 个通知}}。刷新页面以查看更新。", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "自 {lastCheckedAt}以来有 {count, plural, other {# 个通知}}包含错误或警告级别", "xpack.ml.notificationsIndicator.unreadLabel": "自 {lastCheckedAt}以来您有未计通知", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "构建数据帧分析作业之前,请使用 {transforms} 构造一个 {sourcedata}。", "xpack.ml.previewAlert.otherValuesLabel": "和{count, plural, other {另外 # 个}}", "xpack.ml.previewAlert.previewMessage": "在过去 {interval}找到 {alertsCount, plural, other {# 个异常}}。", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message}请联系您的管理员。", @@ -22883,7 +22882,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "已添加异常泳道", "xpack.ml.changePointDetection.pageHeader": "更改点检测", "xpack.ml.chrome.help.appName": "Machine Learning", - "xpack.ml.common.learnMoreQuestion": "希望了解详情?", "xpack.ml.common.readDocumentationLink": "阅读文档", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "蓝", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "绿 - 红", @@ -24510,7 +24508,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "找不到异常", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "找不到结果", "xpack.ml.overview.anomalyDetection.overallScore": "总分", - "xpack.ml.overview.anomalyDetection.panelTitle": "异常检测", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "查看作业", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "在 Anomaly Explorer 中查看", "xpack.ml.overview.anomalyDetection.tableActionLabel": "操作", @@ -24524,8 +24521,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "异常记录结果中的典型值。", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "查看作业", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "在 Anomaly Explorer 中查看", - "xpack.ml.overview.gettingStartedSectionSourceData": "实体中心型源数据集", - "xpack.ml.overview.gettingStartedSectionTransforms": "转换", "xpack.ml.overview.notificationsLabel": "通知", "xpack.ml.overview.overviewLabel": "概览", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失败", From 3a34200afdec9389adda86eb6f395f507d5cd17b Mon Sep 17 00:00:00 2001 From: mohamedhamed-ahmed Date: Tue, 20 Jun 2023 13:16:39 +0100 Subject: [PATCH 2/8] [Infra UI] Add 'host.name' field view for 'Logs' tab (#159561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #159560 ## 📝 Summary This PR adds an inline view for the logs tab in the Hosts view, where the `host.name` column is statically added to the columns definition. ## ✅ Testing 1. Navigate to Stream page 2. Change the settings to have any Date Source and columns 3. Navigate to the logs tab in the Hosts view 4. Verify that the columns applied in step 2 aren't showing in the logs, and that `host.name` is showing. 5. Click the open in logs link and make sure that the settings show the host.name and notes an inline view is being used. https://github.com/elastic/kibana/assets/11225826/a19f7969-31b3-40af-9e07-784631d6292f --- .../tabs/logs/logs_link_to_stream.tsx | 5 +- .../components/tabs/logs/logs_tab_content.tsx | 65 +++++++++++++++++-- .../test/functional/apps/infra/hosts_view.ts | 8 +++ .../page_objects/infra_hosts_view.ts | 7 ++ 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx index f5c2101317f01..ac3981026ea8e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_link_to_stream.tsx @@ -8,15 +8,17 @@ import React from 'react'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { LogViewReference } from '../../../../../../../common/log_views'; import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana'; interface LogsLinkToStreamProps { startTime: number; endTime: number; query: string; + logView: LogViewReference; } -export const LogsLinkToStream = ({ startTime, endTime, query }: LogsLinkToStreamProps) => { +export const LogsLinkToStream = ({ startTime, endTime, query, logView }: LogsLinkToStreamProps) => { const { services } = useKibanaContextForPlugin(); const { locators } = services; @@ -30,6 +32,7 @@ export const LogsLinkToStream = ({ startTime, endTime, query }: LogsLinkToStream endTime, }, filter: query, + logView, })} data-test-subj="hostsView-logs-link-to-stream-button" iconType="popout" diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx index db93a9a4617cc..55a8410618b28 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx @@ -5,9 +5,15 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { DEFAULT_LOG_VIEW } from '../../../../../../../common/log_views'; +import type { + LogIndexReference, + LogViewReference, +} from '../../../../../../../common/log_views/types'; +import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana'; import { InfraLoadingPanel } from '../../../../../../components/loading'; import { LogStream } from '../../../../../../components/log_stream'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; @@ -23,11 +29,57 @@ export const LogsTabContent = () => { const { from, to } = useMemo(() => getDateRangeAsTimestamp(), [getDateRangeAsTimestamp]); const { hostNodes, loading } = useHostsViewContext(); + const [logViewIndices, setLogViewIndices] = useState(); + + const { + services: { + logViews: { client }, + }, + } = useKibanaContextForPlugin(); + + useEffect(() => { + const getLogView = async () => { + const { attributes } = await client.getLogView(DEFAULT_LOG_VIEW); + setLogViewIndices(attributes.logIndices); + }; + getLogView(); + }, [client, setLogViewIndices]); + const hostsFilterQuery = useMemo( () => createHostsFilter(hostNodes.map((p) => p.name)), [hostNodes] ); + const logView: LogViewReference = useMemo(() => { + return { + type: 'log-view-inline', + id: 'hosts-logs-view', + attributes: { + name: 'Hosts Logs View', + description: 'Default view for hosts logs tab', + logIndices: logViewIndices!, + logColumns: [ + { + timestampColumn: { + id: '5e7f964a-be8a-40d8-88d2-fbcfbdca0e2f', + }, + }, + { + fieldColumn: { + id: 'eb9777a8-fcd3-420e-ba7d-172fff6da7a2', + field: 'host.name', + }, + }, + { + messageColumn: { + id: 'b645d6da-824b-4723-9a2a-e8cece1645c0', + }, + }, + ], + }, + }; + }, [logViewIndices]); + const logsLinkToStreamQuery = useMemo(() => { const hostsFilterQueryParam = createHostsFilterQueryParam(hostNodes.map((p) => p.name)); @@ -38,7 +90,7 @@ export const LogsTabContent = () => { return filterQuery.query || hostsFilterQueryParam; }, [filterQuery.query, hostNodes]); - if (loading) { + if (loading || !logViewIndices) { return ( @@ -64,14 +116,19 @@ export const LogsTabContent = () => { - + { it('should load the Logs tab section when clicking on it', async () => { await testSubjects.existOrFail('hostsView-logs'); }); + + it('should load the Logs tab with the right columns', async () => { + await retry.try(async () => { + const columnLabels = await pageObjects.infraHostsView.getLogsTableColumnHeaders(); + + expect(columnLabels).to.eql(['Timestamp', 'host.name', 'Message']); + }); + }); }); describe('Alerts Tab', () => { diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index b9219b53fda2d..c2982ae77f211 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -214,6 +214,13 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return container.findAllByCssSelector('[data-test-subj*=streamEntry]'); }, + async getLogsTableColumnHeaders() { + const columnHeaderElements: WebElementWrapper[] = await testSubjects.findAll( + '~logColumnHeader' + ); + return await Promise.all(columnHeaderElements.map((element) => element.getVisibleText())); + }, + // Alerts Tab getAlertsTab() { return testSubjects.find('hostsView-tabs-alerts'); From ee6f0f773f507f937d9e415359148d8a7c17dc8b Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 20 Jun 2023 14:20:18 +0200 Subject: [PATCH 3/8] [Infrastructure UI] Propagate Kibana version to hosts view feedback form (#159210) closes: [#1053](https://github.com/elastic/obs-infraobs-team/issues/1053) ## Summary This PR changes the Hosts View feedback button to pass also the current Kibana version. image The URL is no longer a shortened one, and that's because it wasn't forwarding query parameters. ### How to test - Start a local Kibana - Navigate to `Infrastructure > Hosts` - Click on "Tell us what you think" button - On the forms, the question "What version of Elastic are you using?" should be filled automatically with the Kibana version --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/pages/metrics/hosts/index.tsx | 19 +++++++++++++++++-- x-pack/plugins/infra/public/plugin.ts | 11 +++++++++-- x-pack/plugins/infra/public/types.ts | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx index 053de350a8a0d..35d0ca77ce23b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/index.tsx @@ -12,6 +12,7 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { SourceErrorPage } from '../../../components/source_error_page'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; @@ -25,10 +26,24 @@ import { HostContainer } from './components/hosts_container'; import { BetaBadge } from '../../../components/beta_badge'; import { NoRemoteCluster } from '../../../components/empty_states'; -const HOSTS_FEEDBACK_LINK = 'https://ela.st/host-feedback'; +const HOSTS_FEEDBACK_LINK = + 'https://docs.google.com/forms/d/e/1FAIpQLScRHG8TIVb1Oq8ZhD4aks3P1TmgiM58TY123QpDCcBz83YC6w/viewform'; +const KIBANA_VERSION_QUERY_PARAM = 'entry.548460210'; + +const getHostFeedbackURL = (kibanaVersion?: string) => { + const url = new URL(HOSTS_FEEDBACK_LINK); + if (kibanaVersion) { + url.searchParams.append(KIBANA_VERSION_QUERY_PARAM, kibanaVersion); + } + + return url.href; +}; export const HostsPage = () => { const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); + const { + services: { kibanaVersion }, + } = useKibanaContextForPlugin(); useTrackPageview({ app: 'infra_metrics', path: 'hosts' }); useTrackPageview({ app: 'infra_metrics', path: 'hosts', delay: 15000 }); @@ -83,7 +98,7 @@ export const HostsPage = () => { rightSideItems: [ (() => ({})); constructor(context: PluginInitializerContext) { @@ -74,6 +75,7 @@ export class Plugin implements InfraClientPluginClass { this.metricsExplorerViews = new MetricsExplorerViewsService(); this.telemetry = new TelemetryService(); this.appTarget = this.config.logs.app_target; + this.kibanaVersion = context.env.packageInfo.version; } setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) { @@ -286,10 +288,15 @@ export class Plugin implements InfraClientPluginClass { deepLinks: infraDeepLinks, mount: async (params: AppMountParameters) => { // mount callback should not use setup dependencies, get start dependencies instead - const [coreStart, pluginsStart, pluginStart] = await core.getStartServices(); + const [coreStart, plugins, pluginStart] = await core.getStartServices(); const { renderApp } = await import('./apps/metrics_app'); - return renderApp(coreStart, pluginsStart, pluginStart, params); + return renderApp( + coreStart, + { ...plugins, kibanaVersion: this.kibanaVersion }, + pluginStart, + params + ); }, }); diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index fb1d2d4ab2a91..15d4f4ea5fb6e 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -93,6 +93,7 @@ export interface InfraClientStartDeps { dataViews: DataViewsPublicPluginStart; discover: DiscoverStart; embeddable?: EmbeddableStart; + kibanaVersion?: string; lens: LensPublicStart; ml: MlPluginStart; observability: ObservabilityPublicStart; From 66e87e63e98f8f092a22a304bc6149588891181a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Tue, 20 Jun 2023 14:54:26 +0200 Subject: [PATCH 4/8] [Fleet][Agent Tampering] Remove unused `created_at` field from uninstall token SO mapping (#159985) ## Summary `created_at` field was added to the mapping for the uninstall token Saved Object, but it's not used and causes trouble. ~There is a discussion whether to remove it from the mapping or not, before the recently added mapping itself is released with v8.9.0, so I prepared this PR to merge in case we want to remove it.~ The discussion ended with the decision to remove the field, so the aim is to merge this PR. --- packages/kbn-check-mappings-update-cli/current_mappings.json | 3 --- .../migrations/group2/check_registered_types.test.ts | 2 +- x-pack/plugins/fleet/server/saved_objects/index.ts | 1 - .../server/services/security/uninstall_token_service/index.ts | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 1e0ac8548906e..525cbba463d2d 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1898,9 +1898,6 @@ "fleet-uninstall-tokens": { "dynamic": false, "properties": { - "created_at": { - "type": "date" - }, "policy_id": { "type": "keyword" }, diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index cd7ef7ea94031..9859886cfd6d5 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -96,7 +96,7 @@ describe('checking migration metadata changes on all registered SO types', () => "fleet-message-signing-keys": "93421f43fed2526b59092a4e3c65d64bc2266c0f", "fleet-preconfiguration-deletion-record": "c52ea1e13c919afe8a5e8e3adbb7080980ecc08e", "fleet-proxy": "6cb688f0d2dd856400c1dbc998b28704ff70363d", - "fleet-uninstall-tokens": "d25a8aedb522d2b839ab0950160777528122070f", + "fleet-uninstall-tokens": "ed8aa37e3cdd69e4360709e64944bb81cae0c025", "graph-workspace": "5cc6bb1455b078fd848c37324672163f09b5e376", "guided-onboarding-guide-state": "d338972ed887ac480c09a1a7fbf582d6a3827c91", "guided-onboarding-plugin-state": "bc109e5ef46ca594fdc179eda15f3095ca0a37a4", diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index c606994edf185..48a997a180365 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -410,7 +410,6 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ mappings: { dynamic: false, properties: { - created_at: { type: 'date' }, policy_id: { type: 'keyword' }, token_plain: { type: 'keyword' }, }, diff --git a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts index 013f77f3005f3..40f0768161368 100644 --- a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts +++ b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts @@ -152,7 +152,7 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { latest: { top_hits: { size: 1, - sort: [{ [`${UNINSTALL_TOKENS_SAVED_OBJECT_TYPE}.created_at`]: { order: 'desc' } }], + sort: [{ created_at: { order: 'desc' } }], }, }, }, From c0e43dbf2834ea526ef43cd5d366e19434cd1cdc Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 20 Jun 2023 16:02:48 +0300 Subject: [PATCH 5/8] [Lens][Visualize] Removes wrong padding on the dashboard (#159992) ## Summary Closes https://github.com/elastic/kibana/issues/159942 If the height of a partition chart exceeds 1000px paddings are added, reducing the chart size. This is caused due to this piece of code https://github.com/elastic/kibana/pull/122420 This was added for the aggbased editor to reduce a bit the pie size (otherwise it was taking the full container size and the pie was huge) Although we want this, we don't want this to be applied in dashboards or lens editor. This PR is fixing this by adding the paddings only on the agg based editor level In agg based editor image Dashboard with very tall treemap, no paddings image ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- src/plugins/chart_expressions/common/index.ts | 7 +- .../chart_expressions/common/utils.test.ts | 76 ++++++++++++++++++- src/plugins/chart_expressions/common/utils.ts | 25 ++++++ .../partition_vis_component.test.tsx | 1 + .../components/partition_vis_component.tsx | 23 ++++-- .../partition_vis_renderer.tsx | 9 ++- .../public/utils/get_partition_theme.test.ts | 23 ++++-- .../public/utils/get_partition_theme.ts | 8 +- 8 files changed, 155 insertions(+), 17 deletions(-) diff --git a/src/plugins/chart_expressions/common/index.ts b/src/plugins/chart_expressions/common/index.ts index 4373260657909..0983b1ed28d4d 100644 --- a/src/plugins/chart_expressions/common/index.ts +++ b/src/plugins/chart_expressions/common/index.ts @@ -6,5 +6,10 @@ * Side Public License, v 1. */ -export { extractContainerType, extractVisualizationType, getOverridesFor } from './utils'; +export { + extractContainerType, + extractVisualizationType, + getOverridesFor, + isOnAggBasedEditor, +} from './utils'; export type { Simplify, MakeOverridesSerializable } from './types'; diff --git a/src/plugins/chart_expressions/common/utils.test.ts b/src/plugins/chart_expressions/common/utils.test.ts index 2ed71e9a17b92..48519c9f6f1a9 100644 --- a/src/plugins/chart_expressions/common/utils.test.ts +++ b/src/plugins/chart_expressions/common/utils.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { getOverridesFor } from './utils'; +import { getOverridesFor, isOnAggBasedEditor } from './utils'; describe('Overrides utilities', () => { describe('getOverridesFor', () => { @@ -31,3 +31,77 @@ describe('Overrides utilities', () => { }); }); }); + +describe('isOnAggBasedEditor', () => { + it('should return false if is on dashboard', () => { + const context = { + type: 'dashboard', + description: 'test', + child: { + type: 'lens', + name: 'lnsPie', + id: 'd8bb29a7-13a4-43fa-a162-d7705050bb6c', + description: 'test', + url: '/gdu/app/lens#/edit_by_value', + }, + }; + expect(isOnAggBasedEditor(context)).toEqual(false); + }); + + it('should return false if is on editor but lens', () => { + const context = { + type: 'application', + description: 'test', + child: { + type: 'lens', + name: 'lnsPie', + id: 'd8bb29a7-13a4-43fa-a162-d7705050bb6c', + description: 'test', + url: '/gdu/app/lens#/edit_by_value', + }, + }; + expect(isOnAggBasedEditor(context)).toEqual(false); + }); + + it('should return false if is on dashboard but agg_based', () => { + const context = { + type: 'dashboard', + description: 'test', + child: { + type: 'agg_based', + name: 'pie', + id: 'd8bb29a7-13a4-43fa-a162-d7705050bb6c', + description: 'test', + url: '', + }, + }; + expect(isOnAggBasedEditor(context)).toEqual(false); + }); + + it('should return true if is on editor but agg_based', () => { + const context = { + type: 'application', + description: 'test', + child: { + type: 'agg_based', + name: 'pie', + id: 'd8bb29a7-13a4-43fa-a162-d7705050bb6c', + description: 'test', + url: '', + }, + }; + expect(isOnAggBasedEditor(context)).toEqual(true); + }); + + it('should return false if child is missing', () => { + const context = { + type: 'application', + description: 'test', + }; + expect(isOnAggBasedEditor(context)).toEqual(false); + }); + + it('should return false if context is missing', () => { + expect(isOnAggBasedEditor()).toEqual(false); + }); +}); diff --git a/src/plugins/chart_expressions/common/utils.ts b/src/plugins/chart_expressions/common/utils.ts index 2966532c44117..db2e564efc4b3 100644 --- a/src/plugins/chart_expressions/common/utils.ts +++ b/src/plugins/chart_expressions/common/utils.ts @@ -20,6 +20,31 @@ export const extractContainerType = (context?: KibanaExecutionContext): string | } }; +/* Function to identify if the pie is rendered inside the aggBased editor + Context comes with this format + { + type: 'dashboard', // application for lens, agg based charts + description: 'test', + child: { + type: 'lens', // agg_based for legacy editor + name: 'pie', + id: 'id', + description: 'test', + url: '', + }, + }; */ +export const isOnAggBasedEditor = (context?: KibanaExecutionContext): boolean => { + if (context) { + return Boolean( + context.type && + context.type === 'application' && + context.child && + context.child.type === 'agg_based' + ); + } + return false; +}; + export const extractVisualizationType = (context?: KibanaExecutionContext): string | undefined => { if (context) { const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => { diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx index 5eb48cfab6cd5..c935ce847e40e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx @@ -83,6 +83,7 @@ describe('PartitionVisComponent', function () { data: dataPluginMock.createStartContract(), fieldFormats: fieldFormatsServiceMock.createStartContract(), }, + hasOpenedOnAggBasedEditor: false, }; }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 94590ff164555..4ce300a7d9bb9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -93,6 +93,7 @@ export type PartitionVisComponentProps = Omit< palettesRegistry: PaletteRegistry; services: Pick; columnCellValueActions: ColumnCellValueActions; + hasOpenedOnAggBasedEditor: boolean; }; const PartitionVisComponent = (props: PartitionVisComponentProps) => { @@ -105,6 +106,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { syncColors, interactive, overrides, + hasOpenedOnAggBasedEditor, } = props; const visParams = useMemo(() => filterOutConfig(visType, preVisParams), [preVisParams, visType]); const chartTheme = props.chartsThemeService.useChartsTheme(); @@ -148,7 +150,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { const [showLegend, setShowLegend] = useState(() => showLegendDefault()); const showToggleLegendElement = props.uiState !== undefined; - + const [chartIsLoaded, setChartIsLoaded] = useState(false); const [containerDimensions, setContainerDimensions] = useState< undefined | PieContainerDimensions >(); @@ -156,12 +158,14 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { const parentRef = useRef(null); useEffect(() => { - if (parentRef && parentRef.current) { + // chart should be loaded to compute the dimensions + // otherwise the height is set to 0 + if (parentRef && parentRef.current && chartIsLoaded) { const parentHeight = parentRef.current!.getBoundingClientRect().height; const parentWidth = parentRef.current!.getBoundingClientRect().width; setContainerDimensions({ width: parentWidth, height: parentHeight }); } - }, [parentRef]); + }, [chartIsLoaded, parentRef]); useEffect(() => { const legendShow = showLegendDefault(); @@ -172,6 +176,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { (isRendered: boolean = true) => { if (isRendered) { props.renderComplete(); + setChartIsLoaded(true); } }, [props] @@ -363,8 +368,16 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { ) as Partial; const themeOverrides = useMemo( - () => getPartitionTheme(visType, visParams, chartTheme, containerDimensions, rescaleFactor), - [visType, visParams, chartTheme, containerDimensions, rescaleFactor] + () => + getPartitionTheme( + visType, + visParams, + chartTheme, + containerDimensions, + rescaleFactor, + hasOpenedOnAggBasedEditor + ), + [visType, visParams, chartTheme, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor] ); const fixedViewPort = document.getElementById('app-fixed-viewport'); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx index 056ba6b7136ce..2379096796639 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/expression_renderers/partition_vis_renderer.tsx @@ -21,7 +21,11 @@ import { withSuspense } from '@kbn/presentation-util-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { METRIC_TYPE } from '@kbn/analytics'; import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; -import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common'; +import { + extractContainerType, + extractVisualizationType, + isOnAggBasedEditor, +} from '@kbn/chart-expressions-common'; import { VisTypePieDependencies } from '../plugin'; import { PARTITION_VIS_RENDERER_NAME } from '../../common/constants'; import { CellValueAction, GetCompatibleCellValueActions } from '../types'; @@ -110,6 +114,8 @@ export const getPartitionVisRenderer: ( plugins.charts.palettes.getPalettes(), ]); + const hasOpenedOnAggBasedEditor = isOnAggBasedEditor(handlers.getExecutionContext()); + render( @@ -128,6 +134,7 @@ export const getPartitionVisRenderer: ( syncColors={syncColors} columnCellValueActions={columnCellValueActions} overrides={overrides} + hasOpenedOnAggBasedEditor={hasOpenedOnAggBasedEditor} />
diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.test.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.test.ts index 345d6ce068d0c..31d025ac0310f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.test.ts @@ -144,9 +144,16 @@ const runPieDonutWaffleTestSuites = (chartType: ChartTypes, visParams: Partition }); }); - it('should return adjusted padding settings if dimensions are specified', () => { + it('should return adjusted padding settings if dimensions are specified and is on aggBased editor', () => { const specifiedDimensions = { width: 2000, height: 2000 }; - const theme = getPartitionTheme(chartType, visParams, chartTheme, specifiedDimensions); + const theme = getPartitionTheme( + chartType, + visParams, + chartTheme, + specifiedDimensions, + undefined, + true + ); expect(theme).toEqual({ ...getStaticThemeOptions(chartTheme, visParams), @@ -233,7 +240,6 @@ const runPieDonutWaffleTestSuites = (chartType: ChartTypes, visParams: Partition expect(theme).toEqual({ ...getStaticThemeOptions(chartTheme, visParams), - chartPaddings: { top: 500, bottom: 500, left: 500, right: 500 }, partition: { ...getStaticThemePartition(chartTheme, visParams), outerSizeRatio: rescaleFactor, @@ -263,7 +269,6 @@ const runPieDonutWaffleTestSuites = (chartType: ChartTypes, visParams: Partition expect(theme).toEqual({ ...getStaticThemeOptions(chartTheme, vParams), - chartPaddings: { top: 500, bottom: 500, left: 500, right: 500 }, partition: { ...getStaticThemePartition(chartTheme, vParams), outerSizeRatio: 0.5, @@ -285,7 +290,6 @@ const runPieDonutWaffleTestSuites = (chartType: ChartTypes, visParams: Partition expect(theme).toEqual({ ...getStaticThemeOptions(chartTheme, vParams), - chartPaddings: { top: 500, bottom: 500, left: 500, right: 500 }, partition: { ...getStaticThemePartition(chartTheme, vParams), linkLabel: linkLabelWithEnoughSpace(vParams), @@ -420,7 +424,14 @@ const runTreemapMosaicTestSuites = (chartType: ChartTypes, visParams: PartitionV it('should return fullfilled padding settings if dimensions are specified', () => { const specifiedDimensions = { width: 2000, height: 2000 }; - const theme = getPartitionTheme(chartType, visParams, chartTheme, specifiedDimensions); + const theme = getPartitionTheme( + chartType, + visParams, + chartTheme, + specifiedDimensions, + undefined, + true + ); expect(theme).toEqual({ ...getStaticThemeOptions(chartTheme, visParams), diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.ts b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.ts index edb1aaea64aad..3714cac911829 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/utils/get_partition_theme.ts @@ -26,7 +26,8 @@ type GetThemeFn = ( visParams: PartitionVisParams, chartTheme: RecursivePartial, dimensions?: PieContainerDimensions, - rescaleFactor?: number + rescaleFactor?: number, + hasOpenedOnAggBasedEditor?: boolean ) => PartialTheme; type GetPieDonutWaffleThemeFn = ( @@ -118,12 +119,13 @@ export const getPartitionTheme: GetThemeFn = ( visParams, chartTheme, dimensions, - rescaleFactor = 1 + rescaleFactor = 1, + hasOpenedOnAggBasedEditor ) => { // On small multiples we want the labels to only appear inside const isSplitChart = Boolean(visParams.dimensions.splitColumn || visParams.dimensions.splitRow); const paddingProps: PartialTheme | null = - dimensions && !isSplitChart + dimensions && !isSplitChart && hasOpenedOnAggBasedEditor ? { chartPaddings: { top: ((1 - Math.min(1, MAX_SIZE / dimensions?.height)) / 2) * dimensions?.height, From bd0e09504c60bdf4c68b89cc8459f48fb30ccede Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 20 Jun 2023 09:11:56 -0400 Subject: [PATCH 6/8] [Fleet] Fix navigation from multi page layout (#159967) --- .../hooks/navigation.tsx | 57 ++++++++++++------- .../single_page_layout/hooks/form.tsx | 4 +- .../single_page_layout/index.test.tsx | 14 +++-- .../single_page_layout/index.tsx | 17 +++--- x-pack/plugins/fleet/tsconfig.json | 1 + 5 files changed, 59 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/hooks/navigation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/hooks/navigation.tsx index b908cb4908ade..097323962bc0c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/hooks/navigation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/hooks/navigation.tsx @@ -4,9 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { useCallback, useMemo, useEffect, useRef } from 'react'; -import { useHistory } from 'react-router-dom'; +import type { ApplicationStart } from '@kbn/core-application-browser'; +import { PLUGIN_ID } from '../../../../constants'; import { useStartServices, useLink, useIntraAppState } from '../../../../hooks'; import type { CreatePackagePolicyRouteState, @@ -67,7 +69,6 @@ export const useOnSaveNavigate = (params: UseOnSaveNavigateParams) => { const routeState = useIntraAppState(); const doOnSaveNavigation = useRef(true); const { getPath } = useLink(); - const history = useHistory(); const { application: { navigateToApp }, @@ -81,32 +82,46 @@ export const useOnSaveNavigate = (params: UseOnSaveNavigateParams) => { }, []); const onSaveNavigate = useCallback( - (policy?: PackagePolicy, paramsToApply: OnSaveQueryParamKeys[] = []) => { + (policy: PackagePolicy, paramsToApply: OnSaveQueryParamKeys[] = []) => { if (!doOnSaveNavigation.current) { return; } - const packagePolicyPath = getPath('policy_details', { policyId: packagePolicy.policy_id }); - if (routeState?.onSaveNavigateTo && policy) { - const [appId, options] = routeState.onSaveNavigateTo; - if (options?.path) { - const pathWithQueryString = appendOnSaveQueryParamsToPath({ - // In cases where we want to navigate back to a new/existing policy, we need to override the initial `path` - // value and navigate to the actual agent policy instead - path: queryParamsPolicyId ? packagePolicyPath : options.path, - policy, - mappingOptions: routeState.onSaveQueryParams, - paramsToApply, - }); - navigateToApp(appId, { ...options, path: pathWithQueryString }); - } else { - navigateToApp(...routeState.onSaveNavigateTo); - } + + const [onSaveNavigateTo, onSaveQueryParams]: [ + Parameters, + CreatePackagePolicyRouteState['onSaveQueryParams'] + ] = routeState?.onSaveNavigateTo + ? [routeState.onSaveNavigateTo, routeState?.onSaveQueryParams] + : [ + [ + PLUGIN_ID, + { + path: packagePolicyPath, + }, + ], + { + showAddAgentHelp: true, + openEnrollmentFlyout: true, + }, + ]; + + const [appId, options] = onSaveNavigateTo; + if (options?.path) { + const pathWithQueryString = appendOnSaveQueryParamsToPath({ + // In cases where we want to navigate back to a new/existing policy, we need to override the initial `path` + // value and navigate to the actual agent policy instead + path: queryParamsPolicyId ? packagePolicyPath : options.path, + policy, + mappingOptions: onSaveQueryParams, + paramsToApply, + }); + navigateToApp(appId, { ...options, path: pathWithQueryString }); } else { - history.push(packagePolicyPath); + navigateToApp(...onSaveNavigateTo); } }, - [packagePolicy.policy_id, getPath, navigateToApp, history, routeState, queryParamsPolicyId] + [packagePolicy.policy_id, getPath, navigateToApp, routeState, queryParamsPolicyId] ); return onSaveNavigate; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index cc01cadccb788..83b476b774913 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -233,10 +233,10 @@ export function useOnSubmit({ queryParamsPolicyId, }); - const navigateAddAgent = (policy?: PackagePolicy) => + const navigateAddAgent = (policy: PackagePolicy) => onSaveNavigate(policy, ['openEnrollmentFlyout']); - const navigateAddAgentHelp = (policy?: PackagePolicy) => + const navigateAddAgentHelp = (policy: PackagePolicy) => onSaveNavigate(policy, ['showAddAgentHelp']); const onSubmit = useCallback( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index e2f2ec5e908f7..458f4fab53384 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { useHistory } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import React from 'react'; import { fireEvent, act, waitFor } from '@testing-library/react'; @@ -127,6 +126,8 @@ describe('when on the package policy create page', () => { mockApiCalls(testRenderer.startServices.http); testRenderer.mountHistory.push(createPageUrlPath); + jest.mocked(useStartServices().application.navigateToApp).mockReset(); + mockPackageInfo = { data: { item: { @@ -339,12 +340,15 @@ describe('when on the package policy create page', () => { test('should navigate to save navigate path with query param if set', async () => { const routeState = { onSaveNavigateTo: [PLUGIN_ID, { path: '/save/url/here' }], + onSaveQueryParams: { + openEnrollmentFlyout: true, + }, }; const queryParamsPolicyId = 'agent-policy-1'; await setupSaveNavigate(routeState, queryParamsPolicyId); expect(useStartServices().application.navigateToApp).toHaveBeenCalledWith(PLUGIN_ID, { - path: '/policies/agent-policy-1', + path: '/policies/agent-policy-1?openEnrollmentFlyout=true', }); }); @@ -357,10 +361,12 @@ describe('when on the package policy create page', () => { expect(useStartServices().application.navigateToApp).toHaveBeenCalledWith(PLUGIN_ID); }); - test('should set history if no routeState', async () => { + test('should navigate to agent policy if no route state is set', async () => { await setupSaveNavigate({}); - expect(useHistory().push).toHaveBeenCalledWith('/policies/agent-policy-1'); + expect(useStartServices().application.navigateToApp).toHaveBeenCalledWith(PLUGIN_ID, { + path: '/policies/agent-policy-1?openEnrollmentFlyout=true', + }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 81c1c518ccd4f..fd62addd08391 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -402,13 +402,16 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onCancel={() => setFormState('VALID')} /> )} - {formState === 'SUBMITTED_NO_AGENTS' && agentPolicy && packageInfo && ( - navigateAddAgent(savedPackagePolicy)} - onCancel={() => navigateAddAgentHelp(savedPackagePolicy)} - /> - )} + {formState === 'SUBMITTED_NO_AGENTS' && + agentPolicy && + packageInfo && + savedPackagePolicy && ( + navigateAddAgent(savedPackagePolicy)} + onCancel={() => navigateAddAgentHelp(savedPackagePolicy)} + /> + )} {packageInfo && ( Date: Tue, 20 Jun 2023 15:16:26 +0200 Subject: [PATCH 7/8] [Enterprise Search] Prefix target index for access control syncs with "search-acl-filter-" (#159996) ## Summary Closes https://github.com/elastic/enterprise-search-team/issues/4979 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../plugins/enterprise_search/server/index.ts | 1 + .../server/lib/connectors/start_sync.test.ts | 10 +++++++--- .../server/lib/connectors/start_sync.ts | 18 ++++++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index 692d17d9952bc..bb4e1e2e0bf0e 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -52,3 +52,4 @@ export const CURRENT_CONNECTORS_INDEX = '.elastic-connectors-v1'; export const CONNECTORS_JOBS_INDEX = '.elastic-connectors-sync-jobs'; export const CONNECTORS_VERSION = 1; export const CRAWLERS_INDEX = '.ent-search-actastic-crawler2_configurations_v2'; +export const CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX = 'search-acl-filter-'; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index 6c69273e2e240..ca7ae9fc76a66 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -7,7 +7,11 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; +import { + CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, + CONNECTORS_INDEX, + CONNECTORS_JOBS_INDEX, +} from '../..'; import { SyncJobType, SyncStatus, TriggerMethod } from '../../../common/types/connectors'; import { ErrorCode } from '../../../common/types/error_codes'; @@ -311,7 +315,7 @@ describe('startSync lib function', () => { created_at: null, custom_scheduling: {}, error: null, - index_name: 'index_name', + index_name: 'search-index_name', language: null, last_access_control_sync_status: null, last_seen: null, @@ -345,7 +349,7 @@ describe('startSync lib function', () => { configuration: {}, filtering: null, id: 'connectorId', - index_name: 'index_name', + index_name: `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}index_name`, language: null, pipeline: null, service_type: null, diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index ff9e0419e69b0..8d2ac4715e8df 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -7,18 +7,22 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; +import { + CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX, + CONNECTORS_INDEX, + CONNECTORS_JOBS_INDEX, +} from '../..'; import { isConfigEntry } from '../../../common/connectors/is_category_entry'; import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../../common/constants'; import { - ConnectorSyncConfiguration, ConnectorDocument, + ConnectorSyncConfiguration, ConnectorSyncJobDocument, + SyncJobType, SyncStatus, TriggerMethod, - SyncJobType, } from '../../../common/types/connectors'; import { ErrorCode } from '../../../common/types/error_codes'; @@ -63,6 +67,12 @@ export const startConnectorSync = async ( }); } + const indexNameWithoutSearchPrefix = index_name.replace('search-', ''); + const targetIndexName = + jobType === SyncJobType.ACCESS_CONTROL + ? `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexNameWithoutSearchPrefix}` + : index_name; + return await client.asCurrentUser.index({ document: { cancelation_requested_at: null, @@ -72,7 +82,7 @@ export const startConnectorSync = async ( configuration, filtering: filtering ? filtering[0]?.active ?? null : null, id: connectorId, - index_name, + index_name: targetIndexName, language, pipeline: pipeline ?? null, service_type, From ca0b7395fba26142feab406971c3662063344b67 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 20 Jun 2023 14:25:48 +0100 Subject: [PATCH 8/8] [ML] File data visualizer moving import buttons (#159722) Relates to https://github.com/elastic/kibana/issues/112285 Removes the black bar at the bottom of the app which housed the import and cancel buttons. Rather than having a button called "Change import settings" I've left this as the "Back" button. "Change import settings" isn't really a correct description of the page the button returns you to. Something like "Rerun analysis of the already selected file" would be accurate, but that is too long for a button. @mdefazio Does this look ok? Any other suggestions? Note, I'd like to keep this PR small, the main aim was just to remove the black bar at the bottom of the page and change the name of the Cancel button after the import has already run. ![image](https://github.com/elastic/kibana/assets/22172091/9fc77009-6690-4a6f-9845-800d2486da23) ![image](https://github.com/elastic/kibana/assets/22172091/f986d0dd-1f3a-4cda-b715-e7a1a56e8cb6) ![image](https://github.com/elastic/kibana/assets/22172091/c24c9358-fa36-4e78-b5df-216c0dd3e734) --- .../components/bottom_bar/bottom_bar.tsx | 96 ------ .../components/bottom_bar/index.ts | 8 - .../file_data_visualizer_view.js | 58 +--- .../components/import_view/import_view.js | 284 ++++++++++-------- .../components/results_view/results_view.tsx | 46 ++- .../translations/translations/fr-FR.json | 5 - .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 8 files changed, 209 insertions(+), 298 deletions(-) delete mode 100644 x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/bottom_bar.tsx delete mode 100644 x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/index.ts diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/bottom_bar.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/bottom_bar.tsx deleted file mode 100644 index b19c02669f818..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/bottom_bar.tsx +++ /dev/null @@ -1,96 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiBottomBar, - EuiButton, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiToolTip, -} from '@elastic/eui'; - -import { MODE as DATAVISUALIZER_MODE } from '../file_data_visualizer_view/constants'; - -interface BottomBarProps { - mode: DATAVISUALIZER_MODE; - onChangeMode: (mode: DATAVISUALIZER_MODE) => void; - onCancel: () => void; - disableImport?: boolean; -} - -/** - * Bottom bar component for Data Visualizer page. - */ -export const BottomBar: FC = ({ mode, onChangeMode, onCancel, disableImport }) => { - if (mode === DATAVISUALIZER_MODE.READ) { - return ( - - - - - ) : null - } - > - onChangeMode(DATAVISUALIZER_MODE.IMPORT)} - data-test-subj="dataVisualizerFileOpenImportPageButton" - > - - - - - - onCancel()}> - - - - - - ); - } else { - return ( - - - - onChangeMode(DATAVISUALIZER_MODE.READ)}> - - - - - onCancel()}> - - - - - - ); - } -}; diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/index.ts b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/index.ts deleted file mode 100644 index 5291268824a62..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/bottom_bar/index.ts +++ /dev/null @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { BottomBar } from './bottom_bar'; diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js index b1378769efc92..dfa98a3713130 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js @@ -13,7 +13,6 @@ import { EuiSpacer } from '@elastic/eui'; import { isEqual } from 'lodash'; import { AboutPanel, LoadingPanel } from '../about_panel'; -import { BottomBar } from '../bottom_bar'; import { ResultsView } from '../results_view'; import { FileCouldNotBeRead, @@ -54,7 +53,6 @@ export class FileDataVisualizerView extends Component { mode: MODE.READ, isEditFlyoutVisible: false, isExplanationFlyoutVisible: false, - bottomBarVisible: false, hasPermissionToImport: false, fileCouldNotBeReadPermissionError: false, }; @@ -85,7 +83,6 @@ export class FileDataVisualizerView extends Component { this.setState( { loading: files.length > 0, - bottomBarVisible: files.length > 0, loaded: false, fileName: '', fileContents: '', @@ -213,30 +210,18 @@ export class FileDataVisualizerView extends Component { closeEditFlyout = () => { this.setState({ isEditFlyoutVisible: false }); - this.showBottomBar(); }; showEditFlyout = () => { this.setState({ isEditFlyoutVisible: true }); - this.hideBottomBar(); }; closeExplanationFlyout = () => { this.setState({ isExplanationFlyoutVisible: false }); - this.showBottomBar(); }; showExplanationFlyout = () => { this.setState({ isExplanationFlyoutVisible: true }); - this.hideBottomBar(); - }; - - showBottomBar = () => { - this.setState({ bottomBarVisible: true }); - }; - - hideBottomBar = () => { - this.setState({ bottomBarVisible: false }); }; setOverrides = (overrides) => { @@ -282,7 +267,6 @@ export class FileDataVisualizerView extends Component { mode, isEditFlyoutVisible, isExplanationFlyoutVisible, - bottomBarVisible, hasPermissionToImport, fileCouldNotBeReadPermissionError, } = this.state; @@ -333,6 +317,9 @@ export class FileDataVisualizerView extends Component { showEditFlyout={this.showEditFlyout} showExplanationFlyout={this.showExplanationFlyout} disableButtons={isEditFlyoutVisible || isExplanationFlyoutVisible} + onChangeMode={this.changeMode} + onCancel={this.onCancel} + disableImport={hasPermissionToImport === false} /> )} )} - - {bottomBarVisible && loaded && ( - <> - - - - )} )} {mode === MODE.IMPORT && ( @@ -369,23 +344,13 @@ export class FileDataVisualizerView extends Component { fileContents={fileContents} data={data} dataViewsContract={this.props.dataViewsContract} - showBottomBar={this.showBottomBar} - hideBottomBar={this.hideBottomBar} fileUpload={this.props.fileUpload} getAdditionalLinks={this.props.getAdditionalLinks} capabilities={this.props.capabilities} + mode={mode} + onChangeMode={this.changeMode} + onCancel={this.onCancel} /> - - {bottomBarVisible && ( - <> - - - - )} )} @@ -393,14 +358,3 @@ export class FileDataVisualizerView extends Component { ); } } - -function BottomPadding() { - // padding for the BottomBar - return ( - <> - - - - - ); -} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js index eb42fde664850..db6929c7d4a66 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js @@ -10,12 +10,14 @@ import React, { Component } from 'react'; import { EuiButton, - EuiPage, EuiPageBody, EuiPageContentHeader_Deprecated as EuiPageContentHeader, EuiPanel, EuiSpacer, EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -32,6 +34,7 @@ import { addCombinedFieldsToMappings, getDefaultCombinedFields, } from '../../../common/components/combined_fields'; +import { MODE as DATAVISUALIZER_MODE } from '../file_data_visualizer_view/constants'; const DEFAULT_TIME_FIELD = '@timestamp'; const DEFAULT_INDEX_SETTINGS = { number_of_shards: 1 }; @@ -98,7 +101,7 @@ export class ImportView extends Component { // TODO - sort this function out. it's a mess async import() { - const { data, results, dataViewsContract, showBottomBar, fileUpload } = this.props; + const { data, results, dataViewsContract, fileUpload } = this.props; const { format } = results; let { timeFieldName } = this.state; @@ -149,7 +152,6 @@ export class ImportView extends Component { permissionCheckStatus: IMPORT_STATUS.COMPLETE, }, () => { - this.props.hideBottomBar(); setTimeout(async () => { let success = true; const createPipeline = pipelineString !== ''; @@ -309,8 +311,6 @@ export class ImportView extends Component { } } - showBottomBar(); - this.setState({ importing: false, imported: success, @@ -408,12 +408,10 @@ export class ImportView extends Component { showFilebeatFlyout = () => { this.setState({ isFilebeatFlyoutVisible: true }); - this.props.hideBottomBar(); }; closeFilebeatFlyout = () => { this.setState({ isFilebeatFlyoutVisible: false }); - this.props.showBottomBar(); }; async loadDataViewNames() { @@ -482,128 +480,172 @@ export class ImportView extends Component { checkingValidIndex === true; return ( - - - - -

{this.props.fileName}

-
-
+ + + +

{this.props.fileName}

+
+
+ + + +

+ +

+
+ + + - - -

- -

-
- - - + {(initialized === false || importing === true) && ( + + + + + + + + this.props.onChangeMode(DATAVISUALIZER_MODE.READ)} + isDisabled={importing} + > + + + + + this.props.onCancel()} isDisabled={importing}> + + + + + )} - {(initialized === false || importing === true) && ( - - - - )} - - {initialized === true && importing === false && ( - - - - )} -
- - {initialized === true && ( - - - - - - - {imported === true && ( - - - - + {initialized === true && importing === false && ( + + + + + + + + this.props.onChangeMode(DATAVISUALIZER_MODE.READ)} + isDisabled={importing} + > + + + + + this.props.onCancel()} isDisabled={importing}> + + + + + )} + - + {initialized === true && ( + + - + + + {imported === true && ( + + + + + + + + + + {isFilebeatFlyoutVisible && ( + + )} + + )} +
+ + )} + {errors.length > 0 && ( + + - {isFilebeatFlyoutVisible && ( - - )} - - )} - - - )} - {errors.length > 0 && ( - - - - - - )} -
-
+ + + )} + ); } } diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx index 6977114954652..26a727a7a922e 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/results_view/results_view.tsx @@ -12,7 +12,6 @@ import { EuiButton, EuiButtonEmpty, EuiPageBody, - EuiPageContentHeader_Deprecated as EuiPageContentHeader, EuiPanel, EuiSpacer, EuiTitle, @@ -24,6 +23,7 @@ import { FindFileStructureResponse } from '@kbn/file-upload-plugin/common'; import { FileContents } from '../file_contents'; import { AnalysisSummary } from '../analysis_summary'; import { FieldsStatsGrid } from '../../../common/components/fields_stats_grid'; +import { MODE as DATAVISUALIZER_MODE } from '../file_data_visualizer_view/constants'; interface Props { data: string; @@ -32,6 +32,9 @@ interface Props { showEditFlyout(): void; showExplanationFlyout(): void; disableButtons: boolean; + onChangeMode: (mode: DATAVISUALIZER_MODE) => void; + onCancel: () => void; + disableImport?: boolean; } export const ResultsView: FC = ({ @@ -41,14 +44,32 @@ export const ResultsView: FC = ({ showEditFlyout, showExplanationFlyout, disableButtons, + onChangeMode, + onCancel, + disableImport, }) => { return ( - - -

{fileName}

-
-
+ + + +

{fileName}

+
+
+ + + + + +
+
@@ -67,6 +88,19 @@ export const ResultsView: FC = ({ + + onChangeMode(DATAVISUALIZER_MODE.IMPORT)} + data-test-subj="dataVisualizerFileOpenImportPageButton" + > + + + showEditFlyout()} disabled={disableButtons}>