From c1cf970fe1ab911ebb8d9068c1d9ec91db2ae165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 14 Jan 2020 16:28:27 +0100 Subject: [PATCH 01/52] [Logs UI] Move beta badges from tabs to headings (#54572) This moves the beta badges for the Ml integration tabs from the tabbed navigation bar into the primary headings of the respective setup and result pages. --- .../infra/public/components/beta_badge.tsx | 26 +++++++++++++++++++ .../components/navigation/routed_tabs.tsx | 21 +-------------- .../plugins/infra/public/pages/logs/index.tsx | 16 +++--------- .../page_setup_content.tsx | 4 ++- .../top_categories/top_categories_section.tsx | 5 +++- .../log_entry_rate/page_setup_content.tsx | 4 ++- .../sections/log_rate/index.tsx | 13 ++++++---- 7 files changed, 48 insertions(+), 41 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/beta_badge.tsx diff --git a/x-pack/legacy/plugins/infra/public/components/beta_badge.tsx b/x-pack/legacy/plugins/infra/public/components/beta_badge.tsx new file mode 100644 index 00000000000000..5d5770af1a41e8 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/beta_badge.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiBetaBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export const BetaBadge: React.FunctionComponent = () => ( + +); +const betaBadgeLabel = i18n.translate('xpack.infra.common.tabBetaBadgeLabel', { + defaultMessage: 'Beta', +}); + +const betaBadgeTooltipContent = i18n.translate('xpack.infra.common.tabBetaBadgeTooltipContent', { + defaultMessage: + 'This feature is under active development. Extra functionality is coming, and some functionality may change.', +}); diff --git a/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx b/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx index 0b1d34870a72d4..c43ade12ded6db 100644 --- a/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx +++ b/x-pack/legacy/plugins/infra/public/components/navigation/routed_tabs.tsx @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBetaBadge, EuiLink, EuiTab, EuiTabs } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { EuiLink, EuiTab, EuiTabs } from '@elastic/eui'; import React from 'react'; import { Route } from 'react-router-dom'; @@ -56,24 +55,6 @@ export class RoutedTabs extends React.Component { } } -const tabBetaBadgeLabel = i18n.translate('xpack.infra.common.tabBetaBadgeLabel', { - defaultMessage: 'Beta', -}); - -const tabBetaBadgeTooltipContent = i18n.translate('xpack.infra.common.tabBetaBadgeTooltipContent', { - defaultMessage: - 'This feature is under active development. Extra functionality is coming, and some functionality may change.', -}); - -export const TabBetaBadge = euiStyled(EuiBetaBadge).attrs({ - 'aria-label': tabBetaBadgeLabel, - label: tabBetaBadgeLabel, - tooltipContent: tabBetaBadgeTooltipContent, -})` - margin-left: 4px; - vertical-align: baseline; -`; - const TabContainer = euiStyled.div` .euiLink { color: inherit !important; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx index f38f066b5323fd..505878f0239dcf 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/index.tsx @@ -11,7 +11,7 @@ import { Route, RouteComponentProps, Switch } from 'react-router-dom'; import { DocumentTitle } from '../../components/document_title'; import { HelpCenterContent } from '../../components/help_center_content'; import { Header } from '../../components/header'; -import { RoutedTabs, TabBetaBadge } from '../../components/navigation/routed_tabs'; +import { RoutedTabs } from '../../components/navigation/routed_tabs'; import { ColumnarPage } from '../../components/page'; import { SourceLoadingPage } from '../../components/source_loading_page'; import { SourceErrorPage } from '../../components/source_error_page'; @@ -41,22 +41,12 @@ export const LogsPage = ({ match }: RouteComponentProps) => { }; const logRateTab = { - title: ( - <> - {logRateTabTitle} - - - ), + title: logRateTabTitle, path: `${match.path}/log-rate`, }; const logCategoriesTab = { - title: ( - <> - {logCategoriesTabTitle} - - - ), + title: logCategoriesTabTitle, path: `${match.path}/log-categories`, }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx index f0e90cb7ccc054..53e485e0ea8b3c 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/page_setup_content.tsx @@ -8,6 +8,7 @@ import { EuiSpacer, EuiSteps, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; +import { BetaBadge } from '../../../components/beta_badge'; import { createInitialConfigurationStep, createProcessStep, @@ -82,7 +83,8 @@ export const LogEntryCategoriesSetupContent: React.FunctionComponent = () => { + />{' '} + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx index 0281615a59c785..962b5065362533 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { LogEntryCategory } from '../../../../../../common/http_api/log_analysis'; import { TimeRange } from '../../../../../../common/http_api/shared'; +import { BetaBadge } from '../../../../../components/beta_badge'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; import { RecreateJobButton } from '../../../../../components/logging/log_analysis_job_status'; import { AnalyzeInMlButton } from '../../../../../components/logging/log_analysis_results'; @@ -42,7 +43,9 @@ export const TopCategoriesSection: React.FunctionComponent<{ -

{title}

+

+ {title} +

diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx index 7e90cf29072e1e..d6d8e241c996b6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/page_setup_content.tsx @@ -8,6 +8,7 @@ import { EuiSpacer, EuiSteps, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; +import { BetaBadge } from '../../../components/beta_badge'; import { createInitialConfigurationStep, createProcessStep, @@ -82,7 +83,8 @@ export const LogEntryRateSetupContent: React.FunctionComponent = () => { + />{' '} + diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx index a11dc9d4d607ae..3da025d90119f1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/log_entry_rate/sections/log_rate/index.tsx @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; -import { LogEntryRateResults as Results } from '../../use_log_entry_rate_results'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; -import { LogEntryRateBarChart } from './bar_chart'; -import { getLogEntryRatePartitionedSeries } from '../helpers/data_formatters'; +import { BetaBadge } from '../../../../../components/beta_badge'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; +import { LogEntryRateResults as Results } from '../../use_log_entry_rate_results'; +import { getLogEntryRatePartitionedSeries } from '../helpers/data_formatters'; +import { LogEntryRateBarChart } from './bar_chart'; export const LogRateResults = ({ isLoading, @@ -33,7 +34,9 @@ export const LogRateResults = ({ return ( <> -

{title}

+

+ {title} +

}> {!results || (results && results.histogramBuckets && !results.histogramBuckets.length) ? ( From 6a45241b79a518c321fd46414cc1a00eef7b6e9a Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Tue, 14 Jan 2020 16:29:43 +0100 Subject: [PATCH 02/52] [ML] Functional tests - basic tests for single metric viewer and anomaly explorer (#54699) This PR adds basic functional UI tests for the single metric viewer and the anomaly explorer. --- .../anomalies_table/anomalies_table.js | 1 + .../anomalies_table_columns.js | 12 +++ .../influencers_list/influencers_list.js | 4 +- .../job_selector_badge/job_selector_badge.js | 2 +- .../loading_indicator/loading_indicator.js | 6 +- .../timeseriesexplorer/timeseriesexplorer.js | 1 + .../anomaly_detection/anomaly_explorer.ts | 95 +++++++++++++++++++ .../anomaly_detection/index.ts | 2 + .../anomaly_detection/single_metric_viewer.ts | 91 ++++++++++++++++++ .../machine_learning/anomalies_table.ts | 26 +++++ .../machine_learning/anomaly_explorer.ts | 27 ++++++ .../services/machine_learning/api.ts | 86 +++++++++++++++++ .../services/machine_learning/common.ts | 6 ++ .../services/machine_learning/index.ts | 2 + .../machine_learning/job_selection.ts | 27 ++++++ .../services/machine_learning/job_table.ts | 10 ++ .../machine_learning/single_metric_viewer.ts | 36 +++++++ x-pack/test/functional/services/ml.ts | 7 ++ 18 files changed, 437 insertions(+), 4 deletions(-) create mode 100644 x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts create mode 100644 x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts create mode 100644 x-pack/test/functional/services/machine_learning/anomalies_table.ts create mode 100644 x-pack/test/functional/services/machine_learning/job_selection.ts diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.js index bc3ce88921110a..6728f019a6bd57 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.js @@ -195,6 +195,7 @@ class AnomaliesTable extends Component { return { onMouseOver: () => this.onMouseOverRow(item), onMouseLeave: () => this.onMouseLeaveRow(), + 'data-test-subj': `mlAnomaliesListRow row-${item.rowId}`, }; }; diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 36faac45164f47..5454911673fe26 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -80,11 +80,13 @@ export function getColumns( }) } data-row-id={item.rowId} + data-test-subj="mlJobListRowDetailsToggle" /> ), }, { field: 'time', + 'data-test-subj': 'mlAnomaliesListColumnTime', name: i18n.translate('xpack.ml.anomaliesTable.timeColumnName', { defaultMessage: 'time', }), @@ -95,6 +97,7 @@ export function getColumns( }, { field: 'severity', + 'data-test-subj': 'mlAnomaliesListColumnSeverity', name: i18n.translate('xpack.ml.anomaliesTable.severityColumnName', { defaultMessage: 'severity', }), @@ -105,6 +108,7 @@ export function getColumns( }, { field: 'detector', + 'data-test-subj': 'mlAnomaliesListColumnDetector', name: i18n.translate('xpack.ml.anomaliesTable.detectorColumnName', { defaultMessage: 'detector', }), @@ -119,6 +123,7 @@ export function getColumns( if (items.some(item => item.entityValue !== undefined)) { columns.push({ field: 'entityValue', + 'data-test-subj': 'mlAnomaliesListColumnFoundFor', name: i18n.translate('xpack.ml.anomaliesTable.entityValueColumnName', { defaultMessage: 'found for', }), @@ -138,6 +143,7 @@ export function getColumns( if (items.some(item => item.influencers !== undefined)) { columns.push({ field: 'influencers', + 'data-test-subj': 'mlAnomaliesListColumnInfluencers', name: i18n.translate('xpack.ml.anomaliesTable.influencersColumnName', { defaultMessage: 'influenced by', }), @@ -159,6 +165,7 @@ export function getColumns( if (items.some(item => item.actual !== undefined)) { columns.push({ field: 'actualSort', + 'data-test-subj': 'mlAnomaliesListColumnActual', name: i18n.translate('xpack.ml.anomaliesTable.actualSortColumnName', { defaultMessage: 'actual', }), @@ -176,6 +183,7 @@ export function getColumns( if (items.some(item => item.typical !== undefined)) { columns.push({ field: 'typicalSort', + 'data-test-subj': 'mlAnomaliesListColumnTypical', name: i18n.translate('xpack.ml.anomaliesTable.typicalSortColumnName', { defaultMessage: 'typical', }), @@ -198,6 +206,7 @@ export function getColumns( if (nonTimeOfDayOrWeek === true) { columns.push({ field: 'metricDescriptionSort', + 'data-test-subj': 'mlAnomaliesListColumnDescription', name: i18n.translate('xpack.ml.anomaliesTable.metricDescriptionSortColumnName', { defaultMessage: 'description', }), @@ -213,6 +222,7 @@ export function getColumns( if (jobIds && jobIds.length > 1) { columns.push({ field: 'jobId', + 'data-test-subj': 'mlAnomaliesListColumnJobID', name: i18n.translate('xpack.ml.anomaliesTable.jobIdColumnName', { defaultMessage: 'job ID', }), @@ -223,6 +233,7 @@ export function getColumns( const showExamples = items.some(item => item.entityName === 'mlcategory'); if (showExamples === true) { columns.push({ + 'data-test-subj': 'mlAnomaliesListColumnCategoryExamples', name: i18n.translate('xpack.ml.anomaliesTable.categoryExamplesColumnName', { defaultMessage: 'category examples', }), @@ -254,6 +265,7 @@ export function getColumns( if (showLinks === true) { columns.push({ + 'data-test-subj': 'mlAnomaliesListColumnAction', name: i18n.translate('xpack.ml.anomaliesTable.actionsColumnName', { defaultMessage: 'actions', }), diff --git a/x-pack/legacy/plugins/ml/public/application/components/influencers_list/influencers_list.js b/x-pack/legacy/plugins/ml/public/application/components/influencers_list/influencers_list.js index 6a395c5cbc114d..ae61e65f917996 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/influencers_list/influencers_list.js +++ b/x-pack/legacy/plugins/ml/public/application/components/influencers_list/influencers_list.js @@ -56,7 +56,7 @@ function Influencer({ influencerFieldName, influencerFilter, valueData }) { const tooltipContent = getTooltipContent(maxScoreLabel, totalScoreLabel); return ( -
+
{influencerFieldName !== 'mlcategory' ? ( - +

{influencerFieldName}

diff --git a/x-pack/legacy/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js b/x-pack/legacy/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js index ff5bb475e3a733..4d2ab01e2a0544 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js +++ b/x-pack/legacy/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js @@ -33,7 +33,7 @@ export function JobSelectorBadge({ icon, id, isGroup = false, numJobs, removeId } return ( - + {`${id}${jobCount ? jobCount : ''}`} ); diff --git a/x-pack/legacy/plugins/ml/public/application/components/loading_indicator/loading_indicator.js b/x-pack/legacy/plugins/ml/public/application/components/loading_indicator/loading_indicator.js index e84ef2f87c3ba1..20f4fb86b5372c 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/loading_indicator/loading_indicator.js +++ b/x-pack/legacy/plugins/ml/public/application/components/loading_indicator/loading_indicator.js @@ -12,7 +12,11 @@ import { EuiLoadingChart, EuiSpacer } from '@elastic/eui'; export function LoadingIndicator({ height, label }) { height = height ? +height : 100; return ( -
+
{label && ( <> diff --git a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 807a368fc9b349..016f054430fa3c 100644 --- a/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/legacy/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1314,6 +1314,7 @@ export class TimeSeriesExplorer extends React.Component { onChange={this.detectorIndexChangeHandler} value={selectedDetectorIndex} options={detectorSelectOptions} + data-test-subj="mlSingleMetricViewerDetectorSelect" /> diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts new file mode 100644 index 00000000000000..83c348e824fa82 --- /dev/null +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/anomaly_explorer.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { + Job, + Datafeed, +} from '../../../../..//legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/configs'; + +const JOB_CONFIG: Job = { + job_id: `fq_multi_1_ae`, + description: + 'mean/min/max(responsetime) partition=airline on farequote dataset with 1h bucket span', + groups: ['farequote', 'automated', 'multi-metric'], + analysis_config: { + bucket_span: '1h', + influencers: ['airline'], + detectors: [ + { function: 'mean', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'min', field_name: 'responsetime', partition_field_name: 'airline' }, + { function: 'max', field_name: 'responsetime', partition_field_name: 'airline' }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '20mb' }, + model_plot_config: { enabled: true }, +}; + +const DATAFEED_CONFIG: Datafeed = { + datafeed_id: 'datafeed-fq_multi_1_se', + indices: ['farequote'], + job_id: 'fq_multi_1_ae', + query: { bool: { must: [{ match_all: {} }] } }, +}; + +// eslint-disable-next-line import/no-default-export +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('anomaly explorer', function() { + this.tags(['smoke', 'mlqa']); + before(async () => { + await esArchiver.load('ml/farequote'); + await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); + }); + + after(async () => { + await esArchiver.unload('ml/farequote'); + await ml.api.cleanMlIndices(); + }); + + it('loads from job list row link', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.jobTable.waitForJobsToLoad(); + await ml.jobTable.filterWithSearchString(JOB_CONFIG.job_id); + const rows = await ml.jobTable.parseJobTable(); + expect(rows.filter(row => row.id === JOB_CONFIG.job_id)).to.have.length(1); + + await ml.jobTable.clickOpenJobInAnomalyExplorerButton(JOB_CONFIG.job_id); + await ml.common.waitForMlLoadingIndicatorToDisappear(); + }); + + it('pre-fills the job selection', async () => { + await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]); + }); + + it('displays the influencers list', async () => { + await ml.anomalyExplorer.assertInfluencerListExists(); + for (const influencerField of JOB_CONFIG.analysis_config.influencers) { + await ml.anomalyExplorer.assertInfluencerFieldExists(influencerField); + await ml.anomalyExplorer.assertInfluencerFieldListNotEmpty(influencerField); + } + }); + + it('displays the swimlanes', async () => { + await ml.anomalyExplorer.assertOverallSwimlaneExists(); + await ml.anomalyExplorer.assertSwimlaneViewByExists(); + }); + + it('displays the anomalies table', async () => { + await ml.anomaliesTable.assertTableExists(); + }); + + it('anomalies table is not empty', async () => { + await ml.anomaliesTable.assertTableNotEmpty(); + }); + }); +} diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts index d5d617587fc3bb..a52e3d3aca2c03 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/index.ts @@ -14,5 +14,7 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./population_job')); loadTestFile(require.resolve('./saved_search_job')); loadTestFile(require.resolve('./advanced_job')); + loadTestFile(require.resolve('./single_metric_viewer')); + loadTestFile(require.resolve('./anomaly_explorer')); }); } diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts new file mode 100644 index 00000000000000..a52ef6442cf214 --- /dev/null +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/single_metric_viewer.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { + Job, + Datafeed, +} from '../../../../..//legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/configs'; + +const JOB_CONFIG: Job = { + job_id: `fq_single_1_smv`, + description: 'mean(responsetime) on farequote dataset with 15m bucket span', + groups: ['farequote', 'automated', 'single-metric'], + analysis_config: { + bucket_span: '15m', + influencers: [], + detectors: [ + { + function: 'mean', + field_name: 'responsetime', + }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '10mb' }, + model_plot_config: { enabled: true }, +}; + +const DATAFEED_CONFIG: Datafeed = { + datafeed_id: 'datafeed-fq_single_1_smv', + indices: ['farequote'], + job_id: 'fq_single_1_smv', + query: { bool: { must: [{ match_all: {} }] } }, +}; + +// eslint-disable-next-line import/no-default-export +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('single metric viewer', function() { + this.tags(['smoke', 'mlqa']); + before(async () => { + await esArchiver.load('ml/farequote'); + await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); + }); + + after(async () => { + await esArchiver.unload('ml/farequote'); + await ml.api.cleanMlIndices(); + }); + + it('loads from job list row link', async () => { + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.jobTable.waitForJobsToLoad(); + await ml.jobTable.filterWithSearchString(JOB_CONFIG.job_id); + const rows = await ml.jobTable.parseJobTable(); + expect(rows.filter(row => row.id === JOB_CONFIG.job_id)).to.have.length(1); + + await ml.jobTable.clickOpenJobInSingleMetricViewerButton(JOB_CONFIG.job_id); + await ml.common.waitForMlLoadingIndicatorToDisappear(); + }); + + it('pre-fills the job selection', async () => { + await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]); + }); + + it('pre-fills the detector input', async () => { + await ml.singleMetricViewer.assertDetectorInputExsist(); + await ml.singleMetricViewer.assertDetectorInputValue('0'); + }); + + it('displays the chart', async () => { + await ml.singleMetricViewer.assertChartExsist(); + }); + + it('displays the anomalies table', async () => { + await ml.anomaliesTable.assertTableExists(); + }); + + it('anomalies table is not empty', async () => { + await ml.anomaliesTable.assertTableNotEmpty(); + }); + }); +} diff --git a/x-pack/test/functional/services/machine_learning/anomalies_table.ts b/x-pack/test/functional/services/machine_learning/anomalies_table.ts new file mode 100644 index 00000000000000..c8701099dcd7ad --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/anomalies_table.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningAnomaliesTableProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertTableExists() { + await testSubjects.existOrFail('mlAnomaliesTable'); + }, + + async assertTableNotEmpty() { + const tableRows = await testSubjects.findAll('mlAnomaliesTable > ~mlAnomaliesListRow'); + expect(tableRows.length).to.be.greaterThan( + 0, + 'Anomalies table should have at least one row (got 0)' + ); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts b/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts index 18a185cbf253b5..e18441ba7c3fa5 100644 --- a/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts +++ b/x-pack/test/functional/services/machine_learning/anomaly_explorer.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -13,5 +14,31 @@ export function MachineLearningAnomalyExplorerProvider({ getService }: FtrProvid async assertAnomalyExplorerEmptyListMessageExists() { await testSubjects.existOrFail('mlNoJobsFound'); }, + + async assertInfluencerListExists() { + await testSubjects.existOrFail('mlAnomalyExplorerInfluencerList'); + }, + + async assertInfluencerFieldExists(influencerField: string) { + await testSubjects.existOrFail(`mlInfluencerFieldName ${influencerField}`); + }, + + async assertInfluencerFieldListNotEmpty(influencerField: string) { + const influencerFieldEntries = await testSubjects.findAll( + `mlInfluencerEntry field-${influencerField}` + ); + expect(influencerFieldEntries.length).to.be.greaterThan( + 0, + `Influencer list for field '${influencerField}' should have at least one entry (got 0)` + ); + }, + + async assertOverallSwimlaneExists() { + await testSubjects.existOrFail('mlAnomalyExplorerSwimlaneOverall'); + }, + + async assertSwimlaneViewByExists() { + await testSubjects.existOrFail('mlAnomalyExplorerSwimlaneViewBy'); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/api.ts b/x-pack/test/functional/services/machine_learning/api.ts index 1f3711ff5e506c..7330a7b9812d58 100644 --- a/x-pack/test/functional/services/machine_learning/api.ts +++ b/x-pack/test/functional/services/machine_learning/api.ts @@ -10,6 +10,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { JOB_STATE, DATAFEED_STATE } from '../../../../legacy/plugins/ml/common/constants/states'; import { DATA_FRAME_TASK_STATE } from '../../../../legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/common'; +import { Job } from '../../../..//legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/configs/job'; +import { Datafeed } from '../../../..//legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/configs/datafeed'; export type MlApi = ProvidedType; @@ -270,5 +272,89 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { } }); }, + + async getAnomalyDetectionJob(jobId: string) { + return await esSupertest.get(`/_ml/anomaly_detectors/${jobId}`).expect(200); + }, + + async createAnomalyDetectionJob(jobConfig: Job) { + const jobId = jobConfig.job_id; + log.debug(`Creating anomaly detection job with id '${jobId}'...`); + await esSupertest + .put(`/_ml/anomaly_detectors/${jobId}`) + .send(jobConfig) + .expect(200); + + await retry.waitForWithTimeout(`'${jobId}' to be created`, 5 * 1000, async () => { + if (await this.getAnomalyDetectionJob(jobId)) { + return true; + } else { + throw new Error(`expected anomaly detection job '${jobId}' to be created`); + } + }); + }, + + async getDatafeed(datafeedId: string) { + return await esSupertest.get(`/_ml/datafeeds/${datafeedId}`).expect(200); + }, + + async createDatafeed(datafeedConfig: Datafeed) { + const datafeedId = datafeedConfig.datafeed_id; + log.debug(`Creating datafeed with id '${datafeedId}'...`); + await esSupertest + .put(`/_ml/datafeeds/${datafeedId}`) + .send(datafeedConfig) + .expect(200); + + await retry.waitForWithTimeout(`'${datafeedId}' to be created`, 5 * 1000, async () => { + if (await this.getDatafeed(datafeedId)) { + return true; + } else { + throw new Error(`expected datafeed '${datafeedId}' to be created`); + } + }); + }, + + async openAnomalyDetectionJob(jobId: string) { + log.debug(`Opening anomaly detection job '${jobId}'...`); + const openResponse = await esSupertest + .post(`/_ml/anomaly_detectors/${jobId}/_open`) + .send({ timeout: '10s' }) + .set({ 'Content-Type': 'application/json' }) + .expect(200) + .then((res: any) => res.body); + + expect(openResponse) + .to.have.property('opened') + .eql(true, 'Response for open job request should be acknowledged'); + }, + + async startDatafeed( + datafeedId: string, + startConfig: { start?: string; end?: string } = { start: '0' } + ) { + log.debug( + `Starting datafeed '${datafeedId}' with start: '${startConfig.start}', end: '${startConfig.end}'...` + ); + const startResponse = await esSupertest + .post(`/_ml/datafeeds/${datafeedId}/_start`) + .send(startConfig) + .set({ 'Content-Type': 'application/json' }) + .expect(200) + .then((res: any) => res.body); + + expect(startResponse) + .to.have.property('started') + .eql(true, 'Response for start datafeed request should be acknowledged'); + }, + + async createAndRunAnomalyDetectionLookbackJob(jobConfig: Job, datafeedConfig: Datafeed) { + await this.createAnomalyDetectionJob(jobConfig); + await this.createDatafeed(datafeedConfig); + await this.openAnomalyDetectionJob(jobConfig.job_id); + await this.startDatafeed(datafeedConfig.datafeed_id, { start: '0', end: `${Date.now()}` }); + await this.waitForDatafeedState(datafeedConfig.datafeed_id, DATAFEED_STATE.STOPPED); + await this.waitForJobState(jobConfig.job_id, JOB_STATE.CLOSED); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/common.ts b/x-pack/test/functional/services/machine_learning/common.ts index 35ee32fa5d94ee..fc72ac16992727 100644 --- a/x-pack/test/functional/services/machine_learning/common.ts +++ b/x-pack/test/functional/services/machine_learning/common.ts @@ -72,5 +72,11 @@ export function MachineLearningCommonProvider({ getService }: FtrProviderContext } }); }, + + async waitForMlLoadingIndicatorToDisappear() { + await retry.tryForTime(10 * 1000, async () => { + await testSubjects.missingOrFail('mlLoadingIndicator'); + }); + }, }; } diff --git a/x-pack/test/functional/services/machine_learning/index.ts b/x-pack/test/functional/services/machine_learning/index.ts index 7c393689d166c0..b01f127519670d 100644 --- a/x-pack/test/functional/services/machine_learning/index.ts +++ b/x-pack/test/functional/services/machine_learning/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export { MachineLearningAnomaliesTableProvider } from './anomalies_table'; export { MachineLearningAnomalyExplorerProvider } from './anomaly_explorer'; export { MachineLearningAPIProvider } from './api'; export { MachineLearningCommonProvider } from './common'; @@ -14,6 +15,7 @@ export { MachineLearningDataFrameAnalyticsTableProvider } from './data_frame_ana export { MachineLearningDataVisualizerProvider } from './data_visualizer'; export { MachineLearningDataVisualizerIndexBasedProvider } from './data_visualizer_index_based'; export { MachineLearningJobManagementProvider } from './job_management'; +export { MachineLearningJobSelectionProvider } from './job_selection'; export { MachineLearningJobSourceSelectionProvider } from './job_source_selection'; export { MachineLearningJobTableProvider } from './job_table'; export { MachineLearningJobTypeSelectionProvider } from './job_type_selection'; diff --git a/x-pack/test/functional/services/machine_learning/job_selection.ts b/x-pack/test/functional/services/machine_learning/job_selection.ts new file mode 100644 index 00000000000000..db693a8f9bb99e --- /dev/null +++ b/x-pack/test/functional/services/machine_learning/job_selection.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningJobSelectionProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertJobSelection(jobOrGroupIds: string[]) { + const selectedJobsOrGroups = await testSubjects.findAll( + 'mlJobSelectionBadges > ~mlJobSelectionBadge' + ); + const actualJobOrGroupLabels = await Promise.all( + selectedJobsOrGroups.map(async badge => await badge.getVisibleText()) + ); + expect(actualJobOrGroupLabels).to.eql( + jobOrGroupIds, + `Job selection should display jobs or groups '${jobOrGroupIds}' (got '${actualJobOrGroupLabels}')` + ); + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_table.ts b/x-pack/test/functional/services/machine_learning/job_table.ts index 7eded43d1f0586..eed6d180b4dca9 100644 --- a/x-pack/test/functional/services/machine_learning/job_table.ts +++ b/x-pack/test/functional/services/machine_learning/job_table.ts @@ -235,5 +235,15 @@ export function MachineLearningJobTableProvider({ getService }: FtrProviderConte await testSubjects.click('mlDeleteJobConfirmModal > confirmModalConfirmButton'); await testSubjects.missingOrFail('mlDeleteJobConfirmModal', { timeout: 30 * 1000 }); } + + public async clickOpenJobInSingleMetricViewerButton(jobId: string) { + await testSubjects.click(`~openJobsInSingleMetricViewer-${jobId}`); + await testSubjects.existOrFail('~mlPageSingleMetricViewer'); + } + + public async clickOpenJobInAnomalyExplorerButton(jobId: string) { + await testSubjects.click(`~openJobsInSingleAnomalyExplorer-${jobId}`); + await testSubjects.existOrFail('~mlPageAnomalyExplorer'); + } })(); } diff --git a/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts b/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts index 36abeecbddcea9..b2c3e19020e62d 100644 --- a/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts +++ b/x-pack/test/functional/services/machine_learning/single_metric_viewer.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -13,5 +14,40 @@ export function MachineLearningSingleMetricViewerProvider({ getService }: FtrPro async assertSingleMetricViewerEmptyListMessageExsist() { await testSubjects.existOrFail('mlNoSingleMetricJobsFound'); }, + + async assertForecastButtonExistsExsist() { + await testSubjects.existOrFail( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + }, + + async assertDetectorInputExsist() { + await testSubjects.existOrFail( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerDetectorSelect' + ); + }, + + async assertDetectorInputValue(expectedDetectorOptionValue: string) { + const actualDetectorValue = await testSubjects.getAttribute( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerDetectorSelect', + 'value' + ); + expect(actualDetectorValue).to.eql( + expectedDetectorOptionValue, + `Detector input option value should be '${expectedDetectorOptionValue}' (got '${actualDetectorValue}')` + ); + }, + + async setDetectorInputValue(detectorOptionValue: string) { + await testSubjects.selectValue( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerDetectorSelect', + detectorOptionValue + ); + await this.assertDetectorInputValue(detectorOptionValue); + }, + + async assertChartExsist() { + await testSubjects.existOrFail('mlSingleMetricViewerChart'); + }, }; } diff --git a/x-pack/test/functional/services/ml.ts b/x-pack/test/functional/services/ml.ts index 8e71c16921078f..5957a8a2eeb0ad 100644 --- a/x-pack/test/functional/services/ml.ts +++ b/x-pack/test/functional/services/ml.ts @@ -7,6 +7,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; import { + MachineLearningAnomaliesTableProvider, MachineLearningAnomalyExplorerProvider, MachineLearningAPIProvider, MachineLearningCommonProvider, @@ -17,6 +18,7 @@ import { MachineLearningDataVisualizerProvider, MachineLearningDataVisualizerIndexBasedProvider, MachineLearningJobManagementProvider, + MachineLearningJobSelectionProvider, MachineLearningJobSourceSelectionProvider, MachineLearningJobTableProvider, MachineLearningJobTypeSelectionProvider, @@ -32,6 +34,7 @@ import { export function MachineLearningProvider(context: FtrProviderContext) { const common = MachineLearningCommonProvider(context); + const anomaliesTable = MachineLearningAnomaliesTableProvider(context); const anomalyExplorer = MachineLearningAnomalyExplorerProvider(context); const api = MachineLearningAPIProvider(context); const customUrls = MachineLearningCustomUrlsProvider(context); @@ -41,6 +44,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataVisualizer = MachineLearningDataVisualizerProvider(context); const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider(context); const jobManagement = MachineLearningJobManagementProvider(context, api); + const jobSelection = MachineLearningJobSelectionProvider(context); const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context); const jobTable = MachineLearningJobTableProvider(context); const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context); @@ -53,8 +57,10 @@ export function MachineLearningProvider(context: FtrProviderContext) { const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context); return { + anomaliesTable, anomalyExplorer, api, + common, customUrls, dataFrameAnalytics, dataFrameAnalyticsCreation, @@ -62,6 +68,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { dataVisualizer, dataVisualizerIndexBased, jobManagement, + jobSelection, jobSourceSelection, jobTable, jobTypeSelection, From e20fbd8e8f4026b411a887bf669bc722016c4b37 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Tue, 14 Jan 2020 16:36:15 +0100 Subject: [PATCH 03/52] [SIEM] Detection Engine UI improvements (#54712) --- .../components/alerts_viewer/alerts_table.tsx | 18 ++++++++------- .../events_viewer/events_viewer.tsx | 2 +- .../siem/public/components/toasters/index.tsx | 22 +++++++++++++------ .../detection_engine/rules/all/index.tsx | 6 ++++- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/alerts_table.tsx b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/alerts_table.tsx index 179474ee6e9d4a..19011019573076 100644 --- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/alerts_table.tsx @@ -59,6 +59,15 @@ interface Props { const AlertsTableComponent: React.FC = ({ endDate, startDate, pageFilters = [] }) => { const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]); + const timelineTypeContext = useMemo( + () => ({ + documentType: i18n.ALERTS_DOCUMENT_TYPE, + footerText: i18n.TOTAL_COUNT_OF_ALERTS, + title: i18n.ALERTS_TABLE_TITLE, + }), + [] + ); + return ( = ({ endDate, startDate, pageFilters end={endDate} id={ALERTS_TABLE_ID} start={startDate} - timelineTypeContext={useMemo( - () => ({ - documentType: i18n.ALERTS_DOCUMENT_TYPE, - footerText: i18n.TOTAL_COUNT_OF_ALERTS, - title: i18n.ALERTS_TABLE_TITLE, - }), - [] - )} + timelineTypeContext={timelineTypeContext} /> ); }; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 2c669c259d07e3..77d51f68c18ca4 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -113,7 +113,7 @@ const EventsViewerComponent: React.FC = ({ ); return ( - + {({ measureRef, content: { width = 0 } }) => ( <> diff --git a/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx b/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx index 7098e618aeb55a..b046c91dcd912b 100644 --- a/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx @@ -60,6 +60,12 @@ export const ManageGlobalToaster = ({ children }: ManageGlobalToasterProps) => { ); }; +const GlobalToasterListContainer = styled.div` + position: absolute; + right: 0; + bottom: 0; +`; + interface GlobalToasterProps { toastLifeTimeMs?: number; } @@ -82,13 +88,15 @@ export const GlobalToaster = ({ toastLifeTimeMs = 5000 }: GlobalToasterProps) => return ( <> {toasts.length > 0 && !isShowing && ( - { - dispatch({ type: 'deleteToaster', id }); - }} - toastLifeTimeMs={toastLifeTimeMs} - /> + + { + dispatch({ type: 'deleteToaster', id }); + }} + toastLifeTimeMs={toastLifeTimeMs} + /> + )} {toastInModal != null && ( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index 202be75f09e699..819b513ecc9bc9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -186,6 +186,10 @@ export const AllRules = React.memo<{ filter: filterString, }, }); + dispatch({ + type: 'updatePagination', + pagination: { ...pagination, page: 1 }, + }); }} /> @@ -221,7 +225,7 @@ export const AllRules = React.memo<{ Date: Tue, 14 Jan 2020 15:56:08 +0000 Subject: [PATCH 04/52] [ML] Adding categorization job wizard icon (#54721) --- .../job_type/categorization_job_icon.tsx | 22 +++++++++++++++++++ .../jobs/new_job/pages/job_type/page.tsx | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/categorization_job_icon.tsx diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/categorization_job_icon.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/categorization_job_icon.tsx new file mode 100644 index 00000000000000..d69814e3e13479 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/categorization_job_icon.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +export const CategorizationIcon = ( + + + + +); diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/page.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/page.tsx index 9a44d561c2d944..1b57dd341a046b 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/page.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/job_type/page.tsx @@ -24,6 +24,7 @@ import { DataRecognizer } from '../../../../components/data_recognizer'; import { addItemToRecentlyAccessed } from '../../../../util/recently_accessed'; import { timeBasedIndexCheck } from '../../../../util/index_utils'; import { CreateJobLinkCard } from '../../../../components/create_job_link_card'; +import { CategorizationIcon } from './categorization_job_icon'; export const Page: FC = () => { const kibanaContext = useKibanaContext(); @@ -154,7 +155,7 @@ export const Page: FC = () => { { href: getUrl('#jobs/new_job/categorization'), icon: { - type: 'createAdvancedJob', + type: CategorizationIcon, ariaLabel: i18n.translate('xpack.ml.newJob.wizard.jobType.categorizationAriaLabel', { defaultMessage: 'Categorization job', }), From 040aee9fedc8ffe7a804d5525296e7768e64cb46 Mon Sep 17 00:00:00 2001 From: Dmitry Lemeshko Date: Tue, 14 Jan 2020 16:56:38 +0100 Subject: [PATCH 05/52] Functional tests: refactor dashboard_page (#54588) * [test/functional] cleanup & tsfy dashboard_page * fix & improvement Co-authored-by: Elastic Machine --- .../apps/dashboard/dashboard_clone.js | 15 +- .../apps/dashboard/dashboard_filter_bar.js | 6 +- .../apps/dashboard/dashboard_filtering.js | 6 +- .../apps/dashboard/dashboard_listing.js | 54 +-- .../apps/dashboard/dashboard_save.js | 17 +- .../apps/dashboard/dashboard_snapshots.js | 6 +- .../apps/dashboard/dashboard_state.js | 7 +- .../apps/dashboard/dashboard_time_picker.js | 4 +- .../apps/dashboard/panel_controls.js | 12 +- test/functional/apps/dashboard/view_edit.js | 4 +- .../{dashboard_page.js => dashboard_page.ts} | 337 ++++++------------ test/functional/page_objects/index.ts | 1 - test/functional/page_objects/time_picker.js | 16 + .../functional/page_objects/visualize_page.ts | 10 +- .../services/dashboard/visualizations.js | 11 +- test/functional/services/listing_table.ts | 116 +++++- .../dashboard_mode/dashboard_view_mode.js | 4 +- .../functional/apps/lens/lens_reporting.ts | 3 +- .../apps/spaces/spaces_selection.ts | 5 +- 19 files changed, 312 insertions(+), 322 deletions(-) rename test/functional/page_objects/{dashboard_page.js => dashboard_page.ts} (60%) diff --git a/test/functional/apps/dashboard/dashboard_clone.js b/test/functional/apps/dashboard/dashboard_clone.js index 2a955a2dc90b1e..f5485c1db206e7 100644 --- a/test/functional/apps/dashboard/dashboard_clone.js +++ b/test/functional/apps/dashboard/dashboard_clone.js @@ -21,6 +21,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const retry = getService('retry'); + const listingTable = getService('listingTable'); const PageObjects = getPageObjects(['dashboard', 'header', 'common']); describe('dashboard clone', function describeIndexTests() { @@ -40,10 +41,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.clickClone(); await PageObjects.dashboard.confirmClone(); - - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + await PageObjects.dashboard.gotoDashboardLandingPage(); + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', clonedDashboardName ); + expect(countOfDashboards).to.equal(1); }); @@ -70,8 +73,10 @@ export default function({ getService, getPageObjects }) { it("and doesn't save", async () => { await PageObjects.dashboard.cancelClone(); + await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(1); @@ -85,8 +90,10 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.expectDuplicateTitleWarningDisplayed({ displayed: true }); await PageObjects.dashboard.confirmClone(); await PageObjects.dashboard.waitForRenderComplete(); + await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName + ' Copy' ); expect(countOfDashboards).to.equal(2); diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.js b/test/functional/apps/dashboard/dashboard_filter_bar.js index 5dcb18374c51f7..6d2a30fa85325d 100644 --- a/test/functional/apps/dashboard/dashboard_filter_bar.js +++ b/test/functional/apps/dashboard/dashboard_filter_bar.js @@ -27,7 +27,7 @@ export default function({ getService, getPageObjects }) { const pieChart = getService('pieChart'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize']); + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'timePicker']); describe('dashboard filter bar', () => { before(async () => { @@ -91,7 +91,7 @@ export default function({ getService, getPageObjects }) { await filterBar.ensureFieldEditorModalIsClosed(); await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); }); it('are not selected by default', async function() { @@ -136,7 +136,7 @@ export default function({ getService, getPageObjects }) { await filterBar.ensureFieldEditorModalIsClosed(); await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); }); it('are added when a cell magnifying glass is clicked', async function() { diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js index bd31bb010f2609..1cb9f1490d4427 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.js +++ b/test/functional/apps/dashboard/dashboard_filtering.js @@ -34,7 +34,7 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const dashboardPanelActions = getService('dashboardPanelActions'); - const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize']); + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'timePicker']); describe('dashboard filtering', function() { this.tags('smoke'); @@ -52,7 +52,7 @@ export default function({ getService, getPageObjects }) { describe('adding a filter that excludes all data', () => { before(async () => { await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); await dashboardAddPanel.addEveryVisualization('"Filter Bytes Test"'); await dashboardAddPanel.addEverySavedSearch('"Filter Bytes Test"'); @@ -234,7 +234,7 @@ export default function({ getService, getPageObjects }) { it('visualization saved with a query filters data', async () => { await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); await dashboardAddPanel.addVisualization('Rendering-Test:-animal-sounds-pie'); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/test/functional/apps/dashboard/dashboard_listing.js b/test/functional/apps/dashboard/dashboard_listing.js index 179f10223afb2e..e3e835109da2cd 100644 --- a/test/functional/apps/dashboard/dashboard_listing.js +++ b/test/functional/apps/dashboard/dashboard_listing.js @@ -22,6 +22,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const PageObjects = getPageObjects(['dashboard', 'header', 'common']); const browser = getService('browser'); + const listingTable = getService('listingTable'); describe('dashboard listing page', function describeIndexTests() { const dashboardName = 'Dashboard Listing Test'; @@ -41,7 +42,8 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.saveDashboard(dashboardName); await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(1); @@ -53,7 +55,8 @@ export default function({ getService, getPageObjects }) { }); it('is not shown when there are no dashboards shown during a search', async function() { - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', 'gobeldeguck' ); expect(countOfDashboards).to.equal(0); @@ -65,9 +68,9 @@ export default function({ getService, getPageObjects }) { describe('delete', function() { it('default confirm action is cancel', async function() { - await PageObjects.dashboard.searchForDashboardWithName(dashboardName); - await PageObjects.dashboard.checkDashboardListingSelectAllCheckbox(); - await PageObjects.dashboard.clickDeleteSelectedDashboards(); + await listingTable.searchForItemWithName(dashboardName); + await listingTable.checkListingSelectAllCheckbox(); + await listingTable.clickDeleteSelected(); await PageObjects.common.expectConfirmModalOpenState(true); @@ -75,19 +78,21 @@ export default function({ getService, getPageObjects }) { await PageObjects.common.expectConfirmModalOpenState(false); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(1); }); it('succeeds on confirmation press', async function() { - await PageObjects.dashboard.checkDashboardListingSelectAllCheckbox(); - await PageObjects.dashboard.clickDeleteSelectedDashboards(); + await listingTable.checkListingSelectAllCheckbox(); + await listingTable.clickDeleteSelected(); await PageObjects.common.clickConfirmOnModal(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(0); @@ -96,44 +101,45 @@ export default function({ getService, getPageObjects }) { describe('search', function() { before(async () => { - await PageObjects.dashboard.clearSearchValue(); + await listingTable.clearSearchFilter(); await PageObjects.dashboard.clickNewDashboard(); await PageObjects.dashboard.saveDashboard('Two Words'); + await PageObjects.dashboard.gotoDashboardLandingPage(); }); it('matches on the first word', async function() { - await PageObjects.dashboard.searchForDashboardWithName('Two'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('Two'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(1); }); it('matches the second word', async function() { - await PageObjects.dashboard.searchForDashboardWithName('Words'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('Words'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(1); }); it('matches the second word prefix', async function() { - await PageObjects.dashboard.searchForDashboardWithName('Wor'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('Wor'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(1); }); it('does not match mid word', async function() { - await PageObjects.dashboard.searchForDashboardWithName('ords'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('ords'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(0); }); it('is case insensitive', async function() { - await PageObjects.dashboard.searchForDashboardWithName('two words'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('two words'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(1); }); it('is using AND operator', async function() { - await PageObjects.dashboard.searchForDashboardWithName('three words'); - const countOfDashboards = await PageObjects.dashboard.getCountOfDashboardsInListingTable(); + await listingTable.searchForItemWithName('three words'); + const countOfDashboards = await listingTable.getItemsCount('dashboard'); expect(countOfDashboards).to.equal(0); }); }); @@ -176,7 +182,7 @@ export default function({ getService, getPageObjects }) { }); it('preloads search filter bar when there is no match', async function() { - const searchFilter = await PageObjects.dashboard.getSearchFilterValue(); + const searchFilter = await listingTable.getSearchFilterValue(); expect(searchFilter).to.equal('"nodashboardsnamedme"'); }); @@ -196,7 +202,7 @@ export default function({ getService, getPageObjects }) { }); it('preloads search filter bar when there is more than one match', async function() { - const searchFilter = await PageObjects.dashboard.getSearchFilterValue(); + const searchFilter = await listingTable.getSearchFilterValue(); expect(searchFilter).to.equal('"two words"'); }); diff --git a/test/functional/apps/dashboard/dashboard_save.js b/test/functional/apps/dashboard/dashboard_save.js index 23bb784c79cd0b..2ea1389b89ad43 100644 --- a/test/functional/apps/dashboard/dashboard_save.js +++ b/test/functional/apps/dashboard/dashboard_save.js @@ -19,8 +19,9 @@ import expect from '@kbn/expect'; -export default function({ getPageObjects }) { +export default function({ getPageObjects, getService }) { const PageObjects = getPageObjects(['dashboard', 'header']); + const listingTable = getService('listingTable'); describe('dashboard save', function describeIndexTests() { this.tags('smoke'); @@ -47,8 +48,10 @@ export default function({ getPageObjects }) { it('does not save on reject confirmation', async function() { await PageObjects.dashboard.cancelSave(); + await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(1); @@ -68,15 +71,17 @@ export default function({ getPageObjects }) { // wait till it finishes reloading or it might reload the url after simulating the // dashboard landing page click. await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardName ); expect(countOfDashboards).to.equal(2); }); it('Does not warn when you save an existing dashboard with the title it already has, and that title is a duplicate', async function() { - await PageObjects.dashboard.selectDashboard(dashboardName); + await listingTable.clickItemLink('dashboard', dashboardName); await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); await PageObjects.dashboard.switchToEditMode(); await PageObjects.dashboard.saveDashboard(dashboardName); @@ -121,8 +126,10 @@ export default function({ getPageObjects }) { // wait till it finishes reloading or it might reload the url after simulating the // dashboard landing page click. await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.dashboard.gotoDashboardLandingPage(); - const countOfDashboards = await PageObjects.dashboard.getDashboardCountWithName( + const countOfDashboards = await listingTable.searchAndGetItemsCount( + 'dashboard', dashboardNameEnterKey ); expect(countOfDashboards).to.equal(1); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.js b/test/functional/apps/dashboard/dashboard_snapshots.js index 9900881e4690d9..3a09b46a713cc1 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.js +++ b/test/functional/apps/dashboard/dashboard_snapshots.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects, updateBaselines }) { - const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'common']); + const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'common', 'timePicker']); const screenshot = getService('screenshots'); const browser = getService('browser'); const esArchiver = getService('esArchiver'); @@ -48,7 +48,7 @@ export default function({ getService, getPageObjects, updateBaselines }) { it('compare TSVB snapshot', async () => { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInLogstashDataRange(); + await PageObjects.timePicker.setLogstashDataRange(); await dashboardAddPanel.addVisualization('Rendering Test: tsvb-ts'); await PageObjects.common.closeToast(); @@ -71,7 +71,7 @@ export default function({ getService, getPageObjects, updateBaselines }) { it('compare area chart snapshot', async () => { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInLogstashDataRange(); + await PageObjects.timePicker.setLogstashDataRange(); await dashboardAddPanel.addVisualization('Rendering Test: area with not filter'); await PageObjects.common.closeToast(); diff --git a/test/functional/apps/dashboard/dashboard_state.js b/test/functional/apps/dashboard/dashboard_state.js index 3b9e404e9b94d1..b9172990c501dc 100644 --- a/test/functional/apps/dashboard/dashboard_state.js +++ b/test/functional/apps/dashboard/dashboard_state.js @@ -34,6 +34,7 @@ export default function({ getService, getPageObjects }) { 'discover', 'tileMap', 'visChart', + 'timePicker', ]); const testSubjects = getService('testSubjects'); const browser = getService('browser'); @@ -58,7 +59,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await dashboardAddPanel.addVisualization(AREA_CHART_VIS_NAME); await PageObjects.dashboard.saveDashboard('Overridden colors'); @@ -83,7 +84,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.header.clickDiscover(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await PageObjects.discover.clickFieldListItemAdd('bytes'); await PageObjects.discover.saveSearch('my search'); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -147,7 +148,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await dashboardAddPanel.addVisualization('Visualization TileMap'); await PageObjects.dashboard.saveDashboard('No local edits'); diff --git a/test/functional/apps/dashboard/dashboard_time_picker.js b/test/functional/apps/dashboard/dashboard_time_picker.js index 0b73bc224ab742..b99de9fee6db19 100644 --- a/test/functional/apps/dashboard/dashboard_time_picker.js +++ b/test/functional/apps/dashboard/dashboard_time_picker.js @@ -44,7 +44,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]); await pieChart.expectPieSliceCount(0); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await pieChart.expectPieSliceCount(10); }); @@ -95,7 +95,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.clickNewDashboard(); await PageObjects.dashboard.addVisualizations([PIE_CHART_VIS_NAME]); - // Same date range as `setTimepickerInHistoricalDataRange` + // Same date range as `timePicker.setHistoricalDataRange()` await PageObjects.timePicker.setAbsoluteRange( '2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000' diff --git a/test/functional/apps/dashboard/panel_controls.js b/test/functional/apps/dashboard/panel_controls.js index 683f3683e65e51..f30f58913bd970 100644 --- a/test/functional/apps/dashboard/panel_controls.js +++ b/test/functional/apps/dashboard/panel_controls.js @@ -33,7 +33,13 @@ export default function({ getService, getPageObjects }) { const dashboardReplacePanel = getService('dashboardReplacePanel'); const dashboardVisualizations = getService('dashboardVisualizations'); const renderable = getService('renderable'); - const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'discover']); + const PageObjects = getPageObjects([ + 'dashboard', + 'header', + 'visualize', + 'discover', + 'timePicker', + ]); const dashboardName = 'Dashboard Panel Controls Test'; describe('dashboard panel controls', function viewEditModeTests() { @@ -52,7 +58,7 @@ export default function({ getService, getPageObjects }) { let intialDimensions; before(async () => { await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME); await dashboardAddPanel.addVisualization(LINE_CHART_VIS_NAME); intialDimensions = await PageObjects.dashboard.getPanelDimensions(); @@ -110,7 +116,7 @@ export default function({ getService, getPageObjects }) { describe('panel edit controls', function() { before(async () => { await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME); }); diff --git a/test/functional/apps/dashboard/view_edit.js b/test/functional/apps/dashboard/view_edit.js index 212044d898251a..a0b972f3ab63c2 100644 --- a/test/functional/apps/dashboard/view_edit.js +++ b/test/functional/apps/dashboard/view_edit.js @@ -68,7 +68,7 @@ export default function({ getService, getPageObjects }) { }); it('when time changed is stored with dashboard', async function() { - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); const originalTime = await PageObjects.timePicker.getTimeConfig(); @@ -196,7 +196,7 @@ export default function({ getService, getPageObjects }) { describe('and preserves edits on cancel', function() { it('when time changed is stored with dashboard', async function() { await PageObjects.dashboard.gotoDashboardEditMode(dashboardName); - await PageObjects.dashboard.setTimepickerInDataRange(); + await PageObjects.timePicker.setDefaultDataRange(); await PageObjects.dashboard.saveDashboard(dashboardName, true); await PageObjects.dashboard.switchToEditMode(); await PageObjects.timePicker.setAbsoluteRange( diff --git a/test/functional/page_objects/dashboard_page.js b/test/functional/page_objects/dashboard_page.ts similarity index 60% rename from test/functional/page_objects/dashboard_page.js rename to test/functional/page_objects/dashboard_page.ts index b0f1a3304a9b8e..af0a0160a81d80 100644 --- a/test/functional/page_objects/dashboard_page.js +++ b/test/functional/page_objects/dashboard_page.ts @@ -17,89 +17,85 @@ * under the License. */ -import _ from 'lodash'; import { DashboardConstants } from '../../../src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_constants'; export const PIE_CHART_VIS_NAME = 'Visualization PieChart'; export const AREA_CHART_VIS_NAME = 'Visualization漢字 AreaChart'; export const LINE_CHART_VIS_NAME = 'Visualization漢字 LineChart'; +import { FtrProviderContext } from '../ftr_provider_context'; -export function DashboardPageProvider({ getService, getPageObjects }) { +export function DashboardPageProvider({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const find = getService('find'); const retry = getService('retry'); - const config = getService('config'); const browser = getService('browser'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); const renderable = getService('renderable'); - const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize', 'timePicker']); - - const defaultFindTimeout = config.get('timeouts.find'); + const listingTable = getService('listingTable'); + const PageObjects = getPageObjects(['common', 'header', 'visualize']); + + interface SaveDashboardOptions { + waitDialogIsClosed: boolean; + needsConfirm?: boolean; + storeTimeWithDashboard?: boolean; + saveAsNew?: boolean; + } class DashboardPage { async initTests({ kibanaIndex = 'dashboard/legacy', defaultIndex = 'logstash-*' } = {}) { log.debug('load kibana index with visualizations and log data'); await esArchiver.load(kibanaIndex); - await kibanaServer.uiSettings.replace({ - defaultIndex: defaultIndex, - }); + await kibanaServer.uiSettings.replace({ defaultIndex }); await PageObjects.common.navigateToApp('dashboard'); } - async preserveCrossAppState() { + public async preserveCrossAppState() { const url = await browser.getCurrentUrl(); await browser.get(url, false); await PageObjects.header.waitUntilLoadingHasFinished(); } - async selectDefaultIndex(indexName) { - await PageObjects.settings.navigateTo(); - await PageObjects.settings.clickKibanaIndexPatterns(); - await find.clickByPartialLinkText(indexName); - await PageObjects.settings.clickDefaultIndexButton(); - } - - async clickFullScreenMode() { + public async clickFullScreenMode() { log.debug(`clickFullScreenMode`); await testSubjects.click('dashboardFullScreenMode'); await testSubjects.exists('exitFullScreenModeLogo'); await this.waitForRenderComplete(); } - async fullScreenModeMenuItemExists() { + public async fullScreenModeMenuItemExists() { return await testSubjects.exists('dashboardFullScreenMode'); } - async exitFullScreenTextButtonExists() { + public async exitFullScreenTextButtonExists() { return await testSubjects.exists('exitFullScreenModeText'); } - async getExitFullScreenTextButton() { + public async getExitFullScreenTextButton() { return await testSubjects.find('exitFullScreenModeText'); } - async exitFullScreenLogoButtonExists() { + public async exitFullScreenLogoButtonExists() { return await testSubjects.exists('exitFullScreenModeLogo'); } - async getExitFullScreenLogoButton() { + public async getExitFullScreenLogoButton() { return await testSubjects.find('exitFullScreenModeLogo'); } - async clickExitFullScreenLogoButton() { + public async clickExitFullScreenLogoButton() { await testSubjects.click('exitFullScreenModeLogo'); await this.waitForRenderComplete(); } - async clickExitFullScreenTextButton() { + public async clickExitFullScreenTextButton() { await testSubjects.click('exitFullScreenModeText'); await this.waitForRenderComplete(); } - async getDashboardIdFromCurrentUrl() { + public async getDashboardIdFromCurrentUrl() { const currentUrl = await browser.getCurrentUrl(); const urlSubstring = 'kibana#/dashboard/'; const startOfIdIndex = currentUrl.indexOf(urlSubstring) + urlSubstring.length; @@ -115,25 +111,25 @@ export function DashboardPageProvider({ getService, getPageObjects }) { * Returns true if already on the dashboard landing page (that page doesn't have a link to itself). * @returns {Promise} */ - async onDashboardLandingPage() { + public async onDashboardLandingPage() { log.debug(`onDashboardLandingPage`); return await testSubjects.exists('dashboardLandingPage', { timeout: 5000, }); } - async expectExistsDashboardLandingPage() { + public async expectExistsDashboardLandingPage() { log.debug(`expectExistsDashboardLandingPage`); await testSubjects.existOrFail('dashboardLandingPage'); } - async clickDashboardBreadcrumbLink() { + public async clickDashboardBreadcrumbLink() { log.debug('clickDashboardBreadcrumbLink'); await find.clickByCssSelector(`a[href="#${DashboardConstants.LANDING_PAGE_PATH}"]`); await this.expectExistsDashboardLandingPage(); } - async gotoDashboardLandingPage() { + public async gotoDashboardLandingPage() { log.debug('gotoDashboardLandingPage'); const onPage = await this.onDashboardLandingPage(); if (!onPage) { @@ -141,26 +137,26 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } } - async clickClone() { + public async clickClone() { log.debug('Clicking clone'); await testSubjects.click('dashboardClone'); } - async getCloneTitle() { + public async getCloneTitle() { return await testSubjects.getAttribute('clonedDashboardTitle', 'value'); } - async confirmClone() { + public async confirmClone() { log.debug('Confirming clone'); await testSubjects.click('cloneConfirmButton'); } - async cancelClone() { + public async cancelClone() { log.debug('Canceling clone'); await testSubjects.click('cloneCancelButton'); } - async setClonedDashboardTitle(title) { + public async setClonedDashboardTitle(title: string) { await testSubjects.setValue('clonedDashboardTitle', title); } @@ -168,7 +164,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) { * Asserts that the duplicate title warning is either displayed or not displayed. * @param { displayed: boolean } */ - async expectDuplicateTitleWarningDisplayed({ displayed }) { + public async expectDuplicateTitleWarningDisplayed({ displayed = true }) { if (displayed) { await testSubjects.existOrFail('titleDupicateWarnMsg'); } else { @@ -180,18 +176,16 @@ export function DashboardPageProvider({ getService, getPageObjects }) { * Asserts that the toolbar pagination (count and arrows) is either displayed or not displayed. * @param { displayed: boolean } */ - async expectToolbarPaginationDisplayed({ displayed }) { + public async expectToolbarPaginationDisplayed({ displayed = true }) { const subjects = ['btnPrevPage', 'btnNextPage', 'toolBarPagerText']; if (displayed) { - return await Promise.all(subjects.map(async subj => await testSubjects.existOrFail(subj))); + await Promise.all(subjects.map(async subj => await testSubjects.existOrFail(subj))); } else { - return await Promise.all( - subjects.map(async subj => await testSubjects.missingOrFail(subj)) - ); + await Promise.all(subjects.map(async subj => await testSubjects.missingOrFail(subj))); } } - async switchToEditMode() { + public async switchToEditMode() { log.debug('Switching to edit mode'); await testSubjects.click('dashboardEditMode'); // wait until the count of dashboard panels equals the count of toggle menu icons @@ -204,66 +198,34 @@ export function DashboardPageProvider({ getService, getPageObjects }) { }); } - async getIsInViewMode() { + public async getIsInViewMode() { log.debug('getIsInViewMode'); return await testSubjects.exists('dashboardEditMode'); } - async clickCancelOutOfEditMode() { + public async clickCancelOutOfEditMode() { log.debug('clickCancelOutOfEditMode'); - return await testSubjects.click('dashboardViewOnlyMode'); + await testSubjects.click('dashboardViewOnlyMode'); } - async clickNewDashboard() { - // One or the other will eventually show up on the landing page, depending on whether there are - // dashboards. - await retry.try(async () => { - const createNewItemButtonExists = await testSubjects.exists('newItemButton'); - if (createNewItemButtonExists) { - return await testSubjects.click('newItemButton'); - } - const createNewItemPromptExists = await this.getCreateDashboardPromptExists(); - if (createNewItemPromptExists) { - return await this.clickCreateDashboardPrompt(); - } - - throw new Error( - 'Page is still loading... waiting for create new prompt or button to appear' - ); - }); + public async clickNewDashboard() { + await listingTable.clickNewButton('createDashboardPromptButton'); } - async clickCreateDashboardPrompt() { + public async clickCreateDashboardPrompt() { await testSubjects.click('createDashboardPromptButton'); } - async getCreateDashboardPromptExists() { + public async getCreateDashboardPromptExists() { return await testSubjects.exists('createDashboardPromptButton'); } - async checkDashboardListingRow(id) { - await testSubjects.click(`checkboxSelectRow-${id}`); - } - - async checkDashboardListingSelectAllCheckbox() { - const element = await testSubjects.find('checkboxSelectAll'); - const isSelected = await element.isSelected(); - if (!isSelected) { - log.debug(`checking checkbox "checkboxSelectAll"`); - await testSubjects.click('checkboxSelectAll'); - } - } - - async clickDeleteSelectedDashboards() { - await testSubjects.click('deleteSelectedItems'); - } - - async isOptionsOpen() { + public async isOptionsOpen() { log.debug('isOptionsOpen'); return await testSubjects.exists('dashboardOptionsMenu'); } - async openOptions() { + public async openOptions() { log.debug('openOptions'); const isOpen = await this.isOptionsOpen(); if (!isOpen) { @@ -272,36 +234,36 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } // avoids any 'Object with id x not found' errors when switching tests. - async clearSavedObjectsFromAppLinks() { + public async clearSavedObjectsFromAppLinks() { await PageObjects.header.clickVisualize(); await PageObjects.visualize.gotoLandingPage(); await PageObjects.header.clickDashboard(); await this.gotoDashboardLandingPage(); } - async isMarginsOn() { + public async isMarginsOn() { log.debug('isMarginsOn'); await this.openOptions(); return await testSubjects.getAttribute('dashboardMarginsCheckbox', 'checked'); } - async useMargins(on = true) { + public async useMargins(on = true) { await this.openOptions(); const isMarginsOn = await this.isMarginsOn(); - if (isMarginsOn !== on) { + if (isMarginsOn !== 'on') { return await testSubjects.click('dashboardMarginsCheckbox'); } } - async gotoDashboardEditMode(dashboardName) { + public async gotoDashboardEditMode(dashboardName: string) { await this.loadSavedDashboard(dashboardName); await this.switchToEditMode(); } - async renameDashboard(dashName) { - log.debug(`Naming dashboard ` + dashName); + public async renameDashboard(dashboardName: string) { + log.debug(`Naming dashboard ` + dashboardName); await testSubjects.click('dashboardRenameButton'); - await testSubjects.setValue('savedObjectTitle', dashName); + await testSubjects.setValue('savedObjectTitle', dashboardName); } /** @@ -309,11 +271,14 @@ export function DashboardPageProvider({ getService, getPageObjects }) { * verify that the save was successful, close the toast and return the * toast message * - * @param dashName {String} + * @param dashboardName {String} * @param saveOptions {{storeTimeWithDashboard: boolean, saveAsNew: boolean, needsConfirm: false, waitDialogIsClosed: boolean }} */ - async saveDashboard(dashName, saveOptions = { waitDialogIsClosed: true }) { - await this.enterDashboardTitleAndClickSave(dashName, saveOptions); + public async saveDashboard( + dashboardName: string, + saveOptions: SaveDashboardOptions = { waitDialogIsClosed: true } + ) { + await this.enterDashboardTitleAndClickSave(dashboardName, saveOptions); if (saveOptions.needsConfirm) { await this.clickSave(); @@ -328,37 +293,24 @@ export function DashboardPageProvider({ getService, getPageObjects }) { return message; } - async deleteDashboard(dashboardName, dashboardId) { - await this.gotoDashboardLandingPage(); - await this.searchForDashboardWithName(dashboardName); - await this.checkDashboardListingRow(dashboardId); - await this.clickDeleteSelectedDashboards(); - await PageObjects.common.clickConfirmOnModal(); - } - - async cancelSave() { + public async cancelSave() { log.debug('Canceling save'); await testSubjects.click('saveCancelButton'); } - async clickSave() { + public async clickSave() { log.debug('DashboardPage.clickSave'); await testSubjects.click('confirmSaveSavedObjectButton'); } - async pressEnterKey() { - log.debug('DashboardPage.pressEnterKey'); - await PageObjects.common.pressEnterKey(); - } - /** * * @param dashboardTitle {String} * @param saveOptions {{storeTimeWithDashboard: boolean, saveAsNew: boolean, waitDialogIsClosed: boolean}} */ - async enterDashboardTitleAndClickSave( - dashboardTitle, - saveOptions = { waitDialogIsClosed: true } + public async enterDashboardTitleAndClickSave( + dashboardTitle: string, + saveOptions: SaveDashboardOptions = { waitDialogIsClosed: true } ) { await testSubjects.click('dashboardSaveMenuItem'); const modalDialog = await testSubjects.find('savedObjectSaveModal'); @@ -380,128 +332,66 @@ export function DashboardPageProvider({ getService, getPageObjects }) { } } - async ensureDuplicateTitleCallout() { + public async ensureDuplicateTitleCallout() { await testSubjects.existOrFail('titleDupicateWarnMsg'); } /** * @param dashboardTitle {String} */ - async enterDashboardTitleAndPressEnter(dashboardTitle) { + public async enterDashboardTitleAndPressEnter(dashboardTitle: string) { await testSubjects.click('dashboardSaveMenuItem'); const modalDialog = await testSubjects.find('savedObjectSaveModal'); log.debug('entering new title'); await testSubjects.setValue('savedObjectTitle', dashboardTitle); - await this.pressEnterKey(); + await PageObjects.common.pressEnterKey(); await testSubjects.waitForDeleted(modalDialog); } - async selectDashboard(dashName) { - await testSubjects.click(`dashboardListingTitleLink-${dashName.split(' ').join('-')}`); - } - - async clearSearchValue() { - log.debug(`clearSearchValue`); - - await this.gotoDashboardLandingPage(); - - await retry.try(async () => { - const searchFilter = await this.getSearchFilter(); - await searchFilter.clearValue(); - await PageObjects.common.pressEnterKey(); - }); - } - - async getSearchFilterValue() { - const searchFilter = await this.getSearchFilter(); - return await searchFilter.getAttribute('value'); - } - - async getSearchFilter() { - const searchFilter = await find.allByCssSelector('.euiFieldSearch'); - return searchFilter[0]; - } - - async searchForDashboardWithName(dashName) { - log.debug(`searchForDashboardWithName: ${dashName}`); - - await this.gotoDashboardLandingPage(); - - await retry.try(async () => { - const searchFilter = await this.getSearchFilter(); - await searchFilter.clearValue(); - await searchFilter.click(); - // Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed. - await searchFilter.type(dashName.replace('-', ' ')); - await PageObjects.common.pressEnterKey(); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading', 5000); - }); - - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async getCountOfDashboardsInListingTable() { - const dashboardTitles = await find.allByCssSelector( - '[data-test-subj^="dashboardListingTitleLink"]' - ); - return dashboardTitles.length; - } - - async getDashboardCountWithName(dashName) { - log.debug(`getDashboardCountWithName: ${dashName}`); - - await this.searchForDashboardWithName(dashName); - const links = await testSubjects.findAll( - `dashboardListingTitleLink-${dashName.replace(/ /g, '-')}` - ); - return links.length; - } - // use the search filter box to narrow the results down to a single // entry, or at least to a single page of results - async loadSavedDashboard(dashName) { - log.debug(`Load Saved Dashboard ${dashName}`); + public async loadSavedDashboard(dashboardName: string) { + log.debug(`Load Saved Dashboard ${dashboardName}`); await this.gotoDashboardLandingPage(); - await this.searchForDashboardWithName(dashName); + await listingTable.searchForItemWithName(dashboardName); await retry.try(async () => { - await this.selectDashboard(dashName); + await listingTable.clickItemLink('dashboard', dashboardName); await PageObjects.header.waitUntilLoadingHasFinished(); // check Dashboard landing page is not present await testSubjects.missingOrFail('dashboardLandingPage', { timeout: 10000 }); }); } - async getPanelTitles() { + public async getPanelTitles() { log.debug('in getPanelTitles'); const titleObjects = await testSubjects.findAll('dashboardPanelTitle'); return await Promise.all(titleObjects.map(async title => await title.getVisibleText())); } - async getPanelDimensions() { + public async getPanelDimensions() { const panels = await find.allByCssSelector('.react-grid-item'); // These are gridster-defined elements and classes - async function getPanelDimensions(panel) { - const size = await panel.getSize(); - return { - width: size.width, - height: size.height, - }; - } - - const getDimensionsPromises = _.map(panels, getPanelDimensions); - return await Promise.all(getDimensionsPromises); + return await Promise.all( + panels.map(async panel => { + const size = await panel.getSize(); + return { + width: size.width, + height: size.height, + }; + }) + ); } - async getPanelCount() { + public async getPanelCount() { log.debug('getPanelCount'); const panels = await testSubjects.findAll('embeddablePanel'); return panels.length; } - getTestVisualizations() { + public getTestVisualizations() { return [ { name: PIE_CHART_VIS_NAME, description: 'PieChart' }, { name: 'Visualization☺ VerticalBarChart', description: 'VerticalBarChart' }, @@ -513,69 +403,45 @@ export function DashboardPageProvider({ getService, getPageObjects }) { ]; } - getTestVisualizationNames() { + public getTestVisualizationNames() { return this.getTestVisualizations().map(visualization => visualization.name); } - getTestVisualizationDescriptions() { + public getTestVisualizationDescriptions() { return this.getTestVisualizations().map(visualization => visualization.description); } - async getDashboardPanels() { + public async getDashboardPanels() { return await testSubjects.findAll('embeddablePanel'); } - async addVisualizations(visualizations) { + public async addVisualizations(visualizations: string[]) { await dashboardAddPanel.addVisualizations(visualizations); } - async setTimepickerInHistoricalDataRange() { - await PageObjects.timePicker.setDefaultAbsoluteRange(); - } - - async setTimepickerInDataRange() { - const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; - const toTime = 'Apr 13, 2018 @ 00:00:00.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - } - - async setTimepickerInLogstashDataRange() { - const fromTime = 'Apr 9, 2018 @ 00:00:00.000'; - const toTime = 'Apr 13, 2018 @ 00:00:00.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - } - - async setSaveAsNewCheckBox(checked) { + public async setSaveAsNewCheckBox(checked: boolean) { log.debug('saveAsNewCheckbox: ' + checked); - const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); + let saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); const isAlreadyChecked = (await saveAsNewCheckbox.getAttribute('aria-checked')) === 'true'; if (isAlreadyChecked !== checked) { log.debug('Flipping save as new checkbox'); - const saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); + saveAsNewCheckbox = await testSubjects.find('saveAsNewCheckbox'); await retry.try(() => saveAsNewCheckbox.click()); } } - async setStoreTimeWithDashboard(checked) { + public async setStoreTimeWithDashboard(checked: boolean) { log.debug('Storing time with dashboard: ' + checked); - const storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); + let storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); const isAlreadyChecked = (await storeTimeCheckbox.getAttribute('aria-checked')) === 'true'; if (isAlreadyChecked !== checked) { log.debug('Flipping store time checkbox'); - const storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); + storeTimeCheckbox = await testSubjects.find('storeTimeWithDashboard'); await retry.try(() => storeTimeCheckbox.click()); } } - async getFilterDescriptions(timeout = defaultFindTimeout) { - const filters = await find.allByCssSelector( - '.filter-bar > .filter > .filter-description', - timeout - ); - return _.map(filters, async filter => await filter.getVisibleText()); - } - - async getSharedItemsCount() { + public async getSharedItemsCount() { log.debug('in getSharedItemsCount'); const attributeName = 'data-shared-items-count'; const element = await find.byCssSelector(`[${attributeName}]`); @@ -586,13 +452,14 @@ export function DashboardPageProvider({ getService, getPageObjects }) { throw new Error('no element'); } - async waitForRenderComplete() { + public async waitForRenderComplete() { log.debug('waitForRenderComplete'); const count = await this.getSharedItemsCount(); + // eslint-disable-next-line radix await renderable.waitForRender(parseInt(count)); } - async getSharedContainerData() { + public async getSharedContainerData() { log.debug('getSharedContainerData'); const sharedContainer = await find.byCssSelector('[data-shared-items-container]'); return { @@ -602,7 +469,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) { }; } - async getPanelSharedItemData() { + public async getPanelSharedItemData() { log.debug('in getPanelSharedItemData'); const sharedItems = await find.allByCssSelector('[data-shared-item]'); return await Promise.all( @@ -615,17 +482,17 @@ export function DashboardPageProvider({ getService, getPageObjects }) { ); } - async checkHideTitle() { + public async checkHideTitle() { log.debug('ensure that you can click on hide title checkbox'); await this.openOptions(); return await testSubjects.click('dashboardPanelTitlesCheckbox'); } - async expectMissingSaveOption() { + public async expectMissingSaveOption() { await testSubjects.missingOrFail('dashboardSaveMenuItem'); } - async getNotLoadedVisualizations(vizList) { + public async getNotLoadedVisualizations(vizList: string[]) { const checkList = []; for (const name of vizList) { const isPresent = await testSubjects.exists( diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 5526243ea2bbde..4ba8ddb0359132 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -22,7 +22,6 @@ import { CommonPageProvider } from './common_page'; import { ConsolePageProvider } from './console_page'; // @ts-ignore not TS yet import { ContextPageProvider } from './context_page'; -// @ts-ignore not TS yet import { DashboardPageProvider } from './dashboard_page'; // @ts-ignore not TS yet import { DiscoverPageProvider } from './discover_page'; diff --git a/test/functional/page_objects/time_picker.js b/test/functional/page_objects/time_picker.js index 8717517f448648..7c676784294784 100644 --- a/test/functional/page_objects/time_picker.js +++ b/test/functional/page_objects/time_picker.js @@ -264,6 +264,22 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { await this.closeQuickSelectTimeMenu(); } + + async setHistoricalDataRange() { + await this.setDefaultAbsoluteRange(); + } + + async setDefaultDataRange() { + const fromTime = 'Jan 1, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; + await this.setAbsoluteRange(fromTime, toTime); + } + + async setLogstashDataRange() { + const fromTime = 'Apr 9, 2018 @ 00:00:00.000'; + const toTime = 'Apr 13, 2018 @ 00:00:00.000'; + await this.setAbsoluteRange(fromTime, toTime); + } } return new TimePickerPage(); diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts index 4ba64ea771effe..0071b8d993f706 100644 --- a/test/functional/page_objects/visualize_page.ts +++ b/test/functional/page_objects/visualize_page.ts @@ -44,15 +44,7 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide } public async clickNewVisualization() { - // newItemButton button is only visible when there are items in the listing table is displayed. - let exists = await testSubjects.exists('newItemButton'); - if (exists) { - return await testSubjects.click('newItemButton'); - } - - exists = await testSubjects.exists('createVisualizationPromptButton'); - // no viz exist, click createVisualizationPromptButton to create new dashboard - return await this.createVisualizationPromptButton(); + await listingTable.clickNewButton('createVisualizationPromptButton'); } public async createVisualizationPromptButton() { diff --git a/test/functional/services/dashboard/visualizations.js b/test/functional/services/dashboard/visualizations.js index 5e722ccce89701..f7a6fb7d2f694f 100644 --- a/test/functional/services/dashboard/visualizations.js +++ b/test/functional/services/dashboard/visualizations.js @@ -24,7 +24,14 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); - const PageObjects = getPageObjects(['dashboard', 'visualize', 'visEditor', 'header', 'discover']); + const PageObjects = getPageObjects([ + 'dashboard', + 'visualize', + 'visEditor', + 'header', + 'discover', + 'timePicker', + ]); return new (class DashboardVisualizations { async createAndAddTSVBVisualization(name) { @@ -43,7 +50,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { log.debug(`createSavedSearch(${name})`); await PageObjects.header.clickDiscover(); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); if (query) { await queryBar.setQuery(query); diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts index ec886cf694f2e4..c7667ae7b4049e 100644 --- a/test/functional/services/listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -25,20 +25,35 @@ export function ListingTableProvider({ getService, getPageObjects }: FtrProvider const log = getService('log'); const retry = getService('retry'); const { common, header } = getPageObjects(['common', 'header']); + const prefixMap = { visualize: 'vis', dashboard: 'dashboard' }; + /** + * This class provides functions for dashboard and visualize landing pages + */ class ListingTable { - public async getSearchFilter() { + private async getSearchFilter() { const searchFilter = await find.allByCssSelector('.euiFieldSearch'); return searchFilter[0]; } - public async clearFilter() { + /** + * Returns search input value on landing page + */ + public async getSearchFilterValue() { + const searchFilter = await this.getSearchFilter(); + return await searchFilter.getAttribute('value'); + } + + /** + * Clears search input on landing page + */ + public async clearSearchFilter() { const searchFilter = await this.getSearchFilter(); await searchFilter.clearValue(); await searchFilter.click(); } - public async getAllVisualizationNamesOnCurrentPage(): Promise { + private async getAllItemsNamesOnCurrentPage(): Promise { const visualizationNames = []; const links = await find.allByCssSelector('.kuiLink'); for (let i = 0; i < links.length; i++) { @@ -48,14 +63,39 @@ export function ListingTableProvider({ getService, getPageObjects }: FtrProvider return visualizationNames; } + /** + * Navigates through all pages on Landing page and returns array of items names + */ + public async getAllItemsNames(): Promise { + log.debug('ListingTable.getAllItemsNames'); + let morePages = true; + let visualizationNames: string[] = []; + while (morePages) { + visualizationNames = visualizationNames.concat(await this.getAllItemsNamesOnCurrentPage()); + morePages = !((await testSubjects.getAttribute('pagerNextButton', 'disabled')) === 'true'); + if (morePages) { + await testSubjects.click('pagerNextButton'); + await header.waitUntilLoadingHasFinished(); + } + } + return visualizationNames; + } + + /** + * Returns items count on landing page + * @param appName 'visualize' | 'dashboard' + */ public async getItemsCount(appName: 'visualize' | 'dashboard'): Promise { - const prefixMap = { visualize: 'vis', dashboard: 'dashboard' }; const elements = await find.allByCssSelector( `[data-test-subj^="${prefixMap[appName]}ListingTitleLink"]` ); return elements.length; } + /** + * Types name into search field on Landing page and waits till search completed + * @param name item name + */ public async searchForItemWithName(name: string) { log.debug(`searchForItemWithName: ${name}`); @@ -71,10 +111,51 @@ export function ListingTableProvider({ getService, getPageObjects }: FtrProvider await header.waitUntilLoadingHasFinished(); } + /** + * Searches for item on Landing page and retruns items count that match `ListingTitleLink-${name}` pattern + * @param appName 'visualize' | 'dashboard' + * @param name item name + */ + public async searchAndGetItemsCount(appName: 'visualize' | 'dashboard', name: string) { + await this.searchForItemWithName(name); + const links = await testSubjects.findAll( + `${prefixMap[appName]}ListingTitleLink-${name.replace(/ /g, '-')}` + ); + return links.length; + } + public async clickDeleteSelected() { await testSubjects.click('deleteSelectedItems'); } + public async clickItemCheckbox(id: string) { + await testSubjects.click(`checkboxSelectRow-${id}`); + } + + /** + * Searches for item by name, selects checbox and deletes it + * @param name item name + * @param id row id + */ + public async deleteItem(name: string, id: string) { + await this.searchForItemWithName(name); + await this.clickItemCheckbox(id); + await this.clickDeleteSelected(); + await common.clickConfirmOnModal(); + } + + /** + * Clicks item on Landing page by link name if it is present + * @param appName 'dashboard' | 'visualize' + * @param name item name + */ + public async clickItemLink(appName: 'dashboard' | 'visualize', name: string) { + await testSubjects.click(`${appName}ListingTitleLink-${name.split(' ').join('-')}`); + } + + /** + * Checks 'SelectAll' checkbox on + */ public async checkListingSelectAllCheckbox() { const element = await testSubjects.find('checkboxSelectAll'); const isSelected = await element.isSelected(); @@ -84,21 +165,20 @@ export function ListingTableProvider({ getService, getPageObjects }: FtrProvider } } - public async getAllVisualizationNames(): Promise { - log.debug('ListingTable.getAllVisualizationNames'); - let morePages = true; - let visualizationNames: string[] = []; - while (morePages) { - visualizationNames = visualizationNames.concat( - await this.getAllVisualizationNamesOnCurrentPage() - ); - morePages = !((await testSubjects.getAttribute('pagerNextButton', 'disabled')) === 'true'); - if (morePages) { - await testSubjects.click('pagerNextButton'); - await header.waitUntilLoadingHasFinished(); + /** + * Clicks NewItem button on Landing page + * @param promptBtnTestSubj testSubj locator for Prompt button + */ + public async clickNewButton(promptBtnTestSubj: string): Promise { + await retry.try(async () => { + // newItemButton button is only visible when there are items in the listing table is displayed. + if (await testSubjects.exists('newItemButton')) { + await testSubjects.click('newItemButton'); + } else { + // no items exist, click createPromptButton to create new dashboard/visualization + await testSubjects.click(promptBtnTestSubj); } - } - return visualizationNames; + }); } } diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js index 510a8b035f053e..bab798dacc453b 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js @@ -43,7 +43,7 @@ export default function({ getService, getPageObjects }) { await browser.setWindowSize(1600, 1000); await PageObjects.common.navigateToApp('discover'); - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await PageObjects.discover.saveSearch(savedSearchName); await PageObjects.common.navigateToApp('dashboard'); @@ -142,7 +142,7 @@ export default function({ getService, getPageObjects }) { }); it('can filter on a visualization', async () => { - await PageObjects.dashboard.setTimepickerInHistoricalDataRange(); + await PageObjects.timePicker.setHistoricalDataRange(); await pieChart.filterOnPieSlice(); const filterCount = await filterBar.getFilterCount(); expect(filterCount).to.equal(1); diff --git a/x-pack/test/functional/apps/lens/lens_reporting.ts b/x-pack/test/functional/apps/lens/lens_reporting.ts index 8c742537431c86..3d70390a2de41a 100644 --- a/x-pack/test/functional/apps/lens/lens_reporting.ts +++ b/x-pack/test/functional/apps/lens/lens_reporting.ts @@ -12,6 +12,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'dashboard', 'reporting']); const find = getService('find'); const esArchiver = getService('esArchiver'); + const listingTable = getService('listingTable'); describe('lens reporting', () => { before(async () => { @@ -24,7 +25,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { it('should not cause PDF reports to fail', async () => { await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.selectDashboard('Lens reportz'); + await listingTable.clickItemLink('dashboard', 'Lens reportz'); await PageObjects.reporting.openPdfReportingPanel(); await PageObjects.reporting.clickGenerateReportButton(); diff --git a/x-pack/test/functional/apps/spaces/spaces_selection.ts b/x-pack/test/functional/apps/spaces/spaces_selection.ts index 3d1ef40262b1dd..5af9bc135ae27c 100644 --- a/x-pack/test/functional/apps/spaces/spaces_selection.ts +++ b/x-pack/test/functional/apps/spaces/spaces_selection.ts @@ -10,6 +10,7 @@ export default function spaceSelectorFunctonalTests({ getPageObjects, }: FtrProviderContext) { const esArchiver = getService('esArchiver'); + const listingTable = getService('listingTable'); const PageObjects = getPageObjects([ 'common', 'dashboard', @@ -55,8 +56,8 @@ export default function spaceSelectorFunctonalTests({ const sampleDataHash = '/home/tutorial_directory/sampleData'; const expectDashboardRenders = async (dashName: string) => { - await PageObjects.dashboard.searchForDashboardWithName(dashName); - await PageObjects.dashboard.selectDashboard(dashName); + await listingTable.searchForItemWithName(dashName); + await listingTable.clickItemLink('dashboard', dashName); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.dashboard.waitForRenderComplete(); // throws if all items are not rendered }; From 643912e4f58fe2a411a328325ca48c45b77b4c29 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 14 Jan 2020 11:12:53 -0500 Subject: [PATCH 06/52] [Maps] add labels to sample data maps (#54671) * [Maps] add count labels to sample data maps * [Maps] add labels to sample data maps * review feedback Co-authored-by: Elastic Machine --- .../es_geo_grid_source/es_geo_grid_source.js | 19 ++++++----- .../es_pew_pew_source/es_pew_pew_source.js | 9 +----- .../sample_data/ecommerce_saved_objects.js | 32 +++++++++++++++---- .../sample_data/flights_saved_objects.js | 6 ---- .../sample_data/web_logs_saved_objects.js | 26 ++++++++++++--- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js index 4a18fdc1a2e96b..d4f36a63e5989f 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -23,12 +23,7 @@ import { RENDER_AS } from './render_as'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; import { GRID_RESOLUTION } from '../../grid_resolution'; -import { - SOURCE_DATA_ID_ORIGIN, - ES_GEO_GRID, - COUNT_PROP_LABEL, - COUNT_PROP_NAME, -} from '../../../../common/constants'; +import { SOURCE_DATA_ID_ORIGIN, ES_GEO_GRID, COUNT_PROP_NAME } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { AbstractESAggSource } from '../es_agg_source'; @@ -251,7 +246,6 @@ export class ESGeoGridSource extends AbstractESAggSource { options: { ...defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR].options, field: { - label: COUNT_PROP_LABEL, name: COUNT_PROP_NAME, origin: SOURCE_DATA_ID_ORIGIN, }, @@ -275,7 +269,16 @@ export class ESGeoGridSource extends AbstractESAggSource { options: { ...defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options, field: { - label: COUNT_PROP_LABEL, + name: COUNT_PROP_NAME, + origin: SOURCE_DATA_ID_ORIGIN, + }, + }, + }, + [VECTOR_STYLES.LABEL_TEXT]: { + type: DynamicStyleProperty.type, + options: { + ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options, + field: { name: COUNT_PROP_NAME, origin: SOURCE_DATA_ID_ORIGIN, }, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js index 66d62dd5644ac9..3579027b278479 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js @@ -17,12 +17,7 @@ import { VECTOR_STYLES, } from '../../styles/vector/vector_style_defaults'; import { i18n } from '@kbn/i18n'; -import { - SOURCE_DATA_ID_ORIGIN, - ES_PEW_PEW, - COUNT_PROP_NAME, - COUNT_PROP_LABEL, -} from '../../../../common/constants'; +import { SOURCE_DATA_ID_ORIGIN, ES_PEW_PEW, COUNT_PROP_NAME } from '../../../../common/constants'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { convertToLines } from './convert_to_lines'; import { Schemas } from 'ui/vis/editors/default/schemas'; @@ -138,7 +133,6 @@ export class ESPewPewSource extends AbstractESAggSource { options: { ...defaultDynamicProperties[VECTOR_STYLES.LINE_COLOR].options, field: { - label: COUNT_PROP_LABEL, name: COUNT_PROP_NAME, origin: SOURCE_DATA_ID_ORIGIN, }, @@ -150,7 +144,6 @@ export class ESPewPewSource extends AbstractESAggSource { options: { ...defaultDynamicProperties[VECTOR_STYLES.LINE_WIDTH].options, field: { - label: COUNT_PROP_LABEL, name: COUNT_PROP_NAME, origin: SOURCE_DATA_ID_ORIGIN, }, diff --git a/x-pack/legacy/plugins/maps/server/sample_data/ecommerce_saved_objects.js b/x-pack/legacy/plugins/maps/server/sample_data/ecommerce_saved_objects.js index d5bcfa7cd1512c..a3dbf8b1438fa9 100644 --- a/x-pack/legacy/plugins/maps/server/sample_data/ecommerce_saved_objects.js +++ b/x-pack/legacy/plugins/maps/server/sample_data/ecommerce_saved_objects.js @@ -39,7 +39,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'count of kibana_sample_data_ecommerce:geoip.country_iso_code', name: '__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.country_iso_code', origin: 'join', }, @@ -104,7 +103,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'count of kibana_sample_data_ecommerce:geoip.region_name', name: '__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name', origin: 'join', }, @@ -169,7 +167,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'count of kibana_sample_data_ecommerce:geoip.region_name', name: '__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name', origin: 'join', }, @@ -234,7 +231,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'count of kibana_sample_data_ecommerce:geoip.region_name', name: '__kbnjoin__count_groupby_kibana_sample_data_ecommerce.geoip.region_name', origin: 'join', }, @@ -314,7 +310,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'taxful_total_price', name: 'taxful_total_price', origin: 'source', }, @@ -376,7 +371,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'Count', name: 'doc_count', origin: 'source', }, @@ -399,7 +393,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'sum of taxful_total_price', name: 'sum_of_taxful_total_price', origin: 'source', }, @@ -407,6 +400,31 @@ const layerList = [ maxSize: 20, }, }, + labelText: { + type: 'DYNAMIC', + options: { + field: { + name: 'sum_of_taxful_total_price', + origin: 'source', + }, + }, + }, + labelSize: { + type: 'DYNAMIC', + options: { + field: { + name: 'sum_of_taxful_total_price', + origin: 'source', + }, + minSize: 12, + maxSize: 24, + }, + }, + labelBorderSize: { + options: { + size: 'MEDIUM', + }, + }, }, }, type: 'VECTOR', diff --git a/x-pack/legacy/plugins/maps/server/sample_data/flights_saved_objects.js b/x-pack/legacy/plugins/maps/server/sample_data/flights_saved_objects.js index aa3d5488764ad2..cff4ad8182a9d1 100644 --- a/x-pack/legacy/plugins/maps/server/sample_data/flights_saved_objects.js +++ b/x-pack/legacy/plugins/maps/server/sample_data/flights_saved_objects.js @@ -54,7 +54,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'FlightTimeMin', name: 'FlightTimeMin', origin: 'source', }, @@ -77,7 +76,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'DistanceMiles', name: 'DistanceMiles', origin: 'source', }, @@ -122,7 +120,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'Count', name: 'doc_count', origin: 'source', }, @@ -145,7 +142,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'avg of FlightTimeMin', name: 'avg_of_FlightTimeMin', origin: 'source', }, @@ -190,7 +186,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'Count', name: 'doc_count', origin: 'source', }, @@ -213,7 +208,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'avg of FlightDelayMin', name: 'avg_of_FlightDelayMin', origin: 'source', }, diff --git a/x-pack/legacy/plugins/maps/server/sample_data/web_logs_saved_objects.js b/x-pack/legacy/plugins/maps/server/sample_data/web_logs_saved_objects.js index 74039b11db727d..ec445567de21c6 100644 --- a/x-pack/legacy/plugins/maps/server/sample_data/web_logs_saved_objects.js +++ b/x-pack/legacy/plugins/maps/server/sample_data/web_logs_saved_objects.js @@ -39,7 +39,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'count of kibana_sample_data_logs:geo.src', name: '__kbnjoin__count_groupby_kibana_sample_data_logs.geo.src', origin: 'join', }, @@ -135,7 +134,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'bytes', name: 'bytes', origin: 'source', }, @@ -179,7 +177,6 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'Count', name: 'doc_count', origin: 'source', }, @@ -202,14 +199,33 @@ const layerList = [ type: 'DYNAMIC', options: { field: { - label: 'sum of bytes', name: 'sum_of_bytes', origin: 'source', }, - minSize: 1, + minSize: 7, maxSize: 25, }, }, + labelText: { + type: 'DYNAMIC', + options: { + field: { + name: 'doc_count', + origin: 'source', + }, + }, + }, + labelSize: { + type: 'DYNAMIC', + options: { + field: { + name: 'doc_count', + origin: 'source', + }, + minSize: 12, + maxSize: 24, + }, + }, }, }, type: 'VECTOR', From 569b1f66064144763e6cf1e5fa2cb46eeecd5071 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Tue, 14 Jan 2020 09:25:07 -0700 Subject: [PATCH 07/52] [SIEM] Use import/export API instead of client implementation (#54680) ## Summary This PR switches the Rule Import / Export functionality away from the client-side implementation (that was leveraging the create/read Rule API) to the new explicit `/rules/_import` & `/rules/_export` API introduced in https://github.com/elastic/kibana/pull/54332. Note: This PR also disables the ability to export `immutable` rules. ![image](https://user-images.githubusercontent.com/2946766/72311962-c0963680-3643-11ea-812f-237bc51be7dc.png) Sample error message: Screen Shot 2020-01-13 at 20 22 45 ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. - [X] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) - [X] 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/master/packages/kbn-i18n/README.md) - [ ] ~[Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~ - [X] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios - [ ] ~This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~ ### For maintainers - [ ] ~This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~ - [ ] ~This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~ --- .../siem/public/components/toasters/index.tsx | 28 ++++- .../containers/detection_engine/rules/api.ts | 79 +++++++++++++ .../detection_engine/rules/types.ts | 27 +++++ .../rules/all/batch_actions.tsx | 2 +- .../detection_engine/rules/all/columns.tsx | 1 + .../detection_engine/rules/all/index.tsx | 6 +- .../detection_engine/rules/all/reducer.ts | 6 +- .../__snapshots__/index.test.tsx.snap | 7 +- .../components/import_rule_modal/index.tsx | 105 ++++++++---------- .../import_rule_modal/translations.ts | 13 ++- .../__snapshots__/index.test.tsx.snap | 3 - .../components/json_downloader/index.test.tsx | 60 ---------- .../components/json_downloader/index.tsx | 75 ------------- .../__snapshots__/index.test.tsx.snap | 3 + .../components/rule_downloader/index.test.tsx | 18 +++ .../components/rule_downloader/index.tsx | 89 +++++++++++++++ .../rule_downloader/translations.ts | 14 +++ .../detection_engine/rules/translations.ts | 2 +- 18 files changed, 325 insertions(+), 213 deletions(-) delete mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/__snapshots__/index.test.tsx.snap delete mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.test.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/translations.ts diff --git a/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx b/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx index b046c91dcd912b..6d13bbd778f53f 100644 --- a/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/toasters/index.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiGlobalToastList, EuiGlobalToastListToast as Toast, EuiButton } from '@elastic/eui'; +import { EuiButton, EuiGlobalToastList, EuiGlobalToastListToast as Toast } from '@elastic/eui'; import { noop } from 'lodash/fp'; -import React, { createContext, Dispatch, useReducer, useContext, useState } from 'react'; +import React, { createContext, Dispatch, useContext, useReducer, useState } from 'react'; import styled from 'styled-components'; import uuid from 'uuid'; @@ -143,7 +143,7 @@ export const displayErrorToast = ( errorTitle: string, errorMessages: string[], dispatchToaster: React.Dispatch -) => { +): void => { const toast: AppToast = { id: uuid.v4(), title: errorTitle, @@ -156,3 +156,25 @@ export const displayErrorToast = ( toast, }); }; + +/** + * Displays a success toast for the provided title and message + * + * @param title success message to display in toaster and modal + * @param dispatchToaster provided by useStateToaster() + */ +export const displaySuccessToast = ( + title: string, + dispatchToaster: React.Dispatch +): void => { + const toast: AppToast = { + id: uuid.v4(), + title, + color: 'success', + iconType: 'check', + }; + dispatchToaster({ + type: 'addToaster', + toast, + }); +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts index 8f8b66ae35a3b5..a13d6b75af6301 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts @@ -16,13 +16,17 @@ import { Rule, FetchRuleProps, BasicFetchProps, + ImportRulesProps, + ExportRulesProps, RuleError, + ImportRulesResponse, } from './types'; import { throwIfNotOk } from '../../../hooks/api/api'; import { DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_PREPACKAGED_URL, } from '../../../../common/constants'; +import * as i18n from '../../../pages/detection_engine/rules/translations'; /** * Add provided Rule @@ -223,3 +227,78 @@ export const createPrepackagedRules = async ({ signal }: BasicFetchProps): Promi await throwIfNotOk(response); return true; }; + +/** + * Imports rules in the same format as exported via the _export API + * + * @param fileToImport File to upload containing rules to import + * @param overwrite whether or not to overwrite rules with the same ruleId + * @param signal AbortSignal for cancelling request + * + * @throws An error if response is not OK + */ +export const importRules = async ({ + fileToImport, + overwrite = false, + signal, +}: ImportRulesProps): Promise => { + const formData = new FormData(); + formData.append('file', fileToImport); + + const response = await fetch( + `${chrome.getBasePath()}${DETECTION_ENGINE_RULES_URL}/_import?overwrite=${overwrite}`, + { + method: 'POST', + credentials: 'same-origin', + headers: { + 'kbn-xsrf': 'true', + }, + body: formData, + signal, + } + ); + + await throwIfNotOk(response); + return response.json(); +}; + +/** + * Export rules from the server as a file download + * + * @param excludeExportDetails whether or not to exclude additional details at bottom of exported file (defaults to false) + * @param filename of exported rules. Be sure to include `.ndjson` extension! (defaults to localized `rules_export.ndjson`) + * @param ruleIds array of rule_id's (not id!) to export (empty array exports _all_ rules) + * @param signal AbortSignal for cancelling request + * + * @throws An error if response is not OK + */ +export const exportRules = async ({ + excludeExportDetails = false, + filename = `${i18n.EXPORT_FILENAME}.ndjson`, + ruleIds = [], + signal, +}: ExportRulesProps): Promise => { + const body = + ruleIds.length > 0 + ? JSON.stringify({ objects: ruleIds.map(rule => ({ rule_id: rule })) }) + : undefined; + + const response = await fetch( + `${chrome.getBasePath()}${DETECTION_ENGINE_RULES_URL}/_export?exclude_export_details=${excludeExportDetails}&file_name=${encodeURIComponent( + filename + )}`, + { + method: 'POST', + credentials: 'same-origin', + headers: { + 'content-type': 'application/json', + 'kbn-xsrf': 'true', + }, + body, + signal, + } + ); + + await throwIfNotOk(response); + return response.blob(); +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index 147b04567f6c7c..7714779edf0571 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -148,3 +148,30 @@ export interface DuplicateRulesProps { export interface BasicFetchProps { signal: AbortSignal; } + +export interface ImportRulesProps { + fileToImport: File; + overwrite?: boolean; + signal: AbortSignal; +} + +export interface ImportRulesResponseError { + rule_id: string; + error: { + status_code: number; + message: string; + }; +} + +export interface ImportRulesResponse { + success: boolean; + success_count: number; + errors: ImportRulesResponseError[]; +} + +export interface ExportRulesProps { + ruleIds?: string[]; + filename?: string; + excludeExportDetails?: boolean; + signal: AbortSignal; +} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx index 3356ef101677d0..0971ef0149304d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx @@ -51,7 +51,7 @@ export const getBatchItems = ( { closePopover(); await exportRulesAction( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index 0c1804f26ecdd8..ed5dc6913151a4 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -64,6 +64,7 @@ const getActions = ( icon: 'exportAction', name: i18n.EXPORT_RULE, onClick: (rowItem: TableData) => exportRulesAction([rowItem.sourceRule], dispatch), + enabled: (rowItem: TableData) => !rowItem.immutable, }, { description: i18n.DELETE_RULE, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index 819b513ecc9bc9..cb4ffa127781db 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -31,7 +31,7 @@ import { getBatchItems } from './batch_actions'; import { EuiBasicTableOnChange, TableData } from '../types'; import { allRulesReducer, State } from './reducer'; import * as i18n from '../translations'; -import { JSONDownloader } from '../components/json_downloader'; +import { RuleDownloader } from '../components/rule_downloader'; import { useStateToaster } from '../../../../components/toasters'; const initialState: State = { @@ -150,9 +150,9 @@ export const AllRules = React.memo<{ return ( <> - { dispatchToaster({ type: 'addToaster', diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts index 4a6be1a7e4da7e..22d6ca2195fe66 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts @@ -20,7 +20,7 @@ export interface State { filterOptions: FilterOptions; refreshToggle: boolean; tableData: TableData[]; - exportPayload?: object[]; + exportPayload?: Rule[]; } export type Action = @@ -28,7 +28,7 @@ export type Action = | { type: 'loading'; isLoading: boolean } | { type: 'deleteRules'; rules: Rule[] } | { type: 'duplicate'; rule: Rule } - | { type: 'setExportPayload'; exportPayload?: object[] } + | { type: 'setExportPayload'; exportPayload?: Rule[] } | { type: 'setSelected'; selectedItems: TableData[] } | { type: 'updateLoading'; ids: string[]; isLoading: boolean } | { type: 'updateRules'; rules: Rule[]; appendRuleId?: string; pagination?: PaginationOptions } @@ -143,7 +143,7 @@ export const allRulesReducer = (state: State, action: Action): State => { case 'setExportPayload': { return { ...state, - exportPayload: action.exportPayload, + exportPayload: [...(action.exportPayload ?? [])], }; } default: diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/__snapshots__/index.test.tsx.snap index 6b0aa02d4edfac..64b88912e53a3d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/__snapshots__/index.test.tsx.snap @@ -28,9 +28,8 @@ exports[`ImportRuleModal renders correctly against snapshot 1`] = ` display="large" fullWidth={true} id="rule-file-picker" - initialPromptText="Select or drag and drop files" + initialPromptText="Select or drag and drop a valid rules_export.ndjson file" isLoading={false} - multiple={true} onChange={[Function]} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx index 75be92f2fe8467..91b2ee283609fa 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/index.tsx @@ -8,28 +8,25 @@ import { EuiButton, EuiButtonEmpty, EuiCheckbox, + // @ts-ignore no-exported-member + EuiFilePicker, EuiModal, EuiModalBody, EuiModalFooter, EuiModalHeader, EuiModalHeaderTitle, EuiOverlayMask, - // @ts-ignore no-exported-member - EuiFilePicker, EuiSpacer, EuiText, } from '@elastic/eui'; -import { noop } from 'lodash/fp'; import React, { useCallback, useState } from 'react'; -import { failure } from 'io-ts/lib/PathReporter'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import uuid from 'uuid'; - -import { duplicateRules, RulesSchema } from '../../../../../containers/detection_engine/rules'; -import { useStateToaster } from '../../../../../components/toasters'; -import { ndjsonToJSON } from '../json_downloader'; + +import { importRules } from '../../../../../containers/detection_engine/rules'; +import { + displayErrorToast, + displaySuccessToast, + useStateToaster, +} from '../../../../../components/toasters'; import * as i18n from './translations'; interface ImportRuleModalProps { @@ -40,10 +37,6 @@ interface ImportRuleModalProps { /** * Modal component for importing Rules from a json file - * - * @param filename name of file to be downloaded - * @param payload JSON string to write to file - * */ export const ImportRuleModalComponent = ({ showModal, @@ -52,6 +45,7 @@ export const ImportRuleModalComponent = ({ }: ImportRuleModalProps) => { const [selectedFiles, setSelectedFiles] = useState(null); const [isImporting, setIsImporting] = useState(false); + const [overwrite, setOverwrite] = useState(false); const [, dispatchToaster] = useStateToaster(); const cleanupAndCloseModal = () => { @@ -60,49 +54,41 @@ export const ImportRuleModalComponent = ({ closeModal(); }; - const importRules = useCallback(async () => { + const importRulesCallback = useCallback(async () => { if (selectedFiles != null) { setIsImporting(true); - const reader = new FileReader(); - reader.onload = async event => { - // @ts-ignore type is string, not ArrayBuffer as FileReader.readAsText is called - const importedRules = ndjsonToJSON(event?.target?.result ?? ''); - - const decodedRules = pipe( - RulesSchema.decode(importedRules), - fold(errors => { - cleanupAndCloseModal(); - dispatchToaster({ - type: 'addToaster', - toast: { - id: uuid.v4(), - title: i18n.IMPORT_FAILED, - color: 'danger', - iconType: 'alert', - errors: failure(errors), - }, - }); - throw new Error(failure(errors).join('\n')); - }, identity) - ); - - const duplicatedRules = await duplicateRules({ rules: decodedRules }); - importComplete(); - cleanupAndCloseModal(); + const abortCtrl = new AbortController(); - dispatchToaster({ - type: 'addToaster', - toast: { - id: uuid.v4(), - title: i18n.SUCCESSFULLY_IMPORTED_RULES(duplicatedRules.length), - color: 'success', - iconType: 'check', - }, + try { + const importResponse = await importRules({ + fileToImport: selectedFiles[0], + overwrite, + signal: abortCtrl.signal, }); - }; - Object.values(selectedFiles).map(f => reader.readAsText(f)); + + // TODO: Improve error toast details for better debugging failed imports + // e.g. When success == true && success_count === 0 that means no rules were overwritten, etc + if (importResponse.success) { + displaySuccessToast( + i18n.SUCCESSFULLY_IMPORTED_RULES(importResponse.success_count), + dispatchToaster + ); + } + if (importResponse.errors.length > 0) { + const formattedErrors = importResponse.errors.map(e => + i18n.IMPORT_FAILED_DETAILED(e.rule_id, e.error.status_code, e.error.message) + ); + displayErrorToast(i18n.IMPORT_FAILED, formattedErrors, dispatchToaster); + } + + importComplete(); + cleanupAndCloseModal(); + } catch (e) { + cleanupAndCloseModal(); + displayErrorToast(i18n.IMPORT_FAILED, [e.message], dispatchToaster); + } } - }, [selectedFiles]); + }, [selectedFiles, overwrite]); return ( <> @@ -121,7 +107,6 @@ export const ImportRuleModalComponent = ({ { setSelectedFiles(Object.keys(files).length > 0 ? files : null); @@ -134,14 +119,18 @@ export const ImportRuleModalComponent = ({ noop} + checked={overwrite} + onChange={() => setOverwrite(!overwrite)} /> {i18n.CANCEL_BUTTON} - + {i18n.IMPORT_RULE} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/translations.ts index 50c3c75b6109f3..dab1c9490591f1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/import_rule_modal/translations.ts @@ -23,14 +23,14 @@ export const SELECT_RULE = i18n.translate( export const INITIAL_PROMPT_TEXT = i18n.translate( 'xpack.siem.detectionEngine.components.importRuleModal.initialPromptTextDescription', { - defaultMessage: 'Select or drag and drop files', + defaultMessage: 'Select or drag and drop a valid rules_export.ndjson file', } ); export const OVERWRITE_WITH_SAME_NAME = i18n.translate( 'xpack.siem.detectionEngine.components.importRuleModal.overwriteDescription', { - defaultMessage: 'Automatically overwrite saved objects with the same name', + defaultMessage: 'Automatically overwrite saved objects with the same rule ID', } ); @@ -57,3 +57,12 @@ export const IMPORT_FAILED = i18n.translate( defaultMessage: 'Failed to import rules', } ); + +export const IMPORT_FAILED_DETAILED = (ruleId: string, statusCode: number, message: string) => + i18n.translate( + 'xpack.siem.detectionEngine.components.importRuleModal.importFailedDetailedTitle', + { + values: { ruleId, statusCode, message }, + defaultMessage: 'Rule ID: {ruleId}\n Status Code: {statusCode}\n Message: {message}', + } + ); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/__snapshots__/index.test.tsx.snap deleted file mode 100644 index c4377c265c2c29..00000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`JSONDownloader renders correctly against snapshot 1`] = ``; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.test.tsx deleted file mode 100644 index 859918cdc8e605..00000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.test.tsx +++ /dev/null @@ -1,60 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; -import { JSONDownloaderComponent, jsonToNDJSON, ndjsonToJSON } from './index'; - -const jsonArray = [ - { - description: 'Detecting root and admin users1', - created_by: 'elastic', - false_positives: [], - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - max_signals: 100, - }, - { - description: 'Detecting root and admin users2', - created_by: 'elastic', - false_positives: [], - index: ['auditbeat-*', 'packetbeat-*', 'winlogbeat-*'], - max_signals: 101, - }, -]; - -const ndjson = `{"description":"Detecting root and admin users1","created_by":"elastic","false_positives":[],"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"max_signals":100} -{"description":"Detecting root and admin users2","created_by":"elastic","false_positives":[],"index":["auditbeat-*","packetbeat-*","winlogbeat-*"],"max_signals":101}`; - -const ndjsonSorted = `{"created_by":"elastic","description":"Detecting root and admin users1","false_positives":[],"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"max_signals":100} -{"created_by":"elastic","description":"Detecting root and admin users2","false_positives":[],"index":["auditbeat-*","packetbeat-*","winlogbeat-*"],"max_signals":101}`; - -describe('JSONDownloader', () => { - test('renders correctly against snapshot', () => { - const wrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); - }); - - describe('jsonToNDJSON', () => { - test('converts to NDJSON', () => { - const output = jsonToNDJSON(jsonArray, false); - expect(output).toEqual(ndjson); - }); - - test('converts to NDJSON with keys sorted', () => { - const output = jsonToNDJSON(jsonArray); - expect(output).toEqual(ndjsonSorted); - }); - }); - - describe('ndjsonToJSON', () => { - test('converts to JSON', () => { - const output = ndjsonToJSON(ndjson); - expect(output).toEqual(jsonArray); - }); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.tsx deleted file mode 100644 index 2810e0b5e16800..00000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/json_downloader/index.tsx +++ /dev/null @@ -1,75 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect, useRef } from 'react'; -import styled from 'styled-components'; - -const InvisibleAnchor = styled.a` - display: none; -`; - -export interface JSONDownloaderProps { - filename: string; - payload?: object[]; - onExportComplete: (exportCount: number) => void; -} - -/** - * Component for downloading JSON as a file. Download will occur on each update to `payload` param - * - * @param filename name of file to be downloaded - * @param payload JSON string to write to file - * - */ -export const JSONDownloaderComponent = ({ - filename, - payload, - onExportComplete, -}: JSONDownloaderProps) => { - const anchorRef = useRef(null); - - useEffect(() => { - if (anchorRef && anchorRef.current && payload != null) { - const blob = new Blob([jsonToNDJSON(payload)], { type: 'application/json' }); - // @ts-ignore function is not always defined -- this is for supporting IE - if (window.navigator.msSaveOrOpenBlob) { - window.navigator.msSaveBlob(blob); - } else { - const objectURL = window.URL.createObjectURL(blob); - anchorRef.current.href = objectURL; - anchorRef.current.download = filename; - anchorRef.current.click(); - window.URL.revokeObjectURL(objectURL); - } - onExportComplete(payload.length); - } - }, [payload]); - - return ; -}; - -JSONDownloaderComponent.displayName = 'JSONDownloaderComponent'; - -export const JSONDownloader = React.memo(JSONDownloaderComponent); - -JSONDownloader.displayName = 'JSONDownloader'; - -export const jsonToNDJSON = (jsonArray: object[], sortKeys = true): string => { - return jsonArray - .map(j => JSON.stringify(j, sortKeys ? Object.keys(j).sort() : null, 0)) - .join('\n'); -}; - -export const ndjsonToJSON = (ndjson: string): object[] => { - const jsonLines = ndjson.split(/\r?\n/); - return jsonLines.reduce((acc, line) => { - try { - return [...acc, JSON.parse(line)]; - } catch (e) { - return acc; - } - }, []); -}; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000000..4259b68bf14a21 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/__snapshots__/index.test.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RuleDownloader renders correctly against snapshot 1`] = ``; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.test.tsx new file mode 100644 index 00000000000000..6306260dfc872f --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.test.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import React from 'react'; +import { RuleDownloaderComponent } from './index'; + +describe('RuleDownloader', () => { + test('renders correctly against snapshot', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.tsx new file mode 100644 index 00000000000000..b41265adea6b10 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/index.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useRef } from 'react'; +import styled from 'styled-components'; +import { isFunction } from 'lodash/fp'; +import { exportRules, Rule } from '../../../../../containers/detection_engine/rules'; +import { displayErrorToast, useStateToaster } from '../../../../../components/toasters'; +import * as i18n from './translations'; + +const InvisibleAnchor = styled.a` + display: none; +`; + +export interface RuleDownloaderProps { + filename: string; + rules?: Rule[]; + onExportComplete: (exportCount: number) => void; +} + +/** + * Component for downloading Rules as an exported .ndjson file. Download will occur on each update to `rules` param + * + * @param filename of file to be downloaded + * @param payload Rule[] + * + */ +export const RuleDownloaderComponent = ({ + filename, + rules, + onExportComplete, +}: RuleDownloaderProps) => { + const anchorRef = useRef(null); + const [, dispatchToaster] = useStateToaster(); + + useEffect(() => { + let isSubscribed = true; + const abortCtrl = new AbortController(); + + async function exportData() { + if (anchorRef && anchorRef.current && rules != null) { + try { + const exportResponse = await exportRules({ + ruleIds: rules.map(r => r.rule_id), + signal: abortCtrl.signal, + }); + + if (isSubscribed) { + // this is for supporting IE + if (isFunction(window.navigator.msSaveOrOpenBlob)) { + window.navigator.msSaveBlob(exportResponse); + } else { + const objectURL = window.URL.createObjectURL(exportResponse); + // These are safe-assignments as writes to anchorRef are isolated to exportData + anchorRef.current.href = objectURL; // eslint-disable-line require-atomic-updates + anchorRef.current.download = filename; // eslint-disable-line require-atomic-updates + anchorRef.current.click(); + window.URL.revokeObjectURL(objectURL); + } + + onExportComplete(rules.length); + } + } catch (error) { + if (isSubscribed) { + displayErrorToast(i18n.EXPORT_FAILURE, [error.message], dispatchToaster); + } + } + } + } + + exportData(); + + return () => { + isSubscribed = false; + abortCtrl.abort(); + }; + }, [rules]); + + return ; +}; + +RuleDownloaderComponent.displayName = 'RuleDownloaderComponent'; + +export const RuleDownloader = React.memo(RuleDownloaderComponent); + +RuleDownloader.displayName = 'RuleDownloader'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/translations.ts new file mode 100644 index 00000000000000..72efefa1c461b6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_downloader/translations.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const EXPORT_FAILURE = i18n.translate( + 'xpack.siem.detectionEngine.rules.components.ruleDownloader.exportFailureTitle', + { + defaultMessage: 'Failed to export rules…', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts index d55e08e9ecd738..1e47d1a57facc2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts @@ -123,7 +123,7 @@ export const SUCCESSFULLY_EXPORTED_RULES = (totalRules: number) => i18n.translate('xpack.siem.detectionEngine.rules.allRules.successfullyExportedRulesTitle', { values: { totalRules }, defaultMessage: - 'Successfully exported {totalRules} {totalRules, plural, =1 {rule} other {rules}}', + 'Successfully exported {totalRules, plural, =0 {all rules} =1 {{totalRules} rule} other {{totalRules} rules}}', }); export const ALL_RULES = i18n.translate('xpack.siem.detectionEngine.rules.allRules.tableTitle', { From ea82503f491bac8ff636eae7d8924ed7f182b198 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Tue, 14 Jan 2020 11:38:14 -0500 Subject: [PATCH 08/52] Clarify the section on TLS for "Securing the reporting endpoints" (#54741) --- docs/user/security/reporting.asciidoc | 40 +++++++++++++++++---------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/user/security/reporting.asciidoc b/docs/user/security/reporting.asciidoc index c2ed295e83ce95..5f5d85fe8d3beb 100644 --- a/docs/user/security/reporting.asciidoc +++ b/docs/user/security/reporting.asciidoc @@ -125,23 +125,33 @@ the {reporting} endpoints to authorized users. This requires that you: . Enable {security} on your {es} cluster. For more information, see {ref}/security-getting-started.html[Getting Started with Security]. -. Configure an SSL certificate for Kibana. For more information, see -<>. -. Configure {watcher} to trust the Kibana server's certificate by adding it to -the {watcher} truststore on each node: -.. Import the {kib} server certificate into the {watcher} truststore using -Java Keytool: +. Configure TLS/SSL encryption for the {kib} server. For more information, see +<>. +. Specify the {kib} server's CA certificate chain in `elasticsearch.yml`: + -[source,shell] ---------------------------------------------------------- -keytool -importcert -keystore watcher-truststore.jks -file server.crt ---------------------------------------------------------- -+ -NOTE: If the truststore doesn't already exist, it is created. +-- +If you are using your own CA to sign the {kib} server certificate, then you need +to specify the CA certificate chain in {es} to properly establish trust in TLS +connections between {watcher} and {kib}. If your CA certificate chain is +contained in a PKCS #12 trust store, specify it like so: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.http.ssl.truststore.path: "/path/to/your/truststore.p12" +xpack.http.ssl.truststore.type: "PKCS12" +xpack.http.ssl.truststore.password: "optional decryption password" +-------------------------------------------------------------------------------- + +Otherwise, if your CA certificate chain is in PEM format, specify it like so: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.http.ssl.certificate_authorities: ["/path/to/your/cacert1.pem", "/path/to/your/cacert2.pem"] +-------------------------------------------------------------------------------- + +For more information, see {ref}/notification-settings.html#ssl-notification-settings[the {watcher} HTTP TLS/SSL Settings]. +-- -.. Make sure the `xpack.http.ssl.truststore.path` setting in -`elasticsearch.yml` specifies the location of the {watcher} -truststore. . Add one or more users who have the permissions necessary to use {kib} and {reporting}. For more information, see <>. From b298dd4c20fa1c39e634f4d25c8d76978cd51f9e Mon Sep 17 00:00:00 2001 From: Tim Schnell Date: Tue, 14 Jan 2020 10:53:27 -0600 Subject: [PATCH 09/52] fixing color and toggle accessibility (#54661) * fixing color and toggle accessibility * updating snapshots * fixing more snapshots * fixing toggle console warning --- .../uis/arguments/palette.js | 4 ++- .../canvas_plugin_src/uis/arguments/shape.js | 1 + .../canvas_plugin_src/uis/arguments/toggle.js | 5 ++- .../legacy/plugins/canvas/i18n/components.ts | 10 +++++- .../color_palette.examples.storyshot | 36 +++++++++++++++++++ .../color_palette/color_palette.tsx | 1 + .../color_picker.examples.storyshot | 21 +++++++++++ .../color_picker_popover.examples.storyshot | 4 +++ .../color_picker_popover.examples.tsx | 4 +++ .../color_picker_popover.tsx | 10 ++++-- .../palette_picker/palette_picker.js | 4 +-- .../shape_picker_popover.tsx | 5 +-- .../text_style_picker/text_style_picker.js | 1 + .../workpad_color_picker.tsx | 11 +++++- .../expression_types/arg_types/color.js | 9 +++-- .../extended_template.examples.storyshot | 3 ++ .../simple_template.examples.storyshot | 2 ++ .../arg_types/container_style/border_form.tsx | 1 + .../container_style/simple_template.tsx | 4 +++ .../series_style/simple_template.tsx | 1 + 20 files changed, 125 insertions(+), 12 deletions(-) diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js index 69f584af41556b..d60dc13f0105b4 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js @@ -61,7 +61,9 @@ const PaletteArgInput = ({ onValueChange, argValue, renderError }) => { const palette = astToPalette(argValue); - return ; + return ( + + ); }; PaletteArgInput.propTypes = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js index c056e7d1f2281c..baa2127b03c3c4 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js @@ -20,6 +20,7 @@ const ShapeArgInput = ({ onValueChange, argValue, typeInstance }) => ( value={argValue} onChange={onValueChange} shapes={typeInstance.options.shapes} + ariaLabel={typeInstance.displayName} /> diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js index de19d3e29221bb..bcad4678e0b6a8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js @@ -12,7 +12,7 @@ import { ArgumentStrings } from '../../../i18n'; const { Toggle: strings } = ArgumentStrings; -const ToggleArgInput = ({ onValueChange, argValue, argId, renderError }) => { +const ToggleArgInput = ({ onValueChange, argValue, argId, renderError, typeInstance }) => { const handleChange = () => onValueChange(!argValue); if (typeof argValue !== 'boolean') { renderError(); @@ -26,6 +26,9 @@ const ToggleArgInput = ({ onValueChange, argValue, argId, renderError }) => { checked={argValue} onChange={handleChange} className="canvasArg__switch" + aria-label={typeInstance.displayName} + label="" + showLabel={false} /> ); diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index c898db7467b445..d0a9051d7af87b 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -912,6 +912,10 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.textStylePicker.styleUnderlineOption', { defaultMessage: 'Underline', }), + getFontColorLabel: () => + i18n.translate('xpack.canvas.textStylePicker.fontColorLabel', { + defaultMessage: 'Font Color', + }), }, TimePicker: { getApplyButtonLabel: () => @@ -1007,7 +1011,11 @@ export const ComponentStrings = { getUSLetterButtonLabel: () => i18n.translate('xpack.canvas.workpadConfig.USLetterButtonLabel', { defaultMessage: 'US Letter', - description: 'This is referring to the dimentions of U.S. standard letter paper.', + description: 'This is referring to the dimensions of U.S. standard letter paper.', + }), + getBackgroundColorLabel: () => + i18n.translate('xpack.canvas.workpadConfig.backgroundColorLabel', { + defaultMessage: 'Background color', }), }, WorkpadCreate: { diff --git a/x-pack/legacy/plugins/canvas/public/components/color_palette/__examples__/__snapshots__/color_palette.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/color_palette/__examples__/__snapshots__/color_palette.examples.storyshot index badbf96029f123..8610ed2f1b4a38 100644 --- a/x-pack/legacy/plugins/canvas/public/components/color_palette/__examples__/__snapshots__/color_palette.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/color_palette/__examples__/__snapshots__/color_palette.examples.storyshot @@ -8,6 +8,7 @@ exports[`Storyshots components/Color/ColorPalette interactive 1`] = ` className="item-grid-row" > ); diff --git a/x-pack/legacy/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx b/x-pack/legacy/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx index 970f72da698ba0..717ec6d0faeccf 100644 --- a/x-pack/legacy/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx @@ -17,12 +17,13 @@ interface Props { }; onChange?: (key: string) => void; value?: string; + ariaLabel?: string; } -export const ShapePickerPopover = ({ shapes, onChange, value }: Props) => { +export const ShapePickerPopover = ({ shapes, onChange, value, ariaLabel }: Props) => { const button = (handleClick: (ev: MouseEvent) => void) => ( - + diff --git a/x-pack/legacy/plugins/canvas/public/components/text_style_picker/text_style_picker.js b/x-pack/legacy/plugins/canvas/public/components/text_style_picker/text_style_picker.js index 1a441814750912..179455e15b36ee 100644 --- a/x-pack/legacy/plugins/canvas/public/components/text_style_picker/text_style_picker.js +++ b/x-pack/legacy/plugins/canvas/public/components/text_style_picker/text_style_picker.js @@ -127,6 +127,7 @@ export const TextStylePicker = ({ value={color} onChange={value => doChange('color', value)} colors={colors} + ariaLabel={strings.getFontColorLabel()} /> diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.tsx index 69401c89c79a59..c81f3e78efddd2 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.tsx @@ -6,9 +6,18 @@ import React from 'react'; import { ColorPickerPopover, Props } from '../color_picker_popover'; +import { ComponentStrings } from '../../../i18n'; + +const { WorkpadConfig: strings } = ComponentStrings; export const WorkpadColorPicker = (props: Props) => { - return ; + return ( + + ); }; WorkpadColorPicker.propTypes = ColorPickerPopover.propTypes; diff --git a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/color.js b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/color.js index 2a47150b4a1b94..8d756dd8111b14 100644 --- a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/color.js +++ b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/color.js @@ -13,10 +13,15 @@ import { ArgTypesStrings } from '../../../i18n'; const { Color: strings } = ArgTypesStrings; -const ColorArgInput = ({ onValueChange, argValue, workpad }) => ( +const ColorArgInput = ({ onValueChange, argValue, workpad, typeInstance }) => ( - + ); diff --git a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/container_style/__examples__/__snapshots__/extended_template.examples.storyshot b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/container_style/__examples__/__snapshots__/extended_template.examples.storyshot index 2915d3bfef57b8..649d11cb2dbaba 100644 --- a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/container_style/__examples__/__snapshots__/extended_template.examples.storyshot +++ b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/container_style/__examples__/__snapshots__/extended_template.examples.storyshot @@ -467,6 +467,7 @@ exports[`Storyshots arguments/ContainerStyle extended 1`] = ` className="euiPopover__anchor" >
); diff --git a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.tsx b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.tsx index e05c48b97f54a4..ba1f4305167a41 100644 --- a/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.tsx +++ b/x-pack/legacy/plugins/canvas/public/expression_types/arg_types/series_style/simple_template.tsx @@ -76,6 +76,7 @@ export const SimpleTemplate: FunctionComponent = props => { colors={workpad.colors} onChange={val => handleChange('color', val)} value={color} + ariaLabel={strings.getColorLabel()} /> From 8c878bcaa8192f44fe783cbc481753a5d97d3fff Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 14 Jan 2020 18:02:49 +0100 Subject: [PATCH 10/52] add graph sample data (#54558) --- x-pack/plugins/graph/common/constants.ts | 11 + .../plugins/graph/server/lib/license_state.ts | 13 +- x-pack/plugins/graph/server/plugin.ts | 11 +- .../graph/server/sample_data/ecommerce.ts | 391 ++++ .../graph/server/sample_data/flights.ts | 1645 +++++++++++++++++ .../plugins/graph/server/sample_data/index.ts | 7 + .../plugins/graph/server/sample_data/logs.ts | 463 +++++ .../sample_data/register_sample_data.ts | 36 + 8 files changed, 2573 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/graph/common/constants.ts create mode 100644 x-pack/plugins/graph/server/sample_data/ecommerce.ts create mode 100644 x-pack/plugins/graph/server/sample_data/flights.ts create mode 100644 x-pack/plugins/graph/server/sample_data/index.ts create mode 100644 x-pack/plugins/graph/server/sample_data/logs.ts create mode 100644 x-pack/plugins/graph/server/sample_data/register_sample_data.ts diff --git a/x-pack/plugins/graph/common/constants.ts b/x-pack/plugins/graph/common/constants.ts new file mode 100644 index 00000000000000..3c3ee2b1258964 --- /dev/null +++ b/x-pack/plugins/graph/common/constants.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const APP_ICON = 'graphApp'; + +export function createWorkspacePath(id: string) { + return `/app/graph/#/workspace/${id}`; +} diff --git a/x-pack/plugins/graph/server/lib/license_state.ts b/x-pack/plugins/graph/server/lib/license_state.ts index 1f5744e41534da..d86cb5380a2e15 100644 --- a/x-pack/plugins/graph/server/lib/license_state.ts +++ b/x-pack/plugins/graph/server/lib/license_state.ts @@ -5,6 +5,7 @@ */ import Boom from 'boom'; +import { map } from 'rxjs/operators'; import { Observable, Subscription } from 'rxjs'; import { ILicense } from '../../../licensing/common/types'; import { checkLicense, GraphLicenseInformation } from '../../common/check_license'; @@ -12,13 +13,15 @@ import { checkLicense, GraphLicenseInformation } from '../../common/check_licens export class LicenseState { private licenseInformation: GraphLicenseInformation = checkLicense(undefined); private subscription: Subscription | null = null; + private observable: Observable | null = null; - private updateInformation(license: ILicense | undefined) { - this.licenseInformation = checkLicense(license); + private updateInformation(licenseInformation: GraphLicenseInformation) { + this.licenseInformation = licenseInformation; } public start(license$: Observable) { - this.subscription = license$.subscribe(this.updateInformation.bind(this)); + this.observable = license$.pipe(map(checkLicense)); + this.subscription = this.observable.subscribe(this.updateInformation.bind(this)); } public stop() { @@ -30,6 +33,10 @@ export class LicenseState { public getLicenseInformation() { return this.licenseInformation; } + + public getLicenseInformation$() { + return this.observable; + } } export function verifyApiAccess(licenseState: LicenseState) { diff --git a/x-pack/plugins/graph/server/plugin.ts b/x-pack/plugins/graph/server/plugin.ts index c7ada3af31b76c..dcdf3f51d60bd2 100644 --- a/x-pack/plugins/graph/server/plugin.ts +++ b/x-pack/plugins/graph/server/plugin.ts @@ -9,15 +9,24 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { LicenseState } from './lib/license_state'; import { registerSearchRoute } from './routes/search'; import { registerExploreRoute } from './routes/explore'; +import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; +import { registerSampleData } from './sample_data'; export class GraphPlugin implements Plugin { private licenseState: LicenseState | null = null; - public async setup(core: CoreSetup, { licensing }: { licensing: LicensingPluginSetup }) { + public async setup( + core: CoreSetup, + { licensing, home }: { licensing: LicensingPluginSetup; home?: HomeServerPluginSetup } + ) { const licenseState = new LicenseState(); licenseState.start(licensing.license$); this.licenseState = licenseState; + if (home) { + registerSampleData(home.sampleData, licenseState); + } + const router = core.http.createRouter(); registerSearchRoute({ licenseState, router }); registerExploreRoute({ licenseState, router }); diff --git a/x-pack/plugins/graph/server/sample_data/ecommerce.ts b/x-pack/plugins/graph/server/sample_data/ecommerce.ts new file mode 100644 index 00000000000000..f70c7039b9b724 --- /dev/null +++ b/x-pack/plugins/graph/server/sample_data/ecommerce.ts @@ -0,0 +1,391 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { SampleDataRegistrySetup } from '../../../../../src/plugins/home/server'; +import { APP_ICON, createWorkspacePath } from '../../common/constants'; + +const datasetId = 'ecommerce'; + +const wsState: any = { + selectedFields: [ + { + name: 'customer_gender', + hopSize: 5, + lastValidHopSize: 5, + color: '#7B000B', + selected: true, + iconClass: 'fa-user', + }, + { + name: 'geoip.continent_name', + hopSize: 5, + lastValidHopSize: 5, + color: '#B0916F', + selected: true, + iconClass: 'fa-map-marker', + }, + { + name: 'products.category.keyword', + hopSize: 5, + lastValidHopSize: 5, + color: '#34130C', + selected: true, + iconClass: 'fa-heart', + }, + ], + blacklist: [ + { + x: 491.3880229084531, + y: 572.375603969653, + label: 'South America', + color: '#B0916F', + field: 'geoip.continent_name', + term: 'South America', + parent: null, + size: 15, + }, + ], + vertices: [ + { + x: 458.033767981859, + y: 181.9021747060339, + label: "Women's Shoes", + color: '#34130C', + field: 'products.category.keyword', + term: "Women's Shoes", + parent: null, + size: 15, + }, + { + x: 236.16141155056786, + y: 371.69388982857515, + label: "Men's Accessories", + color: '#34130C', + field: 'products.category.keyword', + term: "Men's Accessories", + parent: null, + size: 15, + }, + { + x: 334.27149182482333, + y: 244.99855207230468, + label: "Women's Accessories", + color: '#34130C', + field: 'products.category.keyword', + term: "Women's Accessories", + parent: null, + size: 15, + }, + { + x: 452.21475063865597, + y: 330.39076195279216, + label: 'Asia', + color: '#B0916F', + field: 'geoip.continent_name', + term: 'Asia', + parent: null, + size: 15, + }, + { + x: 397.79868111013536, + y: 280.55152377990424, + label: 'Europe', + color: '#B0916F', + field: 'geoip.continent_name', + term: 'Europe', + parent: null, + size: 15, + }, + { + x: 324.72030800021247, + y: 352.6641597050945, + label: "Men's Shoes", + color: '#34130C', + field: 'products.category.keyword', + term: "Men's Shoes", + parent: null, + size: 15, + }, + { + x: 372.16854727881645, + y: 390.62646298282147, + label: "Men's Clothing", + color: '#34130C', + field: 'products.category.keyword', + term: "Men's Clothing", + parent: null, + size: 15, + }, + { + x: 508.049093768632, + y: 242.4089619805834, + label: "Women's Clothing", + color: '#34130C', + field: 'products.category.keyword', + term: "Women's Clothing", + parent: null, + size: 15, + }, + { + x: 440.1331838313072, + y: 289.96431350734645, + label: 'Africa', + color: '#B0916F', + field: 'geoip.continent_name', + term: 'Africa', + parent: null, + size: 15, + }, + { + x: 387.0908385779075, + y: 210.10263143650025, + label: 'FEMALE', + color: '#7B000B', + field: 'customer_gender', + term: 'FEMALE', + parent: null, + size: 15, + }, + { + x: 290.59483393305874, + y: 298.89363320612324, + label: 'MALE', + color: '#7B000B', + field: 'customer_gender', + term: 'MALE', + parent: null, + size: 15, + }, + { + x: 413.02719526683677, + y: 322.2286023727188, + label: 'North America', + color: '#B0916F', + field: 'geoip.continent_name', + term: 'North America', + parent: null, + size: 15, + }, + ], + links: [ + { + weight: 0.005857130017177792, + width: 8.232101279777059, + inferred: false, + source: 7, + target: 7, + }, + { weight: 0.00040740358951793883, width: 2, inferred: false, source: 2, target: 10 }, + { weight: 0.00013791546585173228, width: 2, inferred: false, source: 5, target: 1 }, + { weight: 0.0004858267848737237, width: 2, inferred: false, source: 4, target: 2 }, + { weight: 0.0010153751000293245, width: 2, inferred: false, source: 10, target: 11 }, + { weight: 0.0028264125846644674, width: 2, inferred: false, source: 2, target: 9 }, + { weight: 0.0005650580249547761, width: 2, inferred: false, source: 3, target: 0 }, + { weight: 0.0009324185728321393, width: 2, inferred: false, source: 6, target: 11 }, + { + weight: 0.0014323721292750112, + width: 2.0131758052049205, + inferred: false, + source: 4, + target: 4, + }, + { + weight: 0.002342547575207893, + width: 3.2924126381437944, + inferred: false, + source: 0, + target: 0, + }, + { weight: 0.000591872989689757, width: 2, inferred: false, source: 8, target: 5 }, + { weight: 0.0008255717897066532, width: 2, inferred: false, source: 3, target: 5 }, + { weight: 0.0006448216532672799, width: 2, inferred: false, source: 7, target: 8 }, + { weight: 0.0002511052407839208, width: 2, inferred: false, source: 8, target: 0 }, + { weight: 0.0013789044568299467, width: 2, inferred: false, source: 8, target: 10 }, + { weight: 0.000783301409144887, width: 2, inferred: false, source: 8, target: 9 }, + { + weight: 0.00560336629275442, + width: 7.875440479272377, + inferred: false, + source: 10, + target: 6, + }, + { weight: 0.0005016633048258001, width: 2, inferred: false, source: 5, target: 11 }, + { weight: 0.0003926052511049418, width: 2, inferred: false, source: 2, target: 3 }, + { weight: 0.0008115500826586831, width: 2, inferred: false, source: 8, target: 8 }, + { weight: 0.0014948001891181592, width: 2, inferred: false, source: 3, target: 9 }, + { weight: 0.000029981623011246145, width: 2, inferred: false, source: 2, target: 5 }, + { + weight: 0.002480454462292142, + width: 3.4862385321100913, + inferred: false, + source: 9, + target: 4, + }, + { weight: 0.002025814694014704, width: 2, inferred: false, source: 11, target: 7 }, + { + weight: 0.0026700604482252604, + width: 2.137643910722111, + inferred: false, + source: 3, + target: 10, + }, + { weight: 0.00010444018788598492, width: 2, inferred: false, source: 9, target: 6 }, + { + weight: 0.0024519155063363668, + width: 3.44612749220522, + inferred: false, + source: 6, + target: 3, + }, + { weight: 0.0006267747531045212, width: 2, inferred: false, source: 10, target: 1 }, + { weight: 0.00016502073678324177, width: 2, inferred: false, source: 2, target: 8 }, + { weight: 0.0005870783606720878, width: 2, inferred: false, source: 6, target: 4 }, + { weight: 0.00038852356835608306, width: 2, inferred: false, source: 6, target: 2 }, + { + weight: 0.001950556798534224, + width: 2.741475956722648, + inferred: false, + source: 5, + target: 5, + }, + { weight: 0.0005728452602402718, width: 2, inferred: false, source: 2, target: 11 }, + { + weight: 0.0068966839555125534, + width: 9.693177486223698, + inferred: false, + source: 9, + target: 9, + }, + { + weight: 0.003985530212040768, + width: 3.2924126381437944, + inferred: false, + source: 0, + target: 7, + }, + { weight: 0.0005390262876698882, width: 2, inferred: false, source: 4, target: 10 }, + { weight: 0.001230534654985059, width: 2, inferred: false, source: 3, target: 7 }, + { weight: 0.0012265720435530507, width: 2, inferred: false, source: 2, target: 2 }, + { weight: 0.00010444018788598492, width: 2, inferred: false, source: 9, target: 5 }, + { weight: 0.00246087192706352, width: 2, inferred: false, source: 11, target: 9 }, + { weight: 0.001266247444586856, width: 2, inferred: false, source: 6, target: 8 }, + { + weight: 0.0040928391377725235, + width: 5.752419052533403, + inferred: false, + source: 10, + target: 5, + }, + { + weight: 0.003998804111234147, + width: 2.741475956722648, + inferred: false, + source: 5, + target: 6, + }, + { weight: 0.0000201191575509262, width: 2, inferred: false, source: 2, target: 1 }, + { weight: 0.0019559590149107486, width: 2, inferred: false, source: 4, target: 7 }, + { + weight: 0.005399134008600699, + width: 7.588395315032752, + inferred: false, + source: 10, + target: 10, + }, + { weight: 0.0008406249972756651, width: 2, inferred: false, source: 11, target: 0 }, + { weight: 0.002434040312854235, width: 2, inferred: false, source: 2, target: 7 }, + { weight: 0.0007632277713300751, width: 2, inferred: false, source: 4, target: 0 }, + { weight: 0.007114987799732724, width: 10, inferred: false, source: 9, target: 7 }, + { weight: 0.00029149607092423423, width: 2, inferred: false, source: 4, target: 5 }, + { + weight: 0.004628005825697707, + width: 3.2924126381437944, + inferred: false, + source: 0, + target: 9, + }, + { weight: 0.0001769629690348846, width: 2, inferred: false, source: 0, target: 2 }, + { + weight: 0.0017862657198589743, + width: 2.510567509231816, + inferred: false, + source: 11, + target: 11, + }, + { + weight: 0.0023385207220538266, + width: 3.286752961321555, + inferred: false, + source: 3, + target: 3, + }, + { weight: 0.0005977285667016662, width: 2, inferred: false, source: 6, target: 1 }, + { + weight: 0.00523765988442745, + width: 7.361446051424297, + inferred: false, + source: 6, + target: 6, + }, + ], + urlTemplates: [ + { + url: + '/app/kibana#/discover?_a=(columns%3A!(_source)%2Cindex%3Aff959d40-b880-11e8-a6d9-e546fe2bba5f%2Cinterval%3Aauto%2Cquery%3A(language%3Akuery%2Cquery%3A{{gquery}})%2Csort%3A!(_score%2Cdesc))', + description: 'Raw documents', + isDefault: true, + encoderID: 'kql-loose', + }, + ], + exploreControls: { + useSignificance: false, + sampleSize: 2000, + timeoutMillis: 5000, + maxValuesPerDoc: 1, + minDocCount: 3, + }, + indexPatternRefName: 'indexPattern_0', +}; + +export function registerEcommerceSampleData(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addSavedObjectsToSampleDataset(datasetId, [ + { + type: 'graph-workspace', + id: '46fa9d30-319c-11ea-bbe4-818d9c786051', + version: '2', + attributes: { + title: 'Kibana Sample Data - eCommerce', + description: + 'This is a sample graph based on an eCommerce data set. It shows the gender, continent, and product category of purchases. The thicker the line is, there are more correlated documents between the vertices.', + numLinks: 57, + numVertices: 12, + version: 1, + wsState: JSON.stringify(JSON.stringify(wsState)), + }, + references: [ + { + name: 'indexPattern_0', + type: 'index-pattern', + id: 'kibana_sample_data_ecommerce', + }, + ], + migrationVersion: { + 'graph-workspace': '7.0.0', + }, + updated_at: '2020-01-09T16:40:36.122Z', + }, + ]); +} +export function registerEcommerceSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [ + { + path: createWorkspacePath('46fa9d30-319c-11ea-bbe4-818d9c786051'), + label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }), + icon: APP_ICON, + }, + ]); +} diff --git a/x-pack/plugins/graph/server/sample_data/flights.ts b/x-pack/plugins/graph/server/sample_data/flights.ts new file mode 100644 index 00000000000000..3a61eb49cb5b16 --- /dev/null +++ b/x-pack/plugins/graph/server/sample_data/flights.ts @@ -0,0 +1,1645 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { SampleDataRegistrySetup } from '../../../../../src/plugins/home/server'; +import { APP_ICON, createWorkspacePath } from '../../common/constants'; + +const datasetId = 'flights'; + +const wsState: any = { + selectedFields: [ + { + name: 'Carrier', + hopSize: 5, + lastValidHopSize: 5, + color: '#CE0060', + selected: true, + iconClass: 'fa-plane', + }, + { + name: 'Dest', + hopSize: 5, + lastValidHopSize: 5, + color: '#38007E', + selected: true, + iconClass: 'fa-map-marker', + }, + { + name: 'DestWeather', + hopSize: 5, + lastValidHopSize: 5, + color: '#34130C', + selected: true, + iconClass: 'fa-cube', + }, + ], + blacklist: [], + vertices: [ + { + x: 324.55695700802687, + y: 346.73294548497137, + label: 'Sydney Kingsford Smith International Airport', + color: '#38007E', + field: 'Dest', + term: 'Sydney Kingsford Smith International Airport', + parent: null, + size: 15, + }, + { + x: 438.93290232680494, + y: 677.3830617564242, + label: 'Charles de Gaulle International Airport', + color: '#38007E', + field: 'Dest', + term: 'Charles de Gaulle International Airport', + parent: null, + size: 15, + }, + { + x: 499.6180566078833, + y: -5.098830831803902, + label: 'Bologna Guglielmo Marconi Airport', + color: '#38007E', + field: 'Dest', + term: 'Bologna Guglielmo Marconi Airport', + parent: null, + size: 15, + }, + { + x: 389.5752820793805, + y: 386.37176544482463, + label: 'Rain', + color: '#34130C', + field: 'DestWeather', + term: 'Rain', + parent: null, + size: 15, + }, + { + x: 490.9417508188848, + y: 391.65617271236334, + label: 'Milano Linate Airport', + color: '#38007E', + field: 'Dest', + term: 'Milano Linate Airport', + parent: null, + size: 15, + }, + { + x: 614.188166571076, + y: 572.9459065381061, + label: 'Cloudy', + color: '#34130C', + field: 'DestWeather', + term: 'Cloudy', + parent: null, + size: 15, + }, + { + x: 330.387175680331, + y: 695.0441997424184, + label: 'Guangzhou Baiyun International Airport', + color: '#38007E', + field: 'Dest', + term: 'Guangzhou Baiyun International Airport', + parent: null, + size: 15, + }, + { + x: 379.9660844804916, + y: 638.9082566905925, + label: 'Ottawa Macdonald-Cartier International Airport', + color: '#38007E', + field: 'Dest', + term: 'Ottawa Macdonald-Cartier International Airport', + parent: null, + size: 15, + }, + { + x: 57.947780190100346, + y: 360.393931221208, + label: 'Hail', + color: '#34130C', + field: 'DestWeather', + term: 'Hail', + parent: null, + size: 15, + }, + { + x: 397.4430051185168, + y: 504.8512181744145, + label: 'Logstash Airways', + color: '#CE0060', + field: 'Carrier', + term: 'Logstash Airways', + parent: null, + size: 15, + }, + { + x: 424.42756723778075, + y: 354.33078205093506, + label: 'Sunny', + color: '#34130C', + field: 'DestWeather', + term: 'Sunny', + parent: null, + size: 15, + }, + { + x: 254.09885556506697, + y: 434.3607906063371, + label: 'Norfolk International Airport', + color: '#38007E', + field: 'Dest', + term: 'Norfolk International Airport', + parent: null, + size: 15, + }, + { + x: 568.6877204517741, + y: 340.3641831872856, + label: 'Heavy Fog', + color: '#34130C', + field: 'DestWeather', + term: 'Heavy Fog', + parent: null, + size: 15, + }, + { + x: 429.8163554957372, + y: -28.901855935511612, + label: 'Dublin Airport', + color: '#38007E', + field: 'Dest', + term: 'Dublin Airport', + parent: null, + size: 15, + }, + { + x: 420.5639687132341, + y: 247.7760184038354, + label: 'JetBeats', + color: '#CE0060', + field: 'Carrier', + term: 'JetBeats', + parent: null, + size: 15, + }, + { + x: 384.3810911831613, + y: 437.9949106467699, + label: 'Clear', + color: '#34130C', + field: 'DestWeather', + term: 'Clear', + parent: null, + size: 15, + }, + { + x: 493.9944277688558, + y: 431.38149316403536, + label: 'Kempegowda International Airport', + color: '#38007E', + field: 'Dest', + term: 'Kempegowda International Airport', + parent: null, + size: 15, + }, + { + x: 595.7619057497752, + y: 497.83788593573075, + label: 'Helsinki Vantaa Airport', + color: '#38007E', + field: 'Dest', + term: 'Helsinki Vantaa Airport', + parent: null, + size: 15, + }, + { + x: 185.93617979891062, + y: 422.59954588194796, + label: 'Falcone Borsellino Airport', + color: '#38007E', + field: 'Dest', + term: 'Falcone Borsellino Airport', + parent: null, + size: 15, + }, + { + x: 310.7985722936307, + y: 565.8106567909418, + label: 'Damaging Wind', + color: '#34130C', + field: 'DestWeather', + term: 'Damaging Wind', + parent: null, + size: 15, + }, + { + x: 226.7458591666634, + y: 107.70616617436147, + label: 'Thunder & Lightning', + color: '#34130C', + field: 'DestWeather', + term: 'Thunder & Lightning', + parent: null, + size: 15, + }, + { + x: 841.0362982575906, + y: 271.86719405075036, + label: 'Turin Airport', + color: '#38007E', + field: 'Dest', + term: 'Turin Airport', + parent: null, + size: 15, + }, + { + x: 607.8580597431672, + y: 674.8142192796432, + label: 'ES-Air', + color: '#CE0060', + field: 'Carrier', + term: 'ES-Air', + parent: null, + size: 15, + }, + { + x: 147.48792054443953, + y: 564.0675506645817, + label: 'Philadelphia International Airport', + color: '#38007E', + field: 'Dest', + term: 'Philadelphia International Airport', + parent: null, + size: 15, + }, + { + x: 195.87484752441807, + y: 187.0207407631843, + label: 'Mariscal Sucre International Airport', + color: '#38007E', + field: 'Dest', + term: 'Mariscal Sucre International Airport', + parent: null, + size: 15, + }, + { + x: 284.28484320054537, + y: 126.299295414221, + label: 'Kibana Airlines', + color: '#CE0060', + field: 'Carrier', + term: 'Kibana Airlines', + parent: null, + size: 15, + }, + { + x: 194.4295924748303, + y: 272.79043826611775, + label: 'Indianapolis International Airport', + color: '#38007E', + field: 'Dest', + term: 'Indianapolis International Airport', + parent: null, + size: 15, + }, + { + x: 465.97943124701703, + y: 142.61050933505095, + label: 'Spokane International Airport', + color: '#38007E', + field: 'Dest', + term: 'Spokane International Airport', + parent: null, + size: 15, + }, + { + x: 563.1081895874394, + y: 461.0383551052199, + label: 'Savannah Hilton Head International Airport', + color: '#38007E', + field: 'Dest', + term: 'Savannah Hilton Head International Airport', + parent: null, + size: 15, + }, + { + x: 174.0117366992591, + y: 221.18597939550128, + label: 'Olenya Air Base', + color: '#38007E', + field: 'Dest', + term: 'Olenya Air Base', + parent: null, + size: 15, + }, + { + x: -148.89474582077094, + y: 464.5050002661098, + label: "Xi'an Xianyang International Airport", + color: '#38007E', + field: 'Dest', + term: "Xi'an Xianyang International Airport", + parent: null, + size: 15, + }, + { + x: 541.411089567981, + y: 580.3869624463574, + label: 'Kansas City International Airport', + color: '#38007E', + field: 'Dest', + term: 'Kansas City International Airport', + parent: null, + size: 15, + }, + { + x: -162.65034295458753, + y: 413.712547647303, + label: 'Brisbane International Airport', + color: '#38007E', + field: 'Dest', + term: 'Brisbane International Airport', + parent: null, + size: 15, + }, + { + x: -116.84605252338424, + y: 517.8977089765682, + label: 'Cape Town International Airport', + color: '#38007E', + field: 'Dest', + term: 'Cape Town International Airport', + parent: null, + size: 15, + }, + { + x: 596.9667234907166, + y: 194.18359952998938, + label: 'Wichita Mid Continent Airport', + color: '#38007E', + field: 'Dest', + term: 'Wichita Mid Continent Airport', + parent: null, + size: 15, + }, + { + x: 500.4899840585019, + y: 177.09233549147297, + label: 'Ministro Pistarini International Airport', + color: '#38007E', + field: 'Dest', + term: 'Ministro Pistarini International Airport', + parent: null, + size: 15, + }, + { + x: 826.9908015391406, + y: 416.75687496452565, + label: 'OR Tambo International Airport', + color: '#38007E', + field: 'Dest', + term: 'OR Tambo International Airport', + parent: null, + size: 15, + }, + { + x: 555.157034647214, + y: 166.9646207668324, + label: 'Shanghai Hongqiao International Airport', + color: '#38007E', + field: 'Dest', + term: 'Shanghai Hongqiao International Airport', + parent: null, + size: 15, + }, + { + x: 580.8551934489927, + y: 241.47782924175658, + label: 'Indira Gandhi International Airport', + color: '#38007E', + field: 'Dest', + term: 'Indira Gandhi International Airport', + parent: null, + size: 15, + }, + { + x: 142.49629635003708, + y: 518.4470370636079, + label: 'Frankfurt am Main Airport', + color: '#38007E', + field: 'Dest', + term: 'Frankfurt am Main Airport', + parent: null, + size: 15, + }, + { + x: 716.6279150760975, + y: 545.8773030597199, + label: 'Vienna International Airport', + color: '#38007E', + field: 'Dest', + term: 'Vienna International Airport', + parent: null, + size: 15, + }, + { + x: -70.06067646092055, + y: 561.1605661590851, + label: 'Cagliari Elmas Airport', + color: '#38007E', + field: 'Dest', + term: 'Cagliari Elmas Airport', + parent: null, + size: 15, + }, + { + x: 533.2686334784598, + y: 219.21154460683562, + label: 'Tucson International Airport', + color: '#38007E', + field: 'Dest', + term: 'Tucson International Airport', + parent: null, + size: 15, + }, + { + x: -93.03511592199457, + y: 381.08072776851094, + label: 'Louisville International Standiford Field', + color: '#38007E', + field: 'Dest', + term: 'Louisville International Standiford Field', + parent: null, + size: 15, + }, + { + x: -182.53556363207895, + y: 361.554383031446, + label: 'Salt Lake City International Airport', + color: '#38007E', + field: 'Dest', + term: 'Salt Lake City International Airport', + parent: null, + size: 15, + }, + { + x: 506.6699374507708, + y: 547.2465972305006, + label: 'Rochester International Airport', + color: '#38007E', + field: 'Dest', + term: 'Rochester International Airport', + parent: null, + size: 15, + }, + { + x: -148.82859321240085, + y: 318.0132218264011, + label: 'Itami Airport', + color: '#38007E', + field: 'Dest', + term: 'Itami Airport', + parent: null, + size: 15, + }, + { + x: 222.5302956739488, + y: 232.51590505210746, + label: 'Portland International Airport', + color: '#38007E', + field: 'Dest', + term: 'Portland International Airport', + parent: null, + size: 15, + }, + { + x: -80.62574246014822, + y: 303.3255679653993, + label: 'Dubai International Airport', + color: '#38007E', + field: 'Dest', + term: 'Dubai International Airport', + parent: null, + size: 15, + }, + { + x: -34.72394927217507, + y: 217.5466871636975, + label: 'Zurich Airport', + color: '#38007E', + field: 'Dest', + term: 'Zurich Airport', + parent: null, + size: 15, + }, + { + x: -9.750451947080853, + y: 568.5500808550931, + label: 'Leonardo da Vinci - Fiumicino Airport', + color: '#38007E', + field: 'Dest', + term: 'Leonardo da Vinci - Fiumicino Airport', + parent: null, + size: 15, + }, + { + x: -171.03799922521074, + y: 268.04401840229406, + label: 'Jorge Chavez International Airport', + color: '#38007E', + field: 'Dest', + term: 'Jorge Chavez International Airport', + parent: null, + size: 15, + }, + { + x: 254.95546516251608, + y: 360.7470259628306, + label: 'Newport News Williamsburg International Airport', + color: '#38007E', + field: 'Dest', + term: 'Newport News Williamsburg International Airport', + parent: null, + size: 15, + }, + { + x: 705.9928093405911, + y: 260.92721232939704, + label: 'Narita International Airport', + color: '#38007E', + field: 'Dest', + term: 'Narita International Airport', + parent: null, + size: 15, + }, + { + x: 300.39350107798504, + y: 296.31322539642935, + label: 'Phoenix Sky Harbor International Airport', + color: '#38007E', + field: 'Dest', + term: 'Phoenix Sky Harbor International Airport', + parent: null, + size: 15, + }, + { + x: 841.3679622941146, + y: 362.994358559615, + label: 'Gulfport Biloxi International Airport', + color: '#38007E', + field: 'Dest', + term: 'Gulfport Biloxi International Airport', + parent: null, + size: 15, + }, + { + x: -141.05365305439858, + y: 210.2303430439021, + label: 'Copenhagen Kastrup Airport', + color: '#38007E', + field: 'Dest', + term: 'Copenhagen Kastrup Airport', + parent: null, + size: 15, + }, + { + x: 743.5349870766893, + y: 126.70030466255093, + label: 'Syracuse Hancock International Airport', + color: '#38007E', + field: 'Dest', + term: 'Syracuse Hancock International Airport', + parent: null, + size: 15, + }, + { + x: 764.618951678557, + y: 248.832832419093, + label: 'Tokyo Haneda International Airport', + color: '#38007E', + field: 'Dest', + term: 'Tokyo Haneda International Airport', + parent: null, + size: 15, + }, + { + x: 763.09219411721, + y: 513.1544667969338, + label: 'Munich Airport', + color: '#38007E', + field: 'Dest', + term: 'Munich Airport', + parent: null, + size: 15, + }, + { + x: 156.05708853251238, + y: 473.87653900869924, + label: 'Washington Dulles International Airport', + color: '#38007E', + field: 'Dest', + term: 'Washington Dulles International Airport', + parent: null, + size: 15, + }, + { + x: -86.411003945306, + y: 240.80939633249233, + label: 'Malpensa International Airport', + color: '#38007E', + field: 'Dest', + term: 'Malpensa International Airport', + parent: null, + size: 15, + }, + { + x: 685.9006745563539, + y: 80.38825995741794, + label: 'Tulsa International Airport', + color: '#38007E', + field: 'Dest', + term: 'Tulsa International Airport', + parent: null, + size: 15, + }, + { + x: 813.5591404529398, + y: 319.41126742174833, + label: 'Chhatrapati Shivaji International Airport', + color: '#38007E', + field: 'Dest', + term: 'Chhatrapati Shivaji International Airport', + parent: null, + size: 15, + }, + { + x: 764.1118449187941, + y: 425.24554477772796, + label: 'King Shaka International Airport', + color: '#38007E', + field: 'Dest', + term: 'King Shaka International Airport', + parent: null, + size: 15, + }, + { + x: 703.5657989067569, + y: 465.484110126664, + label: 'London Gatwick Airport', + color: '#38007E', + field: 'Dest', + term: 'London Gatwick Airport', + parent: null, + size: 15, + }, + { + x: 344.9519826614776, + y: -13.093056231864056, + label: 'Nashville International Airport', + color: '#38007E', + field: 'Dest', + term: 'Nashville International Airport', + parent: null, + size: 15, + }, + { + x: -81.11616869938695, + y: 450.9378117999539, + label: 'Jeju International Airport', + color: '#38007E', + field: 'Dest', + term: 'Jeju International Airport', + parent: null, + size: 15, + }, + { + x: 520.9186276242698, + y: 119.42964509536168, + label: 'Naples International Airport', + color: '#38007E', + field: 'Dest', + term: 'Naples International Airport', + parent: null, + size: 15, + }, + { + x: 816.422014720576, + y: 212.4314895109656, + label: 'Seattle Tacoma International Airport', + color: '#38007E', + field: 'Dest', + term: 'Seattle Tacoma International Airport', + parent: null, + size: 15, + }, + { + x: 25.734384706872138, + y: 124.10051087501682, + label: 'London Luton Airport', + color: '#38007E', + field: 'Dest', + term: 'London Luton Airport', + parent: null, + size: 15, + }, + { + x: 185.9498162384016, + y: 590.068422735089, + label: 'Quad City International Airport', + color: '#38007E', + field: 'Dest', + term: 'Quad City International Airport', + parent: null, + size: 15, + }, + { + x: 213.56366767998443, + y: 470.3771672046229, + label: 'San Diego International Airport', + color: '#38007E', + field: 'Dest', + term: 'San Diego International Airport', + parent: null, + size: 15, + }, + { + x: 349.5963617759899, + y: 42.24365374774675, + label: 'Genoa Cristoforo Colombo Airport', + color: '#38007E', + field: 'Dest', + term: 'Genoa Cristoforo Colombo Airport', + parent: null, + size: 15, + }, + { + x: 674.2709501560455, + y: 132.40588179256156, + label: 'Rajiv Gandhi International Airport', + color: '#38007E', + field: 'Dest', + term: 'Rajiv Gandhi International Airport', + parent: null, + size: 15, + }, + { + x: -93.72766628851922, + y: 163.56728717209475, + label: 'Tampa International Airport', + color: '#38007E', + field: 'Dest', + term: 'Tampa International Airport', + parent: null, + size: 15, + }, + { + x: 569.5207860148963, + y: 130.37661454397244, + label: 'Kansai International Airport', + color: '#38007E', + field: 'Dest', + term: 'Kansai International Airport', + parent: null, + size: 15, + }, + { + x: 750.1154214852191, + y: 323.5264978888758, + label: 'Chengdu Shuangliu International Airport', + color: '#38007E', + field: 'Dest', + term: 'Chengdu Shuangliu International Airport', + parent: null, + size: 15, + }, + { + x: 802.7414370705463, + y: 473.82537149991913, + label: 'Scott AFB/Midamerica Airport', + color: '#38007E', + field: 'Dest', + term: 'Scott AFB/Midamerica Airport', + parent: null, + size: 15, + }, + { + x: 418.5925126330155, + y: 24.230521636621365, + label: 'Edmonton International Airport', + color: '#38007E', + field: 'Dest', + term: 'Edmonton International Airport', + parent: null, + size: 15, + }, + { + x: 722.7028867550214, + y: 384.02115146468276, + label: 'Venice Marco Polo Airport', + color: '#38007E', + field: 'Dest', + term: 'Venice Marco Polo Airport', + parent: null, + size: 15, + }, + { + x: 566.9547084529621, + y: 536.9780471490457, + label: 'Licenciado Benito Juarez International Airport', + color: '#38007E', + field: 'Dest', + term: 'Licenciado Benito Juarez International Airport', + parent: null, + size: 15, + }, + { + x: 9.092519123754876, + y: 180.41434896279804, + label: 'Richmond International Airport', + color: '#38007E', + field: 'Dest', + term: 'Richmond International Airport', + parent: null, + size: 15, + }, + { + x: 526.9718956870161, + y: 499.9130353149581, + label: 'Oslo Gardermoen Airport', + color: '#38007E', + field: 'Dest', + term: 'Oslo Gardermoen Airport', + parent: null, + size: 15, + }, + { + x: 776.8528805322672, + y: 172.1720828990032, + label: 'Orlando Sanford International Airport', + color: '#38007E', + field: 'Dest', + term: 'Orlando Sanford International Airport', + parent: null, + size: 15, + }, + { + x: 194.56093032633905, + y: 506.61947778758895, + label: 'Manchester Airport', + color: '#38007E', + field: 'Dest', + term: 'Manchester Airport', + parent: null, + size: 15, + }, + { + x: 708.6785061187419, + y: 191.9304211208653, + label: 'Cologne Bonn Airport', + color: '#38007E', + field: 'Dest', + term: 'Cologne Bonn Airport', + parent: null, + size: 15, + }, + { + x: -37.77191138443358, + y: 501.2430928096333, + label: 'Verona Villafranca Airport', + color: '#38007E', + field: 'Dest', + term: 'Verona Villafranca Airport', + parent: null, + size: 15, + }, + { + x: -40.417571635448624, + y: 123.95944445826335, + label: "Treviso-Sant'Angelo Airport", + color: '#38007E', + field: 'Dest', + term: "Treviso-Sant'Angelo Airport", + parent: null, + size: 15, + }, + { + x: 429.6512757101672, + y: 754.2512422806057, + label: 'Ukrainka Air Base', + color: '#38007E', + field: 'Dest', + term: 'Ukrainka Air Base', + parent: null, + size: 15, + }, + { + x: 210.79822994078665, + y: 547.8887998341944, + label: 'London Heathrow Airport', + color: '#38007E', + field: 'Dest', + term: 'London Heathrow Airport', + parent: null, + size: 15, + }, + ], + links: [ + { weight: 0.00036108815767164356, width: 2, inferred: false, source: 16, target: 15 }, + { weight: 0.00006920523704931667, width: 2, inferred: false, source: 10, target: 16 }, + { weight: 0.000025994919070600338, width: 2, inferred: false, source: 14, target: 2 }, + { weight: 0.0018162486758829953, width: 2, inferred: false, source: 4, target: 12 }, + { weight: 0.00018081732691649835, width: 2, inferred: false, source: 9, target: 16 }, + { weight: 0.000021285526043182752, width: 2, inferred: false, source: 12, target: 14 }, + { weight: 0.0007282200503396025, width: 2, inferred: false, source: 9, target: 0 }, + { weight: 0.004793219772186124, width: 2, inferred: false, source: 15, target: 14 }, + { weight: 0.0007516261919537721, width: 2, inferred: false, source: 11, target: 3 }, + { weight: 0.00001604668217899462, width: 2, inferred: false, source: 15, target: 6 }, + { + weight: 0.00102064538814533, + width: 2.129352369920287, + inferred: false, + source: 10, + target: 0, + }, + { weight: 0.0005756894672209474, width: 2, inferred: false, source: 11, target: 9 }, + { weight: 0.00043176411158517205, width: 2, inferred: false, source: 16, target: 14 }, + { weight: 0.000298574093986214, width: 2, inferred: false, source: 15, target: 4 }, + { weight: 0.00026684563135348044, width: 2, inferred: false, source: 3, target: 9 }, + { + weight: 0.0015570439724214619, + width: 3.2484301710023926, + inferred: false, + source: 3, + target: 16, + }, + { weight: 0.00034139370106102624, width: 2, inferred: false, source: 12, target: 16 }, + { weight: 0.000019807816572852395, width: 2, inferred: false, source: 15, target: 7 }, + { weight: 0.0018795555437956094, width: 2, inferred: false, source: 0, target: 12 }, + { weight: 0.00008480623691842954, width: 2, inferred: false, source: 14, target: 4 }, + { weight: 0.0005396046174994674, width: 2, inferred: false, source: 0, target: 14 }, + { weight: 0.00030597266180943495, width: 2, inferred: false, source: 11, target: 15 }, + { weight: 0.00016510683587379307, width: 2, inferred: false, source: 12, target: 9 }, + { weight: 0.000877528177750529, width: 2, inferred: false, source: 3, target: 4 }, + { + weight: 0.004021590274418162, + width: 8.390164577377533, + inferred: false, + source: 9, + target: 15, + }, + { weight: 0.00005090637270674858, width: 2, inferred: false, source: 4, target: 5 }, + { weight: 0.000021160301063872648, width: 2, inferred: false, source: 9, target: 7 }, + { weight: 0.0007218599824419075, width: 2, inferred: false, source: 4, target: 9 }, + { weight: 0.000020271897051582118, width: 2, inferred: false, source: 15, target: 1 }, + { weight: 0.0001452986212793978, width: 2, inferred: false, source: 11, target: 8 }, + { weight: 0.0000382567038512752, width: 2, inferred: false, source: 10, target: 14 }, + { weight: 0.00002190156221770533, width: 2, inferred: false, source: 10, target: 9 }, + { + weight: 0.004042935452811213, + width: 8.434696602628934, + inferred: false, + source: 3, + target: 0, + }, + { weight: 0.000048696788032371897, width: 2, inferred: false, source: 3, target: 14 }, + { weight: 0.000005506428294426271, width: 2, inferred: false, source: 0, target: 8 }, + { weight: 0.00037127137164506553, width: 2, inferred: false, source: 10, target: 4 }, + { weight: 0.000022370205464624682, width: 2, inferred: false, source: 14, target: 13 }, + { weight: 0.00001466782860711787, width: 2, inferred: false, source: 16, target: 5 }, + { weight: 0.0001456026369222754, width: 2, inferred: false, source: 15, target: 0 }, + { + weight: 0.006907860404869038, + width: 4.55482755826747, + inferred: false, + source: 12, + target: 21, + }, + { + weight: 0.008296001497090972, + width: 5.470124470920678, + inferred: false, + source: 12, + target: 17, + }, + { weight: 0.00648290935725332, width: 4.2746281000922, inferred: false, source: 8, target: 24 }, + { weight: 0.0009981800125248449, width: 2, inferred: false, source: 9, target: 23 }, + { weight: 0.0015459070412640408, width: 2, inferred: false, source: 9, target: 18 }, + { weight: 0.01516601960557338, width: 10, inferred: false, source: 16, target: 22 }, + { + weight: 0.005893794668845864, + width: 3.886184260687587, + inferred: false, + source: 0, + target: 25, + }, + { + weight: 0.005313459755140434, + width: 3.50352953070678, + inferred: false, + source: 0, + target: 20, + }, + { weight: 0.0008193770353314001, width: 2, inferred: false, source: 9, target: 17 }, + { + weight: 0.005369162122122355, + width: 3.5402579330368495, + inferred: false, + source: 8, + target: 18, + }, + { + weight: 0.0041502680719532024, + width: 2.736557237752755, + inferred: false, + source: 14, + target: 24, + }, + { + weight: 0.005575455098137172, + width: 3.6762810830656187, + inferred: false, + source: 8, + target: 23, + }, + { weight: 0.0006592549345612115, width: 2, inferred: false, source: 0, target: 19 }, + { + weight: 0.0028878207958795113, + width: 2.744516404276922, + inferred: false, + source: 14, + target: 29, + }, + { + weight: 0.00541445375382345, + width: 5.145768452381221, + inferred: false, + source: 14, + target: 26, + }, + { + weight: 0.0077101087361939056, + width: 7.327504509779791, + inferred: false, + source: 12, + target: 28, + }, + { weight: 0.001086168788022982, width: 2, inferred: false, source: 9, target: 28 }, + { + weight: 0.005830015792513137, + width: 5.540708759551927, + inferred: false, + source: 12, + target: 27, + }, + { weight: 0.010522148059961567, width: 10, inferred: false, source: 8, target: 30 }, + { + weight: 0.008028304360541931, + width: 7.629910085651518, + inferred: false, + source: 14, + target: 27, + }, + { + weight: 0.008323509700335742, + width: 7.910466240261349, + inferred: false, + source: 8, + target: 29, + }, + { + weight: 0.00701726324791995, + width: 6.669040587464972, + inferred: false, + source: 8, + target: 26, + }, + { + weight: 0.008035893034703851, + width: 7.493359138625227, + inferred: false, + source: 12, + target: 34, + }, + { + weight: 0.009872040172040198, + width: 9.205540954883924, + inferred: false, + source: 8, + target: 32, + }, + { + weight: 0.006969610945500717, + width: 6.499065834448964, + inferred: false, + source: 12, + target: 31, + }, + { + weight: 0.0033592096430033543, + width: 3.1324165426605663, + inferred: false, + source: 14, + target: 34, + }, + { weight: 0.0020881160512555767, width: 2, inferred: false, source: 9, target: 31 }, + { + weight: 0.005672517253512455, + width: 5.289543902220858, + inferred: false, + source: 12, + target: 35, + }, + { + weight: 0.0038532137077538194, + width: 3.5930685022037694, + inferred: false, + source: 14, + target: 35, + }, + { weight: 0.010724019609953143, width: 10, inferred: false, source: 8, target: 33 }, + { weight: 0.009999612232220277, width: 10, inferred: false, source: 12, target: 36 }, + { + weight: 0.00520836804658599, + width: 5.208570018149137, + inferred: false, + source: 14, + target: 37, + }, + { + weight: 0.0065791804178040895, + width: 6.579435547115483, + inferred: false, + source: 12, + target: 38, + }, + { + weight: 0.0035543396019111486, + width: 3.5544774330933793, + inferred: false, + source: 9, + target: 39, + }, + { + weight: 0.007640437421149957, + width: 7.640733704184349, + inferred: false, + source: 12, + target: 40, + }, + { + weight: 0.005772892244324483, + width: 5.773116107165979, + inferred: false, + source: 12, + target: 37, + }, + { + weight: 0.00683892215386792, + width: 6.839187355517515, + inferred: false, + source: 8, + target: 39, + }, + { + weight: 0.0035554369106046754, + width: 3.5555747843386514, + inferred: false, + source: 14, + target: 38, + }, + { + weight: 0.006442257454823214, + width: 6.700837532628826, + inferred: false, + source: 12, + target: 42, + }, + { + weight: 0.008768456755896941, + width: 9.120406091370558, + inferred: false, + source: 8, + target: 43, + }, + { + weight: 0.002463336629511846, + width: 2.5622103211932767, + inferred: false, + source: 14, + target: 42, + }, + { weight: 0.009614107823766071, width: 10, inferred: false, source: 8, target: 44 }, + { + weight: 0.005362282501083629, + width: 5.577514418787844, + inferred: false, + source: 12, + target: 45, + }, + { + weight: 0.004211820280840009, + width: 4.38087481235481, + inferred: false, + source: 9, + target: 45, + }, + { + weight: 0.008768456755896941, + width: 9.120406091370558, + inferred: false, + source: 8, + target: 41, + }, + { + weight: 0.00901282169263535, + width: 9.230629866820077, + inferred: false, + source: 8, + target: 48, + }, + { + weight: 0.006864761661424646, + width: 7.030658786063203, + inferred: false, + source: 8, + target: 47, + }, + { weight: 0.009764037582128985, width: 10, inferred: false, source: 8, target: 49 }, + { + weight: 0.002218700052240432, + width: 2.272318222434227, + inferred: false, + source: 14, + target: 47, + }, + { + weight: 0.009069404566965875, + width: 9.288580150045213, + inferred: false, + source: 8, + target: 50, + }, + { + weight: 0.008849408882060712, + width: 9.063267943844963, + inferred: false, + source: 8, + target: 46, + }, + { + weight: 0.005626664661994386, + width: 5.715730944253917, + inferred: false, + source: 8, + target: 52, + }, + { + weight: 0.007560194330975868, + width: 7.679867075429226, + inferred: false, + source: 8, + target: 51, + }, + { weight: 0.001752663718499857, width: 2, inferred: false, source: 9, target: 52 }, + { + weight: 0.008026220513934599, + width: 8.153270136528944, + inferred: false, + source: 12, + target: 53, + }, + { + weight: 0.004827356344509919, + width: 4.903770118668455, + inferred: false, + source: 8, + target: 54, + }, + { weight: 0.00984417341696416, width: 10, inferred: false, source: 12, target: 55 }, + { + weight: 0.006914338939083934, + width: 7.023788231085676, + inferred: false, + source: 12, + target: 54, + }, + { + weight: 0.0050433680321645925, + width: 5.123201124711509, + inferred: false, + source: 14, + target: 52, + }, + { + weight: 0.008168227180792522, + width: 9.420967741935492, + inferred: false, + source: 12, + target: 57, + }, + { weight: 0.008670263400259132, width: 10, inferred: false, source: 12, target: 58 }, + { + weight: 0.005167622324806284, + width: 5.960167628415806, + inferred: false, + source: 9, + target: 60, + }, + { + weight: 0.0038411902891512147, + width: 4.430304030943756, + inferred: false, + source: 8, + target: 60, + }, + { + weight: 0.0075177233690892475, + width: 8.670697788563796, + inferred: false, + source: 12, + target: 59, + }, + { + weight: 0.00821636644295359, + width: 9.476490002262242, + inferred: false, + source: 8, + target: 56, + }, + { + weight: 0.008567041135861564, + width: 9.505916995325613, + inferred: false, + source: 12, + target: 62, + }, + { weight: 0.009012324786839896, width: 10, inferred: false, source: 12, target: 64 }, + { + weight: 0.007978951597732759, + width: 8.853377775936234, + inferred: false, + source: 12, + target: 63, + }, + { + weight: 0.007990477494073249, + width: 8.866166813851645, + inferred: false, + source: 8, + target: 61, + }, + { + weight: 0.0068326370809723485, + width: 7.581436801911088, + inferred: false, + source: 12, + target: 65, + }, + { weight: 0.0069628576746968325, width: 10, inferred: false, source: 14, target: 66 }, + { + weight: 0.00630579175761646, + width: 9.056327232612892, + inferred: false, + source: 12, + target: 69, + }, + { + weight: 0.004993595139087726, + width: 7.171761038911585, + inferred: false, + source: 12, + target: 68, + }, + { + weight: 0.006440422196382008, + width: 9.249682382258989, + inferred: false, + source: 8, + target: 70, + }, + { + weight: 0.006461917139048282, + width: 9.280553245445503, + inferred: false, + source: 8, + target: 67, + }, + { weight: 0.0012299141681236386, width: 2, inferred: false, source: 14, target: 68 }, + { + weight: 0.006121250009532553, + width: 8.223889291692453, + inferred: false, + source: 14, + target: 73, + }, + { weight: 0.007443254392682634, width: 10, inferred: false, source: 12, target: 74 }, + { + weight: 0.0036956382919431487, + width: 4.965083949805991, + inferred: false, + source: 8, + target: 72, + }, + { + weight: 0.00417209586464865, + width: 5.605203912888136, + inferred: false, + source: 8, + target: 71, + }, + { + weight: 0.006226305772632526, + width: 8.365031536143016, + inferred: false, + source: 8, + target: 75, + }, + { + weight: 0.00457964360497232, + width: 6.152743629822068, + inferred: false, + source: 9, + target: 72, + }, + { + weight: 0.004811414885204741, + width: 6.464127962541197, + inferred: false, + source: 9, + target: 71, + }, + { + weight: 0.008658220862662045, + width: 9.39624265947584, + inferred: false, + source: 12, + target: 77, + }, + { + weight: 0.005652490696892165, + width: 6.134305772617896, + inferred: false, + source: 14, + target: 76, + }, + { weight: 0.009214556473730996, width: 10, inferred: false, source: 14, target: 79 }, + { + weight: 0.004748575495037549, + width: 5.153341355685282, + inferred: false, + source: 12, + target: 76, + }, + { + weight: 0.0077464423601241485, + width: 8.406744678604802, + inferred: false, + source: 12, + target: 80, + }, + { + weight: 0.008124466937893192, + width: 8.816991855283053, + inferred: false, + source: 12, + target: 78, + }, + { + weight: 0.0060785494322330245, + width: 5.642184271849482, + inferred: false, + source: 12, + target: 81, + }, + { + weight: 0.0055135463710868065, + width: 5.117741488141641, + inferred: false, + source: 9, + target: 83, + }, + { + weight: 0.009734577561953504, + width: 9.035754504504519, + inferred: false, + source: 8, + target: 85, + }, + { weight: 0.0006290814048138334, width: 2, inferred: false, source: 9, target: 85 }, + { + weight: 0.006117429813459821, + width: 5.6782735194384015, + inferred: false, + source: 12, + target: 83, + }, + { + weight: 0.009450881532944542, + width: 8.772424364525301, + inferred: false, + source: 12, + target: 84, + }, + { + weight: 0.005227004044795189, + width: 4.851769380051405, + inferred: false, + source: 9, + target: 81, + }, + { weight: 0.010773397569733226, width: 10, inferred: false, source: 8, target: 82 }, + { + weight: 0.011920043127675903, + width: 6.530170805646604, + inferred: false, + source: 8, + target: 90, + }, + { weight: 0.0034745412270287637, width: 2, inferred: false, source: 12, target: 86 }, + { + weight: 0.015665658081851157, + width: 8.582135312902338, + inferred: false, + source: 8, + target: 88, + }, + { + weight: 0.01040035726960635, + width: 5.697639570832633, + inferred: false, + source: 8, + target: 87, + }, + { + weight: 0.00718982708389688, + width: 3.9388111618405564, + inferred: false, + source: 9, + target: 90, + }, + { weight: 0.018253799911893125, width: 10, inferred: false, source: 9, target: 89 }, + ], + urlTemplates: [ + { + url: + '/app/kibana#/discover?_a=(columns%3A!(_source)%2Cindex%3Ad3d7af60-4c81-11e8-b3d7-01146121b73d%2Cinterval%3Aauto%2Cquery%3A(language%3Akuery%2Cquery%3A{{gquery}})%2Csort%3A!(_score%2Cdesc))', + description: 'Raw documents', + isDefault: true, + encoderID: 'kql-loose', + }, + ], + exploreControls: { + useSignificance: true, + sampleSize: 2000, + timeoutMillis: 5000, + maxValuesPerDoc: 1, + minDocCount: 3, + }, + indexPatternRefName: 'indexPattern_0', +}; + +export function registerFlightsSampleData(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addSavedObjectsToSampleDataset(datasetId, [ + { + type: 'graph-workspace', + id: '5dc018d0-32f8-11ea-bbe4-818d9c786051', + version: '2', + attributes: { + title: 'Kibana Sample Data - Flights', + description: + 'This is a sample graph of the flights demo database. It shows all flights destinations, weather, and which carrier was used.', + numLinks: 142, + numVertices: 91, + version: 1, + wsState: JSON.stringify(JSON.stringify(wsState)), + }, + references: [ + { + name: 'indexPattern_0', + type: 'index-pattern', + id: 'kibana_sample_data_flights', + }, + ], + migrationVersion: { + 'graph-workspace': '7.0.0', + }, + updated_at: '2020-01-09T15:55:24.013Z', + }, + ]); +} +export function registerFlightsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [ + { + path: createWorkspacePath('5dc018d0-32f8-11ea-bbe4-818d9c786051'), + label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }), + icon: APP_ICON, + }, + ]); +} diff --git a/x-pack/plugins/graph/server/sample_data/index.ts b/x-pack/plugins/graph/server/sample_data/index.ts new file mode 100644 index 00000000000000..a12142c84ccb12 --- /dev/null +++ b/x-pack/plugins/graph/server/sample_data/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerSampleData } from './register_sample_data'; diff --git a/x-pack/plugins/graph/server/sample_data/logs.ts b/x-pack/plugins/graph/server/sample_data/logs.ts new file mode 100644 index 00000000000000..b524e3ccd00720 --- /dev/null +++ b/x-pack/plugins/graph/server/sample_data/logs.ts @@ -0,0 +1,463 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { SampleDataRegistrySetup } from '../../../../../src/plugins/home/server'; +import { APP_ICON, createWorkspacePath } from '../../common/constants'; + +const datasetId = 'logs'; + +const wsState: any = { + selectedFields: [ + { + name: 'agent.keyword', + hopSize: 5, + lastValidHopSize: 5, + color: '#CE0060', + selected: true, + iconClass: 'fa-key', + }, + { + name: 'extension.keyword', + hopSize: 5, + lastValidHopSize: 5, + color: '#B0916F', + selected: true, + iconClass: 'fa-key', + }, + { + name: 'geo.src', + hopSize: 5, + lastValidHopSize: 5, + color: '#1EA593', + selected: true, + iconClass: 'fa-map-marker', + }, + { + name: 'response.keyword', + hopSize: 5, + lastValidHopSize: 5, + color: '#7B000B', + selected: true, + iconClass: 'fa-key', + }, + ], + blacklist: [ + { + x: 349.9814471314239, + y: 274.1259761174194, + label: '200', + color: '#7B000B', + field: 'response.keyword', + term: '200', + parent: null, + size: 15, + }, + { + x: 264.83032783224775, + y: 149.28911778947068, + label: '404', + color: '#7B000B', + field: 'response.keyword', + term: '404', + parent: null, + size: 15, + }, + ], + vertices: [ + { + x: 705.0456564066692, + y: 40.62170801995693, + label: 'US', + color: '#1EA593', + field: 'geo.src', + term: 'US', + parent: null, + size: 15, + }, + { + x: 403.9630841139531, + y: 343.70678387260784, + label: 'rpm', + color: '#B0916F', + field: 'extension.keyword', + term: 'rpm', + parent: null, + size: 15, + }, + { + x: 725.3403430314892, + y: 531.7559897276761, + label: 'NG', + color: '#1EA593', + field: 'geo.src', + term: 'NG', + parent: null, + size: 15, + }, + { + x: 226.82596303052026, + y: 412.0884666842448, + label: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', + color: '#CE0060', + field: 'agent.keyword', + term: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', + parent: null, + size: 15, + }, + { + x: 292.5918164195066, + y: 268.48941070600534, + label: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)', + color: '#CE0060', + field: 'agent.keyword', + term: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)', + parent: null, + size: 15, + }, + { + x: 778.0887942801058, + y: 182.63288692820015, + label: 'LK', + color: '#1EA593', + field: 'geo.src', + term: 'LK', + parent: null, + size: 15, + }, + { + x: 341.01411891217134, + y: 416.2991570121247, + label: 'CN', + color: '#1EA593', + field: 'geo.src', + term: 'CN', + parent: null, + size: 15, + }, + { + x: 190.18900623537502, + y: 474.94911053447447, + label: 'deb', + color: '#B0916F', + field: 'extension.keyword', + term: 'deb', + parent: null, + size: 15, + }, + { + x: 232.7845499845522, + y: 292.79169517403824, + label: 'zip', + color: '#B0916F', + field: 'extension.keyword', + term: 'zip', + parent: null, + size: 15, + }, + { + x: 138.90354356936942, + y: 439.57743024730985, + label: 'gz', + color: '#B0916F', + field: 'extension.keyword', + term: 'gz', + parent: null, + size: 15, + }, + { + x: 365.6408169826396, + y: 249.99925380062638, + label: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + color: '#CE0060', + field: 'agent.keyword', + term: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + parent: null, + size: 15, + }, + { + x: 265.07380044771776, + y: 329.42267335684767, + label: 'css', + color: '#B0916F', + field: 'extension.keyword', + term: 'css', + parent: null, + size: 15, + }, + { + x: 580.3896044080259, + y: 667.66318599786, + label: 'ET', + color: '#1EA593', + field: 'geo.src', + term: 'ET', + parent: null, + size: 15, + }, + { + x: 568.0601884734044, + y: -49.27073508690644, + label: 'ZW', + color: '#1EA593', + field: 'geo.src', + term: 'ZW', + parent: null, + size: 15, + }, + { + x: 227.5383201433136, + y: 199.48377517208598, + label: '', + color: '#B0916F', + field: 'extension.keyword', + term: '', + parent: null, + size: 15, + }, + { + x: 288.39123736592035, + y: 393.4288970445101, + label: 'GT', + color: '#1EA593', + field: 'geo.src', + term: 'GT', + parent: null, + size: 15, + }, + { + x: 155.466542394056, + y: 339.05104397852546, + label: 'ES', + color: '#1EA593', + field: 'geo.src', + term: 'ES', + parent: null, + size: 15, + }, + { + x: 401.32323264585983, + y: -108.02214607103728, + label: 'CO', + color: '#1EA593', + field: 'geo.src', + term: 'CO', + parent: null, + size: 15, + }, + { + x: 792.1054319493805, + y: 358.6366312643674, + label: 'PA', + color: '#1EA593', + field: 'geo.src', + term: 'PA', + parent: null, + size: 15, + }, + { + x: 314.32050042836784, + y: 319.42284908520816, + label: '503', + color: '#7B000B', + field: 'response.keyword', + term: '503', + parent: null, + size: 15, + }, + { + x: 340.8606114563168, + y: 186.9348337340475, + label: 'IN', + color: '#1EA593', + field: 'geo.src', + term: 'IN', + parent: null, + size: 15, + }, + { + x: 164.94735578931335, + y: 303.33721636577303, + label: 'SY', + color: '#1EA593', + field: 'geo.src', + term: 'SY', + parent: null, + size: 15, + }, + { + x: 301.2853960233053, + y: 435.9520992987688, + label: 'MM', + color: '#1EA593', + field: 'geo.src', + term: 'MM', + parent: null, + size: 15, + }, + { + x: 184.25271122815448, + y: 64.76252387726619, + label: 'BI', + color: '#1EA593', + field: 'geo.src', + term: 'BI', + parent: null, + size: 15, + }, + { + x: 547.7017436962481, + y: 363.41281417686463, + label: 'JP', + color: '#1EA593', + field: 'geo.src', + term: 'JP', + parent: null, + size: 15, + }, + { + x: 530.0067122142244, + y: 267.5235740844544, + label: 'IT', + color: '#1EA593', + field: 'geo.src', + term: 'IT', + parent: null, + size: 15, + }, + { + x: 502.58809919406076, + y: 453.01592357006285, + label: 'YE', + color: '#1EA593', + field: 'geo.src', + term: 'YE', + parent: null, + size: 15, + }, + ], + links: [ + { weight: 0.0040634810444444, width: 2, inferred: false, source: 8, target: 6 }, + { weight: 0.00001660379672906535, width: 2, inferred: false, source: 11, target: 3 }, + { weight: 0.00001687128485592423, width: 2, inferred: false, source: 1, target: 4 }, + { weight: 0.000042381532626601156, width: 2, inferred: false, source: 3, target: 7 }, + { weight: 0.0020092062171401465, width: 2, inferred: false, source: 6, target: 11 }, + { weight: 0.009186429989786131, width: 2, inferred: false, source: 6, target: 7 }, + { weight: 0.0000305247789830849, width: 2, inferred: false, source: 8, target: 4 }, + { weight: 0.000001708437364178419, width: 2, inferred: false, source: 11, target: 10 }, + { weight: 0.004598490556833276, width: 2, inferred: false, source: 1, target: 6 }, + { weight: 0.0000010807159115480025, width: 2, inferred: false, source: 3, target: 9 }, + { weight: 0.011760987925777693, width: 2, inferred: false, source: 6, target: 3 }, + { weight: 0.00000572246876958705, width: 2, inferred: false, source: 3, target: 8 }, + { weight: 0.00003799465167914097, width: 2, inferred: false, source: 1, target: 10 }, + { weight: 0.00019306796701208605, width: 2, inferred: false, source: 20, target: 10 }, + { weight: 0.001410620591413074, width: 2, inferred: false, source: 16, target: 19 }, + { weight: 0.00000260088891060836, width: 2, inferred: false, source: 16, target: 8 }, + { weight: 0.000004221034247288931, width: 2, inferred: false, source: 21, target: 9 }, + { weight: 0.0003983945660073508, width: 2, inferred: false, source: 19, target: 3 }, + { weight: 8.558271008580196e-7, width: 2, inferred: false, source: 14, target: 23 }, + { weight: 0.00002868576516354896, width: 2, inferred: false, source: 14, target: 10 }, + { weight: 0.0000016586853264248163, width: 2, inferred: false, source: 21, target: 4 }, + { weight: 5.648882390462957e-7, width: 2, inferred: false, source: 21, target: 14 }, + { weight: 0.00003419153715339904, width: 2, inferred: false, source: 19, target: 4 }, + { weight: 0.0005988902747672857, width: 2, inferred: false, source: 8, target: 19 }, + { weight: 0.000993973548776136, width: 2, inferred: false, source: 19, target: 1 }, + { weight: 3.835478702669183e-7, width: 2, inferred: false, source: 20, target: 1 }, + { weight: 0.00000739937347045766, width: 2, inferred: false, source: 16, target: 3 }, + { weight: 0.0014120620624644335, width: 2, inferred: false, source: 21, target: 19 }, + { weight: 0.00011448660448075209, width: 2, inferred: false, source: 20, target: 14 }, + { weight: 0.000029650891466752718, width: 2, inferred: false, source: 10, target: 19 }, + { weight: 0.0016870823187632464, width: 2, inferred: false, source: 19, target: 15 }, + { weight: 0.000008712259181870499, width: 2, inferred: false, source: 8, target: 20 }, + { weight: 0.0006524448834525575, width: 2, inferred: false, source: 19, target: 6 }, + { weight: 0.000003916666394077713, width: 2, inferred: false, source: 8, target: 10 }, + { weight: 0.0000018561676077277568, width: 2, inferred: false, source: 16, target: 9 }, + { weight: 0.000006461917090181791, width: 2, inferred: false, source: 20, target: 19 }, + { weight: 0.000813581545225818, width: 2, inferred: false, source: 19, target: 22 }, + { weight: 0.0000790962811304827, width: 2, inferred: false, source: 14, target: 19 }, + { weight: 0.0000038232332387185065, width: 2, inferred: false, source: 8, target: 15 }, + { weight: 1.3666472391330093e-8, width: 2, inferred: false, source: 4, target: 22 }, + { weight: 6.868214506521744e-7, width: 2, inferred: false, source: 15, target: 7 }, + { weight: 0.000005170326226968777, width: 2, inferred: false, source: 22, target: 1 }, + { weight: 0.000018344494132865016, width: 2, inferred: false, source: 3, target: 22 }, + { weight: 0.000020468166046825227, width: 2, inferred: false, source: 3, target: 1 }, + { weight: 0.0000020332506548392678, width: 2, inferred: false, source: 21, target: 11 }, + { weight: 0.0000019831315870577016, width: 2, inferred: false, source: 21, target: 7 }, + { weight: 3.630137259008554e-7, width: 2, inferred: false, source: 22, target: 9 }, + { weight: 0.000003238006460439622, width: 2, inferred: false, source: 4, target: 15 }, + { weight: 0.0000010228580484196547, width: 2, inferred: false, source: 4, target: 6 }, + { weight: 7.524068083846899e-7, width: 2, inferred: false, source: 1, target: 24 }, + { weight: 0.00000647545523418574, width: 2, inferred: false, source: 22, target: 8 }, + { weight: 0.000005887870505895788, width: 2, inferred: false, source: 15, target: 10 }, + { weight: 8.428178658580805e-7, width: 2, inferred: false, source: 16, target: 14 }, + { weight: 4.0006658216459443e-7, width: 2, inferred: false, source: 1, target: 25 }, + { weight: 9.694642023468333e-7, width: 2, inferred: false, source: 1, target: 26 }, + { weight: 4.714506544331047e-7, width: 2, inferred: false, source: 3, target: 21 }, + { weight: 8.212604651861594e-7, width: 2, inferred: false, source: 16, target: 11 }, + { weight: 3.477810450648006e-7, width: 2, inferred: false, source: 16, target: 7 }, + { weight: 9.208171070957436e-8, width: 2, inferred: false, source: 3, target: 15 }, + { weight: 0.0000010905102327000433, width: 2, inferred: false, source: 4, target: 16 }, + { weight: 0.000014782307685187607, width: 2, inferred: false, source: 15, target: 9 }, + ], + urlTemplates: [ + { + url: + '/app/kibana#/discover?_a=(columns%3A!(_source)%2Cindex%3A%2790943e30-9a47-11e8-b64d-95841ca0b247%27%2Cinterval%3Aauto%2Cquery%3A(language%3Akuery%2Cquery%3A{{gquery}})%2Csort%3A!(_score%2Cdesc))', + description: 'Raw documents', + isDefault: true, + encoderID: 'kql-loose', + }, + ], + exploreControls: { + useSignificance: true, + sampleSize: 2000, + timeoutMillis: 5000, + maxValuesPerDoc: 1, + minDocCount: 3, + }, + indexPatternRefName: 'indexPattern_0', +}; + +export function registerLogsSampleData(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addSavedObjectsToSampleDataset(datasetId, [ + { + type: 'graph-workspace', + id: 'e2141080-32fa-11ea-bbe4-818d9c786051', + version: '2', + attributes: { + title: 'Kibana Sample Data - Data Logs', + description: + 'This is a sample graph based on the data logs index, which shows agents, extensions, source geography of the log, and response codes. The graph has 200 and 404 response codes blocked, as they have low cardinality.', + numLinks: 61, + numVertices: 27, + version: 1, + wsState: JSON.stringify(JSON.stringify(wsState)), + }, + references: [ + { + name: 'indexPattern_0', + type: 'index-pattern', + id: 'kibana_sample_data_logs', + }, + ], + migrationVersion: { + 'graph-workspace': '7.0.0', + }, + updated_at: '2020-01-09T16:40:36.122Z', + }, + ]); +} +export function registerLogsSampleDataLink(sampleDataRegistry: SampleDataRegistrySetup) { + sampleDataRegistry.addAppLinksToSampleDataset(datasetId, [ + { + path: createWorkspacePath('e2141080-32fa-11ea-bbe4-818d9c786051'), + label: i18n.translate('xpack.graph.sampleData.label', { defaultMessage: 'Graph' }), + icon: APP_ICON, + }, + ]); +} diff --git a/x-pack/plugins/graph/server/sample_data/register_sample_data.ts b/x-pack/plugins/graph/server/sample_data/register_sample_data.ts new file mode 100644 index 00000000000000..9a05b656b61a44 --- /dev/null +++ b/x-pack/plugins/graph/server/sample_data/register_sample_data.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SampleDataRegistrySetup } from '../../../../../src/plugins/home/server'; +import { registerLogsSampleData, registerLogsSampleDataLink } from './logs'; +import { registerEcommerceSampleData, registerEcommerceSampleDataLink } from './ecommerce'; +import { registerFlightsSampleData, registerFlightsSampleDataLink } from './flights'; +import { LicenseState } from '../lib/license_state'; + +export function registerSampleData( + sampleDataRegistry: SampleDataRegistrySetup, + licenseState: LicenseState +) { + // always register the saved objects... + registerEcommerceSampleData(sampleDataRegistry); + registerFlightsSampleData(sampleDataRegistry); + registerLogsSampleData(sampleDataRegistry); + + // but wait for a license actually supporting Graph to add links to the sample data panels + const licenseUpdates = licenseState.getLicenseInformation$(); + if (licenseUpdates === null) { + throw new Error('License state has to be initialized before registering sample data'); + } + let registered = false; + licenseUpdates.subscribe(licenseInformation => { + if (!registered && licenseInformation.showAppLink) { + registered = true; + registerEcommerceSampleDataLink(sampleDataRegistry); + registerFlightsSampleDataLink(sampleDataRegistry); + registerLogsSampleDataLink(sampleDataRegistry); + } + }); +} From 8c0440f29ddc1d6966aa6556e0401e8580e10c6b Mon Sep 17 00:00:00 2001 From: Chris Davies Date: Tue, 14 Jan 2020 12:06:51 -0500 Subject: [PATCH 11/52] [Lens] Add clear layer feature (#53627) * [Lens] Add clear layer feature * Move clear / remove layer out of the context menu * Address code review comments * Remove xpack.lens.xyChart.deleteLayer translation * Get rid of unused Lens translations Co-authored-by: Elastic Machine --- .../visualization.test.tsx | 40 ++ .../visualization.tsx | 84 ++-- .../editor_frame/chart_switch.tsx | 1 - .../editor_frame/config_panel_wrapper.tsx | 279 +++++++++++-- .../editor_frame/editor_frame.test.tsx | 88 ++-- .../editor_frame/editor_frame.tsx | 36 +- .../editor_frame/layer_actions.test.ts | 115 ++++++ .../editor_frame/layer_actions.ts | 88 ++++ .../editor_frame/state_management.test.ts | 1 + .../editor_frame/state_management.ts | 16 + .../editor_frame/suggestion_helpers.ts | 3 +- .../editor_frame/suggestion_panel.tsx | 2 +- .../editor_frame/workspace_panel.test.tsx | 2 +- .../editor_frame/workspace_panel.tsx | 7 +- .../lens/public/editor_frame_plugin/mocks.tsx | 9 +- .../indexpattern_plugin/indexpattern.tsx | 24 +- .../metric_config_panel.test.tsx | 1 + .../metric_config_panel.tsx | 51 +-- .../metric_visualization.test.ts | 16 + .../metric_visualization.tsx | 13 +- x-pack/legacy/plugins/lens/public/types.ts | 18 +- .../xy_config_panel.test.tsx | 266 +++--------- .../xy_config_panel.tsx | 381 ++++++------------ .../xy_visualization.test.ts | 48 +++ .../xy_visualization.tsx | 64 ++- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 27 files changed, 1018 insertions(+), 639 deletions(-) create mode 100644 x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/layer_actions.test.ts create mode 100644 x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/layer_actions.ts diff --git a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.test.tsx b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.test.tsx index 25d88fbae5b341..cb9350226575ca 100644 --- a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.test.tsx +++ b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.test.tsx @@ -72,6 +72,42 @@ describe('Datatable Visualization', () => { }); }); + describe('#getLayerIds', () => { + it('return the layer ids', () => { + const state: DatatableVisualizationState = { + layers: [ + { + layerId: 'baz', + columns: ['a', 'b', 'c'], + }, + ], + }; + expect(datatableVisualization.getLayerIds(state)).toEqual(['baz']); + }); + }); + + describe('#clearLayer', () => { + it('should reset the layer', () => { + (generateId as jest.Mock).mockReturnValueOnce('testid'); + const state: DatatableVisualizationState = { + layers: [ + { + layerId: 'baz', + columns: ['a', 'b', 'c'], + }, + ], + }; + expect(datatableVisualization.clearLayer(state, 'baz')).toMatchObject({ + layers: [ + { + layerId: 'baz', + columns: ['testid'], + }, + ], + }); + }); + }); + describe('#getSuggestions', () => { function numCol(columnId: string): TableSuggestionColumn { return { @@ -188,6 +224,7 @@ describe('Datatable Visualization', () => { mount( {} }} frame={frame} layer={layer} @@ -224,6 +261,7 @@ describe('Datatable Visualization', () => { frame.datasourceLayers = { a: datasource.publicAPIMock }; const component = mount( {} }} frame={frame} layer={layer} @@ -258,6 +296,7 @@ describe('Datatable Visualization', () => { frame.datasourceLayers = { a: datasource.publicAPIMock }; const component = mount( {} }} frame={frame} layer={layer} @@ -290,6 +329,7 @@ describe('Datatable Visualization', () => { frame.datasourceLayers = { a: datasource.publicAPIMock }; const component = mount( {} }} frame={frame} layer={layer} diff --git a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.tsx b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.tsx index f9a7ec419a9b9a..79a018635134f6 100644 --- a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/visualization.tsx @@ -6,19 +6,18 @@ import React from 'react'; import { render } from 'react-dom'; -import { EuiForm, EuiFormRow, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; import { MultiColumnEditor } from '../multi_column_editor'; import { SuggestionRequest, Visualization, - VisualizationProps, + VisualizationLayerConfigProps, VisualizationSuggestion, Operation, } from '../types'; import { generateId } from '../id_generator'; -import { NativeRenderer } from '../native_renderer'; import chartTableSVG from '../assets/chart_datatable.svg'; export interface LayerState { @@ -56,7 +55,7 @@ export function DataTableLayer({ state, setState, dragDropContext, -}: { layer: LayerState } & VisualizationProps) { +}: { layer: LayerState } & VisualizationLayerConfigProps) { const datasource = frame.datasourceLayers[layer.layerId]; const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); @@ -64,32 +63,24 @@ export function DataTableLayer({ const sortedColumns = Array.from(new Set(originalOrder.concat(layer.columns))); return ( - - + setState(updateColumns(state, layer, columns => [...columns, generateId()]))} + onRemove={column => + setState(updateColumns(state, layer, columns => columns.filter(c => c !== column))) + } + testSubj="datatable_columns" + data-test-subj="datatable_multicolumnEditor" /> - - - - setState(updateColumns(state, layer, columns => [...columns, generateId()]))} - onRemove={column => - setState(updateColumns(state, layer, columns => columns.filter(c => c !== column))) - } - testSubj="datatable_columns" - data-test-subj="datatable_multicolumnEditor" - /> - - + ); } @@ -110,7 +101,17 @@ export const datatableVisualization: Visualization< }, ], - getDescription(state) { + getLayerIds(state) { + return state.layers.map(l => l.layerId); + }, + + clearLayer(state) { + return { + layers: state.layers.map(l => newLayerState(l.layerId)), + }; + }, + + getDescription() { return { icon: chartTableSVG, label: i18n.translate('xpack.lens.datatable.label', { @@ -187,17 +188,18 @@ export const datatableVisualization: Visualization< ]; }, - renderConfigPanel: (domElement, props) => - render( - - - {props.state.layers.map(layer => ( - - ))} - - , - domElement - ), + renderLayerConfigPanel(domElement, props) { + const layer = props.state.layers.find(l => l.layerId === props.layerId); + + if (layer) { + render( + + + , + domElement + ); + } + }, toExpression(state, frame) { const layer = state.layers[0]; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx index dca6b3e7616d67..5e2fced5777246 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx @@ -81,7 +81,6 @@ export function ChartSwitch(props: Props) { trackUiEvent(`chart_switch`); switchToSuggestion( - props.framePublicAPI, props.dispatch, { ...selection, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/config_panel_wrapper.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/config_panel_wrapper.tsx index 4179a9455eefad..1422ee86be3e9b 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/config_panel_wrapper.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/config_panel_wrapper.tsx @@ -4,14 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useContext, memo } from 'react'; +import React, { useMemo, useContext, memo, useState } from 'react'; +import { + EuiPanel, + EuiSpacer, + EuiPopover, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiToolTip, + EuiButton, + EuiForm, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { NativeRenderer } from '../../native_renderer'; import { Action } from './state_management'; -import { Visualization, FramePublicAPI, Datasource } from '../../types'; +import { + Visualization, + FramePublicAPI, + Datasource, + VisualizationLayerConfigProps, +} from '../../types'; import { DragContext } from '../../drag_drop'; import { ChartSwitch } from './chart_switch'; +import { trackUiEvent } from '../../lens_ui_telemetry'; +import { generateId } from '../../id_generator'; +import { removeLayer, appendLayer } from './layer_actions'; interface ConfigPanelWrapperProps { + activeDatasourceId: string; visualizationState: unknown; visualizationMap: Record; activeVisualizationId: string | null; @@ -28,17 +50,8 @@ interface ConfigPanelWrapperProps { } export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: ConfigPanelWrapperProps) { - const context = useContext(DragContext); - const setVisualizationState = useMemo( - () => (newState: unknown) => { - props.dispatch({ - type: 'UPDATE_VISUALIZATION_STATE', - newState, - clearStagedPreview: false, - }); - }, - [props.dispatch] - ); + const activeVisualization = props.visualizationMap[props.activeVisualizationId || '']; + const { visualizationState } = props; return ( <> @@ -52,19 +65,235 @@ export const ConfigPanelWrapper = memo(function ConfigPanelWrapper(props: Config dispatch={props.dispatch} framePublicAPI={props.framePublicAPI} /> - {props.activeVisualizationId && props.visualizationState !== null && ( -
- -
+ {activeVisualization && visualizationState && ( + )} ); }); + +function LayerPanels( + props: ConfigPanelWrapperProps & { + activeDatasourceId: string; + activeVisualization: Visualization; + } +) { + const { + framePublicAPI, + activeVisualization, + visualizationState, + dispatch, + activeDatasourceId, + datasourceMap, + } = props; + const dragDropContext = useContext(DragContext); + const setState = useMemo( + () => (newState: unknown) => { + props.dispatch({ + type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, + newState, + clearStagedPreview: false, + }); + }, + [props.dispatch, activeVisualization] + ); + const layerIds = activeVisualization.getLayerIds(visualizationState); + + return ( + + {layerIds.map(layerId => ( + { + dispatch({ + type: 'UPDATE_STATE', + subType: 'REMOVE_OR_CLEAR_LAYER', + updater: state => + removeLayer({ + activeVisualization, + layerId, + trackUiEvent, + datasourceMap, + state, + }), + }); + }} + /> + ))} + {activeVisualization.appendLayer && ( + + + { + dispatch({ + type: 'UPDATE_STATE', + subType: 'ADD_LAYER', + updater: state => + appendLayer({ + activeVisualization, + generateId, + trackUiEvent, + activeDatasource: datasourceMap[activeDatasourceId], + state, + }), + }); + }} + iconType="plusInCircleFilled" + /> + + + )} + + ); +} + +function LayerPanel( + props: ConfigPanelWrapperProps & + VisualizationLayerConfigProps & { + isOnlyLayer: boolean; + activeVisualization: Visualization; + onRemove: () => void; + } +) { + const { framePublicAPI, layerId, activeVisualization, isOnlyLayer, onRemove } = props; + const datasourcePublicAPI = framePublicAPI.datasourceLayers[layerId]; + const layerConfigProps = { + layerId, + dragDropContext: props.dragDropContext, + state: props.visualizationState, + setState: props.setState, + frame: props.framePublicAPI, + }; + + return ( + + + + + + + {datasourcePublicAPI && ( + + + + )} + + + + + + + + + + + { + // If we don't blur the remove / clear button, it remains focused + // which is a strange UX in this case. e.target.blur doesn't work + // due to who knows what, but probably event re-writing. Additionally, + // activeElement does not have blur so, we need to do some casting + safeguards. + const el = (document.activeElement as unknown) as { blur: () => void }; + + if (el && el.blur) { + el.blur(); + } + + onRemove(); + }} + > + {isOnlyLayer + ? i18n.translate('xpack.lens.resetLayer', { + defaultMessage: 'Reset layer', + }) + : i18n.translate('xpack.lens.deleteLayer', { + defaultMessage: 'Delete layer', + })} + + + + + ); +} + +function LayerSettings({ + layerId, + activeVisualization, + layerConfigProps, +}: { + layerId: string; + activeVisualization: Visualization; + layerConfigProps: VisualizationLayerConfigProps; +}) { + const [isOpen, setIsOpen] = useState(false); + + if (!activeVisualization.renderLayerContextMenu) { + return null; + } + + return ( + setIsOpen(!isOpen)} + data-test-subj="lns_layer_settings" + /> + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + anchorPosition="leftUp" + > + + + ); +} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx index cf711eea29b963..c9b9a433766515 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.test.tsx @@ -9,7 +9,7 @@ import { ReactWrapper } from 'enzyme'; import { EuiPanel, EuiToolTip } from '@elastic/eui'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { EditorFrame } from './editor_frame'; -import { Visualization, DatasourcePublicAPI, DatasourceSuggestion } from '../../types'; +import { DatasourcePublicAPI, DatasourceSuggestion, Visualization } from '../../types'; import { act } from 'react-dom/test-utils'; import { coreMock } from 'src/core/public/mocks'; import { @@ -24,7 +24,11 @@ import { FrameLayout } from './frame_layout'; // calling this function will wait for all pending Promises from mock // datasources to be processed by its callers. -const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); +async function waitForPromises(n = 3) { + for (let i = 0; i < n; ++i) { + await Promise.resolve(); + } +} function generateSuggestion(state = {}): DatasourceSuggestion { return { @@ -88,6 +92,9 @@ describe('editor_frame', () => { ], }; + mockVisualization.getLayerIds.mockReturnValue(['first']); + mockVisualization2.getLayerIds.mockReturnValue(['second']); + mockDatasource = createMockDatasource(); mockDatasource2 = createMockDatasource(); @@ -202,7 +209,7 @@ describe('editor_frame', () => { ); }); - expect(mockVisualization.renderConfigPanel).not.toHaveBeenCalled(); + expect(mockVisualization.renderLayerConfigPanel).not.toHaveBeenCalled(); expect(mockDatasource.renderDataPanel).not.toHaveBeenCalled(); }); @@ -294,6 +301,7 @@ describe('editor_frame', () => { it('should remove layer on active datasource on frame api call', async () => { const initialState = { datasource2: '' }; + mockDatasource.getLayers.mockReturnValue(['first']); mockDatasource2.initialize.mockReturnValue(Promise.resolve(initialState)); mockDatasource2.getLayers.mockReturnValue(['abc', 'def']); mockDatasource2.removeLayer.mockReturnValue({ removed: true }); @@ -361,7 +369,7 @@ describe('editor_frame', () => { it('should initialize visualization state and render config panel', async () => { const initialState = {}; - + mockDatasource.getLayers.mockReturnValue(['first']); mount( { await waitForPromises(); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: initialState }) ); @@ -390,6 +398,7 @@ describe('editor_frame', () => { it('should render the resulting expression using the expression renderer', async () => { mockDatasource.getLayers.mockReturnValue(['first']); + const instance = mount( { /> ); - await waitForPromises(); await waitForPromises(); instance.update(); @@ -601,6 +609,7 @@ describe('editor_frame', () => { describe('state update', () => { it('should re-render config panel after state update', async () => { + mockDatasource.getLayers.mockReturnValue(['first']); mount( { await waitForPromises(); const updatedState = {}; - const setVisualizationState = (mockVisualization.renderConfigPanel as jest.Mock).mock + const setVisualizationState = (mockVisualization.renderLayerConfigPanel as jest.Mock).mock .calls[0][1].setState; act(() => { setVisualizationState(updatedState); }); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledTimes(2); - expect(mockVisualization.renderConfigPanel).toHaveBeenLastCalledWith( + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(2); + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenLastCalledWith( expect.any(Element), expect.objectContaining({ state: updatedState, @@ -635,6 +644,7 @@ describe('editor_frame', () => { }); it('should re-render data panel after state update', async () => { + mockDatasource.getLayers.mockReturnValue(['first']); mount( { await waitForPromises(); - const updatedPublicAPI = {}; - mockDatasource.getPublicAPI.mockReturnValue( - (updatedPublicAPI as unknown) as DatasourcePublicAPI - ); + const updatedPublicAPI: DatasourcePublicAPI = { + renderLayerPanel: jest.fn(), + renderDimensionPanel: jest.fn(), + getOperationForColumnId: jest.fn(), + getTableSpec: jest.fn(), + }; + mockDatasource.getPublicAPI.mockReturnValue(updatedPublicAPI); const setDatasourceState = (mockDatasource.renderDataPanel as jest.Mock).mock.calls[0][1] .setState; @@ -700,8 +713,8 @@ describe('editor_frame', () => { setDatasourceState({}); }); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledTimes(2); - expect(mockVisualization.renderConfigPanel).toHaveBeenLastCalledWith( + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(2); + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenLastCalledWith( expect.any(Element), expect.objectContaining({ frame: expect.objectContaining({ @@ -754,10 +767,10 @@ describe('editor_frame', () => { await waitForPromises(); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalled(); + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalled(); const datasourceLayers = - mockVisualization.renderConfigPanel.mock.calls[0][1].frame.datasourceLayers; + mockVisualization.renderLayerConfigPanel.mock.calls[0][1].frame.datasourceLayers; expect(datasourceLayers.first).toBe(mockDatasource.publicAPIMock); expect(datasourceLayers.second).toBe(mockDatasource2.publicAPIMock); expect(datasourceLayers.third).toBe(mockDatasource2.publicAPIMock); @@ -919,7 +932,7 @@ describe('editor_frame', () => { } beforeEach(async () => { - mockDatasource.getLayers.mockReturnValue(['first']); + mockDatasource.getLayers.mockReturnValue(['first', 'second']); mockDatasource.getDatasourceSuggestionsFromCurrentState.mockReturnValue([ { state: {}, @@ -1018,7 +1031,7 @@ describe('editor_frame', () => { expect(mockVisualization2.getSuggestions).toHaveBeenCalled(); expect(mockVisualization2.initialize).toHaveBeenCalledWith(expect.anything(), initialState); - expect(mockVisualization2.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: { initial: true } }) ); @@ -1032,9 +1045,11 @@ describe('editor_frame', () => { expect(mockDatasource.publicAPIMock.getTableSpec).toHaveBeenCalled(); expect(mockVisualization2.getSuggestions).toHaveBeenCalled(); expect(mockVisualization2.initialize).toHaveBeenCalledWith( - expect.objectContaining({ datasourceLayers: { first: mockDatasource.publicAPIMock } }) + expect.objectContaining({ + datasourceLayers: expect.objectContaining({ first: mockDatasource.publicAPIMock }), + }) ); - expect(mockVisualization2.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: { initial: true } }) ); @@ -1102,6 +1117,7 @@ describe('editor_frame', () => { }); it('should display top 5 suggestions in descending order', async () => { + mockDatasource.getLayers.mockReturnValue(['first']); const instance = mount( { }); it('should switch to suggested visualization', async () => { + mockDatasource.getLayers.mockReturnValue(['first', 'second', 'third']); const newDatasourceState = {}; const suggestionVisState = {}; const instance = mount( @@ -1228,8 +1245,8 @@ describe('editor_frame', () => { .simulate('click'); }); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledTimes(1); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledTimes(1); + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: suggestionVisState, @@ -1244,6 +1261,7 @@ describe('editor_frame', () => { }); it('should switch to best suggested visualization on field drop', async () => { + mockDatasource.getLayers.mockReturnValue(['first']); const suggestionVisState = {}; const instance = mount( { .simulate('drop'); }); - expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: suggestionVisState, @@ -1302,6 +1320,7 @@ describe('editor_frame', () => { }); it('should use the currently selected visualization if possible on field drop', async () => { + mockDatasource.getLayers.mockReturnValue(['first', 'second', 'third']); const suggestionVisState = {}; const instance = mount( { }); }); - expect(mockVisualization2.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization2.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: suggestionVisState, @@ -1375,10 +1394,12 @@ describe('editor_frame', () => { }); it('should use the highest priority suggestion available', async () => { + mockDatasource.getLayers.mockReturnValue(['first', 'second', 'third']); const suggestionVisState = {}; const mockVisualization3 = { ...createMockVisualization(), id: 'testVis3', + getLayerIds: () => ['third'], visualizationTypes: [ { icon: 'empty', @@ -1460,7 +1481,7 @@ describe('editor_frame', () => { }); }); - expect(mockVisualization3.renderConfigPanel).toHaveBeenCalledWith( + expect(mockVisualization3.renderLayerConfigPanel).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ state: suggestionVisState, @@ -1633,13 +1654,16 @@ describe('editor_frame', () => { await waitForPromises(); expect(onChange).toHaveBeenCalledTimes(2); - (instance.find(FrameLayout).prop('dataPanel') as ReactElement)!.props.dispatch({ - type: 'UPDATE_DATASOURCE_STATE', - updater: () => ({ - newState: true, - }), - datasourceId: 'testDatasource', + act(() => { + (instance.find(FrameLayout).prop('dataPanel') as ReactElement)!.props.dispatch({ + type: 'UPDATE_DATASOURCE_STATE', + updater: () => ({ + newState: true, + }), + datasourceId: 'testDatasource', + }); }); + await waitForPromises(); expect(onChange).toHaveBeenCalledTimes(3); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx index a2745818e19bb0..3284f69b503c5f 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx @@ -52,6 +52,8 @@ export interface EditorFrameProps { export function EditorFrame(props: EditorFrameProps) { const [state, dispatch] = useReducer(reducer, props, getInitialState); const { onError } = props; + const activeVisualization = + state.visualization.activeId && props.visualizationMap[state.visualization.activeId]; const allLoaded = Object.values(state.datasourceStates).every( ({ isLoading }) => typeof isLoading === 'boolean' && !isLoading @@ -125,7 +127,20 @@ export function EditorFrame(props: EditorFrameProps) { return newLayerId; }, - removeLayers: (layerIds: string[]) => { + + removeLayers(layerIds: string[]) { + if (activeVisualization && activeVisualization.removeLayer && state.visualization.state) { + dispatch({ + type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, + newState: layerIds.reduce( + (acc, layerId) => + activeVisualization.removeLayer ? activeVisualization.removeLayer(acc, layerId) : acc, + state.visualization.state + ), + }); + } + layerIds.forEach(layerId => { const layerDatasourceId = Object.entries(props.datasourceMap).find( ([datasourceId, datasource]) => @@ -158,16 +173,15 @@ export function EditorFrame(props: EditorFrameProps) { // Initialize visualization as soon as all datasources are ready useEffect(() => { - if (allLoaded && state.visualization.state === null && state.visualization.activeId !== null) { - const initialVisualizationState = props.visualizationMap[ - state.visualization.activeId - ].initialize(framePublicAPI); + if (allLoaded && state.visualization.state === null && activeVisualization) { + const initialVisualizationState = activeVisualization.initialize(framePublicAPI); dispatch({ type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, newState: initialVisualizationState, }); } - }, [allLoaded, state.visualization.activeId, state.visualization.state]); + }, [allLoaded, activeVisualization, state.visualization.state]); // The frame needs to call onChange every time its internal state changes useEffect(() => { @@ -176,11 +190,7 @@ export function EditorFrame(props: EditorFrameProps) { ? props.datasourceMap[state.activeDatasourceId] : undefined; - const visualization = state.visualization.activeId - ? props.visualizationMap[state.visualization.activeId] - : undefined; - - if (!activeDatasource || !visualization) { + if (!activeDatasource || !activeVisualization) { return; } @@ -208,13 +218,14 @@ export function EditorFrame(props: EditorFrameProps) { }), {} ), - visualization, + visualization: activeVisualization, state, framePublicAPI, }); props.onChange({ filterableIndexPatterns: indexPatterns, doc }); }, [ + activeVisualization, state.datasourceStates, state.visualization, props.query, @@ -248,6 +259,7 @@ export function EditorFrame(props: EditorFrameProps) { configPanel={ allLoaded && ( ({ + id: datasourceId, + clearLayer: (layerIds: unknown, layerId: string) => + (layerIds as string[]).map((id: string) => + id === layerId ? `${datasourceId}_clear_${layerId}` : id + ), + removeLayer: (layerIds: unknown, layerId: string) => + (layerIds as string[]).filter((id: string) => id !== layerId), + insertLayer: (layerIds: unknown, layerId: string) => [...(layerIds as string[]), layerId], + }); + + const activeVisualization = { + clearLayer: (layerIds: unknown, layerId: string) => + (layerIds as string[]).map((id: string) => (id === layerId ? `vis_clear_${layerId}` : id)), + removeLayer: (layerIds: unknown, layerId: string) => + (layerIds as string[]).filter((id: string) => id !== layerId), + getLayerIds: (layerIds: unknown) => layerIds as string[], + appendLayer: (layerIds: unknown, layerId: string) => [...(layerIds as string[]), layerId], + }; + + return { + state: { + activeDatasourceId: 'ds1', + datasourceStates: { + ds1: { + isLoading: false, + state: initialLayerIds.slice(0, 1), + }, + ds2: { + isLoading: false, + state: initialLayerIds.slice(1), + }, + }, + title: 'foo', + visualization: { + activeId: 'vis1', + state: initialLayerIds, + }, + }, + activeVisualization, + datasourceMap: { + ds1: testDatasource('ds1'), + ds2: testDatasource('ds2'), + }, + trackUiEvent, + }; +} + +describe('removeLayer', () => { + it('should clear the layer if it is the only layer', () => { + const { state, trackUiEvent, datasourceMap, activeVisualization } = createTestArgs(['layer1']); + const newState = removeLayer({ + activeVisualization, + datasourceMap, + layerId: 'layer1', + state, + trackUiEvent, + }); + + expect(newState.visualization.state).toEqual(['vis_clear_layer1']); + expect(newState.datasourceStates.ds1.state).toEqual(['ds1_clear_layer1']); + expect(newState.datasourceStates.ds2.state).toEqual([]); + expect(trackUiEvent).toHaveBeenCalledWith('layer_cleared'); + }); + + it('should remove the layer if it is not the only layer', () => { + const { state, trackUiEvent, datasourceMap, activeVisualization } = createTestArgs([ + 'layer1', + 'layer2', + ]); + const newState = removeLayer({ + activeVisualization, + datasourceMap, + layerId: 'layer1', + state, + trackUiEvent, + }); + + expect(newState.visualization.state).toEqual(['layer2']); + expect(newState.datasourceStates.ds1.state).toEqual([]); + expect(newState.datasourceStates.ds2.state).toEqual(['layer2']); + expect(trackUiEvent).toHaveBeenCalledWith('layer_removed'); + }); +}); + +describe('appendLayer', () => { + it('should add the layer to the datasource and visualization', () => { + const { state, trackUiEvent, datasourceMap, activeVisualization } = createTestArgs([ + 'layer1', + 'layer2', + ]); + const newState = appendLayer({ + activeDatasource: datasourceMap.ds1, + activeVisualization, + generateId: () => 'foo', + state, + trackUiEvent, + }); + + expect(newState.visualization.state).toEqual(['layer1', 'layer2', 'foo']); + expect(newState.datasourceStates.ds1.state).toEqual(['layer1', 'foo']); + expect(newState.datasourceStates.ds2.state).toEqual(['layer2']); + expect(trackUiEvent).toHaveBeenCalledWith('layer_added'); + }); +}); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/layer_actions.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/layer_actions.ts new file mode 100644 index 00000000000000..e0562e8ca8e11c --- /dev/null +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/layer_actions.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { EditorFrameState } from './state_management'; +import { Datasource, Visualization } from '../../types'; + +interface RemoveLayerOptions { + trackUiEvent: (name: string) => void; + state: EditorFrameState; + layerId: string; + activeVisualization: Pick; + datasourceMap: Record>; +} + +interface AppendLayerOptions { + trackUiEvent: (name: string) => void; + state: EditorFrameState; + generateId: () => string; + activeDatasource: Pick; + activeVisualization: Pick; +} + +export function removeLayer(opts: RemoveLayerOptions): EditorFrameState { + const { state, trackUiEvent: trackUiEvent, activeVisualization, layerId, datasourceMap } = opts; + const isOnlyLayer = activeVisualization + .getLayerIds(state.visualization.state) + .every(id => id === opts.layerId); + + trackUiEvent(isOnlyLayer ? 'layer_cleared' : 'layer_removed'); + + return { + ...state, + datasourceStates: _.mapValues(state.datasourceStates, (datasourceState, datasourceId) => { + const datasource = datasourceMap[datasourceId!]; + return { + ...datasourceState, + state: isOnlyLayer + ? datasource.clearLayer(datasourceState.state, layerId) + : datasource.removeLayer(datasourceState.state, layerId), + }; + }), + visualization: { + ...state.visualization, + state: + isOnlyLayer || !activeVisualization.removeLayer + ? activeVisualization.clearLayer(state.visualization.state, layerId) + : activeVisualization.removeLayer(state.visualization.state, layerId), + }, + }; +} + +export function appendLayer({ + trackUiEvent, + activeVisualization, + state, + generateId, + activeDatasource, +}: AppendLayerOptions): EditorFrameState { + trackUiEvent('layer_added'); + + if (!activeVisualization.appendLayer) { + return state; + } + + const layerId = generateId(); + + return { + ...state, + datasourceStates: { + ...state.datasourceStates, + [activeDatasource.id]: { + ...state.datasourceStates[activeDatasource.id], + state: activeDatasource.insertLayer( + state.datasourceStates[activeDatasource.id].state, + layerId + ), + }, + }, + visualization: { + ...state.visualization, + state: activeVisualization.appendLayer(state.visualization.state, layerId), + }, + }; +} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.test.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.test.ts index 5168059a332589..4aaf2a3ee9e81e 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.test.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.test.ts @@ -119,6 +119,7 @@ describe('editor_frame state management', () => { }, { type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: 'testVis', newState: newVisState, } ); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts index 78a9a13f48d6aa..7d763bcac2cc9b 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/state_management.ts @@ -31,6 +31,13 @@ export type Action = type: 'UPDATE_TITLE'; title: string; } + | { + type: 'UPDATE_STATE'; + // Just for diagnostics, so we can determine what action + // caused this update. + subType: string; + updater: (prevState: EditorFrameState) => EditorFrameState; + } | { type: 'UPDATE_DATASOURCE_STATE'; updater: unknown | ((prevState: unknown) => unknown); @@ -39,6 +46,7 @@ export type Action = } | { type: 'UPDATE_VISUALIZATION_STATE'; + visualizationId: string; newState: unknown; clearStagedPreview?: boolean; } @@ -128,6 +136,8 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta return action.state; case 'UPDATE_TITLE': return { ...state, title: action.title }; + case 'UPDATE_STATE': + return action.updater(state); case 'UPDATE_LAYER': return { ...state, @@ -249,6 +259,12 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta if (!state.visualization.activeId) { throw new Error('Invariant: visualization state got updated without active visualization'); } + // This is a safeguard that prevents us from accidentally updating the + // wrong visualization. This occurs in some cases due to the uncoordinated + // way we manage state across plugins. + if (state.visualization.activeId !== action.visualizationId) { + return state; + } return { ...state, visualization: { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_helpers.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_helpers.ts index 173f64c6292a87..eabcdfa7a24ab4 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_helpers.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_helpers.ts @@ -10,7 +10,6 @@ import { IconType } from '@elastic/eui/src/components/icon/icon'; import { Visualization, Datasource, - FramePublicAPI, TableChangeType, TableSuggestion, DatasourceSuggestion, @@ -130,7 +129,6 @@ function getVisualizationSuggestions( } export function switchToSuggestion( - frame: FramePublicAPI, dispatch: (action: Action) => void, suggestion: Pick< Suggestion, @@ -145,5 +143,6 @@ export function switchToSuggestion( datasourceState: suggestion.datasourceState, datasourceId: suggestion.datasourceId!, }; + dispatch(action); } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx index 2408d004689c9d..46e226afe9c59b 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/suggestion_panel.tsx @@ -320,7 +320,7 @@ export function SuggestionPanel({ } else { trackSuggestionEvent(`position_${index}_of_${suggestions.length}`); setLastSelectedSuggestion(index); - switchToSuggestion(frame, dispatch, suggestion); + switchToSuggestion(dispatch, suggestion); } }} selected={index === lastSelectedSuggestion} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx index fb3fe770b315b3..74dacd50d7a159 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { ExpressionRendererProps } from '../../../../../../../src/plugins/expressions/public'; -import { Visualization, FramePublicAPI, TableSuggestion } from '../../types'; +import { FramePublicAPI, TableSuggestion, Visualization } from '../../types'; import { createMockVisualization, createMockDatasource, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx index 05dcafcaeba319..1058ccd81d6696 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx @@ -126,12 +126,7 @@ export function InnerWorkspacePanel({ if (suggestionForDraggedField) { trackUiEvent('drop_onto_workspace'); trackUiEvent(expression ? 'drop_non_empty' : 'drop_empty'); - switchToSuggestion( - framePublicAPI, - dispatch, - suggestionForDraggedField, - 'SWITCH_VISUALIZATION' - ); + switchToSuggestion(dispatch, suggestionForDraggedField, 'SWITCH_VISUALIZATION'); } } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx index 5df6cc8106d6af..7257647d5953e5 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx @@ -14,12 +14,14 @@ import { import { embeddablePluginMock } from '../../../../../../src/plugins/embeddable/public/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; -import { DatasourcePublicAPI, FramePublicAPI, Visualization, Datasource } from '../types'; +import { DatasourcePublicAPI, FramePublicAPI, Datasource, Visualization } from '../types'; import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './plugin'; export function createMockVisualization(): jest.Mocked { return { id: 'TEST_VIS', + clearLayer: jest.fn((state, _layerId) => state), + getLayerIds: jest.fn(_state => ['layer1']), visualizationTypes: [ { icon: 'empty', @@ -32,7 +34,7 @@ export function createMockVisualization(): jest.Mocked { getPersistableState: jest.fn(_state => _state), getSuggestions: jest.fn(_options => []), initialize: jest.fn((_frame, _state?) => ({})), - renderConfigPanel: jest.fn(), + renderLayerConfigPanel: jest.fn(), toExpression: jest.fn((_state, _frame) => null), toPreviewExpression: jest.fn((_state, _frame) => null), }; @@ -52,7 +54,8 @@ export function createMockDatasource(): DatasourceMock { return { id: 'mockindexpattern', - getDatasourceSuggestionsForField: jest.fn((_state, item) => []), + clearLayer: jest.fn((state, _layerId) => state), + getDatasourceSuggestionsForField: jest.fn((_state, _item) => []), getDatasourceSuggestionsFromCurrentState: jest.fn(_state => []), getPersistableState: jest.fn(), getPublicAPI: jest.fn().mockReturnValue(publicAPIMock), diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx index b58a2d8ca52c75..2426d7fc14b5d6 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern.tsx @@ -132,11 +132,7 @@ export function getIndexPatternDatasource({ ...state, layers: { ...state.layers, - [newLayerId]: { - indexPatternId: state.currentIndexPatternId, - columns: {}, - columnOrder: [], - }, + [newLayerId]: blankLayer(state.currentIndexPatternId), }, }; }, @@ -151,6 +147,16 @@ export function getIndexPatternDatasource({ }; }, + clearLayer(state: IndexPatternPrivateState, layerId: string) { + return { + ...state, + layers: { + ...state.layers, + [layerId]: blankLayer(state.currentIndexPatternId), + }, + }; + }, + getLayers(state: IndexPatternPrivateState) { return Object.keys(state.layers); }, @@ -280,3 +286,11 @@ export function getIndexPatternDatasource({ return indexPatternDatasource; } + +function blankLayer(indexPatternId: string) { + return { + indexPatternId, + columns: {}, + columnOrder: [], + }; +} diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_config_panel.test.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_config_panel.test.tsx index ff2e55ac83dccd..a66239e5d30f68 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_config_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_config_panel.test.tsx @@ -38,6 +38,7 @@ describe('MetricConfigPanel', () => { const state = testState(); const component = mount( !op.isBucketed && op.dataType === 'number'; -export function MetricConfigPanel(props: VisualizationProps) { - const { state, frame } = props; - const [datasource] = Object.values(frame.datasourceLayers); - const [layerId] = Object.keys(frame.datasourceLayers); +export function MetricConfigPanel(props: VisualizationLayerConfigProps) { + const { state, frame, layerId } = props; + const datasource = frame.datasourceLayers[layerId]; return ( - - - - - - - - - + + + ); } diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.test.ts b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.test.ts index a95b5a2b276319..c131612399cca0 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.test.ts +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.test.ts @@ -50,6 +50,22 @@ describe('metric_visualization', () => { }); }); + describe('#getLayerIds', () => { + it('returns the layer id', () => { + expect(metricVisualization.getLayerIds(exampleState())).toEqual(['l1']); + }); + }); + + describe('#clearLayer', () => { + it('returns a clean layer', () => { + (generateId as jest.Mock).mockReturnValueOnce('test-id1'); + expect(metricVisualization.clearLayer(exampleState(), 'l1')).toEqual({ + accessor: 'test-id1', + layerId: 'l1', + }); + }); + }); + describe('#getPersistableState', () => { it('persists the state as given', () => { expect(metricVisualization.getPersistableState(exampleState())).toEqual(exampleState()); diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.tsx index 00e945c0ce6e5b..6714c057878373 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_visualization.tsx @@ -54,6 +54,17 @@ export const metricVisualization: Visualization = { }, ], + clearLayer(state) { + return { + ...state, + accessor: generateId(), + }; + }, + + getLayerIds(state) { + return [state.layerId]; + }, + getDescription() { return { icon: chartMetricSVG, @@ -76,7 +87,7 @@ export const metricVisualization: Visualization = { getPersistableState: state => state, - renderConfigPanel: (domElement, props) => + renderLayerConfigPanel: (domElement, props) => render( diff --git a/x-pack/legacy/plugins/lens/public/types.ts b/x-pack/legacy/plugins/lens/public/types.ts index f83157b2a80000..923e0aff5ae0e8 100644 --- a/x-pack/legacy/plugins/lens/public/types.ts +++ b/x-pack/legacy/plugins/lens/public/types.ts @@ -135,6 +135,7 @@ export interface Datasource { insertLayer: (state: T, newLayerId: string) => T; removeLayer: (state: T, layerId: string) => T; + clearLayer: (state: T, layerId: string) => T; getLayers: (state: T) => string[]; renderDataPanel: (domElement: Element, props: DatasourceDataPanelProps) => void; @@ -237,7 +238,8 @@ export interface LensMultiTable { }; } -export interface VisualizationProps { +export interface VisualizationLayerConfigProps { + layerId: string; dragDropContext: DragContextState; frame: FramePublicAPI; state: T; @@ -325,6 +327,18 @@ export interface Visualization { visualizationTypes: VisualizationType[]; + getLayerIds: (state: T) => string[]; + + clearLayer: (state: T, layerId: string) => T; + + removeLayer?: (state: T, layerId: string) => T; + + appendLayer?: (state: T, layerId: string) => T; + + getLayerContextMenuIcon?: (opts: { state: T; layerId: string }) => IconType | undefined; + + renderLayerContextMenu?: (domElement: Element, props: VisualizationLayerConfigProps) => void; + getDescription: ( state: T ) => { @@ -339,7 +353,7 @@ export interface Visualization { getPersistableState: (state: T) => P; - renderConfigPanel: (domElement: Element, props: VisualizationProps) => void; + renderLayerConfigPanel: (domElement: Element, props: VisualizationLayerConfigProps) => void; toExpression: (state: T, frame: FramePublicAPI) => Ast | string | null; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.test.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.test.tsx index 5cdf1031a22b04..6ed827bc71c682 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.test.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { ReactWrapper } from 'enzyme'; import { mountWithIntl as mount } from 'test_utils/enzyme_helpers'; import { EuiButtonGroupProps } from '@elastic/eui'; -import { XYConfigPanel } from './xy_config_panel'; +import { XYConfigPanel, LayerContextMenu } from './xy_config_panel'; import { DatasourceDimensionPanelProps, Operation, FramePublicAPI } from '../types'; -import { State, XYState } from './types'; +import { State } from './types'; import { Position } from '@elastic/charts'; import { NativeRendererProps } from '../native_renderer'; import { generateId } from '../id_generator'; @@ -46,15 +46,6 @@ describe('XYConfigPanel', () => { .props(); } - function openComponentPopover(component: ReactWrapper, layerId: string) { - component - .find(`[data-test-subj="lnsXY_layer_${layerId}"]`) - .first() - .find(`[data-test-subj="lnsXY_layer_advanced"]`) - .first() - .simulate('click'); - } - beforeEach(() => { frame = createMockFramePublicAPI(); frame.datasourceLayers = { @@ -67,55 +58,55 @@ describe('XYConfigPanel', () => { test.skip('allows toggling the y axis gridlines', () => {}); test.skip('allows toggling the x axis gridlines', () => {}); - test('enables stacked chart types even when there is no split series', () => { - const state = testState(); - const component = mount( - - ); - - openComponentPopover(component, 'first'); - - const options = component - .find('[data-test-subj="lnsXY_seriesType"]') - .first() - .prop('options') as EuiButtonGroupProps['options']; + describe('LayerContextMenu', () => { + test('enables stacked chart types even when there is no split series', () => { + const state = testState(); + const component = mount( + + ); - expect(options!.map(({ id }) => id)).toEqual([ - 'bar', - 'bar_stacked', - 'line', - 'area', - 'area_stacked', - ]); + const options = component + .find('[data-test-subj="lnsXY_seriesType"]') + .first() + .prop('options') as EuiButtonGroupProps['options']; - expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]); - }); + expect(options!.map(({ id }) => id)).toEqual([ + 'bar', + 'bar_stacked', + 'line', + 'area', + 'area_stacked', + ]); - test('shows only horizontal bar options when in horizontal mode', () => { - const state = testState(); - const component = mount( - - ); + expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]); + }); - openComponentPopover(component, 'first'); + test('shows only horizontal bar options when in horizontal mode', () => { + const state = testState(); + const component = mount( + + ); - const options = component - .find('[data-test-subj="lnsXY_seriesType"]') - .first() - .prop('options') as EuiButtonGroupProps['options']; + const options = component + .find('[data-test-subj="lnsXY_seriesType"]') + .first() + .prop('options') as EuiButtonGroupProps['options']; - expect(options!.map(({ id }) => id)).toEqual(['bar_horizontal', 'bar_horizontal_stacked']); - expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]); + expect(options!.map(({ id }) => id)).toEqual(['bar_horizontal', 'bar_horizontal_stacked']); + expect(options!.filter(({ isDisabled }) => isDisabled).map(({ id }) => id)).toEqual([]); + }); }); test('the x dimension panel accepts only bucketed operations', () => { @@ -123,6 +114,7 @@ describe('XYConfigPanel', () => { const state = testState(); const component = mount( { const state = testState(); const component = mount( { const state = testState(); const component = mount( { /> ); - openComponentPopover(component, 'first'); - const onRemove = component .find('[data-test-subj="lensXY_yDimensionPanel"]') .first() @@ -223,6 +215,7 @@ describe('XYConfigPanel', () => { const state = testState(); const component = mount( { ], }); }); - - describe('layers', () => { - it('adds layers', () => { - frame.addNewLayer = jest.fn().mockReturnValue('newLayerId'); - (generateId as jest.Mock).mockReturnValue('accessor'); - const setState = jest.fn(); - const state = testState(); - const component = mount( - - ); - - component - .find('[data-test-subj="lnsXY_layer_add"]') - .first() - .simulate('click'); - - expect(frame.addNewLayer).toHaveBeenCalled(); - expect(setState).toHaveBeenCalledTimes(1); - expect(generateId).toHaveBeenCalledTimes(4); - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [ - ...state.layers, - expect.objectContaining({ - layerId: 'newLayerId', - xAccessor: 'accessor', - accessors: ['accessor'], - splitAccessor: 'accessor', - }), - ], - }); - }); - - it('should use series type of existing layers if they all have the same', () => { - frame.addNewLayer = jest.fn().mockReturnValue('newLayerId'); - frame.datasourceLayers.second = createMockDatasource().publicAPIMock; - (generateId as jest.Mock).mockReturnValue('accessor'); - const setState = jest.fn(); - const state: XYState = { - ...testState(), - preferredSeriesType: 'bar', - layers: [ - { - seriesType: 'line', - layerId: 'first', - splitAccessor: 'baz', - xAccessor: 'foo', - accessors: ['bar'], - }, - { - seriesType: 'line', - layerId: 'second', - splitAccessor: 'baz', - xAccessor: 'foo', - accessors: ['bar'], - }, - ], - }; - const component = mount( - - ); - - component - .find('[data-test-subj="lnsXY_layer_add"]') - .first() - .simulate('click'); - - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [ - ...state.layers, - expect.objectContaining({ - seriesType: 'line', - }), - ], - }); - }); - - it('should use preffered series type if there are already various different layers', () => { - frame.addNewLayer = jest.fn().mockReturnValue('newLayerId'); - frame.datasourceLayers.second = createMockDatasource().publicAPIMock; - (generateId as jest.Mock).mockReturnValue('accessor'); - const setState = jest.fn(); - const state: XYState = { - ...testState(), - preferredSeriesType: 'bar', - layers: [ - { - seriesType: 'area', - layerId: 'first', - splitAccessor: 'baz', - xAccessor: 'foo', - accessors: ['bar'], - }, - { - seriesType: 'line', - layerId: 'second', - splitAccessor: 'baz', - xAccessor: 'foo', - accessors: ['bar'], - }, - ], - }; - const component = mount( - - ); - - component - .find('[data-test-subj="lnsXY_layer_add"]') - .first() - .simulate('click'); - - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [ - ...state.layers, - expect.objectContaining({ - seriesType: 'bar', - }), - ], - }); - }); - - it('removes layers', () => { - const setState = jest.fn(); - const state = testState(); - const component = mount( - - ); - - openComponentPopover(component, 'first'); - - component - .find('[data-test-subj="lnsXY_layer_remove"]') - .first() - .simulate('click'); - - expect(frame.removeLayers).toHaveBeenCalled(); - expect(setState).toHaveBeenCalledTimes(1); - expect(setState.mock.calls[0][0]).toMatchObject({ - layers: [], - }); - }); - }); }); diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx index f59b1520dbbb43..dbcfa243950015 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx @@ -5,25 +5,11 @@ */ import _ from 'lodash'; -import React, { useState } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiButton, - EuiButtonGroup, - EuiFlexGroup, - EuiFlexItem, - EuiForm, - EuiFormRow, - EuiPanel, - EuiButtonIcon, - EuiPopover, - EuiSpacer, - EuiButtonEmpty, - EuiPopoverFooter, - EuiToolTip, -} from '@elastic/eui'; -import { State, SeriesType, LayerConfig, visualizationTypes } from './types'; -import { VisualizationProps, OperationMetadata } from '../types'; +import { EuiButtonGroup, EuiFormRow } from '@elastic/eui'; +import { State, SeriesType, visualizationTypes } from './types'; +import { VisualizationLayerConfigProps, OperationMetadata } from '../types'; import { NativeRenderer } from '../native_renderer'; import { MultiColumnEditor } from '../multi_column_editor'; import { generateId } from '../id_generator'; @@ -45,253 +31,140 @@ function updateLayer(state: State, layer: UnwrapArray, index: n }; } -function newLayerState(seriesType: SeriesType, layerId: string): LayerConfig { - return { - layerId, - seriesType, - xAccessor: generateId(), - accessors: [generateId()], - splitAccessor: generateId(), - }; -} +export function LayerContextMenu(props: VisualizationLayerConfigProps) { + const { state, layerId } = props; + const horizontalOnly = isHorizontalChart(state.layers); + const index = state.layers.findIndex(l => l.layerId === layerId); + const layer = state.layers[index]; -function LayerSettings({ - layer, - horizontalOnly, - setSeriesType, - removeLayer, -}: { - layer: LayerConfig; - horizontalOnly: boolean; - setSeriesType: (seriesType: SeriesType) => void; - removeLayer: () => void; -}) { - const [isOpen, setIsOpen] = useState(false); - const { icon } = visualizationTypes.find(c => c.id === layer.seriesType)!; + if (!layer) { + return null; + } return ( - setIsOpen(!isOpen)} - data-test-subj="lnsXY_layer_advanced" - /> - } - isOpen={isOpen} - closePopover={() => setIsOpen(false)} - anchorPosition="leftUp" + - - isHorizontalSeries(t.id as SeriesType) === horizontalOnly) - .map(t => ({ - id: t.id, - label: t.label, - iconType: t.icon || 'empty', - }))} - idSelected={layer.seriesType} - onChange={seriesType => { - trackUiEvent('xy_change_layer_display'); - setSeriesType(seriesType as SeriesType); - }} - isIconOnly - buttonSize="compressed" - /> - - - - {i18n.translate('xpack.lens.xyChart.deleteLayer', { - defaultMessage: 'Delete layer', - })} - - - + name="chartType" + className="eui-displayInlineBlock" + data-test-subj="lnsXY_seriesType" + options={visualizationTypes + .filter(t => isHorizontalSeries(t.id as SeriesType) === horizontalOnly) + .map(t => ({ + id: t.id, + label: t.label, + iconType: t.icon || 'empty', + }))} + idSelected={layer.seriesType} + onChange={seriesType => { + trackUiEvent('xy_change_layer_display'); + props.setState( + updateLayer(state, { ...layer, seriesType: seriesType as SeriesType }, index) + ); + }} + isIconOnly + buttonSize="compressed" + /> + ); } -export function XYConfigPanel(props: VisualizationProps) { - const { state, setState, frame } = props; - const horizontalOnly = isHorizontalChart(state.layers); - - return ( - - {state.layers.map((layer, index) => ( - - - - - setState(updateLayer(state, { ...layer, seriesType }, index)) - } - removeLayer={() => { - trackUiEvent('xy_layer_removed'); - frame.removeLayers([layer.layerId]); - setState({ ...state, layers: state.layers.filter(l => l !== layer) }); - }} - /> - - - - - - +export function XYConfigPanel(props: VisualizationLayerConfigProps) { + const { state, setState, frame, layerId } = props; + const index = props.state.layers.findIndex(l => l.layerId === layerId); - + if (index < 0) { + return null; + } - - - - - - setState( - updateLayer( - state, - { - ...layer, - accessors: [...layer.accessors, generateId()], - }, - index - ) - ) - } - onRemove={accessor => - setState( - updateLayer( - state, - { - ...layer, - accessors: layer.accessors.filter(col => col !== accessor), - }, - index - ) - ) - } - filterOperations={isNumericMetric} - data-test-subj="lensXY_yDimensionPanel" - testSubj="lensXY_yDimensionPanel" - layerId={layer.layerId} - /> - - - - - - ))} + const layer = props.state.layers[index]; - - - { - trackUiEvent('xy_layer_added'); - const usedSeriesTypes = _.uniq(state.layers.map(layer => layer.seriesType)); - setState({ - ...state, - layers: [ - ...state.layers, - newLayerState( - usedSeriesTypes.length === 1 ? usedSeriesTypes[0] : state.preferredSeriesType, - frame.addNewLayer() - ), - ], - }); - }} - iconType="plusInCircleFilled" - /> - - - + return ( + <> + + + + + + setState( + updateLayer( + state, + { + ...layer, + accessors: [...layer.accessors, generateId()], + }, + index + ) + ) + } + onRemove={accessor => + setState( + updateLayer( + state, + { + ...layer, + accessors: layer.accessors.filter(col => col !== accessor), + }, + index + ) + ) + } + filterOperations={isNumericMetric} + data-test-subj="lensXY_yDimensionPanel" + testSubj="lensXY_yDimensionPanel" + layerId={layer.layerId} + /> + + + + + ); } diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts index db28e76f829464..89794ec74eaec3 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.test.ts @@ -137,6 +137,54 @@ describe('xy_visualization', () => { }); }); + describe('#removeLayer', () => { + it('removes the specified layer', () => { + const prevState: State = { + ...exampleState(), + layers: [ + ...exampleState().layers, + { + layerId: 'second', + seriesType: 'area', + splitAccessor: 'e', + xAccessor: 'f', + accessors: ['g', 'h'], + }, + ], + }; + + expect(xyVisualization.removeLayer!(prevState, 'second')).toEqual(exampleState()); + }); + }); + + describe('#appendLayer', () => { + it('adds a layer', () => { + const layers = xyVisualization.appendLayer!(exampleState(), 'foo').layers; + expect(layers.length).toEqual(exampleState().layers.length + 1); + expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); + }); + }); + + describe('#clearLayer', () => { + it('clears the specified layer', () => { + (generateId as jest.Mock).mockReturnValue('test_empty_id'); + const layer = xyVisualization.clearLayer(exampleState(), 'first').layers[0]; + expect(layer).toMatchObject({ + accessors: ['test_empty_id'], + layerId: 'first', + seriesType: 'bar', + splitAccessor: 'test_empty_id', + xAccessor: 'test_empty_id', + }); + }); + }); + + describe('#getLayerIds', () => { + it('returns layerids', () => { + expect(xyVisualization.getLayerIds(exampleState())).toEqual(['first']); + }); + }); + describe('#toExpression', () => { let mockDatasource: ReturnType; let frame: ReturnType; diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx index 5ba77cb39d5f81..75d6fcc7d160bf 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_visualization.tsx @@ -11,9 +11,9 @@ import { Position } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { getSuggestions } from './xy_suggestions'; -import { XYConfigPanel } from './xy_config_panel'; +import { XYConfigPanel, LayerContextMenu } from './xy_config_panel'; import { Visualization } from '../types'; -import { State, PersistableState, SeriesType, visualizationTypes } from './types'; +import { State, PersistableState, SeriesType, visualizationTypes, LayerConfig } from './types'; import { toExpression, toPreviewExpression } from './to_expression'; import { generateId } from '../id_generator'; import chartBarStackedSVG from '../assets/chart_bar_stacked.svg'; @@ -67,6 +67,40 @@ export const xyVisualization: Visualization = { visualizationTypes, + getLayerIds(state) { + return state.layers.map(l => l.layerId); + }, + + removeLayer(state, layerId) { + return { + ...state, + layers: state.layers.filter(l => l.layerId !== layerId), + }; + }, + + appendLayer(state, layerId) { + const usedSeriesTypes = _.uniq(state.layers.map(layer => layer.seriesType)); + return { + ...state, + layers: [ + ...state.layers, + newLayerState( + usedSeriesTypes.length === 1 ? usedSeriesTypes[0] : state.preferredSeriesType, + layerId + ), + ], + }; + }, + + clearLayer(state, layerId) { + return { + ...state, + layers: state.layers.map(l => + l.layerId !== layerId ? l : newLayerState(state.preferredSeriesType, layerId) + ), + }; + }, + getDescription(state) { const { icon, label } = getDescription(state); const chartLabel = i18n.translate('xpack.lens.xyVisualization.chartLabel', { @@ -113,7 +147,7 @@ export const xyVisualization: Visualization = { getPersistableState: state => state, - renderConfigPanel: (domElement, props) => + renderLayerConfigPanel: (domElement, props) => render( @@ -121,6 +155,30 @@ export const xyVisualization: Visualization = { domElement ), + getLayerContextMenuIcon({ state, layerId }) { + const layer = state.layers.find(l => l.layerId === layerId); + return visualizationTypes.find(t => t.id === layer?.seriesType)?.icon; + }, + + renderLayerContextMenu(domElement, props) { + render( + + + , + domElement + ); + }, + toExpression, toPreviewExpression, }; + +function newLayerState(seriesType: SeriesType, layerId: string): LayerConfig { + return { + layerId, + seriesType, + xAccessor: generateId(), + accessors: [generateId()], + splitAccessor: generateId(), + }; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ca49aea3ee8856..36c3d4a6c18055 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6935,10 +6935,8 @@ "xpack.lens.xyChart.addLayerTooltip": "複数のレイヤーを使用すると、グラフタイプを組み合わせたり、別のインデックスパターンを可視化したりすることができます。", "xpack.lens.xyChart.chartTypeLabel": "チャートタイプ", "xpack.lens.xyChart.chartTypeLegend": "チャートタイプ", - "xpack.lens.xyChart.deleteLayer": "レイヤーを削除", "xpack.lens.xyChart.help": "X/Y チャート", "xpack.lens.xyChart.isVisible.help": "判例の表示・非表示を指定します。", - "xpack.lens.xyChart.layerSettings": "レイヤー設定を編集", "xpack.lens.xyChart.legend.help": "チャートの凡例を構成します。", "xpack.lens.xyChart.nestUnderRoot": "データセット全体", "xpack.lens.xyChart.position.help": "凡例の配置を指定します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ba6ff117c688d2..a0c514a89feb03 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6934,10 +6934,8 @@ "xpack.lens.xyChart.addLayerTooltip": "使用多个图层以组合图表类型或可视化不同的索引模式。", "xpack.lens.xyChart.chartTypeLabel": "图表类型", "xpack.lens.xyChart.chartTypeLegend": "图表类型", - "xpack.lens.xyChart.deleteLayer": "删除图层", "xpack.lens.xyChart.help": "X/Y 图表", "xpack.lens.xyChart.isVisible.help": "指定图例是否可见。", - "xpack.lens.xyChart.layerSettings": "编辑图层设置", "xpack.lens.xyChart.legend.help": "配置图表图例。", "xpack.lens.xyChart.nestUnderRoot": "整个数据集", "xpack.lens.xyChart.position.help": "指定图例位置。", From 79054afb5a3023583d27fa9cafb1fff476cb8b86 Mon Sep 17 00:00:00 2001 From: Chris Davies Date: Tue, 14 Jan 2020 12:39:50 -0500 Subject: [PATCH 12/52] [Lens] Add support for scripted fields and aliases to the existence API (#54064) * Add support for scripted fields and default index pattern * Add scripted fields and aliases to existence API * Fix TypeScript errors. * Fix mappings parsing * Default to the index pattern timeFieldName Co-authored-by: Elastic Machine --- .../indexpattern_plugin/datapanel.test.tsx | 46 +-- .../public/indexpattern_plugin/datapanel.tsx | 1 + .../public/indexpattern_plugin/loader.test.ts | 2 +- .../lens/public/indexpattern_plugin/loader.ts | 24 +- .../operations/definitions/date_histogram.tsx | 2 +- .../lens/public/indexpattern_plugin/types.ts | 24 +- .../server/routes/existing_fields.test.ts | 117 ++++++-- .../lens/server/routes/existing_fields.ts | 284 ++++++++++++------ .../apis/lens/existing_fields.ts | 4 +- 9 files changed, 345 insertions(+), 159 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx index 52f00a7cd4e9df..b04bd3a4e9be9d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.test.tsx @@ -282,7 +282,7 @@ describe('IndexPattern Data Panel', () => { const parts = url.split('/'); const indexPatternTitle = parts[parts.length - 1]; return { - indexPatternTitle, + indexPatternTitle: `${indexPatternTitle}_testtitle`, existingFieldNames: ['field_1', 'field_2'].map( fieldName => `${indexPatternTitle}_${fieldName}` ), @@ -352,9 +352,9 @@ describe('IndexPattern Data Panel', () => { }); expect(nextState.existingFields).toEqual({ - aaa: { - aaa_field_1: true, - aaa_field_2: true, + a_testtitle: { + a_field_1: true, + a_field_2: true, }, }); }); @@ -369,13 +369,13 @@ describe('IndexPattern Data Panel', () => { }); expect(nextState.existingFields).toEqual({ - aaa: { - aaa_field_1: true, - aaa_field_2: true, + a_testtitle: { + a_field_1: true, + a_field_2: true, }, - bbb: { - bbb_field_1: true, - bbb_field_2: true, + b_testtitle: { + b_field_1: true, + b_field_2: true, }, }); }); @@ -397,7 +397,7 @@ describe('IndexPattern Data Panel', () => { expect(setState).toHaveBeenCalledTimes(2); expect(core.http.get).toHaveBeenCalledTimes(2); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/aaa', { + expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -405,7 +405,7 @@ describe('IndexPattern Data Panel', () => { }, }); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/aaa', { + expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { query: { fromDate: '2019-01-01', toDate: '2020-01-02', @@ -418,9 +418,9 @@ describe('IndexPattern Data Panel', () => { }); expect(nextState.existingFields).toEqual({ - aaa: { - aaa_field_1: true, - aaa_field_2: true, + a_testtitle: { + a_field_1: true, + a_field_2: true, }, }); }); @@ -436,7 +436,7 @@ describe('IndexPattern Data Panel', () => { expect(setState).toHaveBeenCalledTimes(2); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/aaa', { + expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/a', { query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -444,7 +444,7 @@ describe('IndexPattern Data Panel', () => { }, }); - expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/bbb', { + expect(core.http.get).toHaveBeenCalledWith('/api/lens/existing_fields/b', { query: { fromDate: '2019-01-01', toDate: '2020-01-01', @@ -457,13 +457,13 @@ describe('IndexPattern Data Panel', () => { }); expect(nextState.existingFields).toEqual({ - aaa: { - aaa_field_1: true, - aaa_field_2: true, + a_testtitle: { + a_field_1: true, + a_field_2: true, }, - bbb: { - bbb_field_1: true, - bbb_field_2: true, + b_testtitle: { + b_field_1: true, + b_field_2: true, }, }); }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.tsx index 6a2f6234279c70..3231ab7d7ff12c 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/datapanel.tsx @@ -109,6 +109,7 @@ export function IndexPatternDataPanel({ .sort((a, b) => a.localeCompare(b)) .filter(id => !!indexPatterns[id]) .map(id => ({ + id, title: indexPatterns[id].title, timeFieldName: indexPatterns[id].timeFieldName, })); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts index e180ab690d4185..6bea13c32830fb 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.test.ts @@ -551,7 +551,7 @@ describe('loader', () => { dateRange: { fromDate: '1900-01-01', toDate: '2000-01-01' }, // eslint-disable-next-line @typescript-eslint/no-explicit-any fetchJson: fetchJson as any, - indexPatterns: [{ title: 'a' }, { title: 'b' }, { title: 'c' }], + indexPatterns: [{ id: 'a' }, { id: 'b' }, { id: 'c' }], setState, }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts index 7f46f50786cf48..c196cb886e575b 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/loader.ts @@ -15,6 +15,7 @@ import { IndexPatternPersistedState, IndexPatternPrivateState, IndexPatternField, + AggregationRestrictions, } from './types'; import { updateLayerIndexPattern } from './state_helpers'; import { DateRange, ExistingFields } from '../../common/types'; @@ -30,19 +31,7 @@ interface SavedIndexPatternAttributes extends SavedObjectAttributes { } interface SavedRestrictionsObject { - aggs: Record< - string, - Record< - string, - { - agg: string; - fixed_interval?: string; - calendar_interval?: string; - delay?: string; - time_zone?: string; - } - > - >; + aggs: Record; } type SetState = StateSetter; @@ -230,7 +219,7 @@ export async function syncExistingFields({ setState, }: { dateRange: DateRange; - indexPatterns: Array<{ title: string; timeFieldName?: string | null }>; + indexPatterns: Array<{ id: string; timeFieldName?: string | null }>; fetchJson: HttpSetup['get']; setState: SetState; }) { @@ -245,7 +234,7 @@ export async function syncExistingFields({ query.timeFieldName = pattern.timeFieldName; } - return fetchJson(`${BASE_API_URL}/existing_fields/${pattern.title}`, { + return fetchJson(`${BASE_API_URL}/existing_fields/${pattern.id}`, { query, }) as Promise; }) @@ -301,8 +290,9 @@ function fromSavedObject( newFields.forEach((field, index) => { const restrictionsObj: IndexPatternField['aggregationRestrictions'] = {}; aggs.forEach(agg => { - if (typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]) { - restrictionsObj[agg] = typeMeta.aggs[agg][field.name]; + const restriction = typeMeta.aggs[agg] && typeMeta.aggs[agg][field.name]; + if (restriction) { + restrictionsObj[agg] = restriction; } }); if (Object.keys(restrictionsObj).length) { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx index 0bb653ac1e0c21..dbb6278352f096 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/date_histogram.tsx @@ -322,7 +322,7 @@ function parseInterval(currentInterval: string) { }; } -function restrictedInterval(aggregationRestrictions?: AggregationRestrictions) { +function restrictedInterval(aggregationRestrictions?: Partial) { if (!aggregationRestrictions || !aggregationRestrictions.date_histogram) { return; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts index 50478515d19ce2..e556ddda106796 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/types.ts @@ -20,18 +20,16 @@ export interface IndexPattern { >; } -export type AggregationRestrictions = Partial< - Record< - string, - { - agg: string; - interval?: number; - fixed_interval?: string; - calendar_interval?: string; - delay?: string; - time_zone?: string; - } - > +export type AggregationRestrictions = Record< + string, + { + agg?: string; + interval?: number; + fixed_interval?: string; + calendar_interval?: string; + delay?: string; + time_zone?: string; + } >; export interface IndexPatternField { @@ -41,7 +39,7 @@ export interface IndexPatternField { aggregatable: boolean; scripted?: boolean; searchable: boolean; - aggregationRestrictions?: AggregationRestrictions; + aggregationRestrictions?: Partial; } export interface IndexPatternLayer { diff --git a/x-pack/legacy/plugins/lens/server/routes/existing_fields.test.ts b/x-pack/legacy/plugins/lens/server/routes/existing_fields.test.ts index 1647dcccaed3c6..1f19671826807a 100644 --- a/x-pack/legacy/plugins/lens/server/routes/existing_fields.test.ts +++ b/x-pack/legacy/plugins/lens/server/routes/existing_fields.test.ts @@ -4,24 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { existingFields } from './existing_fields'; +import { existingFields, Field, buildFieldList } from './existing_fields'; describe('existingFields', () => { - function field(name: string, parent?: string) { + function field(opts: string | Partial): Field { + const obj = typeof opts === 'object' ? opts : {}; + const name = (typeof opts === 'string' ? opts : opts.name) || 'test'; + return { name, - subType: parent ? { multi: { parent } } : undefined, - aggregatable: true, - esTypes: [], - readFromDocValues: true, - searchable: true, - type: 'string', + isScript: false, + isAlias: false, + path: name.split('.'), + ...obj, }; } + function indexPattern(_source: unknown, fields: unknown = {}) { + return { _source, fields }; + } + it('should handle root level fields', () => { const result = existingFields( - [{ _source: { foo: 'bar' } }, { _source: { baz: 0 } }], + [indexPattern({ foo: 'bar' }), indexPattern({ baz: 0 })], [field('foo'), field('bar'), field('baz')] ); @@ -30,7 +35,7 @@ describe('existingFields', () => { it('should handle arrays of objects', () => { const result = existingFields( - [{ _source: { stuff: [{ foo: 'bar' }, { baz: 0 }] } }], + [indexPattern({ stuff: [{ foo: 'bar' }, { baz: 0 }] })], [field('stuff.foo'), field('stuff.bar'), field('stuff.baz')] ); @@ -38,14 +43,14 @@ describe('existingFields', () => { }); it('should handle basic arrays', () => { - const result = existingFields([{ _source: { stuff: ['heyo', 'there'] } }], [field('stuff')]); + const result = existingFields([indexPattern({ stuff: ['heyo', 'there'] })], [field('stuff')]); expect(result).toEqual(['stuff']); }); it('should handle deep object structures', () => { const result = existingFields( - [{ _source: { geo: { coordinates: { lat: 40, lon: -77 } } } }], + [indexPattern({ geo: { coordinates: { lat: 40, lon: -77 } } })], [field('geo.coordinates')] ); @@ -54,19 +59,97 @@ describe('existingFields', () => { it('should be false if it hits a positive leaf before the end of the path', () => { const result = existingFields( - [{ _source: { geo: { coordinates: 32 } } }], + [indexPattern({ geo: { coordinates: 32 } })], [field('geo.coordinates.lat')] ); expect(result).toEqual([]); }); - it('should prefer parent to name', () => { + it('should use path, not name', () => { const result = existingFields( - [{ _source: { stuff: [{ foo: 'bar' }, { baz: 0 }] } }], - [field('goober', 'stuff.foo'), field('soup', 'stuff.bar'), field('pea', 'stuff.baz')] + [indexPattern({ stuff: [{ foo: 'bar' }, { baz: 0 }] })], + [field({ name: 'goober', path: ['stuff', 'foo'] })] ); - expect(result).toEqual(['goober', 'pea']); + expect(result).toEqual(['goober']); + }); + + it('supports scripted fields', () => { + const result = existingFields( + [indexPattern({}, { bar: 'scriptvalue' })], + [field({ name: 'baz', isScript: true, path: ['bar'] })] + ); + + expect(result).toEqual(['baz']); + }); +}); + +describe('buildFieldList', () => { + const indexPattern = { + id: '', + type: 'indexpattern', + attributes: { + title: 'testpattern', + fields: JSON.stringify([ + { name: 'foo', scripted: true, lang: 'painless', script: '2+2' }, + { name: 'bar' }, + { name: '@bar' }, + { name: 'baz' }, + ]), + }, + references: [], + }; + + const mappings = { + testpattern: { + mappings: { + properties: { + '@bar': { + type: 'alias', + path: 'bar', + }, + }, + }, + }, + }; + + const fieldDescriptors = [ + { + name: 'baz', + subType: { multi: { parent: 'a.b.c' } }, + }, + ]; + + it('uses field descriptors to determine the path', () => { + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + expect(fields.find(f => f.name === 'baz')).toMatchObject({ + isAlias: false, + isScript: false, + name: 'baz', + path: ['a', 'b', 'c'], + }); + }); + + it('uses aliases to determine the path', () => { + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + expect(fields.find(f => f.isAlias)).toMatchObject({ + isAlias: true, + isScript: false, + name: '@bar', + path: ['bar'], + }); + }); + + it('supports scripted fields', () => { + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + expect(fields.find(f => f.isScript)).toMatchObject({ + isAlias: false, + isScript: true, + name: 'foo', + path: ['foo'], + lang: 'painless', + script: '2+2', + }); }); }); diff --git a/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts b/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts index ad1af966983fb3..55f8fd3b1a72bf 100644 --- a/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/legacy/plugins/lens/server/routes/existing_fields.ts @@ -6,28 +6,50 @@ import Boom from 'boom'; import { schema } from '@kbn/config-schema'; -import { SearchResponse } from 'elasticsearch'; import _ from 'lodash'; -import { IScopedClusterClient } from 'src/core/server'; +import { IScopedClusterClient, SavedObject, RequestHandlerContext } from 'src/core/server'; import { CoreSetup } from 'src/core/server'; import { BASE_API_URL } from '../../common'; -import { FieldDescriptor, IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; +import { IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; /** * The number of docs to sample to determine field empty status. */ const SAMPLE_SIZE = 500; -type Document = Record; +interface MappingResult { + [indexPatternTitle: string]: { + mappings: { + properties: Record; + }; + }; +} + +interface FieldDescriptor { + name: string; + subType?: { multi?: { parent?: string } }; +} + +export interface Field { + name: string; + isScript: boolean; + isAlias: boolean; + path: string[]; + lang?: string; + script?: string; +} + +// TODO: Pull this from kibana advanced settings +const metaFields = ['_source', '_id', '_type', '_index', '_score']; export async function existingFieldsRoute(setup: CoreSetup) { const router = setup.http.createRouter(); router.get( { - path: `${BASE_API_URL}/existing_fields/{indexPatternTitle}`, + path: `${BASE_API_URL}/existing_fields/{indexPatternId}`, validate: { params: schema.object({ - indexPatternTitle: schema.string(), + indexPatternId: schema.string(), }), query: schema.object({ fromDate: schema.maybe(schema.string()), @@ -37,31 +59,13 @@ export async function existingFieldsRoute(setup: CoreSetup) { }, }, async (context, req, res) => { - const { indexPatternTitle } = req.params; - const requestClient = context.core.elasticsearch.dataClient; - const indexPatternsFetcher = new IndexPatternsFetcher(requestClient.callAsCurrentUser); - const { fromDate, toDate, timeFieldName } = req.query; - try { - const fields = await indexPatternsFetcher.getFieldsForWildcard({ - pattern: indexPatternTitle, - // TODO: Pull this from kibana advanced settings - metaFields: ['_source', '_id', '_type', '_index', '_score'], - }); - - const results = await fetchIndexPatternStats({ - fromDate, - toDate, - client: requestClient, - index: indexPatternTitle, - timeFieldName, - }); - return res.ok({ - body: { - indexPatternTitle, - existingFieldNames: existingFields(results.hits.hits, fields), - }, + body: await fetchFieldExistence({ + ...req.query, + ...req.params, + context, + }), }); } catch (e) { if (e.status === 404) { @@ -82,6 +86,166 @@ export async function existingFieldsRoute(setup: CoreSetup) { ); } +async function fetchFieldExistence({ + context, + indexPatternId, + fromDate, + toDate, + timeFieldName, +}: { + indexPatternId: string; + context: RequestHandlerContext; + fromDate?: string; + toDate?: string; + timeFieldName?: string; +}) { + const { + indexPattern, + indexPatternTitle, + mappings, + fieldDescriptors, + } = await fetchIndexPatternDefinition(indexPatternId, context); + + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + + const docs = await fetchIndexPatternStats({ + fromDate, + toDate, + client: context.core.elasticsearch.dataClient, + index: indexPatternTitle, + timeFieldName: timeFieldName || indexPattern.attributes.timeFieldName, + fields, + }); + + return { + indexPatternTitle, + existingFieldNames: existingFields(docs, fields), + }; +} + +async function fetchIndexPatternDefinition(indexPatternId: string, context: RequestHandlerContext) { + const savedObjectsClient = context.core.savedObjects.client; + const requestClient = context.core.elasticsearch.dataClient; + const indexPattern = await savedObjectsClient.get('index-pattern', indexPatternId); + const indexPatternTitle = indexPattern.attributes.title; + // TODO: maybe don't use IndexPatternsFetcher at all, since we're only using it + // to look up field values in the resulting documents. We can accomplish the same + // using the mappings which we're also fetching here. + const indexPatternsFetcher = new IndexPatternsFetcher(requestClient.callAsCurrentUser); + const [mappings, fieldDescriptors] = await Promise.all([ + requestClient.callAsCurrentUser('indices.getMapping', { + index: indexPatternTitle, + }), + + indexPatternsFetcher.getFieldsForWildcard({ + pattern: indexPatternTitle, + // TODO: Pull this from kibana advanced settings + metaFields, + }), + ]); + + return { + indexPattern, + indexPatternTitle, + mappings, + fieldDescriptors, + }; +} + +/** + * Exported only for unit tests. + */ +export function buildFieldList( + indexPattern: SavedObject, + mappings: MappingResult, + fieldDescriptors: FieldDescriptor[] +): Field[] { + const aliasMap = Object.entries(Object.values(mappings)[0].mappings.properties) + .map(([name, v]) => ({ ...v, name })) + .filter(f => f.type === 'alias') + .reduce((acc, f) => { + acc[f.name] = f.path; + return acc; + }, {} as Record); + + const descriptorMap = fieldDescriptors.reduce((acc, f) => { + acc[f.name] = f; + return acc; + }, {} as Record); + + return JSON.parse(indexPattern.attributes.fields).map( + (field: { name: string; lang: string; scripted?: boolean; script?: string }) => { + const path = + aliasMap[field.name] || descriptorMap[field.name]?.subType?.multi?.parent || field.name; + return { + name: field.name, + isScript: !!field.scripted, + isAlias: !!aliasMap[field.name], + path: path.split('.'), + lang: field.lang, + script: field.script, + }; + } + ); +} + +async function fetchIndexPatternStats({ + client, + index, + timeFieldName, + fromDate, + toDate, + fields, +}: { + client: IScopedClusterClient; + index: string; + timeFieldName?: string; + fromDate?: string; + toDate?: string; + fields: Field[]; +}) { + if (!timeFieldName || !fromDate || !toDate) { + return []; + } + const viableFields = fields.filter( + f => !f.isScript && !f.isAlias && !metaFields.includes(f.name) + ); + const scriptedFields = fields.filter(f => f.isScript); + + const result = await client.callAsCurrentUser('search', { + index, + body: { + size: SAMPLE_SIZE, + _source: viableFields.map(f => f.name), + query: { + bool: { + filter: [ + { + range: { + [timeFieldName]: { + gte: fromDate, + lte: toDate, + }, + }, + }, + ], + }, + }, + script_fields: scriptedFields.reduce((acc, field) => { + acc[field.name] = { + script: { + lang: field.lang, + source: field.script, + }, + }; + return acc; + }, {} as Record), + }, + }); + + return result.hits.hits; +} + function exists(obj: unknown, path: string[], i = 0): boolean { if (obj == null) { return false; @@ -103,21 +267,13 @@ function exists(obj: unknown, path: string[], i = 0): boolean { } /** - * Exported for testing purposes only. + * Exported only for unit tests. */ export function existingFields( - docs: Array<{ _source: Document }>, - fields: FieldDescriptor[] + docs: Array<{ _source: unknown; fields: unknown }>, + fields: Field[] ): string[] { - const allFields = fields.map(field => { - const parent = field.subType && field.subType.multi && field.subType.multi.parent; - return { - name: field.name, - parent, - path: (parent || field.name).split('.'), - }; - }); - const missingFields = new Set(allFields); + const missingFields = new Set(fields); for (const doc of docs) { if (missingFields.size === 0) { @@ -125,53 +281,11 @@ export function existingFields( } missingFields.forEach(field => { - if (exists(doc._source, field.path)) { + if (exists(field.isScript ? doc.fields : doc._source, field.path)) { missingFields.delete(field); } }); } - return allFields.filter(field => !missingFields.has(field)).map(f => f.name); -} - -async function fetchIndexPatternStats({ - client, - fromDate, - index, - toDate, - timeFieldName, -}: { - client: IScopedClusterClient; - fromDate?: string; - index: string; - toDate?: string; - timeFieldName?: string; -}) { - const body = - !timeFieldName || !fromDate || !toDate - ? {} - : { - query: { - bool: { - filter: [ - { - range: { - [timeFieldName]: { - gte: fromDate, - lte: toDate, - }, - }, - }, - ], - }, - }, - }; - - return (await client.callAsCurrentUser('search', { - index, - body: { - ...body, - size: SAMPLE_SIZE, - }, - })) as SearchResponse; + return fields.filter(field => !missingFields.has(field)).map(f => f.name); } diff --git a/x-pack/test/api_integration/apis/lens/existing_fields.ts b/x-pack/test/api_integration/apis/lens/existing_fields.ts index a16204408ea445..bceb8250edd2df 100644 --- a/x-pack/test/api_integration/apis/lens/existing_fields.ts +++ b/x-pack/test/api_integration/apis/lens/existing_fields.ts @@ -114,13 +114,13 @@ export default ({ getService }: FtrProviderContext) => { const { body } = await supertest .get( `/api/lens/existing_fields/${encodeURIComponent( - 'logstash-2015.09.22' + 'logstash-*' )}?fromDate=${TEST_START_TIME}&toDate=${TEST_END_TIME}` ) .set(COMMON_HEADERS) .expect(200); - expect(body.indexPatternTitle).to.eql('logstash-2015.09.22'); + expect(body.indexPatternTitle).to.eql('logstash-*'); expect(body.existingFieldNames.sort()).to.eql(fieldsWithData.sort()); }); From 97d460e051bf8232297b1255f5ca08256dbe9c89 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 14 Jan 2020 18:40:12 +0100 Subject: [PATCH 13/52] Fix safari layout issue in Visualize, Graph and Lens (#54694) --- .../kibana/public/dashboard/np_ready/application.ts | 8 +++----- src/legacy/core_plugins/kibana/public/index.scss | 3 +++ .../kibana/public/local_application_service/_index.scss | 1 + .../_local_application_service.scss | 5 +++++ .../local_application_service.ts | 2 +- .../kibana/public/visualize/np_ready/application.ts | 5 ++--- x-pack/legacy/plugins/graph/public/application.ts | 5 ++--- 7 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 src/legacy/core_plugins/kibana/public/local_application_service/_index.scss create mode 100644 src/legacy/core_plugins/kibana/public/local_application_service/_local_application_service.scss diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts index 2a5dedab981510..7f7bf7cf47bdaf 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/application.ts @@ -86,11 +86,9 @@ export const renderApp = (element: HTMLElement, appBasePath: string, deps: Rende }; }; -const mainTemplate = (basePath: string) => `
+const mainTemplate = (basePath: string) => `
-
-
-`; +
`; const moduleName = 'app/dashboard'; @@ -98,7 +96,7 @@ const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react']; function mountDashboardApp(appBasePath: string, element: HTMLElement) { const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%'); + mountpoint.setAttribute('class', 'kbnLocalApplicationWrapper'); // eslint-disable-next-line mountpoint.innerHTML = mainTemplate(appBasePath); // bootstrap angular into detached element and attach it later to diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss index 3b49af9a4a6a66..dfe4aa1fd3b9f9 100644 --- a/src/legacy/core_plugins/kibana/public/index.scss +++ b/src/legacy/core_plugins/kibana/public/index.scss @@ -26,6 +26,9 @@ // Management styles @import './management/index'; +// Local application mount wrapper styles +@import 'local_application_service/index'; + // Dashboard styles // MUST STAY AT THE BOTTOM BECAUSE OF DARK THEME IMPORTS @import './dashboard/index'; diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/_index.scss b/src/legacy/core_plugins/kibana/public/local_application_service/_index.scss new file mode 100644 index 00000000000000..12cc1444101e71 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/local_application_service/_index.scss @@ -0,0 +1 @@ +@import 'local_application_service'; diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/_local_application_service.scss b/src/legacy/core_plugins/kibana/public/local_application_service/_local_application_service.scss new file mode 100644 index 00000000000000..33a6100c439759 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/local_application_service/_local_application_service.scss @@ -0,0 +1,5 @@ +.kbnLocalApplicationWrapper { + display: flex; + flex-direction: column; + flex-grow: 1; +} diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts index c09995caab6690..d52bec8304ff91 100644 --- a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts +++ b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts @@ -56,7 +56,7 @@ export class LocalApplicationService { outerAngularWrapperRoute: true, reloadOnSearch: false, reloadOnUrl: false, - template: `
`, + template: `
`, controller($scope: IScope) { const element = document.getElementById(wrapperElementId)!; let unmountHandler: AppUnmount | null = null; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts index dcd68a26743ab5..222b0357089767 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts @@ -63,9 +63,8 @@ export const renderApp = async ( return () => $injector.get('$rootScope').$destroy(); }; -const mainTemplate = (basePath: string) => `
+const mainTemplate = (basePath: string) => `
-
`; @@ -75,7 +74,7 @@ const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react']; function mountVisualizeApp(appBasePath: string, element: HTMLElement) { const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%'); + mountpoint.setAttribute('class', 'kbnLocalApplicationWrapper'); mountpoint.innerHTML = mainTemplate(appBasePath); // bootstrap angular into detached element and attach it later to // make angular-within-angular possible diff --git a/x-pack/legacy/plugins/graph/public/application.ts b/x-pack/legacy/plugins/graph/public/application.ts index 69bc7899746325..8f486ab6ad51a2 100644 --- a/x-pack/legacy/plugins/graph/public/application.ts +++ b/x-pack/legacy/plugins/graph/public/application.ts @@ -96,9 +96,8 @@ export const renderApp = ({ appBasePath, element, ...deps }: GraphDependencies) }; }; -const mainTemplate = (basePath: string) => `
+const mainTemplate = (basePath: string) => `
-
`; @@ -108,7 +107,7 @@ const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.boo function mountGraphApp(appBasePath: string, element: HTMLElement) { const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%'); + mountpoint.setAttribute('class', 'kbnLocalApplicationWrapper'); // eslint-disable-next-line mountpoint.innerHTML = mainTemplate(appBasePath); // bootstrap angular into detached element and attach it later to From e4c73ffbbb91ae318a7979f8c32ccfcb6d978ebb Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 14 Jan 2020 18:47:13 +0100 Subject: [PATCH 14/52] [Console][Chore] Update spec (#54564) * Update spec conversion to exclude deprecated completions * Update OSS spec * Remove console.log * Add skip deprecated endpoints option to script * Actually, remove skip deprecated flag for now. Just do not include deprecated. See this issue: https://github.com/elastic/kibana/issues/48375 * x-pack: Delete data from transform completions * Update to existing x-pack autocomplete extensions * Added ml explain with overrides * Added put trained model with doc override * Added SLM get_status, start and stop with URL param overrides where needed * Add data completion for clear scroll * Remove include_type_name flag from indices and delete create.json override Co-authored-by: Elastic Machine --- packages/kbn-spec-to-console/lib/convert.js | 20 +++++++--- .../spec/generated/cat.indices.json | 9 ++++- .../spec/generated/clear_scroll.json | 3 +- .../api_server/spec/generated/create.json | 3 +- .../api_server/spec/generated/delete.json | 3 +- .../spec/generated/delete_by_query.json | 2 +- .../spec/generated/exists_source.json | 3 +- .../spec/generated/get_script_languages.json | 10 +++++ .../spec/generated/indices.shrink.json | 1 + .../spec/generated/indices.split.json | 1 + .../generated/indices.validate_query.json | 3 +- .../spec/generated/msearch_template.json | 3 +- .../spec/generated/nodes.hot_threads.json | 8 +--- .../api_server/spec/generated/rank_eval.json | 4 ++ .../api_server/spec/generated/scroll.json | 3 +- .../spec/generated/search_template.json | 3 +- .../api_server/spec/generated/update.json | 3 +- .../spec/generated/update_by_query.json | 1 + .../spec/overrides/clear_scroll.json | 7 ++++ .../api_server/spec/overrides/create.json | 16 -------- ...transform_deprecated.delete_transform.json | 14 ------- ...me_transform_deprecated.get_transform.json | 17 --------- ...nsform_deprecated.get_transform_stats.json | 16 -------- ...ransform_deprecated.preview_transform.json | 11 ------ ...me_transform_deprecated.put_transform.json | 14 ------- ..._transform_deprecated.start_transform.json | 14 ------- ...e_transform_deprecated.stop_transform.json | 16 -------- ...transform_deprecated.update_transform.json | 14 ------- .../spec/generated/license.get.json | 3 +- .../ml.delete_data_frame_analytics.json | 3 ++ .../ml.explain_data_frame_analytics.json | 13 +++++++ .../spec/generated/ml.put_trained_model.json | 11 ++++++ .../spec/generated/monitoring.bulk.json | 3 +- .../spec/generated/rollup.rollup_search.json | 3 +- .../spec/generated/security.get_api_key.json | 3 +- .../spec/generated/slm.get_status.json | 11 ++++++ .../spec/generated/slm.start.json | 11 ++++++ .../spec/generated/slm.stop.json | 11 ++++++ .../generated/transform.stop_transform.json | 4 +- ...ransform_deprecated.preview_transform.json | 26 ------------- ...me_transform_deprecated.put_transform.json | 29 -------------- ...transform_deprecated.update_transform.json | 24 ------------ .../ml.explain_data_frame_analytics.json | 38 +++++++++++++++++++ .../spec/overrides/ml.put_trained_model.json | 5 +++ .../spec/overrides/slm.start.json | 8 ++++ .../spec/overrides/slm.stop.json | 8 ++++ 46 files changed, 185 insertions(+), 251 deletions(-) create mode 100644 src/legacy/core_plugins/console/server/api_server/spec/generated/get_script_languages.json create mode 100644 src/legacy/core_plugins/console/server/api_server/spec/overrides/clear_scroll.json delete mode 100644 src/legacy/core_plugins/console/server/api_server/spec/overrides/create.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.delete_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform_stats.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.preview_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.put_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.start_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.stop_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.update_transform.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/ml.explain_data_frame_analytics.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/ml.put_trained_model.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/slm.get_status.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/slm.start.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/generated/slm.stop.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.preview_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.put_transform.json delete mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.update_transform.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/ml.explain_data_frame_analytics.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/ml.put_trained_model.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/slm.start.json create mode 100644 x-pack/legacy/plugins/console_extensions/spec/overrides/slm.stop.json diff --git a/packages/kbn-spec-to-console/lib/convert.js b/packages/kbn-spec-to-console/lib/convert.js index 4c312818607671..5dbdd6e1c94e44 100644 --- a/packages/kbn-spec-to-console/lib/convert.js +++ b/packages/kbn-spec-to-console/lib/convert.js @@ -24,10 +24,16 @@ const convertParts = require('./convert/parts'); module.exports = spec => { const result = {}; - // TODO: - // Since https://github.com/elastic/elasticsearch/pull/42346 has been merged into ES master - // the JSON doc specification has been updated. We need to update this script to take advantage - // of the added information but it will also require updating console's editor autocomplete. + /** + * TODO: + * Since https://github.com/elastic/elasticsearch/pull/42346 has been merged into ES master + * the JSON doc specification has been updated. We need to update this script to take advantage + * of the added information but it will also require updating console editor autocomplete. + * + * Note: for now we exclude all deprecated patterns from the generated spec to prevent them + * from being used in autocompletion. It would be really nice if we could use this information + * instead of just not including it. + */ Object.keys(spec).forEach(api => { const source = spec[api]; if (!source.url) { @@ -46,8 +52,10 @@ module.exports = spec => { const urlComponents = {}; if (source.url.paths) { - patterns = convertPaths(source.url.paths); - source.url.paths.forEach(pathsObject => { + // We filter out all deprecated url patterns here. + const paths = source.url.paths.filter(path => !path.deprecated); + patterns = convertPaths(paths); + paths.forEach(pathsObject => { pathsObject.methods.forEach(method => methodSet.add(method)); if (pathsObject.parts) { for (const partName of Object.keys(pathsObject.parts)) { diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/cat.indices.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/cat.indices.json index 45da7f054bfb41..e6ca1fb575396f 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/cat.indices.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/cat.indices.json @@ -5,8 +5,15 @@ "bytes": [ "b", "k", + "kb", "m", - "g" + "mb", + "g", + "gb", + "t", + "tb", + "p", + "pb" ], "local": "__flag__", "master_timeout": "", diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/clear_scroll.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/clear_scroll.json index 55d9673054276b..7e6e6692f931b2 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/clear_scroll.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/clear_scroll.json @@ -4,8 +4,7 @@ "DELETE" ], "patterns": [ - "_search/scroll", - "_search/scroll/{scroll_id}" + "_search/scroll" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-body.html#_clear_scroll_api" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/create.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/create.json index 6c0ee8a2425eeb..8bbee143c299f9 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/create.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/create.json @@ -22,8 +22,7 @@ "POST" ], "patterns": [ - "{indices}/_create/{id}", - "{indices}/{type}/{id}/_create" + "{indices}/_create/{id}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-index_.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/delete.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/delete.json index aba84d0a10fc2d..0852d8d1848311 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/delete.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/delete.json @@ -23,8 +23,7 @@ "DELETE" ], "patterns": [ - "{indices}/_doc/{id}", - "{indices}/{type}/{id}" + "{indices}/_doc/{id}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-delete.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/delete_by_query.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/delete_by_query.json index 3867efd814238c..2d1636d5f2c028 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/delete_by_query.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/delete_by_query.json @@ -1,7 +1,6 @@ { "delete_by_query": { "url_params": { - "analyzer": "", "analyze_wildcard": "__flag__", "default_operator": [ "AND", @@ -31,6 +30,7 @@ "dfs_query_then_fetch" ], "search_timeout": "", + "size": "", "max_docs": "all documents", "sort": [], "_source": [], diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/exists_source.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/exists_source.json index e96273ffbc0830..9ffc4b55f3037c 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/exists_source.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/exists_source.json @@ -20,8 +20,7 @@ "HEAD" ], "patterns": [ - "{indices}/_source/{id}", - "{indices}/{type}/{id}/_source" + "{indices}/_source/{id}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-get.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/get_script_languages.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/get_script_languages.json new file mode 100644 index 00000000000000..10ea433ca68c56 --- /dev/null +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/get_script_languages.json @@ -0,0 +1,10 @@ +{ + "get_script_languages": { + "methods": [ + "GET" + ], + "patterns": [ + "_script_language" + ] + } +} diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.shrink.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.shrink.json index 31acc86a2fa560..6fbdea0f1244bf 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.shrink.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.shrink.json @@ -1,6 +1,7 @@ { "indices.shrink": { "url_params": { + "copy_settings": "__flag__", "timeout": "", "master_timeout": "", "wait_for_active_shards": "" diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.split.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.split.json index 1bfbaa078b7967..68f2e338cd2013 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.split.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.split.json @@ -1,6 +1,7 @@ { "indices.split": { "url_params": { + "copy_settings": "__flag__", "timeout": "", "master_timeout": "", "wait_for_active_shards": "" diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.validate_query.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.validate_query.json index ceffec26beeccb..33720576ef8a3e 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.validate_query.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/indices.validate_query.json @@ -28,8 +28,7 @@ ], "patterns": [ "_validate/query", - "{indices}/_validate/query", - "{indices}/{type}/_validate/query" + "{indices}/_validate/query" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/search-validate.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/msearch_template.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/msearch_template.json index 0b0ca087b1819f..c2f741066bbdb4 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/msearch_template.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/msearch_template.json @@ -9,8 +9,7 @@ ], "typed_keys": "__flag__", "max_concurrent_searches": "", - "rest_total_hits_as_int": "__flag__", - "ccs_minimize_roundtrips": "__flag__" + "rest_total_hits_as_int": "__flag__" }, "methods": [ "GET", diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/nodes.hot_threads.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/nodes.hot_threads.json index b8aa5dd4ca711d..b3cbbe80e0d000 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/nodes.hot_threads.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/nodes.hot_threads.json @@ -17,13 +17,7 @@ ], "patterns": [ "_nodes/hot_threads", - "_nodes/{nodes}/hot_threads", - "_cluster/nodes/hotthreads", - "_cluster/nodes/{nodes}/hotthreads", - "_nodes/hotthreads", - "_nodes/{nodes}/hotthreads", - "_cluster/nodes/hot_threads", - "_cluster/nodes/{nodes}/hot_threads" + "_nodes/{nodes}/hot_threads" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-nodes-hot-threads.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/rank_eval.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/rank_eval.json index 620f1c629d9592..c2bed081124a8a 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/rank_eval.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/rank_eval.json @@ -8,6 +8,10 @@ "closed", "none", "all" + ], + "search_type": [ + "query_then_fetch", + "dfs_query_then_fetch" ] }, "methods": [ diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/scroll.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/scroll.json index 3e959b9630e986..4ce82a2c25e0e0 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/scroll.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/scroll.json @@ -10,8 +10,7 @@ "POST" ], "patterns": [ - "_search/scroll", - "_search/scroll/{scroll_id}" + "_search/scroll" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-body.html#request-body-search-scroll" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/search_template.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/search_template.json index 582ecab1dd614b..cf5a5c5f32db32 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/search_template.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/search_template.json @@ -22,8 +22,7 @@ "explain": "__flag__", "profile": "__flag__", "typed_keys": "__flag__", - "rest_total_hits_as_int": "__flag__", - "ccs_minimize_roundtrips": "__flag__" + "rest_total_hits_as_int": "__flag__" }, "methods": [ "GET", diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/update.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/update.json index 4e103b0af2195d..43945dfada35ca 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/update.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/update.json @@ -21,8 +21,7 @@ "POST" ], "patterns": [ - "{indices}/_update/{id}", - "{indices}/{type}/{id}/_update" + "{indices}/_update/{id}" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/docs-update.html" } diff --git a/src/legacy/core_plugins/console/server/api_server/spec/generated/update_by_query.json b/src/legacy/core_plugins/console/server/api_server/spec/generated/update_by_query.json index 739ea16888146a..393197949e86ce 100644 --- a/src/legacy/core_plugins/console/server/api_server/spec/generated/update_by_query.json +++ b/src/legacy/core_plugins/console/server/api_server/spec/generated/update_by_query.json @@ -32,6 +32,7 @@ "dfs_query_then_fetch" ], "search_timeout": "", + "size": "", "max_docs": "all documents", "sort": [], "_source": [], diff --git a/src/legacy/core_plugins/console/server/api_server/spec/overrides/clear_scroll.json b/src/legacy/core_plugins/console/server/api_server/spec/overrides/clear_scroll.json new file mode 100644 index 00000000000000..e9d4a6cee54cef --- /dev/null +++ b/src/legacy/core_plugins/console/server/api_server/spec/overrides/clear_scroll.json @@ -0,0 +1,7 @@ +{ + "clear_scroll": { + "data_autocomplete_rules": { + "scroll_id": "" + } + } +} diff --git a/src/legacy/core_plugins/console/server/api_server/spec/overrides/create.json b/src/legacy/core_plugins/console/server/api_server/spec/overrides/create.json deleted file mode 100644 index 0bbf456245c841..00000000000000 --- a/src/legacy/core_plugins/console/server/api_server/spec/overrides/create.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "create": { - "url_params": { - "timeout": "1m", - "ttl": "5m", - "version": "1" - }, - "methods": [ - "PUT", - "POST" - ], - "patterns": [ - "{indices}/{type}/{id}/_create" - ] - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.delete_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.delete_transform.json deleted file mode 100644 index 4401c85da7215b..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.delete_transform.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "data_frame_transform_deprecated.delete_transform": { - "url_params": { - "force": "__flag__" - }, - "methods": [ - "DELETE" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform.json deleted file mode 100644 index d1060d562c626c..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "data_frame_transform_deprecated.get_transform": { - "url_params": { - "from": 0, - "size": 0, - "allow_no_match": "__flag__" - }, - "methods": [ - "GET" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}", - "_data_frame/transforms" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform_stats.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform_stats.json deleted file mode 100644 index aa3c1a21f36ce9..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.get_transform_stats.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "data_frame_transform_deprecated.get_transform_stats": { - "url_params": { - "from": "", - "size": "", - "allow_no_match": "__flag__" - }, - "methods": [ - "GET" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}/_stats" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/get-transform-stats.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.preview_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.preview_transform.json deleted file mode 100644 index 1c878641d02b5a..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.preview_transform.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "data_frame_transform_deprecated.preview_transform": { - "methods": [ - "POST" - ], - "patterns": [ - "_data_frame/transforms/_preview" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/preview-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.put_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.put_transform.json deleted file mode 100644 index 89c124280a4a16..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.put_transform.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "data_frame_transform_deprecated.put_transform": { - "url_params": { - "defer_validation": "__flag__" - }, - "methods": [ - "PUT" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/put-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.start_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.start_transform.json deleted file mode 100644 index 49e09b7922b687..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.start_transform.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "data_frame_transform_deprecated.start_transform": { - "url_params": { - "timeout": "" - }, - "methods": [ - "POST" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}/_start" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/start-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.stop_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.stop_transform.json deleted file mode 100644 index 90e89269aec00a..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.stop_transform.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "data_frame_transform_deprecated.stop_transform": { - "url_params": { - "wait_for_completion": "__flag__", - "timeout": "", - "allow_no_match": "__flag__" - }, - "methods": [ - "POST" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}/_stop" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/stop-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.update_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.update_transform.json deleted file mode 100644 index ac8c854ab6bfcd..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/data_frame_transform_deprecated.update_transform.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "data_frame_transform_deprecated.update_transform": { - "url_params": { - "defer_validation": "__flag__" - }, - "methods": [ - "POST" - ], - "patterns": [ - "_data_frame/transforms/{transform_id}/_update" - ], - "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/update-transform.html" - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/license.get.json b/x-pack/legacy/plugins/console_extensions/spec/generated/license.get.json index f37602296f5a99..2404d65ce1e01a 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/license.get.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/license.get.json @@ -1,7 +1,8 @@ { "license.get": { "url_params": { - "local": "__flag__" + "local": "__flag__", + "accept_enterprise": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/ml.delete_data_frame_analytics.json b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.delete_data_frame_analytics.json index c2c6baf906db6d..c3d7048406ef63 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/ml.delete_data_frame_analytics.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.delete_data_frame_analytics.json @@ -1,5 +1,8 @@ { "ml.delete_data_frame_analytics": { + "url_params": { + "force": "__flag__" + }, "methods": [ "DELETE" ], diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/ml.explain_data_frame_analytics.json b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.explain_data_frame_analytics.json new file mode 100644 index 00000000000000..212098cc3a2027 --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.explain_data_frame_analytics.json @@ -0,0 +1,13 @@ +{ + "ml.explain_data_frame_analytics": { + "methods": [ + "GET", + "POST" + ], + "patterns": [ + "_ml/data_frame/analytics/_explain", + "_ml/data_frame/analytics/{id}/_explain" + ], + "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/current/explain-dfanalytics.html" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/ml.put_trained_model.json b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.put_trained_model.json new file mode 100644 index 00000000000000..27d0393be6086f --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/ml.put_trained_model.json @@ -0,0 +1,11 @@ +{ + "ml.put_trained_model": { + "methods": [ + "PUT" + ], + "patterns": [ + "_ml/inference/{model_id}" + ], + "documentation": "TODO" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/monitoring.bulk.json b/x-pack/legacy/plugins/console_extensions/spec/generated/monitoring.bulk.json index 9f718501e25b58..2b27950e7b0974 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/monitoring.bulk.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/monitoring.bulk.json @@ -10,8 +10,7 @@ "PUT" ], "patterns": [ - "_monitoring/bulk", - "_monitoring/{type}/bulk" + "_monitoring/bulk" ], "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/es-monitoring.html" } diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/rollup.rollup_search.json b/x-pack/legacy/plugins/console_extensions/spec/generated/rollup.rollup_search.json index a5763646990a59..a1771126a71b4f 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/rollup.rollup_search.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/rollup.rollup_search.json @@ -9,8 +9,7 @@ "POST" ], "patterns": [ - "{indices}/_rollup_search", - "{indices}/{type}/_rollup_search" + "{indices}/_rollup_search" ] } } diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/security.get_api_key.json b/x-pack/legacy/plugins/console_extensions/spec/generated/security.get_api_key.json index 431b345a1bcc20..a8cd5de2656b9d 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/security.get_api_key.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/security.get_api_key.json @@ -4,7 +4,8 @@ "id": "", "name": "", "username": "", - "realm_name": "" + "realm_name": "", + "owner": "__flag__" }, "methods": [ "GET" diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/slm.get_status.json b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.get_status.json new file mode 100644 index 00000000000000..a7ffde10b316d0 --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.get_status.json @@ -0,0 +1,11 @@ +{ + "slm.get_status": { + "methods": [ + "GET" + ], + "patterns": [ + "_slm/status" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/slm-get-status.html" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/slm.start.json b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.start.json new file mode 100644 index 00000000000000..a5b94d98f08fbe --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.start.json @@ -0,0 +1,11 @@ +{ + "slm.start": { + "methods": [ + "POST" + ], + "patterns": [ + "_slm/start" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/slm-start.html" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/slm.stop.json b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.stop.json new file mode 100644 index 00000000000000..0b76fe68d2b5ef --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/slm.stop.json @@ -0,0 +1,11 @@ +{ + "slm.stop": { + "methods": [ + "POST" + ], + "patterns": [ + "_slm/stop" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/slm-stop.html" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/generated/transform.stop_transform.json b/x-pack/legacy/plugins/console_extensions/spec/generated/transform.stop_transform.json index 5ce118b8f7925f..27fedcd994ccf9 100644 --- a/x-pack/legacy/plugins/console_extensions/spec/generated/transform.stop_transform.json +++ b/x-pack/legacy/plugins/console_extensions/spec/generated/transform.stop_transform.json @@ -1,9 +1,11 @@ { "transform.stop_transform": { "url_params": { + "force": "__flag__", "wait_for_completion": "__flag__", "timeout": "", - "allow_no_match": "__flag__" + "allow_no_match": "__flag__", + "wait_for_checkpoint": "__flag__" }, "methods": [ "POST" diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.preview_transform.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.preview_transform.json deleted file mode 100644 index fe7148e7fb890c..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.preview_transform.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "data_frame_transform_deprecated.preview_transform": { - "data_autocomplete_rules": { - "source": { - "index": "SOURCE_INDEX_NAME", - "query": { - "__scope_link": "GLOBAL.query" - } - }, - "pivot": { - "group_by": { - "__template": { - "NAME": {} - }, - "__scope_link": "GLOBAL.groupByAggs" - }, - "aggregations": { - "__template": { - "NAME": {} - }, - "__scope_link": "GLOBAL.aggregations" - } - } - } - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.put_transform.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.put_transform.json deleted file mode 100644 index 1a940888fd770f..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.put_transform.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "data_frame_transform_deprecated.put_transform": { - "data_autocomplete_rules": { - "source": { - "index": "SOURCE_INDEX_NAME", - "query": { - "__scope_link": "GLOBAL.query" - } - }, - "dest": { - "index": "DEST_INDEX_NAME" - }, - "pivot": { - "group_by": { - "__template": { - "NAME": {} - }, - "__scope_link": "GLOBAL.groupByAggs" - }, - "aggregations": { - "__template": { - "NAME": {} - }, - "__scope_link": "GLOBAL.aggregations" - } - } - } - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.update_transform.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.update_transform.json deleted file mode 100644 index 3c03dc5fa5c508..00000000000000 --- a/x-pack/legacy/plugins/console_extensions/spec/overrides/data_frame_transform_deprecated.update_transform.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "data_frame_transform_deprecated.update_transform": { - "data_autocomplete_rules": { - "description": "", - "dest": { - "index": "SOURCE_INDEX_NAME", - "pipeline": "" - }, - "frequency": "", - "source": { - "index": "SOURCE_INDEX_NAME", - "query": { - "__scope_link": "GLOBAL.query" - } - }, - "sync": { - "time": { - "field": "FIELD_NAME", - "delay": "" - } - } - } - } -} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.explain_data_frame_analytics.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.explain_data_frame_analytics.json new file mode 100644 index 00000000000000..859ba52d374911 --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.explain_data_frame_analytics.json @@ -0,0 +1,38 @@ +{ + "ml.explain_data_frame_analytics": { + "data_autocomplete_rules": { + "data_frame_analytics_config": { + "source": { + "index": { "__one_of": ["SOURCE_INDEX_NAME", []] }, + "query": {} + }, + "dest": { + "index": "", + "results_field": "" + }, + "analysis": { + "outlier_detection": { + "n_neighbors": 1, + "method": {"__one_of": ["lof", "ldof", "distance_knn_nn", "distance_knn"]}, + "feature_influence_threshold": 1.0 + } + }, + "analyzed_fields": { + "__one_of": [ + "FIELD_NAME", + [], + { + "includes": { + "__one_of": ["FIELD_NAME", []] + }, + "excludes": { + "__one_of": ["FIELD_NAME", []] + } + } + ] + }, + "model_memory_limit": "" + } + } + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.put_trained_model.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.put_trained_model.json new file mode 100644 index 00000000000000..9eabbaac9085b2 --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/overrides/ml.put_trained_model.json @@ -0,0 +1,5 @@ +{ + "ml.put_trained_model": { + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-df-analytics-apis.html" + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.start.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.start.json new file mode 100644 index 00000000000000..2949920313df71 --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.start.json @@ -0,0 +1,8 @@ +{ + "slm.start": { + "url_params": { + "timeout": "", + "master_timeout": "" + } + } +} diff --git a/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.stop.json b/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.stop.json new file mode 100644 index 00000000000000..c401aa65b9c6bc --- /dev/null +++ b/x-pack/legacy/plugins/console_extensions/spec/overrides/slm.stop.json @@ -0,0 +1,8 @@ +{ + "slm.stop": { + "url_params": { + "timeout": "", + "master_timeout": "" + } + } +} From 14be0ee8f4b6616c8c53ab03b8e46b625d279a19 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Tue, 14 Jan 2020 18:53:20 +0100 Subject: [PATCH 15/52] Bump to stable styled-components@5 (#54698) --- package.json | 2 +- .../__snapshots__/index.test.tsx.snap | 86 +++++++++---------- x-pack/package.json | 6 +- yarn.lock | 33 +++---- 4 files changed, 61 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index 6b9640d214a5ef..546a19c6eaba50 100644 --- a/package.json +++ b/package.json @@ -365,7 +365,7 @@ "@types/semver": "^5.5.0", "@types/sinon": "^7.0.13", "@types/strip-ansi": "^3.0.0", - "@types/styled-components": "^4.4.1", + "@types/styled-components": "^4.4.2", "@types/supertest": "^2.0.5", "@types/supertest-as-promised": "^2.0.38", "@types/testing-library__react": "^9.1.2", diff --git a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap index ca06c484dc8a29..31456ba8c4adaa 100644 --- a/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/stat_items/__snapshots__/index.test.tsx.snap @@ -38,18 +38,18 @@ exports[`Stat Items Component disable charts it renders the default widget 1`] = data-test-subj="stat-item" >

@@ -272,18 +272,18 @@ exports[`Stat Items Component disable charts it renders the default widget 2`] = data-test-subj="stat-item" >

@@ -576,18 +576,18 @@ exports[`Stat Items Component rendering kpis with charts it renders the default data-test-subj="stat-item" >

1,714 @@ -799,10 +799,10 @@ exports[`Stat Items Component rendering kpis with charts it renders the default key="stat-items-field-uniqueDestinationIps" >

2,359 @@ -903,10 +903,10 @@ exports[`Stat Items Component rendering kpis with charts it renders the default >

= 1" css-to-react-native "^3.0.0" + hoist-non-react-statics "^3.0.0" shallowequal "^1.1.0" - stylis-rule-sheet "^0.0.10" supports-color "^5.5.0" -stylis-rule-sheet@^0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" - integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== - stylis@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" From b598c9dc7fdfe6a67b6aad43963af6e2cfdc6729 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 14 Jan 2020 17:53:52 +0000 Subject: [PATCH 16/52] [ML] Categorization jobs improvements (#54579) * chunking token examples * disabling bucket span estimator * passing sample size to client * better handing of token errors * changes based on review --- .../plugins/ml/common/constants/new_job.ts | 4 +- .../job_creator/categorization_job_creator.ts | 4 +- .../categorization_examples_loader.ts | 2 +- .../bucket_span_estimator.tsx | 23 +++++- .../categorization_field/description.tsx | 2 +- .../examples_valid_callout.tsx | 20 ++++-- .../categorization_view/metric_selection.tsx | 21 ++++-- .../services/ml_api_service/index.d.ts | 2 +- .../job_service/new_job/categorization.ts | 70 ++++++++++++------- 9 files changed, 104 insertions(+), 44 deletions(-) diff --git a/x-pack/legacy/plugins/ml/common/constants/new_job.ts b/x-pack/legacy/plugins/ml/common/constants/new_job.ts index ccd108cd2698f4..3c98b372afdf73 100644 --- a/x-pack/legacy/plugins/ml/common/constants/new_job.ts +++ b/x-pack/legacy/plugins/ml/common/constants/new_job.ts @@ -27,6 +27,6 @@ export const DEFAULT_QUERY_DELAY = '60s'; export const SHARED_RESULTS_INDEX_NAME = 'shared'; export const NUMBER_OF_CATEGORY_EXAMPLES = 5; -export const CATEGORY_EXAMPLES_MULTIPLIER = 20; +export const CATEGORY_EXAMPLES_SAMPLE_SIZE = 1000; export const CATEGORY_EXAMPLES_WARNING_LIMIT = 0.75; -export const CATEGORY_EXAMPLES_ERROR_LIMIT = 0.2; +export const CATEGORY_EXAMPLES_ERROR_LIMIT = 0.02; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts index cea99eb5ec64c2..7c070ccc6bc534 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts @@ -102,10 +102,10 @@ export class CategorizationJobCreator extends JobCreator { } public async loadCategorizationFieldExamples() { - const { valid, examples } = await this._examplesLoader.loadExamples(); + const { valid, examples, sampleSize } = await this._examplesLoader.loadExamples(); this._categoryFieldExamples = examples; this._categoryFieldValid = valid; - return { valid, examples }; + return { valid, examples, sampleSize }; } public get categoryFieldExamples() { diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts index 16f127ae3d7283..ce1ea0bdaf181c 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts @@ -36,7 +36,7 @@ export class CategorizationExamplesLoader { const analyzer = this._jobCreator.categorizationAnalyzer; const categorizationFieldName = this._jobCreator.categorizationFieldName; if (categorizationFieldName === null) { - return { valid: 0, examples: [] }; + return { valid: 0, examples: [], sampleSize: 0 }; } const start = Math.floor( diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/bucket_span_estimator.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/bucket_span_estimator.tsx index b0f090a993f895..72ebbb0b438a2a 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/bucket_span_estimator.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/bucket_span_estimator.tsx @@ -7,7 +7,9 @@ import React, { FC, useState, useEffect, useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton } from '@elastic/eui'; +import { isAdvancedJobCreator } from '../../../../../common/job_creator'; import { JobCreatorContext } from '../../../job_creator_context'; +import { MLCATEGORY } from '../../../../../../../../../common/constants/field_types'; import { useEstimateBucketSpan, ESTIMATE_STATUS } from './estimate_bucket_span'; @@ -19,6 +21,7 @@ export const BucketSpanEstimator: FC = ({ setEstimating }) => { const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext); const { status, estimateBucketSpan } = useEstimateBucketSpan(); const [noDetectors, setNoDetectors] = useState(jobCreator.detectors.length === 0); + const [isUsingMlCategory, setIsUsingMlCategory] = useState(checkIsUsingMlCategory()); useEffect(() => { setEstimating(status === ESTIMATE_STATUS.RUNNING); @@ -26,11 +29,29 @@ export const BucketSpanEstimator: FC = ({ setEstimating }) => { useEffect(() => { setNoDetectors(jobCreator.detectors.length === 0); + setIsUsingMlCategory(checkIsUsingMlCategory()); }, [jobCreatorUpdate]); + function checkIsUsingMlCategory() { + return ( + isAdvancedJobCreator(jobCreator) && + jobCreator.detectors.some(d => { + if ( + d.partition_field_name === MLCATEGORY || + d.over_field_name === MLCATEGORY || + d.by_field_name === MLCATEGORY + ) { + return true; + } + }) + ); + } + return ( = memo(({ children, isOptional }) => { ) : ( )} diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx index 04934d2dc9a36d..270ba99d938cdc 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/examples_valid_callout.tsx @@ -12,8 +12,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { CategorizationAnalyzer } from '../../../../../../../services/ml_server_info'; import { EditCategorizationAnalyzerFlyout } from '../../../common/edit_categorization_analyzer_flyout'; import { - NUMBER_OF_CATEGORY_EXAMPLES, - CATEGORY_EXAMPLES_MULTIPLIER, CATEGORY_EXAMPLES_ERROR_LIMIT, CATEGORY_EXAMPLES_WARNING_LIMIT, } from '../../../../../../../../../common/constants/new_job'; @@ -22,11 +20,16 @@ type CategorizationAnalyzerType = CategorizationAnalyzer | null; interface Props { examplesValid: number; + sampleSize: number; categorizationAnalyzer: CategorizationAnalyzerType; } -export const ExamplesValidCallout: FC = ({ examplesValid, categorizationAnalyzer }) => { - const percentageText = ; +export const ExamplesValidCallout: FC = ({ + examplesValid, + categorizationAnalyzer, + sampleSize, +}) => { + const percentageText = ; const analyzerUsed = ; let color: EuiCallOutProps['color'] = 'success'; @@ -64,13 +67,16 @@ export const ExamplesValidCallout: FC = ({ examplesValid, categorizationA ); }; -const PercentageText: FC<{ examplesValid: number }> = ({ examplesValid }) => ( +const PercentageText: FC<{ examplesValid: number; sampleSize: number }> = ({ + examplesValid, + sampleSize, +}) => (
diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx index fda0066f9cd379..52b5c61e70fe5e 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx @@ -6,6 +6,7 @@ import React, { FC, useContext, useEffect, useState } from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; +import { mlMessageBarService } from '../../../../../../../components/messagebar'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; @@ -32,6 +33,7 @@ export const CategorizationDetectors: FC = ({ setIsValid }) => { ); const [fieldExamples, setFieldExamples] = useState(null); const [examplesValid, setExamplesValid] = useState(0); + const [sampleSize, setSampleSize] = useState(0); const [categorizationFieldName, setCategorizationFieldName] = useState( jobCreator.categorizationFieldName @@ -69,10 +71,20 @@ export const CategorizationDetectors: FC = ({ setIsValid }) => { async function loadFieldExamples() { if (categorizationFieldName !== null) { setLoadingData(true); - const { valid, examples } = await jobCreator.loadCategorizationFieldExamples(); - setFieldExamples(examples); - setExamplesValid(valid); - setLoadingData(false); + try { + const { + valid, + examples, + sampleSize: tempSampleSize, + } = await jobCreator.loadCategorizationFieldExamples(); + setFieldExamples(examples); + setExamplesValid(valid); + setLoadingData(false); + setSampleSize(tempSampleSize); + } catch (error) { + setLoadingData(false); + mlMessageBarService.notify.error(error); + } } else { setFieldExamples(null); setExamplesValid(0); @@ -97,6 +109,7 @@ export const CategorizationDetectors: FC = ({ setIsValid }) => { {fieldExamples !== null && loadingData === false && ( <> diff --git a/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.d.ts b/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.d.ts index bca32e9528f64a..db9d158c0ead99 100644 --- a/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.d.ts +++ b/x-pack/legacy/plugins/ml/public/application/services/ml_api_service/index.d.ts @@ -185,7 +185,7 @@ declare interface Ml { start: number, end: number, analyzer: any - ): Promise<{ valid: number; examples: any[] }>; + ): Promise<{ valid: number; examples: any[]; sampleSize: number }>; topCategories( jobId: string, count: number diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization.ts index 34e871a936088c..b3c70bf589cd04 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization.ts +++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization.ts @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import { chunk } from 'lodash'; import { ML_RESULTS_INDEX_PATTERN } from '../../../../common/constants/index_patterns'; -import { CATEGORY_EXAMPLES_MULTIPLIER } from '../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_SAMPLE_SIZE } from '../../../../common/constants/new_job'; import { CategoryId, Category, Token } from '../../../../common/types/categories'; import { callWithRequestType } from '../../../../common/types/kibana'; +const VALID_TOKEN_COUNT = 3; +const CHUNK_SIZE = 100; + export function categorizationExamplesProvider(callWithRequest: callWithRequestType) { async function categorizationExamples( indexPatternTitle: string, @@ -54,21 +58,31 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT }); const examples: string[] = results.hits?.hits ?.map((doc: any) => doc._source[categorizationFieldName]) - .filter((example: string | undefined) => example !== undefined); + .filter((example: string | null | undefined) => example !== undefined && example !== null); - let tokens: Token[] = []; + async function loadTokens(chunkSize: number) { + const exampleChunks = chunk(examples, chunkSize); + const tokensPerChunks = await Promise.all(exampleChunks.map(c => getTokens(c, analyzer))); + const tokensPerExample = tokensPerChunks.flat(); + return examples.map((e, i) => ({ text: e, tokens: tokensPerExample[i] })); + } try { - const { tokens: tempTokens } = await callWithRequest('indices.analyze', { - body: { - ...getAnalyzer(analyzer), - text: examples, - }, - }); - tokens = tempTokens; + return loadTokens(CHUNK_SIZE); } catch (error) { - // fail silently, the tokens could not be loaded - // an empty list of tokens will be returned for each example + // if an error is thrown when loading the tokens, lower the chunk size by half and try again + // the error may have been caused by too many tokens being found. + // the _analyze endpoint has a maximum of 10000 tokens. + return loadTokens(CHUNK_SIZE / 2); } + } + + async function getTokens(examples: string[], analyzer?: any) { + const { tokens }: { tokens: Token[] } = await callWithRequest('indices.analyze', { + body: { + ...getAnalyzer(analyzer), + text: examples, + }, + }); const lengths = examples.map(e => e.length); const sumLengths = lengths.map((s => (a: number) => (s += a))(0)); @@ -88,8 +102,7 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT } } }); - - return examples.map((e, i) => ({ text: e, tokens: tokensPerExample[i] })); + return tokensPerExample; } function getAnalyzer(analyzer: any) { @@ -110,10 +123,10 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT end: number, analyzer?: any ) { - const examples = await categorizationExamples( + const resp = await categorizationExamples( indexPatternTitle, query, - size * CATEGORY_EXAMPLES_MULTIPLIER, + CATEGORY_EXAMPLES_SAMPLE_SIZE, categorizationFieldName, timeField, start, @@ -121,20 +134,27 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT analyzer ); - const sortedExamples = examples + const sortedExamples = resp .map((e, i) => ({ ...e, origIndex: i })) .sort((a, b) => b.tokens.length - a.tokens.length); - const validExamples = sortedExamples.filter(e => e.tokens.length > 1); + const validExamples = sortedExamples.filter(e => e.tokens.length >= VALID_TOKEN_COUNT); + const sampleSize = sortedExamples.length; + + const multiple = Math.floor(sampleSize / size) || sampleSize; + const filteredExamples = []; + let i = 0; + while (filteredExamples.length < size && i < sortedExamples.length) { + filteredExamples.push(sortedExamples[i]); + i += multiple; + } + const examples = filteredExamples + .sort((a, b) => a.origIndex - b.origIndex) + .map(e => ({ text: e.text, tokens: e.tokens })); return { + sampleSize, valid: sortedExamples.length === 0 ? 0 : validExamples.length / sortedExamples.length, - examples: sortedExamples - .filter( - (e, i) => - i / CATEGORY_EXAMPLES_MULTIPLIER - Math.floor(i / CATEGORY_EXAMPLES_MULTIPLIER) === 0 - ) - .sort((a, b) => a.origIndex - b.origIndex) - .map(e => ({ text: e.text, tokens: e.tokens })), + examples, }; } From 46b34db465ebcee8a4a42838a97f7776d6c1876c Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 14 Jan 2020 17:54:53 +0000 Subject: [PATCH 17/52] [Dashboard] decode url params, so they are not encoded twice (#54738) Co-authored-by: Elastic Machine --- .../kibana/public/dashboard/__tests__/url_helper.test.ts | 5 +++-- .../kibana/public/dashboard/np_ready/url_helper.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/url_helper.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/url_helper.test.ts index 16773c02f5a7b1..df2dbfd54c130d 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/__tests__/url_helper.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/__tests__/url_helper.test.ts @@ -82,10 +82,11 @@ describe('Dashboard URL Helper', () => { x: 'y', y: 'z', }); - url = 'http://notDashboardUrl'; - expect(getUrlVars(url)).toEqual({}); url = 'http://localhost:5601/app/kibana#/dashboard/777182'; expect(getUrlVars(url)).toEqual({}); + url = + 'http://localhost:5601/app/kibana#/dashboard/777182?title=Some%20Dashboard%20With%20Spaces'; + expect(getUrlVars(url)).toEqual({ title: 'Some Dashboard With Spaces' }); }); it('getLensUrlFromDashboardAbsoluteUrl', () => { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts index ee9e3c4ef4781e..2e360567c4653f 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/url_helper.ts @@ -28,7 +28,7 @@ export function getUrlVars(url: string): Record { // @ts-ignore url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(_, key, value) { // @ts-ignore - vars[key] = value; + vars[key] = decodeURIComponent(value); }); return vars; } From 8d57df0fe615352f09120393eda9e722c2dc1c95 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Tue, 14 Jan 2020 18:57:21 +0100 Subject: [PATCH 18/52] [APM]: Fix render error when license has not been loaded (#54718) --- .../plugins/apm/public/context/LicenseContext/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx b/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx index 0bd38967826034..62cdbd3bbc995b 100644 --- a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx +++ b/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx @@ -16,8 +16,9 @@ export const LicenseContext = React.createContext( export function LicenseProvider({ children }: { children: React.ReactChild }) { const { license$ } = useApmPluginContext().plugins.licensing; - const license = useObservable(license$, { isActive: true } as ILicense); - const hasInvalidLicense = !license.isActive; + const license = useObservable(license$); + // if license is not loaded yet, consider it valid + const hasInvalidLicense = license?.isActive === false; // if license is invalid show an error message if (hasInvalidLicense) { From fdf51a5f8b6ba26a4dfff46362a4845b32a38762 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 14 Jan 2020 18:06:53 +0000 Subject: [PATCH 19/52] Splits dll in chunks (#54600) * feat(NA): implement dll split feature * chore(NA): improve logic to split dlls * feat(NA): support multiple manifests in the clean dll task * fix(NA): patch clean dll task to support multiple manifest files * feat(NA): shuffle seed before split dll in chunks * chore(NA): correctly load chunks into the templates * chore(NA): correctly load chunks on karma tests * docs(NA): add explanation why we need a single runtime for dll chunks * teste(NA): fix jest tests --- package.json | 1 + src/dev/build/README.md | 2 +- .../clean_client_modules_on_dll_task.js | 10 +- .../build/tasks/nodejs_modules/webpack_dll.js | 44 +++--- .../tasks/nodejs_modules/webpack_dll.test.js | 10 +- .../ui/ui_render/bootstrap/template.js.hbs | 5 +- src/legacy/ui/ui_render/ui_render_mixin.js | 10 +- .../dynamic_dll_plugin/dll_compiler.js | 137 ++++++++++++++---- .../dynamic_dll_plugin/dll_config_model.js | 15 +- .../dynamic_dll_plugin/dll_entry_template.js | 16 ++ .../dynamic_dll_plugin/dynamic_dll_plugin.js | 20 ++- tasks/config/karma.js | 10 +- yarn.lock | 5 + 13 files changed, 216 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index 546a19c6eaba50..c8ad736ea976f8 100644 --- a/package.json +++ b/package.json @@ -254,6 +254,7 @@ "rison-node": "1.0.2", "rxjs": "^6.5.3", "script-loader": "0.7.2", + "seedrandom": "^3.0.5", "semver": "^5.5.0", "style-it": "^2.1.3", "style-loader": "0.23.1", diff --git a/src/dev/build/README.md b/src/dev/build/README.md index af08414f0bf4be..3b579033fabe1f 100644 --- a/src/dev/build/README.md +++ b/src/dev/build/README.md @@ -44,7 +44,7 @@ The majority of this logic is extracted from the grunt build that has existed fo We have introduced in our bundle a webpack dll for the client vendor modules in order to improve the optimization time both in dev and in production. As for those modules we already have the -code into the vendors.bundle.dll.js we have decided to delete those bundled modules from the +code into the vendors_${chunk_number}.bundle.dll.js we have decided to delete those bundled modules from the distributable node_modules folder. However, in order to accomplish this, we need to exclude every node_module used in the server side code. This logic is performed under `nodejs_modules/clean_client_modules_on_dll_task.js`. In case we need to add any new cli diff --git a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js b/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js index 19d74bcf89e302..52928d6e47fc46 100644 --- a/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js +++ b/src/dev/build/tasks/nodejs_modules/clean_client_modules_on_dll_task.js @@ -98,12 +98,16 @@ export const CleanClientModulesOnDLLTask = { // Consider this as our whiteList for the modules we can't delete const whiteListedModules = [...serverDependencies, ...kbnWebpackLoaders, ...manualExceptions]; - // Resolve the client vendors dll manifest path - const dllManifestPath = `${baseDir}/built_assets/dlls/vendors.manifest.dll.json`; + // Resolve the client vendors dll manifest paths + // excluding the runtime one + const dllManifestPaths = await globby([ + `${baseDir}/built_assets/dlls/vendors_*.manifest.dll.json`, + `!${baseDir}/built_assets/dlls/vendors_runtime.manifest.dll.json`, + ]); // Get dll entries filtering out the ones // from any whitelisted module - const dllEntries = await getDllEntries(dllManifestPath, whiteListedModules, baseDir); + const dllEntries = await getDllEntries(dllManifestPaths, whiteListedModules, baseDir); for (const relativeEntryPath of dllEntries) { const entryPath = `${baseDir}/${relativeEntryPath}`; diff --git a/src/dev/build/tasks/nodejs_modules/webpack_dll.js b/src/dev/build/tasks/nodejs_modules/webpack_dll.js index ea8cc1e2864071..72910226bb04a7 100644 --- a/src/dev/build/tasks/nodejs_modules/webpack_dll.js +++ b/src/dev/build/tasks/nodejs_modules/webpack_dll.js @@ -28,27 +28,37 @@ function checkDllEntryAccess(entry, baseDir = '') { return isFileAccessible(resolvedPath); } -export async function getDllEntries(manifestPath, whiteListedModules, baseDir = '') { - const manifest = JSON.parse(await read(manifestPath)); - - if (!manifest || !manifest.content) { - // It should fails because if we don't have the manifest file - // or it is malformed something wrong is happening and we - // should stop - throw new Error(`The following dll manifest doesn't exists: ${manifestPath}`); - } +export async function getDllEntries(manifestPaths, whiteListedModules, baseDir = '') { + // Read and parse all manifests + const manifests = await Promise.all( + manifestPaths.map(async manifestPath => JSON.parse(await read(manifestPath))) + ); - const modules = Object.keys(manifest.content); - if (!modules.length) { - // It should fails because if we don't have any - // module inside the client vendors dll something - // wrong is happening and we should stop too - throw new Error(`The following dll manifest is reporting an empty dll: ${manifestPath}`); - } + // Process and group modules from all manifests + const manifestsModules = manifests.flatMap((manifest, idx) => { + if (!manifest || !manifest.content) { + // It should fails because if we don't have the manifest file + // or it is malformed something wrong is happening and we + // should stop + throw new Error(`The following dll manifest doesn't exists: ${manifestPaths[idx]}`); + } + + const modules = Object.keys(manifest.content); + if (!modules.length) { + // It should fails because if we don't have any + // module inside the client vendors dll something + // wrong is happening and we should stop too + throw new Error( + `The following dll manifest is reporting an empty dll: ${manifestPaths[idx]}` + ); + } + + return modules; + }); // Only includes modules who are not in the white list of modules // and that are node_modules - return modules.filter(entry => { + return manifestsModules.filter(entry => { const isWhiteListed = whiteListedModules.some(nonEntry => normalizePosixPath(entry).includes(`node_modules/${nonEntry}`) ); diff --git a/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js b/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js index 1fdd7d8d4f5ff3..ce305169a777b9 100644 --- a/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js +++ b/src/dev/build/tasks/nodejs_modules/webpack_dll.test.js @@ -52,7 +52,7 @@ describe('Webpack DLL Build Tasks Utils', () => { isFileAccessible.mockImplementation(() => true); - const mockManifestPath = '/mock/mock_dll_manifest.json'; + const mockManifestPath = ['/mock/mock_dll_manifest.json']; const mockModulesWhitelist = ['dep1']; const dllEntries = await getDllEntries(mockManifestPath, mockModulesWhitelist); @@ -66,7 +66,7 @@ describe('Webpack DLL Build Tasks Utils', () => { isFileAccessible.mockImplementation(() => false); - const mockManifestPath = '/mock/mock_dll_manifest.json'; + const mockManifestPath = ['/mock/mock_dll_manifest.json']; const mockModulesWhitelist = ['dep1']; const dllEntries = await getDllEntries(mockManifestPath, mockModulesWhitelist); @@ -78,7 +78,7 @@ describe('Webpack DLL Build Tasks Utils', () => { it('should throw an error for no manifest file', async () => { read.mockImplementationOnce(async () => noManifestMock); - const mockManifestPath = '/mock/mock_dll_manifest.json'; + const mockManifestPath = ['/mock/mock_dll_manifest.json']; try { await getDllEntries(mockManifestPath, []); @@ -92,7 +92,7 @@ describe('Webpack DLL Build Tasks Utils', () => { it('should throw an error for no manifest content field', async () => { read.mockImplementation(async () => noContentFieldManifestMock); - const mockManifestPath = '/mock/mock_dll_manifest.json'; + const mockManifestPath = ['/mock/mock_dll_manifest.json']; try { await getDllEntries(mockManifestPath, []); @@ -106,7 +106,7 @@ describe('Webpack DLL Build Tasks Utils', () => { it('should throw an error for manifest file without any content', async () => { read.mockImplementation(async () => emptyManifestContentMock); - const mockManifestPath = '/mock/mock_dll_manifest.json'; + const mockManifestPath = ['/mock/mock_dll_manifest.json']; try { await getDllEntries(mockManifestPath, []); diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 85b6de26b95164..72dd97ff58642b 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -13,7 +13,10 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { window.onload = function () { var files = [ - '{{dllBundlePath}}/vendors.bundle.dll.js', + '{{dllBundlePath}}/vendors_runtime.bundle.dll.js', + {{#each dllJsChunks}} + '{{this}}', + {{/each}} '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedDepsFilename}}', '{{regularBundlePath}}/commons.bundle.js', '{{regularBundlePath}}/{{appId}}.bundle.js' diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index a935270d23fced..4158af19bd858a 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -26,6 +26,7 @@ import { AppBootstrap } from './bootstrap'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { fromRoot } from '../../../core/server/utils'; import { getApmConfig } from '../apm'; +import { DllCompiler } from '../../../optimize/dynamic_dll_plugin'; /** * @typedef {import('../../server/kbn_server').default} KbnServer @@ -103,8 +104,14 @@ export function uiRenderMixin(kbnServer, server, config) { const basePath = config.get('server.basePath'); const regularBundlePath = `${basePath}/bundles`; const dllBundlePath = `${basePath}/built_assets/dlls`; + const dllStyleChunks = DllCompiler.getRawDllConfig().chunks.map( + chunk => `${dllBundlePath}/vendors${chunk}.style.dll.css` + ); + const dllJsChunks = DllCompiler.getRawDllConfig().chunks.map( + chunk => `${dllBundlePath}/vendors${chunk}.bundle.dll.js` + ); const styleSheetPaths = [ - `${dllBundlePath}/vendors.style.dll.css`, + ...dllStyleChunks, ...(darkMode ? [ `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`, @@ -132,6 +139,7 @@ export function uiRenderMixin(kbnServer, server, config) { appId: isCore ? 'core' : app.getId(), regularBundlePath, dllBundlePath, + dllJsChunks, styleSheetPaths, sharedDepsFilename: UiSharedDeps.distFilename, }, diff --git a/src/optimize/dynamic_dll_plugin/dll_compiler.js b/src/optimize/dynamic_dll_plugin/dll_compiler.js index 7d558367c032d7..9889c1f71c3bfb 100644 --- a/src/optimize/dynamic_dll_plugin/dll_compiler.js +++ b/src/optimize/dynamic_dll_plugin/dll_compiler.js @@ -23,6 +23,11 @@ import { notInNodeModules, inDllPluginPublic, } from './dll_allowed_modules'; +import { + dllEntryFileContentArrayToString, + dllEntryFileContentStringToArray, + dllMergeAllEntryFilesContent, +} from './dll_entry_template'; import { fromRoot } from '../../core/server/utils'; import { PUBLIC_PATH_PLACEHOLDER } from '../public_path_placeholder'; import fs from 'fs'; @@ -30,6 +35,8 @@ import webpack from 'webpack'; import { promisify } from 'util'; import path from 'path'; import del from 'del'; +import { chunk } from 'lodash'; +import seedrandom from 'seedrandom'; const readFileAsync = promisify(fs.readFile); const mkdirAsync = promisify(fs.mkdir); @@ -37,11 +44,17 @@ const accessAsync = promisify(fs.access); const writeFileAsync = promisify(fs.writeFile); export class DllCompiler { - static getRawDllConfig(uiBundles = {}, babelLoaderCacheDir = '', threadLoaderPoolConfig = {}) { + static getRawDllConfig( + uiBundles = {}, + babelLoaderCacheDir = '', + threadLoaderPoolConfig = {}, + chunks = Array.from(Array(4).keys()).map(chunkN => `_${chunkN}`) + ) { return { uiBundles, babelLoaderCacheDir, threadLoaderPoolConfig, + chunks, context: fromRoot('.'), entryName: 'vendors', dllName: '[name]', @@ -66,13 +79,49 @@ export class DllCompiler { } async init() { - await this.ensureEntryFileExists(); - await this.ensureManifestFileExists(); + await this.ensureEntryFilesExists(); + await this.ensureManifestFilesExists(); await this.ensureOutputPathExists(); } - async upsertEntryFile(content) { - await this.upsertFile(this.getEntryPath(), content); + seededShuffle(array) { + // Implementation based on https://github.com/TimothyGu/knuth-shuffle-seeded/blob/gh-pages/index.js#L46 + let currentIndex; + let temporaryValue; + let randomIndex; + const rand = seedrandom('predictable', { global: false }); + + if (array.constructor !== Array) throw new Error('Input is not an array'); + currentIndex = array.length; + + // While there remain elements to shuffle... + while (0 !== currentIndex) { + // Pick a remaining element... + randomIndex = Math.floor(rand() * currentIndex--); + + // And swap it with the current element. + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; + } + + async upsertEntryFiles(content) { + const arrayContent = this.seededShuffle(dllEntryFileContentStringToArray(content)); + const chunks = chunk( + arrayContent, + Math.ceil(arrayContent.length / this.rawDllConfig.chunks.length) + ); + const entryPaths = this.getEntryPaths(); + + await Promise.all( + entryPaths.map( + async (entryPath, idx) => + await this.upsertFile(entryPath, dllEntryFileContentArrayToString(chunks[idx])) + ) + ); } async upsertFile(filePath, content = '') { @@ -80,38 +129,57 @@ export class DllCompiler { await writeFileAsync(filePath, content, 'utf8'); } - getDllPath() { - return this.resolvePath(`${this.rawDllConfig.entryName}${this.rawDllConfig.dllExt}`); + getDllPaths() { + return this.rawDllConfig.chunks.map(chunk => + this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.dllExt}`) + ); } - getEntryPath() { - return this.resolvePath(`${this.rawDllConfig.entryName}${this.rawDllConfig.entryExt}`); + getEntryPaths() { + return this.rawDllConfig.chunks.map(chunk => + this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.entryExt}`) + ); } - getManifestPath() { - return this.resolvePath(`${this.rawDllConfig.entryName}${this.rawDllConfig.manifestExt}`); + getManifestPaths() { + return this.rawDllConfig.chunks.map(chunk => + this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.manifestExt}`) + ); } - getStylePath() { - return this.resolvePath(`${this.rawDllConfig.entryName}${this.rawDllConfig.styleExt}`); + getStylePaths() { + return this.rawDllConfig.chunks.map(chunk => + this.resolvePath(`${this.rawDllConfig.entryName}${chunk}${this.rawDllConfig.styleExt}`) + ); } - async ensureEntryFileExists() { - await this.ensureFileExists(this.getEntryPath()); + async ensureEntryFilesExists() { + const entryPaths = this.getEntryPaths(); + + await Promise.all(entryPaths.map(async entryPath => await this.ensureFileExists(entryPath))); } - async ensureManifestFileExists() { - await this.ensureFileExists( - this.getManifestPath(), - JSON.stringify({ - name: this.rawDllConfig.entryName, - content: {}, - }) + async ensureManifestFilesExists() { + const manifestPaths = this.getManifestPaths(); + + await Promise.all( + manifestPaths.map( + async (manifestPath, idx) => + await this.ensureFileExists( + manifestPath, + JSON.stringify({ + name: `${this.rawDllConfig.entryName}${this.rawDllConfig.chunks[idx]}`, + content: {}, + }) + ) + ) ); } async ensureStyleFileExists() { - await this.ensureFileExists(this.getStylePath()); + const stylePaths = this.getStylePaths(); + + await Promise.all(stylePaths.map(async stylePath => await this.ensureFileExists(stylePath))); } async ensureFileExists(filePath, content) { @@ -137,8 +205,10 @@ export class DllCompiler { await this.ensurePathExists(this.rawDllConfig.outputPath); } - dllExistsSync() { - return this.existsSync(this.getDllPath()); + dllsExistsSync() { + const dllPaths = this.getDllPaths(); + + return dllPaths.every(dllPath => this.existsSync(dllPath)); } existsSync(filePath) { @@ -149,8 +219,16 @@ export class DllCompiler { return path.resolve(this.rawDllConfig.outputPath, ...arguments); } - async readEntryFile() { - return await this.readFile(this.getEntryPath()); + async readEntryFiles() { + const entryPaths = this.getEntryPaths(); + + const entryFilesContent = await Promise.all( + entryPaths.map(async entryPath => await this.readFile(entryPath)) + ); + + // merge all the module contents from entry files again into + // sorted single one + return dllMergeAllEntryFilesContent(entryFilesContent); } async readFile(filePath, content) { @@ -160,7 +238,7 @@ export class DllCompiler { async run(dllEntries) { const dllConfig = this.dllConfigGenerator(this.rawDllConfig); - await this.upsertEntryFile(dllEntries); + await this.upsertEntryFiles(dllEntries); try { this.logWithMetadata( @@ -234,7 +312,7 @@ export class DllCompiler { // ignore if this module represents the // dll entry file - if (module.resource === this.getEntryPath()) { + if (this.getEntryPaths().includes(module.resource)) { return; } @@ -259,7 +337,6 @@ export class DllCompiler { // node_module or no? if (notInNodeModules(reason.module.resource)) { notAllowedModules.push(module.resource); - return; } }); } diff --git a/src/optimize/dynamic_dll_plugin/dll_config_model.js b/src/optimize/dynamic_dll_plugin/dll_config_model.js index ecf5def5aa6cac..c7ab2fe30dd141 100644 --- a/src/optimize/dynamic_dll_plugin/dll_config_model.js +++ b/src/optimize/dynamic_dll_plugin/dll_config_model.js @@ -140,6 +140,13 @@ function generateDLL(config) { filename: dllStyleFilename, }), ], + // Single runtime for the dll bundles which assures that common transient dependencies won't be evaluated twice. + // The module cache will be shared, even when module code may be duplicated across chunks. + optimization: { + runtimeChunk: { + name: 'vendors_runtime', + }, + }, performance: { // NOTE: we are disabling this as those hints // are more tailored for the final bundles result @@ -158,6 +165,7 @@ function extendRawConfig(rawConfig) { const dllNoParseRules = rawConfig.uiBundles.getWebpackNoParseRules(); const dllDevMode = rawConfig.uiBundles.isDevMode(); const dllContext = rawConfig.context; + const dllChunks = rawConfig.chunks; const dllEntry = {}; const dllEntryName = rawConfig.entryName; const dllBundleName = rawConfig.dllName; @@ -176,7 +184,12 @@ function extendRawConfig(rawConfig) { const threadLoaderPoolConfig = rawConfig.threadLoaderPoolConfig; // Create webpack entry object key with the provided dllEntryName - dllEntry[dllEntryName] = [`${dllOutputPath}/${dllEntryName}${dllEntryExt}`]; + dllChunks.reduce((dllEntryObj, chunk) => { + dllEntryObj[`${dllEntryName}${chunk}`] = [ + `${dllOutputPath}/${dllEntryName}${chunk}${dllEntryExt}`, + ]; + return dllEntryObj; + }, dllEntry); // Export dll config map return { diff --git a/src/optimize/dynamic_dll_plugin/dll_entry_template.js b/src/optimize/dynamic_dll_plugin/dll_entry_template.js index 584bf0c9e3d354..0c286896d0b714 100644 --- a/src/optimize/dynamic_dll_plugin/dll_entry_template.js +++ b/src/optimize/dynamic_dll_plugin/dll_entry_template.js @@ -23,3 +23,19 @@ export function dllEntryTemplate(requirePaths = []) { .sort() .join('\n'); } + +export function dllEntryFileContentStringToArray(content = '') { + return content.split('\n'); +} + +export function dllEntryFileContentArrayToString(content = []) { + return content.join('\n'); +} + +export function dllMergeAllEntryFilesContent(content = []) { + return content + .join('\n') + .split('\n') + .sort() + .join('\n'); +} diff --git a/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js b/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js index cb941d2ba56831..484c7dfbfd5957 100644 --- a/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js +++ b/src/optimize/dynamic_dll_plugin/dynamic_dll_plugin.js @@ -44,7 +44,7 @@ export class DynamicDllPlugin { async init() { await this.dllCompiler.init(); - this.entryPaths = await this.dllCompiler.readEntryFile(); + this.entryPaths = await this.dllCompiler.readEntryFiles(); } apply(compiler) { @@ -70,12 +70,14 @@ export class DynamicDllPlugin { bindDllReferencePlugin(compiler) { const rawDllConfig = this.dllCompiler.rawDllConfig; const dllContext = rawDllConfig.context; - const dllManifestPath = this.dllCompiler.getManifestPath(); + const dllManifestPaths = this.dllCompiler.getManifestPaths(); - new webpack.DllReferencePlugin({ - context: dllContext, - manifest: dllManifestPath, - }).apply(compiler); + dllManifestPaths.forEach(dllChunkManifestPath => { + new webpack.DllReferencePlugin({ + context: dllContext, + manifest: dllChunkManifestPath, + }).apply(compiler); + }); } registerInitBasicHooks(compiler) { @@ -192,7 +194,7 @@ export class DynamicDllPlugin { // then will be set to false compilation.needsDLLCompilation = this.afterCompilationEntryPaths !== this.entryPaths || - !this.dllCompiler.dllExistsSync() || + !this.dllCompiler.dllsExistsSync() || (this.isToForceDLLCreation() && this.performedCompilations === 0); this.entryPaths = this.afterCompilationEntryPaths; @@ -337,7 +339,9 @@ export class DynamicDllPlugin { // We need to purge the cache into the inputFileSystem // for every single built in previous compilation // that we rely in next ones. - mainCompiler.inputFileSystem.purge(this.dllCompiler.getManifestPath()); + this.dllCompiler + .getManifestPaths() + .forEach(chunkDllManifestPath => mainCompiler.inputFileSystem.purge(chunkDllManifestPath)); this.performedCompilations++; diff --git a/tasks/config/karma.js b/tasks/config/karma.js index 0acd452530b305..ec37277cae0f8c 100644 --- a/tasks/config/karma.js +++ b/tasks/config/karma.js @@ -21,6 +21,7 @@ import { dirname } from 'path'; import { times } from 'lodash'; import { makeJunitReportPath } from '@kbn/test'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; +import { DllCompiler } from '../../src/optimize/dynamic_dll_plugin'; const TOTAL_CI_SHARDS = 4; const ROOT = dirname(require.resolve('../../package.json')); @@ -54,7 +55,10 @@ module.exports = function(grunt) { 'http://localhost:5610/test_bundle/built_css.css', `http://localhost:5610/bundles/kbn-ui-shared-deps/${UiSharedDeps.distFilename}`, - 'http://localhost:5610/built_assets/dlls/vendors.bundle.dll.js', + 'http://localhost:5610/built_assets/dlls/vendors_runtime.bundle.dll.js', + ...DllCompiler.getRawDllConfig().chunks.map( + chunk => `http://localhost:5610/built_assets/dlls/vendors${chunk}.bundle.dll.js` + ), shardNum === undefined ? `http://localhost:5610/bundles/tests.bundle.js` @@ -63,7 +67,9 @@ module.exports = function(grunt) { // this causes tilemap tests to fail, probably because the eui styles haven't been // included in the karma harness a long some time, if ever // `http://localhost:5610/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, - 'http://localhost:5610/built_assets/dlls/vendors.style.dll.css', + ...DllCompiler.getRawDllConfig().chunks.map( + chunk => `http://localhost:5610/built_assets/dlls/vendors${chunk}.style.dll.css` + ), 'http://localhost:5610/bundles/tests.style.css', ]; } diff --git a/yarn.lock b/yarn.lock index dfce24ad77d2e2..3590b10607e14c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25382,6 +25382,11 @@ scss-tokenizer@^0.2.3: js-base64 "^2.1.8" source-map "^0.4.2" +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== + seek-bzip@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" From 6bed80bbd8d51fa30b104eed35cca23b00ee1613 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 14 Jan 2020 11:23:43 -0700 Subject: [PATCH 20/52] Upgraded EUI to 18.0.0 (#54042) * Upgraded EUI to 18.0.0 * Fix breaks from `palette._.colors` changes * snapshots * Updated hard coded hex color codes in tests, fixed TS errors * Updated a functional test's selector; added (BSD-3-Clause AND Apache-2.0) to license checker whitelist * Functional test selector update * Updated vega browser-ci tests for palette changes * rebased on master * One more location for EUI package number update and yarn lock * Fixed lurking [but introduced] TypeScript logic bug * Swap a prop definition for the same value but tied closer to its source Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> Co-authored-by: Elastic Machine --- package.json | 2 +- packages/kbn-ui-shared-deps/package.json | 2 +- src/dev/license_checker/config.ts | 1 + .../shard_failure_table.test.tsx.snap | 1 + .../__snapshots__/controls_tab.test.tsx.snap | 4 - .../list_control_editor.test.tsx.snap | 4 - .../components/editor/list_control_editor.tsx | 11 +- .../__snapshots__/list_control.test.tsx.snap | 3 - .../kbn_vislib_vis_types/public/area.js | 4 +- .../kbn_vislib_vis_types/public/histogram.js | 4 +- .../public/horizontal_bar.js | 4 +- .../kbn_vislib_vis_types/public/line.js | 4 +- .../__snapshots__/clone_modal.test.js.snap | 3 - .../__snapshots__/header.test.js.snap | 6 - .../__snapshots__/time_field.test.js.snap | 10 - .../__jest__/__snapshots__/table.test.js.snap | 1 + .../__jest__/__snapshots__/table.test.js.snap | 1 + .../__snapshots__/add_filter.test.js.snap | 4 - .../__jest__/__snapshots__/table.test.js.snap | 3 +- .../__snapshots__/objects_table.test.js.snap | 1 + .../__snapshots__/flyout.test.js.snap | 2 + .../__snapshots__/relationships.test.js.snap | 4 + .../__jest__/__snapshots__/table.test.js.snap | 2 + .../field/__snapshots__/field.test.js.snap | 45 - .../__snapshots__/new_vis_modal.test.tsx.snap | 118 +++ .../__snapshots__/status_table.test.js.snap | 1 + .../splits/__snapshots__/terms.test.js.snap | 6 - .../public/__tests__/vega_image_512.png | Bin 31097 -> 30730 bytes .../data_model/__tests__/vega_parser.js | 20 +- .../bytes/__snapshots__/bytes.test.js.snap | 3 - .../color/__snapshots__/color.test.js.snap | 3 + .../date/__snapshots__/date.test.js.snap | 3 - .../__snapshots__/date_nanos.test.js.snap | 3 - .../__snapshots__/duration.test.js.snap | 16 - .../number/__snapshots__/number.test.js.snap | 3 - .../__snapshots__/percent.test.js.snap | 3 - .../__snapshots__/static_lookup.test.js.snap | 8 +- .../string/__snapshots__/string.test.js.snap | 4 - .../label_template_flyout.test.js.snap | 1 + .../url/__snapshots__/url.test.js.snap | 40 - .../url_template_flyout.test.js.snap | 1 + .../__snapshots__/samples.test.js.snap | 1 + .../public/field_editor/field_editor.test.js | 4 +- .../__snapshots__/metric_agg.test.tsx.snap | 2 - .../__snapshots__/top_aggregate.test.tsx.snap | 2 - .../point_series/_point_series.js | 4 +- .../query_string_input.test.tsx.snap | 24 +- .../query_string_input/query_bar_top_row.tsx | 3 +- .../query_string_input/query_string_input.tsx | 2 +- .../__snapshots__/field_icon.test.tsx.snap | 6 +- .../public/field_icon/field_icon.tsx | 4 +- .../saved_object_save_modal.test.tsx.snap | 2 - .../page_objects/visualize_chart_page.ts | 3 +- .../page_objects/visualize_editor_page.ts | 5 +- .../plugins/kbn_tp_run_pipeline/package.json | 2 +- .../kbn_tp_custom_visualizations/package.json | 2 +- .../kbn_tp_embeddable_explorer/package.json | 2 +- .../kbn_tp_sample_panel_action/package.json | 2 +- .../waterfall_helpers.test.ts.snap | 6 +- .../__snapshots__/ManagedTable.test.js.snap | 2 + .../shared/SelectWithPlaceholder/index.tsx | 2 +- .../__snapshots__/CustomPlot.test.js.snap | 840 +++++++++--------- .../__snapshots__/Histogram.test.js.snap | 112 +-- .../__tests__/chartSelectors.test.ts | 8 +- .../lib/transactions/breakdown/index.test.ts | 4 +- .../public/components/inputs/input.tsx | 7 +- .../components/field_manager/field_icon.tsx | 4 +- .../graph/public/helpers/style_choices.ts | 4 +- .../services/persistence/deserialize.test.ts | 2 +- .../indexpattern_plugin/field_icon.test.tsx | 50 +- .../public/indexpattern_plugin/field_icon.tsx | 4 +- .../lens_field_icon.test.tsx | 4 +- .../indexpattern_plugin/lens_field_icon.tsx | 4 +- .../operations/definitions/terms.test.tsx | 4 +- .../__snapshots__/xy_expression.test.tsx.snap | 154 ++-- .../pipeline_editor.test.js.snap | 68 -- .../pipelines_table.test.js.snap | 1 + .../geometry_filter_form.test.js.snap | 14 - .../__snapshots__/tooltip_header.test.js.snap | 2 - .../update_source_editor.test.js.snap | 9 - .../maps/public/layers/styles/color_utils.js | 4 +- .../annotations_table.test.js.snap | 1 + .../__snapshots__/filter_bar.test.js.snap | 2 + .../condition_expression.test.js.snap | 16 - .../scope_expression.test.js.snap | 24 - .../__snapshots__/editor.test.tsx.snap | 40 - .../__snapshots__/list.test.tsx.snap | 24 - .../__snapshots__/calendar_form.test.js.snap | 6 - .../__snapshots__/events_table.test.js.snap | 2 + .../table/__snapshots__/table.test.js.snap | 1 + .../edit_description_popover.test.js.snap | 9 - .../edit/__snapshots__/header.test.js.snap | 6 - .../list/__snapshots__/table.test.js.snap | 2 + .../__snapshots__/latest_active.test.js.snap | 1 + .../__snapshots__/latest_types.test.js.snap | 1 + .../latest_versions.test.js.snap | 1 + .../ccr/__snapshots__/ccr.test.js.snap | 1 + .../__snapshots__/ccr_shard.test.js.snap | 1 + .../logs/__snapshots__/logs.test.js.snap | 1 + .../flyout/__snapshots__/flyout.test.js.snap | 10 - .../report_listing.test.tsx.snap | 3 + .../basic_login_form.test.tsx.snap | 6 - .../__snapshots__/feature_table.test.tsx.snap | 1 + .../__snapshots__/index.test.tsx.snap | 1 + .../__snapshots__/jobs_table.test.tsx.snap | 1 + .../note_card_body.test.tsx.snap | 48 +- .../__snapshots__/index.test.tsx.snap | 48 +- .../confirm_delete_modal.test.tsx.snap | 3 - .../customize_space_avatar.test.tsx.snap | 5 +- .../space_identifier.test.tsx.snap | 2 - .../spaces_grid_pages.test.tsx.snap | 11 +- .../__snapshots__/space_avatar.test.tsx.snap | 8 +- .../__snapshots__/popover_form.test.tsx.snap | 3 - .../__snapshots__/popover_form.test.tsx.snap | 7 - .../checkup/deprecations/index_table.test.tsx | 125 +-- .../filter_popover.test.tsx.snap | 2 + .../__snapshots__/monitor_list.test.tsx.snap | 2 + .../monitor_list_pagination.test.tsx.snap | 2 + .../__snapshots__/ping_list.test.tsx.snap | 9 +- x-pack/package.json | 2 +- .../nav_control/nav_control_service.test.ts | 2 +- yarn.lock | 29 +- 122 files changed, 974 insertions(+), 1233 deletions(-) diff --git a/package.json b/package.json index c8ad736ea976f8..7a8596da9d377c 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "@elastic/charts": "^16.1.0", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 014467d204d96c..2e62eb3504aca6 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "devDependencies": { - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "@elastic/charts": "^16.1.0", "@kbn/dev-utils": "1.0.0", "@yarnpkg/lockfile": "^1.1.0", diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index a4aa3474c0762c..bd084767a723f0 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -23,6 +23,7 @@ export const LICENSE_WHITELIST = [ 'Elastic-License', '(BSD-2-Clause OR MIT OR Apache-2.0)', '(BSD-2-Clause OR MIT)', + '(BSD-3-Clause AND Apache-2.0)', '(GPL-2.0 OR MIT)', '(MIT AND CC-BY-3.0)', '(MIT AND Zlib)', diff --git a/src/legacy/core_plugins/data/public/search/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap b/src/legacy/core_plugins/data/public/search/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap index 55e2c63f608d49..257513f20fa94e 100644 --- a/src/legacy/core_plugins/data/public/search/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/search/fetch/components/__snapshots__/shard_failure_table.test.tsx.snap @@ -72,5 +72,6 @@ exports[`ShardFailureTable renders matching snapshot given valid properties 1`] }, } } + tableLayout="fixed" /> `; diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap index 632fe63e9e1485..278811ca85df9d 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/__snapshots__/controls_tab.test.tsx.snap @@ -135,11 +135,7 @@ exports[`renders ControlsTab 1`] = ` > ) => void; handleParentChange: (controlIndex: number, event: ChangeEvent) => void; - parentCandidates: EuiSelectProps['options']; + parentCandidates: React.ComponentProps['options']; deps: InputControlVisDependencies; } diff --git a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap index 31c221b36e2b2a..99482a4be2d7b4 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap +++ b/src/legacy/core_plugins/input_control_vis/public/components/vis/__snapshots__/list_control.test.tsx.snap @@ -8,10 +8,7 @@ exports[`disableMsg 1`] = ` label="list control" > diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js index 049544b504918f..fd13067c84cc00 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js @@ -33,7 +33,7 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { vislibVisController } from './controller'; export const areaDefinition = { @@ -117,7 +117,7 @@ export const areaDefinition = { value: 10, width: 1, style: ThresholdLineStyles.FULL, - color: palettes.euiPaletteColorBlind.colors[9], + color: euiPaletteColorBlind()[9], }, labels: {}, }, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js index fdc18f5bfa0e65..bc017b5a1a871d 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js @@ -32,7 +32,7 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { vislibVisController } from './controller'; export const histogramDefinition = { @@ -120,7 +120,7 @@ export const histogramDefinition = { value: 10, width: 1, style: ThresholdLineStyles.FULL, - color: palettes.euiPaletteColorBlind.colors[9], + color: euiPaletteColorBlind()[9], }, }, }, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js index 15bbf9c01cd77d..ee3570314618ab 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js @@ -32,7 +32,7 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { vislibVisController } from './controller'; export const horizontalBarDefinition = { @@ -119,7 +119,7 @@ export const horizontalBarDefinition = { value: 10, width: 1, style: ThresholdLineStyles.FULL, - color: palettes.euiPaletteColorBlind.colors[9], + color: euiPaletteColorBlind()[9], }, }, }, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js index a3fb874b5aa1b8..d6d075f452fed4 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js +++ b/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js @@ -32,7 +32,7 @@ import { InterpolationModes, getConfigCollections, } from './utils/collections'; -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; import { vislibVisController } from './controller'; @@ -118,7 +118,7 @@ export const lineDefinition = { value: 10, width: 1, style: ThresholdLineStyles.FULL, - color: palettes.euiPaletteColorBlind.colors[9], + color: euiPaletteColorBlind()[9], }, }, }, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/top_nav/__snapshots__/clone_modal.test.js.snap b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/top_nav/__snapshots__/clone_modal.test.js.snap index 6def1b1a198b88..e76f65c45e4286 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/top_nav/__snapshots__/clone_modal.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/top_nav/__snapshots__/clone_modal.test.js.snap @@ -30,11 +30,8 @@ exports[`renders DashboardCloneModal 1`] = ` diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap index 11c41425a0bb53..f2fb17cdb0d602 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/components/header/__jest__/__snapshots__/header.test.js.snap @@ -78,11 +78,8 @@ exports[`Header should mark the input as invalid 1`] = ` labelType="label" > `; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap index 4716fb8f776338..2da4d84463b291 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/scripted_fields_table/components/table/__jest__/__snapshots__/table.test.js.snap @@ -76,6 +76,7 @@ exports[`Table should render normally 1`] = ` } responsive={true} sorting={true} + tableLayout="fixed" /> `; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/add_filter/__jest__/__snapshots__/add_filter.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/add_filter/__jest__/__snapshots__/add_filter.test.js.snap index 432c57d4f473d9..879ea555d33007 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/add_filter/__jest__/__snapshots__/add_filter.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/source_filters_table/components/add_filter/__jest__/__snapshots__/add_filter.test.js.snap @@ -6,9 +6,7 @@ exports[`AddFilter should ignore strings with just spaces 1`] = ` grow={10} > `; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap index 2aaa291f6122bf..4ba0fe480ac423 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/__snapshots__/objects_table.test.js.snap @@ -71,6 +71,7 @@ exports[`ObjectsTable delete should show a confirm modal 1`] = ` pagination={true} responsive={true} sorting={false} + tableLayout="fixed" /> `; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap index ace06e0420a7c2..34ce8394232edc 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/__snapshots__/flyout.test.js.snap @@ -115,6 +115,7 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = ` } } responsive={true} + tableLayout="fixed" /> @@ -445,6 +446,7 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` } } responsive={true} + tableLayout="fixed" /> diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap index 941a0ffded820a..c1241d5d7c1e50 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/relationships/__jest__/__snapshots__/relationships.test.js.snap @@ -154,6 +154,7 @@ exports[`Relationships should render dashboards normally 1`] = ` ], } } + tableLayout="fixed" />
@@ -368,6 +369,7 @@ exports[`Relationships should render index patterns normally 1`] = ` ], } } + tableLayout="fixed" />
@@ -533,6 +535,7 @@ exports[`Relationships should render searches normally 1`] = ` ], } } + tableLayout="fixed" />
@@ -693,6 +696,7 @@ exports[`Relationships should render visualizations normally 1`] = ` ], } } + tableLayout="fixed" />
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap index daac04d07da28c..805131042f3852 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap @@ -203,6 +203,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` "onSelectionChange": [Function], } } + tableLayout="fixed" />
@@ -410,6 +411,7 @@ exports[`Table should render normally 1`] = ` "onSelectionChange": [Function], } } + tableLayout="fixed" />
diff --git a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap index ae168e76d359b0..f4d20b45658802 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/settings/components/field/__snapshots__/field.test.js.snap @@ -50,10 +50,8 @@ exports[`Field for array setting should render as read only if saving is disable >
+
+ +
@@ -565,6 +585,26 @@ exports[`NewVisModal filter for visualization types should render as expected 1` />
+
+ +
@@ -835,6 +875,26 @@ exports[`NewVisModal filter for visualization types should render as expected 1` />
+
+ +
@@ -1139,12 +1199,18 @@ exports[`NewVisModal filter for visualization types should render as expected 1` data-test-subj="filterVisType" fullWidth={true} incremental={false} + isClearable={true} isLoading={false} onChange={[Function]} placeholder="Filter" value="with" > @@ -1209,6 +1280,50 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
+
+ + + + + +
@@ -2775,12 +2890,14 @@ exports[`NewVisModal should render as expected 1`] = ` data-test-subj="filterVisType" fullWidth={true} incremental={false} + isClearable={true} isLoading={false} onChange={[Function]} placeholder="Filter" value="" > diff --git a/src/legacy/core_plugins/status_page/public/components/__snapshots__/status_table.test.js.snap b/src/legacy/core_plugins/status_page/public/components/__snapshots__/status_table.test.js.snap index cc9cdd6af1f393..3379d6cd649c40 100644 --- a/src/legacy/core_plugins/status_page/public/components/__snapshots__/status_table.test.js.snap +++ b/src/legacy/core_plugins/status_page/public/components/__snapshots__/status_table.test.js.snap @@ -37,5 +37,6 @@ exports[`render 1`] = ` noItemsMessage="No items found" responsive={true} rowProps={[Function]} + tableLayout="fixed" /> `; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/splits/__snapshots__/terms.test.js.snap b/src/legacy/core_plugins/vis_type_timeseries/public/components/splits/__snapshots__/terms.test.js.snap index ffd4d08204a7e0..654e7d9da4dcaa 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/splits/__snapshots__/terms.test.js.snap +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/splits/__snapshots__/terms.test.js.snap @@ -87,9 +87,6 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js labelType="label" > @@ -112,9 +109,6 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js labelType="label" > diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_image_512.png b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_image_512.png index 44cd0d320931facaa78b5095e7cf01c91c45a6ce..cc28886794f0359da3097fa2320dcc35b3257ff8 100644 GIT binary patch literal 30730 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
(OVW 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 KQ7-ny|Nj8YQ~@^t literal 31097 zcmbrmby!s07dCum7+?s2p}Rx6q@=sMLj)-SNlB?eIwYkV>244Z1{4I5RzW~YK$LDI z-^24fzwf=i>-zrt{*swFXYaN5UU9E`t!<2smhwGpN^AfC?y0IM=m7u}{1b8k#{mEF z$T}tm0D3@GLDs;}ay!pAkoxP}vjPVPqm;M>?lae6qm!|0Jce)`LP9tz@vp_T#RqzB zl&bWL$u_u@Vls;@>Ynm)Lzg1O$Cqu4>12H)_D^6QHt8DN#nn%kk~*r+W%w-Q~^KI z;XO2E{ckE6bYS!!dGExMVwOQi;$!A<5&id7Ar!gs|16h|un>xh3e!rD`R^-mxzGQ9 zFV~%(Zx50#ZlE0)#!ngvyEJ{~*bpmkf-MWvU;?Fae9F!TN*2sO&{?opH-SqN9rgFu&>USf4*J)#RSL{u7IuZ@ z$KaM14*zokLrA1SWd@qf*%=u$RFXI?%-4Y+zmpI$4cCEKQJP_cdxxCC>ONU`Jbea1 z4MDn3a}g2UE@xKdGp0$>|F0?Gp$&w+84w=v0_ws2>PF8MGQ4CBfoMPy`rl~}g`);W zW~2Sm7Y6}P%$R1yw1GH*Va1>rBn9j#ItsUnI2Am{aiS!SV_rzpE&mf@G`V0etTswV zb(d(QAt-c1?1Oa%P-paE%!>6GR@OFU`I@CxgQz62a>? z&4&m>Ya?9*DvU-3aB$npBNb3kP@znKEIm+%mbsxR#n*&DPi6`y@!=OZt!5>@5G$1R z?sOG8h_k^_+z6A1cVIxg+5in>pfDvOBu93Oo1X~^4KoP8P;vEF+T#j-#p{gk}LrMCN987}&9E072{XRH` zB?~>f*`e4?hu0N4ePe^!SZvvJ#svXn5nS`K=Id1iD6A<|PjLwy8OrTQWqa<@YH_YR zcU{SZSiaNa1U`0RaF|6_dc!|Un1ku%TPcv5*x^;~->2ol3P?84O#G%wOhDcD%SM(w zr2>{ya5ZT4?K}c8t}8ha%SzxFuJ_pq?nGhTKOGGz7dRnTfj_U#SWVFQsT0DO#10N2 zz@l;u>efCwq*CrulwM+>Z#$%_={x|><30>A6b)J%#mbv8aFX)~aFXG;ke&~Hd$$!$ z=Oq6hPso_WhzO$mvn~`PomwpT{9<&S zrSPxpojWB*B1<-ffSS(_WQOYWnEHgu2Fg(i%My1_{Ql>O%_vlV!1oD-{aP|$^pZYU z@fCd7Ir6tWj0?0<eY+w`z{4E+K1|IB=hn0I2i`AwUpw6eCat1mWq&+&ge1+u&s7#C-CMqxvwGEnM zr{dfD_Ymh~cVi3uJ2s=$Fc$I`!QN*QPqW^HcMk{@P-=HBOf|>z&MWvqi(=31qR%7K zD!=A!j!!xtRkJP0Ex1YE+RBDj5;fP3DjKVjUF?3Fv9FbzR)mAiF z>W-38nUqQddfO11?UaZ}2xGG@TIneQ6gcp~tZf=7a442a+Cc&Ief|9KeV~8Mi2{GF zGSu@j3h96YR0`Y#Te&kq#~gt7lF}t`Or$l@`wv@gfDU`1``4Q>M=Ip@hZU;fr>p)eF__0mYy0WX@T7EF(co_)>( zAWt>p-L1XVPa9Zzo~QsbLT*gNB=rFuM4K3xLVG5#8Iy5!T^oWNj@$o3A=_a$FFE_W_qS$3 z43Zye%@Qn$7C3C;d}PMZUr>xVNZqh298y@mg?ID-aBiD7P}4aj4ADdkA9^VHJ$Pt! zb#D~ShiGutGcW*O&jZ_0cvj82X#?ssg<;I+NaPj{pgOq-11=&c0-Id1K15Vk?F|Tq ziIkEmpdQ0Yuy=H2&U4W$=#1<4J;Z#(WN{q#g&{MfKnme4J<#)KXJutCf&bL1M#2yOb_m25 z9U0*6Yw;s3*hJx*FHBF)nzwQbBOdtWAUSc65z$dHbzA_mqB`x&Uw@HMpUiL9HQM)MxId=iO)Gjv zBiCX5XCLeBA%UZZAJAC-jr_?r$4La^gzk?Z|9a14txul<)ut|iEWr{uEj_*A=3`7- zY44=8TV1;YPP{33Jqb0GA-c?Ar`C1mVm#0+`H(h!?g_jgLGiD3=0WYRkxnB})7-$y`t5q_6(iaFDPTg7~} zr?g(VJmnsYl356MMM%SburM29!d$Rf?HwjFszHgHOim#he*Q{ECFXG)vR-u57uk4b zn1Pv^$hs*HV(8^qs7%#FFh0CM;1v`dL*WVx;u3QAqmc#haN=t!_%@r?Q9Mu(`)QRf zuPpA^FAn(=2e>$O`yMV{haP^_$>P`F6#qIJKC1!x!^cn*nM?Y?;&HV4*Jzy;(MQZf z`Y`Ip02nxNfb*vE3h}SFO$C+7%ObfPiCzd0$uTRMqXEW;2{|4T7`?h9iVwHBY?6q< zm`eQ!cwJ}pW*|Hm4m3R_=Vl<7A?oj*EA1FbdGvG6>-e| z(a1MANS#tz*+F9b1D_s}@ZJi;0yg~-C2++k4#}r5i|4# zEQ!+Ac$p5y8v^;O1UoE!4Fv~C2|PZ#TmzMijx6~g4M*0OZAU)bXWlR7h!fezRcVYy zrozKHT7D7qHF7@kB1NX!$BDFxHUaJzyk#Qga?*7st8%Rp&ed*LDY}mlNw3d+fX}`) zj-UvQ@;^ze6F32$z$DEts~5ol+z-7eE@+~)gojolMT#%9*p7=61aLmbOB4@rkYx?S zFlu8!CGFxYv~ivFvTR=oi@r)8}IJ71FFIf^~m#A;ZrbILW$p_df9?_`pckh8(^Z;0p zlV1~sYF9S-<@_{Are{oI1wp1{D9~R1{YR54gn-p}#BZW0{PmDAj2iUZyf!vXO~-3* z7mhvXA?XCzPt+R+LZ?&+K`0BRcr#F&Qg+{#4tanm!$7%7*c3eTzu`z5Y@~a{&12l} zA(!D@R4!6gpH}l+iM6t0P;WtnRo?}MJRVXEE}xpb?U?%gP_;lDOSB9~B7R4SjQ9~V zNmeC95J7PluE|`7OB}E)8eWcmBf7v7A-6^PezaX-5P8b-YL{s_8Zi?ybHb9w(XPRX_yiyl5wHpnD5Jv)K=XQuk*59RTGLj~A0$zTe-wdqi?lZMH~p2DGleI)J#TVJAeHn3}%r@Ud$c!82n=a-?@XcaDWNe31JTe6=(MZ_Efn}th&&!q zj9iLM$B{k|f(h)Iff*&sW81tnQnVUvavJjlwjaeAUY$`$B5dT^2io7Xwk5;>UPY5= zgbD$q9dX}mFw(){cb@AM&BRI5pFM|}x2l4U-k1<`5CQ0QVUb^r(p0Fde}|EWX%BuH zkVcR&F0(>$(t6yX(66pjxNtgsrq$-UDUBzOWKrLs^cI!Z$^7TO#z~CHzX(tj95^^w zI0$zl5aIb?%KI&7W+$)A6tDNccb(EO`yq>JgZ>A1d;t`JZy{saFG3kxV<$Ee@>H@Q_>@rT(88w_BcH~mQr&K+4|^LuJUy#F8kkuqIe!!vu>-@j#&VQWL9iaRXqlRI_5b z46mtLuPB{Mo+Ao9LNt^DaO0=FPvBmyHO3pPO?05UZh!YAD$p-g=KSXTD;Ki zebsu$$Fw=s78@*+$H3Lm_U#*h?^~mRiisMGpzx^WzQ%VS{LVkfwAUJXHZymMX-vWA z>soHJeeLA$Ub}@EiYpq6Tt}DbDBdHC48507Eb~Nw6$SKCq(&6jQ%6JZ$b#^+b8Yfv z5e;lNCzFSMBL>|)_gwuU^oAddfz`7>^@TTs6sWOGuCap($)tv4?yCV%vCk5=zn*HTu+)nB2mqZsq4-nDgp|12uez}b;Yhtdr>o51kF9Xx=VIh=i z1t;9Z4D?RH_`qc-=TOqn1FQyT(3;0YYNx$4e&lM?2TF6=Lf%dORm0Sa<~Jl|KKsMA zuElKS0V#Zk1!O!#z#LbVmW2#S>sLr$K@kukL6M}id!yrOMv4~w(`4k4RPW$DpRGxE7uCo`Eck9xFlB1g(7dc*(+~ER6{m_qhj1F;yAa zdDy_oKuEjC(A)n?eVIJ9XN9SyE~_R0GFcvU)*-Q)gINPYT=aO>t2qB<<8mjU){6BuD79{6al7`A zi$%gC%Y8Z8Z8j?>5YSl)KR|rZh9Iqa4fU*;LV3QiX}mE6sFYD3D_IoPEum|#dS&ku zS0lU*@-U?Gqw!i?ogf5NUtE}hwHx1OhHhVE zv-BBvuSNyVs-wV^dy-Zb$vf+u+$0_j)IvpV>93fXYChCEAbI@<1kpMKS>r5cCzsP0 zJX-Pf?=p>3DC&VR5#zxEoX<*4126qKj!I}WiXPSPZC#Hnek5c`q3UqXR|_O@ zm?HSz^-&0`M##gxPEN1{DbsDbUOwFLYQM^f?k#5 zWRKnk|NEbW6!8Q={xsEu`JDSB7L#l~GeO;ir9FJce`rD4!1OqU4*%Y(%Np>NiBst_ zO^i6=kKVeOj{@-9OkXEXoo9l?TNhwe=1b6EZ-__w;v>FTV=9szUFN&|v&@O|X-h+K zdcO`4D{@jN6~4%&vK3ICcm~*zIY~K;qvh{6yyzpa1r?)LKK8< z9H9U!Pi2Cwi+~Uo`k4@qSBB7D-TEV8~_SmdbePQ~wt)FDk4^h0_`d}Xo>Il;Tr zd!u1C>sso1r9PZ|Ccm{#(5k=5^46v`Z@t@yyE4&6)j+NA0gd)WJUcHENcea);y~n| zSSC+jvnhaPv_jd35%B~WB-+Wmnbmv`cdpEym0;Nt_#ez5YSB~KLx|EUSTAnCY0vV7 zJZ(kp$Uq20;;asf2E+F3^PyJoE{G!Zx`-&QK8Z&U#SFv#;V^P?!}>3iz|ArTmZ9~g z7lpQvTLOD*^1(x~ns@Zla}=t&eIw7n&Z9;1DSK@ce4iWOXd#1y zrk6=PkmYLgG=szK%rlm?;*RtlCW4se0Fz?bSJHm#u@BV4+T`S&-(dC4P=*lfoD_)t zb`KEyrc%yj38w$T1GWc*yf2uvzuf_$J45%Ts2wd~3;)}y?vc}K%APX=cjpB9(+)?frq+!BBm*7sx|Gtw>iu07h7wz09biJK7$`;`ykz$G^=KDt z7TXX{se3CiH%k>2mI1uOA(KD%;$plR(}*OVzl5EW0I{bSfW=0T?b-wf6nB3ylZ&gJ zgKqr*BNRQYh92eR#+%5*W61RKSAy8-Ag)&;4i7r28`ET0M^k#8cE03`BJ3u>w!q!@ ztLL^yvVDpZQ>c6CsqWyi7!`|2@(GO)Zd7sT`u>YW)R-bPkJ6; z{ZeO%@F9YOq`K@NZIgLa)?bRSU!d22bw^SzvZ2ZWomFXTob)m}ooZ1+~Biv0Xj-;HE9dbW|K6AcK!~6|6xj zaeXgQ>0!_vfb?a0$9oO0+w|8+8#t)bQDdZ0r~e5p+_p?-gPpuj@K))UPz6F=&`_~Y z3-%pM60o$hne`nJpm%`-em4a_hol%xTB%!G=yln^oR5MG3@Dc(uydkGW~eY?wvS+! z?klH+EHC!l11O$a*FNSmTe%tShbk$KdQdED*Au-3YcnYJwm&qB3?P}aiQcCePh||# zVw*t6HDYLz9vvoo{K#MYj~1E6#W|6Q&ZMs{fR=Nv>|?2rZbcW(ea}ctn}XdFCp7*J z#ERqM9xgn|^tyP7(v|UpBCk!sEbF|VKPxZ}q)?=WftzJaYWRL-1=r0azUA;6F_-`c z(9bz*kv?JmIax_J1|WlWA!e5dqw8^p5k_tuIH_lb6CdBeuDlu9gSFElFeM(owjF^& zDM)}KwH+ED1k%9k5lT&We)hqj;bVW$w3y~09G^9NGaSnk#{s~o!cfXO!{n;gK= zz>VL(CfhBVur^Ey%>tFw2M(~BFr_sSKt|xlKobqBfdttK#c5$we140W2 zSt+$i{7`hx)fsfc^QMC3il)rdC3k5L#`NLfFZbn#OQkn+4vR)x6Z0)w3sf~u_0ZwQ&1FxZuw zk;MLH*}96ceU6HyUdvi+Nqyar-B4Y$;!C&8=rp;2Oc^tDq``13^7lR34Y_a8fE!d2 z*jLtoOULpIurBto)81K%{=woN8|jc0|D)8Ma>TDKq1w9`bjMxi))U7n!2;!g6<$Ct zf>QQHO#08H%XPv+U#9na6;1oMqEAY6q!^QoK)~#jh!!Ru_#vi-5V8v+N2L-F?8b2S z{a6UQl$dQG&`vjS#DZZeXuYKdty!#plIdRQJhj!tUUvPb8Q~RA0F?G|hD)AOZ_RdyGjhAUgl1b*n01ZN?EMe0+*EBgFHOXIj9I ztD)p6FMx`Yv>!>L5lNE81pey33>Ksi>R)M z5k+pJI%x*6N@)DH=#*yFZ}8CllevvkTFz^1B#u$T_ZMTlLfsT>msLJhD=48ZKLb({|)u{1k;4y>Ff35PCK zH=P$ao}J2mL_a$r#e@|tVj5kxM#Je6l|J-de3kyIRUo4VDo4>ZFZ~4Fn`3`{ZN?-4 z2*&wnCfE5^{3Y>y)wH=<6urMl46pP9=|Hh<3ypFk;QkyBkU7jEh<;ZMz7L@>UBx!r z*rCfXs0^VaH+Lm2o0(&1h${Bncdh@+D7`_%X8~B8)8|wSoP=$t7vq9EaV)=FNJc|M0a zNh??Yc5_rx+rWad69We>fXOn}jO|O1QAQ2RSPa$okR`sIfl9BhkUZm2-{ihx>=e*z z=ZKSkRGF(VuZ9BaHsiN0G)!t*urZbfy|jUOEF75%xl3|JiJ!itK|%eqk?WgX^90vY zyIB_^24Jq%Mo#Y^C3aUTb%%#ifv{nc!R%QoLhqObUNa>O!BedXvozy$xIE>8Md5~R z*^+YuH<&=r+CyLFOt@c$jI&j)&f1wiv{Y?p9JNfeNMrwgtb+bQd@N;;Jjx0};08m< z^(%pz83w24(Piq#r!E=tWKSuc0U88Gh{U-V`}7+D{GDZ@wbX;@9%aX~*re=FlE zmqdc+V4}FJK^^}6M1Z!9qiFhY0uLFI0Ee2>riGn7bO}^RY{tU11Pq?iVTPCIx`cf} zK$Zy4OF7~mpT4>2_W!#qJSp(F_wTqQfKrM!mwwz!r_b1LpTEFbr;x8+GBN=kki7OHqYg zsVvun=wQKZP(JMij6(lvqbsDhMmH^N;Gb!hyV3tw6$X$p9I}<%U}1=q$SMZs>tRhT zsY9#KBbCV;Bv8;|82&t^K+n#jp$LJf8-ze_b%t@7)0Iz-G6q?I#fLoMHw`Q( zAfNcN)}`F_UtScPjkYEv2>rzkFtY^H0X(Bz=qGp8Y(nV2tWWxyrwpO$q31DDNrKEZ<28jme>oDXdEAT-Q>a&1{aki=$KCZ;H>ZXaV-6yGNg#wQ+ z{xLT0((!9^0eP5&Hj1tqgcpw^QJc#-zM~FTJsdb^0l~4UvaT=;E|5dM@J(ur>aZ)8 zxjSn)B!7pb5l>wUbC#y+UCl#lqpLPCY&lpVO|i3ipiJ!dU<=wE=y)EzuPc(xII4hJ zf_3+@S?I0KL->}Keg-B4VDMI~uPR4lA^6m+mX$%ciUquXg;DGSjxQREVgnU?K{r-3 z#`GMH!Xd3}bH9*%Z+McOD?cX($u&5MIi<6f+Sak~$v_IPDaZr+TkBLrK?PBT);bmN;iud5 ztBZoHqju=Z%)7Oi46=Xu+6ZHb*q<)YYcdc|00681{KL0Q&_y;-iSCLL@75mFJgpZ* z;`8P5#$eZA|4a3k{51|wes1@gkxZJyOv0mTb5iQ~Y;>0UF^Yq={OsOS6n zA3W*%7z1%OV^ZvDmDZ5RDAM@>?&?Oezi`3zghlN~Zquw;439XA?$WkZ=Iig^gUb zd!}KOo*&qwL;2p1(AM2CLw)PPIHLsXn5CIgWdV1p zk`P{~V?xunVQ?u{bS+oPn~RibC2A2hMZrmT|gKb7TIZY>3H`%c-d;(P8k0O!j zG>_s@!IWuXOAqMtvd?o--@$U^-&=x$Bl=4P6djDbOPd?N(|$nv-$Ft8JuSw7p&av z9ilQ29@Ty6GFy`O+@xU{Md#hw9upG_;A6BqX&1)9^0=|u5xov<=7?*_i6heBjq(RK z+jw)yb|`q;?#UjKzm?8?;?1 z*D7n=N04ezPp9$%7Hy-cS}frNmqS%FP)86qPetZ z|GV$bU%Hxcap{70zhb{KiI)YVo?hb4OId*_zrwxi~wI= zE~IZK!!a)oCa-p-3_q`ZV4{0x#oe-~N#0kgvFQ>wBL27yOi}^muHU@^KPR*-HqlK9 zdhT8A(B-?fAr!jVC=s*|#1k`Cd*4y*VgZ>#4ZLkMkein;&rMFw-0SDB-~7|)M%Yfp za1HKc2?)0rC{hQMrxUF#wJQZI@o`fFc4+pkmA^Kz4MJt;AMvMm<3{f{P$Fnrof#il z8Xe>OpzFm(J7)yeN|N>_k@O3D8*=ofz{E*5d%UO+3v`hg^-sC)ftwwEqqSVYZe}yK z)~kiy7a!xjDY#d(ox3WWEH1Z$wei-5ZcRY~XpleOK|X@xX9m;z7GCWqK(E?|h|<=x zRYbdNF%;#(r1T;NsR|=lPN?9La@0F->F>E7edcYJr@7e@w97fKjqfsEq4OygUs7km zEA?$x9l1gE`YJjr{l39a6XvXe?6e_3o>A18io3E4HjsHodT{M%p(9H>*eMY1d=T4v zPRu5&%n@L;eu?79D~u`X5i#ma89BO~x=?I=!r1j$;jyxz>1RRy9yU4k*fw;)k5p|7 zinSv6Rox?s3ebftN9r=ZQb0X~CcFrcFes9Yg44B*-Jr6x)@e$cf{5ML zW}UV2?Mc*&?E_QuGP;fqI61B$d1N*s6msK>Dlcw2R~A12>*QeFM9{~{kL|3EVyjv^ z;n~j@IZK&D&BEiez6Xl-e{G^Yxfn%P(bbwI%7~2hQrxbabsh#y?}h1d6LgfhV&$ty zW8CH&0Q(M3P6`;?`*m4v)SKj!tS1>yZGWw0fA6a20x;w?NaXt7X%!l#De4#K;$!H5 z`TgPTLf@R}{?WA4_n!-Ke*mI!u8J=2ikOR#%1^5qn(=#`6D!y*2>J7B$$tKCGi2vh z?>(Qjv)u+X>p_-ZC-W7C74=jH-!wC|TJ2>X3DJdgTfP5sy`p|Eyo6Q3$_k3xTa%8{ z+k&S1{T-J!{7=-*P>}1`;`THZ)h|hhPmYy#ui~D(Om`V;*Zmg@=AtIpDW6FjrdW?= zSuF?6@Ahf2@fAT)SZ}Ok(By-Zudf`G9s4Zh-4w`NdxL|t#-FO!(F)NQWOt;ZCS@!# zO@nyz8_k0MVWX%It}d3tFTA5zzx0mdVhEmDcD^_srl82<5^dnAF|@M|rRe-YE@)8s z1BrXH3+zy#d8~x_Tv_IAn)da6aLovT9I+=Q^0UpjoElv<3zWhLK{ z--$*AfLW40Q-6?AYBEQQGGyhs(XSoe$B$M7Kek#W7WIo8zI&TTw6(F@S@qy+FvffV zSQ+iEj==coSDXdLhM&Q_zAq_k_tjb~x))u!4S-omD8}Jl_JRG$aB_ftFvRiV2FEy2 zQtD%g8yQBpJ`pd$CY0E|8lkahZ7QUsJ4ll-s>! z)j?s2=3$*l)Z>`F!kw0h!?|kAOfXYA^>MomzOP|GN|u%PEAXHaXh`h|U0_pgGu+8; zl&alPhDgq~7OwmpPDLqEs3>Yi%k+XSj7-<`s>;w$?yNZwcXPh>lW&iNg!V1feQl5F zSLRKt?|B})=Vc8q-qd?0z|@xW1cXGyK;VL7HeyyAEEVPW{01E&&mqPkEA+*4mjnIh z6iqGK)!D7TVykp%^rt!KaaBrKZ|DoRuo!xM*jania&EBh>aGn@n%cJXeRULW3fc(^ zWWo~hBs) zn7l+tudbiE`8X(LBu1N^9!w1?mPzz}{y=-;O%s}7XnK`IQpKVefB{f@q`7GR{R<>~o_O{fA*LFihFXT*~e&v`K zf3Ja#L}TmbdFtIdpv_Wmr$~F~*>(8m*KYTm?o9XLz9K&to4Z7GfA=Ut!qsTwe|(|o zfqu-q)C=FQ`HocF{((jdfzxF*Jw;^Y#p715^k+%GEOl}x5ons)YxGeHd5b|X_CIEF z1%KkA0%;RmpvT_m%3$@d``WFcdwPziigHG3KK{wiyJ|57SFdphtx?M_3ZyfhJQz8_ z=614LN_-BWhS7yjTYD_#L=&C&xire*un66mjhelCsu0&n35LOpgq*HyrYx)5)29%- zNCWQgym>YA9)X_gDq{_kK(L#$o+0GdSJr1Dw%k?JTq>!d)m`td50@Covd_ag;u6h^ zaJ0wkTTD}=xl%cOL^fe)D13Y550RQM_eXsR1b5ALmuMf9 z%70Kp#7M%=-Z^NcnXF`9`c*JirK?9f1(|FPc?o#o7;O5(y2!2wn+I!&juA-3*PLHC| zH4RM3pirGm08oCG2LdtE51qXqIqX_DCv5H*XFHxj8$Luh`G5PS;nz`HZ!qZ#v9gMr z_bRBJiAC!G`_6}Oe^Ul~X^w2qbUeGH8cHd2@Jg^y#Rft&0%C0q-t(}} zmaCeO!h;V_&u!n*dsUYvVnXP5&^o60`KxSRoYY8Wlm@PyS-nl_&^u94gy#zwD1g}zU5YdOZ1ChRgII}~2QrTI9_?wTu+c5DUr z%GAkL?}1J$$DMxMD(m*|(aIx#&A;OHJP*FjxmWubk_LG&m~`k};@a$IS_|wtbtsP$ z#ya=LmHQ8uL6Ky`4_2Q(acil2n#MlvGsec&?yR@7khdoil;}vQQW1vCG zI4dh#bZIJualE6|w2&20QRQuvmN(#GQT3<>4VWQ;ORAHP9nnRl4sJi=){8i0q&M+& zE~UZsqay)bE6tm8pD{3jc6FkAHy@89gW=8cl}D?GMV$=|Pc4?RMAeFpenDu0wCvye zSV1&^=pU=9w#u(m2?>sk$H@zC14k&`YDpd>JBQNB6g512IqH~D%GpWsPd}g6G*6W$ zcDMra(c@<3%mty;gD7ldJfe&YY5hdG=6HB*zNd|P+O%Vb$!+6!!!kYp#UDjjJi-dd zLH6CorN!NLCUgJOl5>5gb@{8n%fR@!ohWw-|9!#r^>wAOtFaOqp z{c--V^VYvv&&DUxzp@eIxAogAu#f;s_p>H8+%2c)(1@I$b>%u7_BDAEC6JmnolL+2 ziKp^lK7bV_(0tXHfv~_mdDl3VHvOtas9AF{`LCOX7;$H=GhXClqmhWUOVa#nut~qW z@Xw2JN{JD2fphN`iz~S@%SvGAwbhI3@egXo*tjR#$DIy~oaMf2`TZ_*`N{swv3H4K zp?LYvR~s+M{@(o5MAOt7A9@rqFa6oyoG(BIq!rZV6F56R2O!t+z8@wv*Ynd%vL50{ShIARPk43VBXSdiQ z`jTn+BN*vnAN>-rN%UH@nNV&tET01TyTEoMMXKHYT`zrIND|Lm+zTNvy3I+J1i4}` zba>@0dH$f1@BBD>^C~Ki0$UCyz}yftRy|rCMbqr1zAKZWPDNT-oX>6d^!N6zTUFEM z_a$#$+8Z0&ZN$I#5t7@kTZ^Fdk31bur_pOG+s@e+F9WCb1XolhsZ#ON{7)0*%ycSJ zrLnrbArnO30j^~j@Z9IoTYH6Hw)wo!U1K|sDx&U#Z|=VnXOr@G+wpFPS83+a4~`Y+ zJ*-DlxP)kG4S*7P?l7sSL&_Q2TodQZ!;mRCKB;3B)oIORIDObbN!=oSAy$x(_dv*_ zWnz(^^G6~YF6Kkt;$V|*+qS4cj-$z>s{^2&nfLMS0e{&iIkRd-@H&ckm4lO7exw4u z)R(yyGzXloKiY7zP=6zs$~%~76j0=$V+-=mW)6l2SA#BG2S9Dp&-Zx5LO+I1d~|T( zemovaR4r1h8AbJvVfl;RwBha$YNmo<}woUUDtdM+sC5aXZAOLf=P0N z`~CYQ2tTsWw&AZ-i3!ecKd73N%ni^0d?`6)ur8xVl!0mH5}2^g9aYwrw#}W~$C+Np z=GQD0Jjp9FmoQN`z6Qi@AO8;)Kvuw9BvfGdAhHr+ty^nC~qzC_r7LOU_Ckle`_3H$-%`i*>{NGR0BtC)NkGkZ48MfUVYHCL%;ik{ zX6}cbMi^SzhczoPd>5@4gi%X@AlS&Rn26@JQ21#zPRSd*FZS9Xawj3d*>YM~Y;Y$- zOkQm74{cSK3#NcI-gqW)9N4!hZ5*ua|-0p}TW^DD0co)6Cjacs2qUVd|)!4nruUqs`> znWM zanxtHugjHV6=4{RI^Yt@^^JdhqI9h!Ivdh=1~6wB0bME580_%}YiQcIlpgC{oC zWJD=c5C0}(14KgwG$}$v1n2ENKv0>^8r);|2D-D7Mhoh1^^N55yE z^BtPkn+jfi7IodjW6{YpChFT6qsuzt#I3aw_{(`5WmcQgeFY>AqQO@P~g zubcr(o!RKTAgY__9ZtHQ{mS~8EF}H}>u=+FrUdKTC43Tq6<_Y>)vvi1&069&IVZ2t ziiG?>Zalv%vlcMI)`oQuh&~JsFq|)`u)#TADLmfo67{ zf3FbnF$B%T=yp{nCj5~4d)#n(wdV1=IDI;cglAEA(BfH%m43<@W8f-|ss=3X`>1Y(?ixJR|2&`>hY`EU838rq&3Ch#1WdflH3e?Q=+A*Cll zW3t?>DF5k&sE-WlqJrz#d`%C6LL9#>a*Dfs!T1%+t~gg#K0}x%HrA|-sa^gaM?NJ% zC@~f@n4D@3t9`70LE_T8@@hLD5m>il|IZz`lITymqaeDZ1YB3xodRy|f&jI80MBQT zo5|*9vllx>aG-UIv*O_Ajl^RE`OTX(Chz*{UcHJK7vJ@2Jo=vZACsDWU@^-4i8vi3 zFbe}VV_HX_KlZtTnEaO?<;(fibxY!6Yms}zR|>1ge@{If0!4$q@r9T^Wj%f;TPlF7<2^NbL%0?N8K%Z`8P`2A1ZYj1zqPCzTqdxp|HAJ4x`_bFHm!m-JWW3nL(CiPjbog-Y3>L=3RH%6kz6!dsghW z{TPCMq_eQ?Z_rfC1VyWd3MW!D&WmRkffxZ4lKc+Vd`-tg(q|Gu!X?SK0Rv|fQ=-5u_{yENgz+1`1B zM!IBkgkbia#Oh#}L)#ZJsgcq`fntZLqNY}&L}IaxcSv6zoMIAy=}m@QPAbz}mCvZf z_|~f*!~#c-54S(hx}ew2bZ+Dz=w^rq++Ya_OFd;SdS8ShTo)VrMarp7P+JwdWT5T! zx!CZq&yfPlO%an-ZnqACwBAnH51*f{KS!Xj_DtV-n-Y)!xgj{&M~@d5CD#-nzZ%jb z-=KwZ4QLKOqo4RM%Tz7^{DjjPe!MZ?Xtx+&=c9+UadTi!FCMwRckh#n&{RVPBb9d* zWaf?Z4_|XX_KeJ<=#uO`nN!ue4<%S>1_{ngUAK=ag+0w>UhB^i7+kOnzjfSaefoW2 zDZX_8mxTp-O;Eue^hX2QTIBlj{gH~5A?c*yBFRoOuunysofz+C)*Pbu%B5QRUuLey zsSZu+k)#$nkVtXj6Hml+X0(&Pa+Pz7(JTSR@oFV6EzcdB_`a*j@oP;a`f)ml0eF_m zYMI{_-n8w6hNJAfb#42PpW{a~PN`SD?T>AXg)HPc_1?_QkV^lk#PKlCSW(W)ts0vP zi1_eco0b0#y)KFcN>^LQfW-AusD?`5K^tuc75fs)hjXo!J*jsp34kKJQxq`+qW*R$+JpQ75R0bK~n z#s`Yz`9S3qSHQDDJob(6d(iv&*lg)P=bZ5_5embP)R#9wzRD%F+Ql5IMYa$hL{>}j z{E0B{j{>mmqS33>kCD@M#;D?y=KDZlz*=5FjDA6M|b{ zfZ*1~Id((b=4Y?vp7hGOl6}qmWq?+XK~X89!6U z;KgD1_bV9TiBX@gQ~C4GX#0bl^&Q3QaKE1`64>ZUHXIwiMScv8(1k-(IYcv(8FcJ0 zHTdkLJ;|K#>>#UBn+5+lo|XO(gk5#~3D8A~Bc_^9&{`~{RQ(7NF~?1`XKEtj+2}Xz z55x+76!W94i>}Yl&y{#8H7s7V#RmmF&RNVM@iRvZhx#B=%9PXeZ(p<+^2g*@z4i%P z^Wcd4T?Jk5;p*?qWzF-rinxRPrXsrhVlM{V2~X}R<5qI3V1zc) zu|>V1&$2n&-(?(9UBd&O02!OaMuu+#tzW! zm`}S?$Z|z#lck~XYu&Jf&`$e9qyJeY2b44Ep?&6|s?8<)wTG_-fpaaAwEu_2@%(Ka zf8#yStMl&m$hf0TOy-JghN0^v&12Ad+6PWTkq;;5!X?H<>Fva8*hqq6Kcy<2uW5y})almhHtjB|VcjP@(U znfsM1>GXg*v5v0hMV-Zq7|Pj@#lV_dy&2UAFc7;)(moxP1(x~7ORQ6I$##wMD})cx z8*yQ;6hNeU%)WmW5i39uxu(@{aMo34-JCJ{z>Vjqf-RaFl)rYRm=874`x7ivUmv-k zU2k$I_Mwa^KE~NQgTX6<)(@ahLHUM8ONZ&qbFI%yJ$lg46#f>HR-2}yL|OPtN&Xu? z-yA=8|a~isFIUhFnX>9B&ip=i<8XTc@`*M z?){zWRB-<_{&Hy4`};!Xq_f>@5>&mSA`AXf0*DF|Xv?8`e+$dNK{&-*VC*&Qq*cKz z2RnbwD(zRv5gh91{fLjS&d)Tvov*S8!pJIn-fE48`}%cg=lB^Q)`HAv6Dbx_87Y}! zYQLTEWgnw*8ORAT?NU{170M6qmLA z;gosKB0pilRO0W87Fiw2O`h@SiaN_FqEMN1;Qlk@e7a19azp^dBX|(;mULvA^ECsC z2=gL+G5$1P>=I7_mJf?qe)XT`SMA6h%B1h4_fCWuz!#KK=Vg==Y{o}@xrUk$PVLb* zHUUf4uFQolu#p%NxoV5P_1P1WQFk4)6ii?biAXZx2_fOLq zxJXRVOn>DQmGasWodHZ3TSmCVdHZVudi;ZoG8g91bk=-Y z6BGRYgQvBQ8eX@k=_1E^VA_M5h26rBkmbreb>C?7OtUC^^y|Z3_3)XGBfY`ibsSojO)Ru`k}2KfA*D0G^P~w22NLy<@0%M&acA zO=16TIPJInQ%m+rF7tEHATAFPEshOK+Pr_DA_W184F4~(s|>SLw#LZSon&UB*M^F9 zCw<`y?cG!El7PC>wcqh{xmq?&fWOC|=$voN7;cz4y6kXwI7v9YFDlR;SE-7^}XlM#gZl83Wj!m2itY8{aD_u%er2y?;Zrnhz1Of3iDq+trN)Vl2?s9EZJTwU5M8i-`2>m z_=MfXPLeyHgqg#xvcc<*ahvtM-n_YQJ(KwjnVImBZMv^-6@ok{+)k+`Vx7!|ryp^6 z+MQfxip>toRuUaJ(`O>MVSpW5ta`SrkqdJyXJ-%YHQaaaBS(az!asdIZEZ8Z3O4&* zF{cP)n<02Odbl{l@#3~-{d9!#t}!vf@^L3?=9zdsi2YZu>)7N}L2C2+>F|MyFfqTQ zvJ^s!H>s$wPpFCC5a4I0`ppujUb<$4t{YGK{B2iXRTY6a#*de{t%eJ>a$V$MY_W5J z+O~qiu$);bVxq1spjRuBSD7pZmY?L_7&)p-h0s`;(a%EoqQ^)W_d-Mibwd;0qG%eE z0w#NSJdYpZ9T{mDN2zag#%`T?K4_$&8@j6LC_Rsrj<=El84X69w)y4-aq+?#ITP?I zl?E0k89VS5{UGos1CtIRk29uSPn?HpJ)Z#=O$|hgo@c8x0M%36o#?;)F@Y zwaYeff0FxSz&Oi5JPh6!>O~+cRc!%tM$V)I1B6#HY2Oty%Uhgt2LAaITSxCGnlX}zBj7#$vT6r?%1_HvE622E+d~@;{Uqij6FRo&;MN&Hhy{8IYx*#eVlJI=v16tv<^=wi_K2sb)#o@Tf8NPQlnR%i3mcZ`@j~@CdT)~68!@Wv)m?S zH@!P5WZZ>CwJztJsnQ&KnSO2>^1guOew``J;n<=9(ZcJIh@S5P)^L0j#U5@zSWE z&!c^*K-q?atD$#V6SRiMDmc!iFlQ@0#NKt?N^aQ|>ho_0=%B^iEsg1K3=w;bFC-dl>x8Vak z;_;o*4Z}Az1sS-XoVhEh4u|1=emMISjhBzuGJMJn&cC8_!Xk;T4;dEkzEObPr9C&8 zF`P&;6n#1WXA*D#5%)(fD_$*UC-9z@cDMdl3~*bX_AL_(Pr8{8K3aX*&imQJSAQkR z_1M48AOU3d)SCOiCkqR1EYP0M5~rbdnc=qA2TgM%2gBXsFhmW)$Qs=3H@@O_ow8iPLtkpoDeRvO zRHLk!=`NNZGG~6rPFDluP;~Lx#I{PrYM^?mAQJH`jX)6Yr}{cBCA3E>lf^NxMsPL5 zigK17TE<=*c9PbtbAmgWFgdI-4b_!rJ-yX(=ilM^K9lT`SK>5x)M3DKU_00n93O@+r^&{XPS9BQTl>D(3rK*yafU7q_b8^zzp2zxOaPm6bZs`2g>h##$KO% zR6eP<1>E#RW1irFZgDG{kmfhOEMS=N%5CL$mD}~W0w(5ZQVkea6VQRWqPdcr#uA)G zU;X&b#mJ}nkyDFym%};P{{A}qb|nMLhvU)dCHj^DPZk-~u!-^nzGz^rr!=wGaxFfE zdj16S==|U=7$Qx48@TH0s1KJ~kljH5Y6_SB3@v6)-EFr`-g^Cu!tWvi$1)4crxCu? zc`GjWk(D>Hf|noudX_C`x^c;dT!7?5b@aQF;|0$46JDm2;aJdhIiI`Cv) z!#m9dbhIpPb|wJZI*kNyOXZq z`Z@xdS4GMokk5ySLksDQ&5K96@71;{YQXEPK4<$DB8b9dE(_ z%BHARJWe?~9q>8!Jx-f7#=040B2X{9qBhK%ISEFV%W)Jbt!kn`j7YW0W`tJ>O@BVp zbnTxtWa=8YHGJ$cn?YI|)ozgaeG_tTtlTik_N9I~VE&1(L{)D7LG5#xf_kcA!z%DE z=@&Y!4|iWKqft>T#)+D}4RD{=5J`?+hV}5)ejMZwub{xqN`L(o1*&=+`f;EbF!Cto z-*Qs1wmKV7t4k_`w;8w2Ufx%FU-XggJ{^&=UpcW_sUN)^`_Ip|+flqqMk6_d}$ya&tPgrF*TLNAh`r4HR5_I~-0NpXrgwB*$~g z@6XI}bVVrcG2}MX`z)As=tFDLj81=5k9>iAp4Vsgl5&E4ZhTs187?)1>O~@hc;99Ib>b!Z_oN82=o3d++Z{ zU_g5RJbI&V1YdeEKpz{fEXf_Ep?R{bh%j z?uiZd`35o&f}t5c@H0 zgr~fOY84ifjLHt$d3amW`~DV>Nw8z`M%$$evGV7Fzabn z>~lhRUL|h}B2dC_#rBaMcu%%E(f0HsKf@`CiKbhzt4jW$#kYWYRM%{em2HfgVMdxh zxV6!Ft@B09wx9{1-yKM zMT$K`-v1sDdFp|4{5iJ*|1Rw+3gQAqf4arl|zb z?TJ-p04biJa`{_a*`~}4{jV1AAFTI0el))iZV2^T4sXz zLZxGjpC1O!SYQ6IP!~SqK3DQ|KfkTc9-TveXR|lXPX*!W=x`iNd?*Odu^Es_K(G+eyq_vEUCrPw|M zB0OSQmhHw><~zzl@xP?NO*%XatzWLAnym7n=hMP1-D!#OJ@?d_94bA=%sO3_FJ~L3 zD79)_ecrTvPn1hMhmq%qGvzK&BT^HYu%YCRDwfPQ?ias*wv+4*ATfh*Rnxp$P zr*!G-Zh&x=FKUgD+pE`v}=Bb8e(#@X0 zJ8SJQ)wClZ<3l88MZZ_q_@A&GFPev1b`L57g-(j?OG!~MszOj1G{Qn#$nSeuCi;H0 zHisTArPbcr^bX1tf1GL2_!o!+6vzirARZ{HV?=s&DQ7y3_ILp95q*3lvg>eP0K^(o zSOIS^j}!*irh^r0!iMHKQJ!mSjK#SR5bb9u)VD@;W#O;*^yzicx_sfdSy7JmbSVlr z6ksv%QYt}S_~Qx1F7CA`A;C)tQoS#7-Qm4abCJ7N#RA_9DmF2ER@$&G9SP|l(HOr* zV9v5!CQ!I^hLr$kbB|_^u6orgSvC|8oz-NfDx4n%D(|3jz9<+bNpl5msE8aUQs$Q= zdbmroYhcBK{vg$|F1n!=Ho`_wiBf?SRC>&S4t8Pm{k}_WN7WYSJ2Y!uqB9Or`SQ@7 zU-6s#XX+ICjzwQb2x~5>ZnUAt+5W`9TKNaFB#&~c{GTMI_7bi9+brKIZv9k?qiH!| zIBe@zuUj`&}6{Pn;_L%Vt@hCdiT+axL5V4S{Gr$9%IF8 ztfhCwR+CNhgODzu)H@QkZz}e73)&oz{|&Pf^x@%7CD3G8@?7Pky9}Eb^XYiOLqWgt zEw5DtDF!HJu_k1LMbX$fBE7zpvpnZxWM-d+bwGY(#T(~zh2;+=wL6yq^1B`6dlQnL zrx^u_lh@DR!)z1KD-KtQ4%(8)*q`ku)a(#J;}^qFXennJR~i*OJhf#1ZgC@o6F$G@ zP9=@bqnRy3LCZrg;XFKv{^KsIOU?Ohom;qrP12?F&RRBe8rrvY(+3&E;IIjLIrWSw zbe|us=Szc6mJDxR6p_Pw;Nm>OpRh{BHbXbj8;=W_B~Bh)(zNddy284iXnWIMBbdOR zaWC>=wC$YOz2CWbF+Q^Tu0pF_v-aT}t8Sqa88`;y{oii{&?oJgzgE~I5n8AD2qRGG zjYdii6MZiby|n(Ezxfu%icrln?Or~ilSg)pxjW^D92(&#&%3v~%^Kf6@tmNk3|(?4 zty!!LY8O=WOMr>mB~eslz&^i4B>R1QFcwTBPrk_y62b^`y=a>pg(t%LQx+E)=x(*g zW@Mt-S=p5Y=CNF$A3;QKknm79l1fSzm4ydkMdM2enN zyod_(2OJhk;`Kf3{gMf@LZdD>Y;R4Bp5PX(I;(mvWK?R9B$7>znp#GSQQk^S28r;t zyeEn%&B1>a+|rw?B!X455dItU!3`bd)u;Xeei%Kx7uV^`-{5wIsXNK+<<>Ep4GH34HO9~>UHd{ zx@ZQ}GW6ImiG@gJEicXKRxjoc2>ondR=HGeSBLgWq*Pf?Z*wWp0jIk`xRVZ!TgcvQ9>HhGWUf}mx@fS0TJW{L%wBg zwo6@2mZdcgEbLng@VyuirL#II476XBObfT+!l5B zoHn}gILZ19`J-c#%9|Dfn=MGByg+0WSt~5D^TY2i57(;ATE9qh&VFF6usjrSx>Ra4 zJVc25);nn}?aq*hs-)rAu&(en-?kk`u!yl_2SK40g+Dw&2udUSkL!YB5w9n#Z?hOg~*Ydi_gkIyd*WkrH;AARql6j$J|j*0-Sl*D4g%uG2O7t z8lN6Xhihkz#AiO&FAeIpF)J;-sM$|(rnj(-cIc0-#Og}xi7#c&{lfFbI4~-_syB`W ziKwJ4O+ptCuWqUM$(^#71E9fc)VqGz6lqW18Z$HQI&DhZrHQkN2akiTH;r$0C%_zI zk$yGT4bLT5Zs{W%>el)*j_RK>N$gsrF7#_8Kn5mWgn>tQ(P2@W*37ZXTj-E^tJbPs zrZDg$7Wad<QCJ`hMe1OWPh#$z42dwuX1JXw&%@u(CaW$PrMJL4PbA z$7b!B$?}#Gp8_Z(Z$;3QSkP7q25HWmYdgT>>CLgPYCc9uv$;yKrv{ZIW{^GSbti4y zA7(>Ay2yxJ8gdZd@5$Ux6$uPG(PiZO@Kb>piNJ_Q2LnJoAC2|Rv&7p3k?VtM?`?fo z6#!35;GLPba}v#*%*EtH&bvo>kw=;2zRfN8ERUBVLcD#Z8o@U5q=hY;srs4YUR91m z9+c#tiIFa(4E|{*l#ARQ{#E&w@=%PazwvVid)C|C!JMXJNx!tiiLcB$6wmQZQ5jTE zY6kM1(|hL6=(fqau9rS$MfK>Cc`CNWkR20 z87Z6ZVZKs(ek!DfZeHUb4k@~?QLw{%&^GK0RS7W(CL|af(B<9O*X8sb`eQWTyX&~b zpJL*MHEC04P_LU?5L#oQnO63BiZ{|9mbgV>^Yl^~nfAU&rG+p(;@aWScx?BeadEoZ zImF_1c^~cjaGRWzud6lNr-^8HbS)a4e_)+FYE~4bK2a)Vtg?BzQ(rO$?Q=zDS-VWk zx+{Is7Ey9e830eEgO104FB7hPOpDddG>?AD!9531v|yk<(~glmwS&peg^PT;Mta2^ z8EYJIFXOyRMbQ%IBu}UFIh@0!>Z?g{hu=eK?ZZ24>K&|chL-sdn01vjkG8u6wxT7H z`}nx`=~6Lc_MAFUj@pIA=Z%xc-aUy;>U?^D76v)`KPl#M(qIhTz_n5Z9qs9ma8>lD zzVHxE*s#AbX5QvW70^KUiL!nf9EOs%O!3J7k(@uDey8JvGqKV_weZ;rs2xm z&TXE5`8?SyDU(PCv#uO{K3Guc#IYLfpJ>D*iZSOQThW;yt5Vr|uDR#ckeI~K;n;!&41s$SqhTwJm=uAwNU`uN^`jZD7D984VI~xlDNYDhF-< zReBrNGt+HKfl7R**1z>F7iq8BSw}41?KlQCBiwO;f6)k!?Ih7Q`=!%16I>t4kQ~}@ z_~TnQ>+0jIqrh4BbEoYopK46ee}HG&DPa^?_ok~;^n45u2X-1bb)Ws*Dx9MRQ2HHV z#jh`Dqz}ZT7rmn)s_#(BaaxUQ!{d;3dt1MDjSX(xyIs72aSL3-KWWSJIqKfJhQ-?w zkp_X2m%hh_H_9qFaU>!sWs9~TC|5XNXZ&f1*1758Uaau2JGqKf$l09rrGIEf?Pi2K z2q6sAch&5l)t2*=us+wo-ym;cDGu-vJUNXTw#AUt$w8L2YePT;byk15vo&w;k=^D~ z3Tb2HMFcPSZU$rDaFA@9&~GFHuuAEWl_WMTnd1tBATga!i? z300jCM!TGS&#y%r)BVl7&m3NK(P=`70ykJEx)#CFuJ^J(LX|yPl+xp2dX)^}4 z{O$+-dZt-eJ(G5$UoBrah3=o}!#gO3b{#vVys*Or5-MHV4rqJ+C=na`*Up7i+)%vu z?5cmYnsHRPa0+&}6_GE{E4-B0mz-_^CwMY>uz&3XP)>$1W7jtKmec#nSnp2E}C`xKk51_@-qMVY~=wVfj zFJ@#ph;dO_Z>_j`(m}Q*RTd4-#D2_AbpbzYp(%Z1Us%&Bq&b3%r7se~G!s5fdeVJ} zDOc*OG9rwVn8W#L-nLnIF173WE&4D?^`_NR-S=1B%i*rNwfl@C-l(^tlWELlq%6o_ z^jiY0_+EQ221jl(xslAnTi?LD-)Gxr%K0Y}3us;li(GuX+cO(HxBISO#X0c%ueWF6 z^(#LsU$$0aW`2asj;-3;wB~7YiPN-X)&uV6l?1wCnKuc3RJ^n>e{8VER&JEnyqSmW z--v;x^GM~Zv@f%~HfHzoHO`ux{O;5F)GiavUa%a~GRU92FOQdY)7lj$Kf~(je$lEt zdKD1+`lc1KSz+IH6;Sfv!X8>U$-L{ToD;_J5oR8WMfZhQ<;LUBumtAr%$uhUWPxGE zun<@T=4-Y0zfZkhTjd|`B<9m^myfHy+MauL2sf*yq<+Ud-&OD`KH(on= zpfF7}?;%6VXu->{LUS=2+z^j32TR1U$WOK_tAPH)=u_|Z<9aq_Omf4PPul&2^-)uZ zhrtvsBof`n$IC>oWo+vG7)R7-eYu%YV7?OZ1mnwM`eVBJWCzctidFl(l};Hx_LGWp z+x>vqaxOb(1d1opxayDW8}sDp8b#~wjw)PRqS)k9lSws3ZB7HJ-u55cR ztMJi-ns!=>{i0u9)JL>F`}k#Y|^%j?UT5 z_5*ySH|kw7KRWfrthd^c+j)TFC&9#i$LJS3?S$rY@7t?Vn0KZmsI4X)OXsF%3M0JR zCYwly(AV1Kx<4*)TokQ%(nB$tc*Dafax$(#m$gvWu*I1#gj;!`ega>W7tpxt@ zKb7guF~JfbRQwo>eO&LC3@9V5m3Z6N!+UY(3+&)yDe7is#V_(i)Jbej-V~;Uy)xQs z19GeA-(vlA(UTWPcclufs@7{+pw$#+iUd z+o|UJfp{R^0t^d%Cf-P8HW zVW%;a>I*C2#upzTI?8OLX%CgA;62?qSV+*y^%^NZ+xd(?nv<@<&ryK zb*_vejgLZ;R9aNL(r{@(*Dd9Iav>JHv{~$5DTzQz4kCB;6(W4&Tms+`T@N{_>A&gy zdyDq*r%>2-GpH<(uibuKgLsC^!c*v_pm5|dcQsf`e0Qrp^hs0;Y(CR}0;uvBY3)vTYXy4-^jgrOOqH~V0(EvAz$+xdLGxgG$HgXAh-lf_cY z{n#~s9poACHfE0ChgqbS5ssx=s#js-RHa}sSq!aM|0_JvHPW+O@M@;P{0QXP?0TjvzTg`Gc?{~ zNf$@D`!2-;Ac-c`{dCxR11hk-fME1{-(7cRNv{z9lmlu%Cm$p%-$M*TJb=eik^?|= z091;YjbNw*q^oWfp0L;lj1ost`&##NvJ|F%BGPM^WK`2Mk8VKnCmEu>|Hly7@Oja&-%5ZG z91y|}GI4%RomD1vOEe1PHY`bY(r|MVV1IQ)} zJJ(HIDK!y-6`YJl7VYwd5!9ndcwopuznBAh$PqE`4nF{-U#i9YASL)AEkh*w#j~DZWq$0xC#V%jofPicULQx8!nmD+f$c`{auUs|3>#V&F zg=jH8z}svOL{I@8`Vb^_GLvRq zIDIGRv$0^P8T7ioRhYheG_e>KE>}>199+x>_vY*ARjWov&uXW{Gny0~Iw)I0C?X5? z!lhJ|JQ>Jw=3URs_v@Ho_|SMVKNe&uc?c3b%tr)e;4MnAqa0W!c>(28Ew-Qy#W)0z>4Um8J`jp$@rBk6SSw>pf|7xYh_g(=|5Q8-?*2@Nv}~SX2F-A_5WCYC4#n+9esqxiHyBv~ z5UhX?2Y6O`JXxAOHPZ7fqtb8wn%Ks9;WQZe{bJZC5HZK!Ye29Wl39RWA_V9qd{z@; z8$gNna-~`l%or;QLIkNl`>_4Jr@(vPuqv;1!^bPW;nJf_<+05ARrUo(0+p8@w-eWZ zDA4fJhS`V5*4zKCpm~ z6wsSRj@)6d|3yj%A}x}Epx^)A6hU4RcM(#v9X{@xS>^AV94CTr@1 z5&vH#XCRVkBxoMxUlS>SCMvfOiv5c;AqPY{K?6PfYhn>O&_t=mZqt8}7XL>2e-Hcr gM)v=A8sB=;Qu!T+x*7v@5)epIR8FKqNYC&81MmA$w*UYD diff --git a/src/legacy/core_plugins/vis_type_vega/public/data_model/__tests__/vega_parser.js b/src/legacy/core_plugins/vis_type_vega/public/data_model/__tests__/vega_parser.js index de2d9132214511..c442f8f17884af 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/data_model/__tests__/vega_parser.js +++ b/src/legacy/core_plugins/vis_type_vega/public/data_model/__tests__/vega_parser.js @@ -53,7 +53,7 @@ describe(`VegaParser._setDefaultColors`, () => { test({}, true, { config: { range: { category: { scheme: 'elastic' } }, - mark: { color: '#00B3A4' }, + mark: { color: '#5BBAA0' }, }, }) ); @@ -63,15 +63,15 @@ describe(`VegaParser._setDefaultColors`, () => { test({}, false, { config: { range: { category: { scheme: 'elastic' } }, - arc: { fill: '#00B3A4' }, - area: { fill: '#00B3A4' }, - line: { stroke: '#00B3A4' }, - path: { stroke: '#00B3A4' }, - rect: { fill: '#00B3A4' }, - rule: { stroke: '#00B3A4' }, - shape: { stroke: '#00B3A4' }, - symbol: { fill: '#00B3A4' }, - trail: { fill: '#00B3A4' }, + arc: { fill: '#5BBAA0' }, + area: { fill: '#5BBAA0' }, + line: { stroke: '#5BBAA0' }, + path: { stroke: '#5BBAA0' }, + rect: { fill: '#5BBAA0' }, + rule: { stroke: '#5BBAA0' }, + shape: { stroke: '#5BBAA0' }, + symbol: { fill: '#5BBAA0' }, + trail: { fill: '#5BBAA0' }, }, }) ); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap index 463c1bfb975f57..1f77660c9784cb 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap @@ -44,10 +44,7 @@ exports[`BytesFormatEditor should render normally 1`] = ` labelType="label" > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.js.snap index d7026df761d94a..7e49e93e4cc4fb 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.js.snap @@ -75,6 +75,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.js.snap index 04d59640554fdb..cb570144fcee3f 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.js.snap @@ -44,11 +44,8 @@ exports[`DateFormatEditor should render normally 1`] = ` labelType="label" > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.js.snap index 9722a019864348..ef11d70926ad75 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.js.snap @@ -20,11 +20,7 @@ exports[`DurationFormatEditor should render human readable output normally 1`] = labelType="label" > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap index fea665a918f064..30d1de270522ed 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap @@ -44,10 +44,7 @@ exports[`PercentFormatEditor should render normally 1`] = ` labelType="label" > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.js.snap index 2891b99bba30cd..2bfb0bbd15013b 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.js.snap @@ -59,6 +59,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn "maxWidth": "400px", } } + tableLayout="fixed" /> diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.js.snap index 4b246fecb81463..c727f54874db4d 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.js.snap @@ -26,11 +26,7 @@ exports[`UrlFormatEditor should render label template help 1`] = ` labelType="label" > @@ -116,10 +109,7 @@ exports[`UrlFormatEditor should render label template help 1`] = ` labelType="label" > @@ -157,11 +147,7 @@ exports[`UrlFormatEditor should render normally 1`] = ` labelType="label" > @@ -247,10 +230,7 @@ exports[`UrlFormatEditor should render normally 1`] = ` labelType="label" > @@ -288,11 +268,7 @@ exports[`UrlFormatEditor should render url template help 1`] = ` labelType="label" > @@ -378,10 +351,7 @@ exports[`UrlFormatEditor should render url template help 1`] = ` labelType="label" > @@ -419,11 +389,7 @@ exports[`UrlFormatEditor should render width and height fields if image 1`] = ` labelType="label" > @@ -510,10 +473,7 @@ exports[`UrlFormatEditor should render width and height fields if image 1`] = ` labelType="label" > diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.js.snap index 39189caeedb320..849e307f7b5274 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.js.snap @@ -110,6 +110,7 @@ exports[`UrlTemplateFlyout should render normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/samples/__snapshots__/samples.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/samples/__snapshots__/samples.test.js.snap index 25cbbb7c8684b4..73a7c1141c6014 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/samples/__snapshots__/samples.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/samples/__snapshots__/samples.test.js.snap @@ -54,6 +54,7 @@ exports[`FormatEditorSamples should render normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> `; diff --git a/src/legacy/ui/public/field_editor/field_editor.test.js b/src/legacy/ui/public/field_editor/field_editor.test.js index f811ad91627287..cf61b6140f42cc 100644 --- a/src/legacy/ui/public/field_editor/field_editor.test.js +++ b/src/legacy/ui/public/field_editor/field_editor.test.js @@ -50,9 +50,7 @@ jest.mock('@elastic/eui', () => ({ EuiText: 'eui-text', EuiTextArea: 'eui-textArea', htmlIdGenerator: () => 42, - palettes: { - euiPaletteColorBlind: { colors: ['red'] }, - }, + euiPaletteColorBlind: () => ['red'], })); jest.mock('ui/scripting_languages', () => ({ diff --git a/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/metric_agg.test.tsx.snap b/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/metric_agg.test.tsx.snap index a176295260c441..b51c25952580a8 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/metric_agg.test.tsx.snap +++ b/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/metric_agg.test.tsx.snap @@ -16,9 +16,7 @@ exports[`MetricAggParamEditor should be rendered with default set of props 1`] = compressed={true} data-test-subj="visEditorSubAggMetric1" fullWidth={true} - hasNoInitialSelection={false} isInvalid={false} - isLoading={false} onChange={[Function]} options={ Array [ diff --git a/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/top_aggregate.test.tsx.snap b/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/top_aggregate.test.tsx.snap index 74c952dbf059f2..b3a2c058de9760 100644 --- a/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/top_aggregate.test.tsx.snap +++ b/src/legacy/ui/public/vis/editors/default/controls/__snapshots__/top_aggregate.test.tsx.snap @@ -31,9 +31,7 @@ exports[`TopAggregateParamEditor should init with the default set of props 1`] = data-test-subj="visDefaultEditorAggregateWith" disabled={false} fullWidth={true} - hasNoInitialSelection={false} isInvalid={false} - isLoading={false} onBlur={[MockFunction]} onChange={[Function]} options={ diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js b/src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js index b9f50b6d941e65..b882048a6b2699 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js +++ b/src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js @@ -18,14 +18,14 @@ */ import _ from 'lodash'; -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; const thresholdLineDefaults = { show: false, value: 10, width: 1, style: 'full', - color: palettes.euiPaletteColorBlind.colors[9], + color: euiPaletteColorBlind()[9], }; export class PointSeries { diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap index 15e74e98920e29..4c8edd85eb559a 100644 --- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap +++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap @@ -1076,11 +1076,9 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA aria-label="Start typing to search and filter the test page" autoComplete="off" autoFocus={false} - compressed={false} data-test-subj="queryInput" fullWidth={true} inputRef={[Function]} - isLoading={false} onChange={[Function]} onClick={[Function]} onKeyDown={[Function]} @@ -1098,9 +1096,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA onSelectLanguage={[Function]} /> } - compressed={false} fullWidth={true} - isLoading={false} >
- +
} - compressed={false} fullWidth={true} - isLoading={false} >
- +
} - compressed={false} fullWidth={true} - isLoading={false} >
- +
; intl: InjectedIntl; isLoading?: boolean; - prepend?: React.ReactNode; + prepend?: React.ComponentProps['prepend']; showQueryInput?: boolean; showDatePicker?: boolean; dateRangeFrom?: string; diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index 16b22a164f2f02..960a843f98ab90 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -58,7 +58,7 @@ interface Props { query: Query; disableAutoFocus?: boolean; screenTitle?: string; - prepend?: React.ReactNode; + prepend?: React.ComponentProps['prepend']; persistedLog?: PersistedLog; bubbleSubmitEvent?: boolean; placeholder?: string; diff --git a/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap b/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap index 5abce10c5be61b..fb56bf0e4255ee 100644 --- a/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap +++ b/src/plugins/kibana_react/public/field_icon/__snapshots__/field_icon.test.tsx.snap @@ -11,7 +11,7 @@ exports[`FieldIcon renders a blackwhite icon for a string 1`] = ` exports[`FieldIcon renders a colored icon for a number 1`] = ` @@ -20,7 +20,7 @@ exports[`FieldIcon renders a colored icon for a number 1`] = ` exports[`FieldIcon renders an icon for an unknown type 1`] = ` @@ -30,7 +30,7 @@ exports[`FieldIcon renders with className if provided 1`] = ` diff --git a/src/plugins/kibana_react/public/field_icon/field_icon.tsx b/src/plugins/kibana_react/public/field_icon/field_icon.tsx index f9bdf3a25adaa0..0c5d2b0c24831c 100644 --- a/src/plugins/kibana_react/public/field_icon/field_icon.tsx +++ b/src/plugins/kibana_react/public/field_icon/field_icon.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { palettes, EuiIcon } from '@elastic/eui'; +import { euiPaletteColorBlind, EuiIcon } from '@elastic/eui'; import { IconSize } from '@elastic/eui/src/components/icon/icon'; interface IconMapEntry { @@ -43,7 +43,7 @@ interface FieldIconProps { className?: string; } -const { colors } = palettes.euiPaletteColorBlind; +const colors = euiPaletteColorBlind(); // defaultIcon => a unknown datatype const defaultIcon = { icon: 'questionInCircle', color: colors[0] }; diff --git a/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap b/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap index 978705a3ad0969..18f84f41d5d998 100644 --- a/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap +++ b/src/plugins/kibana_react/public/saved_objects/__snapshots__/saved_object_save_modal.test.tsx.snap @@ -43,11 +43,9 @@ exports[`SavedObjectSaveModal should render matching snapshot 1`] = ` > diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts index 138e5758ede7cd..0f14489a39dbce 100644 --- a/test/functional/page_objects/visualize_chart_page.ts +++ b/test/functional/page_objects/visualize_chart_page.ts @@ -204,8 +204,7 @@ export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrPr public async filterLegend(name: string) { await this.toggleLegend(); await testSubjects.click(`legend-${name}`); - const filters = await testSubjects.find(`legend-${name}-filters`); - const [filterIn] = await filters.findAllByCssSelector(`input`); + const filterIn = await testSubjects.find(`legend-${name}-filterIn`); await filterIn.click(); await this.waitForVisualizationRenderingStabilized(); } diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts index 7e512975356f3b..30e13d551fa28d 100644 --- a/test/functional/page_objects/visualize_editor_page.ts +++ b/test/functional/page_objects/visualize_editor_page.ts @@ -97,8 +97,9 @@ export function VisualizeEditorPageProvider({ getService, getPageObjects }: FtrP } public async clickSplitDirection(direction: string) { - const control = await testSubjects.find('visEditorSplitBy'); - const radioBtn = await control.findByCssSelector(`[title="${direction}"]`); + const radioBtn = await find.byCssSelector( + `[data-test-subj="visEditorSplitBy"][title="${direction}"]` + ); await radioBtn.click(); } diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index 02c507dbb3ed8e..1eac93c8538e40 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "react": "^16.12.0", "react-dom": "^16.12.0" } diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 67ad28c083dbcf..1bfb1e8ba4bcad 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "react": "^16.12.0" } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index b22a1ff2d4176b..6d6b04fba889c3 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "react": "^16.12.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json index 8c91826d7b450c..964adacb2ac09e 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "react": "^16.12.0" }, "scripts": { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap index ece396bc4cfc4f..c95855c117047c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap @@ -808,8 +808,8 @@ Object { }, }, "serviceColors": Object { - "opbeans-node": "#3185fc", - "opbeans-ruby": "#00b3a4", + "opbeans-node": "#6092c0", + "opbeans-ruby": "#5bbaa0", }, } `; @@ -1212,7 +1212,7 @@ Object { }, }, "serviceColors": Object { - "opbeans-ruby": "#3185fc", + "opbeans-ruby": "#6092c0", }, } `; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap index 59679bfe116418..655fc5a25b9ef5 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap +++ b/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap @@ -52,6 +52,7 @@ exports[`ManagedTable component should render a page-full of items, with default }, } } + tableLayout="fixed" /> `; @@ -99,5 +100,6 @@ exports[`ManagedTable component should render when specifying initial values 1`] }, } } + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx index 25f8128b272112..a8e6bc0a648af5 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx @@ -20,7 +20,7 @@ export const SelectWithPlaceholder: typeof EuiSelect = props => ( {...props} options={[ { text: props.placeholder, value: NO_SELECTION }, - ...props.options + ...(props.options || []) ]} value={isEmpty(props.value) ? NO_SELECTION : props.value} onChange={e => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap index 557751a0f02268..1bf125c3016448 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap @@ -3,7 +3,7 @@ exports[`when response has data Initially should have 3 legends 1`] = ` Array [ Object { - "color": "#3185fc", + "color": "#6092c0", "disabled": undefined, "onClick": [Function], "text": @@ -14,7 +14,7 @@ Array [ , }, Object { - "color": "#e6c220", + "color": "#fae181", "disabled": undefined, "onClick": [Function], "text": @@ -22,7 +22,7 @@ Array [ , }, Object { - "color": "#f98510", + "color": "#f19f58", "disabled": undefined, "onClick": [Function], "text": @@ -442,7 +442,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -463,9 +463,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -480,9 +480,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -497,9 +497,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -514,9 +514,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -531,9 +531,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -548,9 +548,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -565,9 +565,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -582,9 +582,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -599,9 +599,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -616,9 +616,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -633,9 +633,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -650,9 +650,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -667,9 +667,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -684,9 +684,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -701,9 +701,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -718,9 +718,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -735,9 +735,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -752,9 +752,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -769,9 +769,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -786,9 +786,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -803,9 +803,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -820,9 +820,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -837,9 +837,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -854,9 +854,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -871,9 +871,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -888,9 +888,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -905,9 +905,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -922,9 +922,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -939,9 +939,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -956,9 +956,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -973,9 +973,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -995,7 +995,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -1016,9 +1016,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1033,9 +1033,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1050,9 +1050,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1067,9 +1067,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1084,9 +1084,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1101,9 +1101,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1118,9 +1118,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1135,9 +1135,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1152,9 +1152,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1169,9 +1169,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1186,9 +1186,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1203,9 +1203,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1220,9 +1220,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1237,9 +1237,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1254,9 +1254,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1271,9 +1271,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1288,9 +1288,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1305,9 +1305,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1322,9 +1322,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1339,9 +1339,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1356,9 +1356,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1373,9 +1373,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1390,9 +1390,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1407,9 +1407,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1424,9 +1424,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1441,9 +1441,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1458,9 +1458,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1475,9 +1475,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1492,9 +1492,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1509,9 +1509,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1526,9 +1526,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -1548,7 +1548,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -1569,9 +1569,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1586,9 +1586,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1603,9 +1603,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1620,9 +1620,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1637,9 +1637,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1654,9 +1654,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1671,9 +1671,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1688,9 +1688,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1705,9 +1705,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1722,9 +1722,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1739,9 +1739,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1756,9 +1756,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1773,9 +1773,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1790,9 +1790,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1807,9 +1807,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1824,9 +1824,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1841,9 +1841,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1858,9 +1858,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1875,9 +1875,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1892,9 +1892,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1909,9 +1909,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1926,9 +1926,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1943,9 +1943,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1960,9 +1960,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1977,9 +1977,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -1994,9 +1994,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2011,9 +2011,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2028,9 +2028,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2045,9 +2045,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2062,9 +2062,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2079,9 +2079,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -2651,7 +2651,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #3185fc; + background: #6092c0; border-radius: 100%; } @@ -2659,7 +2659,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #e6c220; + background: #fae181; border-radius: 100%; } @@ -2667,7 +2667,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #f98510; + background: #f19f58; border-radius: 100%; } @@ -2723,14 +2723,14 @@ Array [ onClick={[Function]} > @@ -2764,14 +2764,14 @@ Array [ onClick={[Function]} > @@ -2798,14 +2798,14 @@ Array [ onClick={[Function]} > @@ -2849,17 +2849,17 @@ exports[`when response has data when dragging without releasing should display S exports[`when response has data when setting hoverX should display tooltip 1`] = ` Array [ Object { - "color": "#3185fc", + "color": "#6092c0", "text": "Avg.", "value": 438704.4, }, Object { - "color": "#e6c220", + "color": "#fae181", "text": "95th", "value": 1557383.999999999, }, Object { - "color": "#f98510", + "color": "#f19f58", "text": "99th", "value": 1820377.1200000006, }, @@ -2891,7 +2891,7 @@ Array [ width: 8px; height: 8px; margin-right: 4px; - background: #3185fc; + background: #6092c0; border-radius: 100%; } @@ -2899,7 +2899,7 @@ Array [ width: 8px; height: 8px; margin-right: 4px; - background: #e6c220; + background: #fae181; border-radius: 100%; } @@ -2907,7 +2907,7 @@ Array [ width: 8px; height: 8px; margin-right: 4px; - background: #f98510; + background: #f19f58; border-radius: 100%; } @@ -3378,7 +3378,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -3399,9 +3399,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3416,9 +3416,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3433,9 +3433,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3450,9 +3450,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3467,9 +3467,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3484,9 +3484,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3501,9 +3501,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3518,9 +3518,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3535,9 +3535,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3552,9 +3552,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3569,9 +3569,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3586,9 +3586,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3603,9 +3603,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3620,9 +3620,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3637,9 +3637,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3654,9 +3654,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3671,9 +3671,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3688,9 +3688,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3705,9 +3705,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3722,9 +3722,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3739,9 +3739,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3756,9 +3756,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3773,9 +3773,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3790,9 +3790,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3807,9 +3807,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3824,9 +3824,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3841,9 +3841,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3858,9 +3858,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3875,9 +3875,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3892,9 +3892,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3909,9 +3909,9 @@ Array [ r={0.5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -3931,7 +3931,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -3952,9 +3952,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -3969,9 +3969,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -3986,9 +3986,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4003,9 +4003,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4020,9 +4020,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4037,9 +4037,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4054,9 +4054,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4071,9 +4071,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4088,9 +4088,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4105,9 +4105,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4122,9 +4122,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4139,9 +4139,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4156,9 +4156,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4173,9 +4173,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4190,9 +4190,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4207,9 +4207,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4224,9 +4224,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4241,9 +4241,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4258,9 +4258,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4275,9 +4275,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4292,9 +4292,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4309,9 +4309,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4326,9 +4326,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4343,9 +4343,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4360,9 +4360,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4377,9 +4377,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4394,9 +4394,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4411,9 +4411,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4428,9 +4428,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4445,9 +4445,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4462,9 +4462,9 @@ Array [ r={0.5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -4484,7 +4484,7 @@ Array [ style={ Object { "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeDasharray": undefined, "strokeWidth": undefined, } @@ -4505,9 +4505,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4522,9 +4522,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4539,9 +4539,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4556,9 +4556,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4573,9 +4573,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4590,9 +4590,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4607,9 +4607,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4624,9 +4624,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4641,9 +4641,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4658,9 +4658,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4675,9 +4675,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4692,9 +4692,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4709,9 +4709,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4726,9 +4726,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4743,9 +4743,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4760,9 +4760,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4777,9 +4777,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4794,9 +4794,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4811,9 +4811,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4828,9 +4828,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4845,9 +4845,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4862,9 +4862,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4879,9 +4879,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4896,9 +4896,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4913,9 +4913,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4930,9 +4930,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4947,9 +4947,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4964,9 +4964,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4981,9 +4981,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -4998,9 +4998,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -5015,9 +5015,9 @@ Array [ r={0.5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -5072,9 +5072,9 @@ Array [ r={5} style={ Object { - "fill": "#f98510", + "fill": "#f19f58", "opacity": 1, - "stroke": "#f98510", + "stroke": "#f19f58", "strokeWidth": 1, } } @@ -5089,9 +5089,9 @@ Array [ r={5} style={ Object { - "fill": "#e6c220", + "fill": "#fae181", "opacity": 1, - "stroke": "#e6c220", + "stroke": "#fae181", "strokeWidth": 1, } } @@ -5106,9 +5106,9 @@ Array [ r={5} style={ Object { - "fill": "#3185fc", + "fill": "#6092c0", "opacity": 1, - "stroke": "#3185fc", + "stroke": "#6092c0", "strokeWidth": 1, } } @@ -5158,7 +5158,7 @@ Array [ className="c3" > @@ -5174,14 +5174,14 @@ Array [ fontSize="12px" > @@ -5204,7 +5204,7 @@ Array [ className="c3" > @@ -5220,14 +5220,14 @@ Array [ fontSize="12px" > @@ -5250,7 +5250,7 @@ Array [ className="c3" > @@ -5266,14 +5266,14 @@ Array [ fontSize="12px" > @@ -5830,7 +5830,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #3185fc; + background: #6092c0; border-radius: 100%; } @@ -5838,7 +5838,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #e6c220; + background: #fae181; border-radius: 100%; } @@ -5846,7 +5846,7 @@ Array [ width: 11px; height: 11px; margin-right: 5.5px; - background: #f98510; + background: #f19f58; border-radius: 100%; } @@ -5902,14 +5902,14 @@ Array [ onClick={[Function]} > @@ -5943,14 +5943,14 @@ Array [ onClick={[Function]} > @@ -5977,14 +5977,14 @@ Array [ onClick={[Function]} > diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap b/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap index da71e264ac0995..f1c7d4826fe0c7 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap @@ -434,11 +434,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -453,11 +453,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -472,11 +472,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857142} @@ -491,11 +491,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -510,11 +510,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571405} @@ -529,11 +529,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857142} @@ -548,11 +548,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571405} @@ -567,11 +567,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571405} @@ -586,11 +586,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571377} @@ -605,11 +605,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571462} @@ -624,11 +624,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -643,11 +643,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -662,11 +662,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -681,11 +681,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -700,11 +700,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -719,11 +719,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -738,11 +738,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571547} @@ -757,11 +757,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -776,11 +776,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571547} @@ -795,11 +795,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -814,11 +814,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571433} @@ -833,11 +833,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571377} @@ -852,11 +852,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -871,11 +871,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -890,11 +890,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571604} @@ -909,11 +909,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -928,11 +928,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.62857142857149} @@ -947,11 +947,11 @@ exports[`Histogram Initially should have default markup 1`] = ` onMouseOver={[Function]} style={ Object { - "fill": "#98c2fd", + "fill": "#afc8df", "opacity": 1, "rx": "0px", "ry": "0px", - "stroke": "#98c2fd", + "stroke": "#afc8df", } } width={22.628571428571377} diff --git a/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts b/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts index 2b0263f69db8fa..1218bc726c3b78 100644 --- a/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts +++ b/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts @@ -21,7 +21,7 @@ describe('chartSelectors', () => { it('should return anomalyScoreSeries', () => { const data = [{ x0: 0, x: 10 }]; expect(getAnomalyScoreSeries(data)).toEqual({ - areaColor: 'rgba(146,0,0,0.1)', + areaColor: 'rgba(231,102,76,0.1)', color: 'none', data: [{ x0: 0, x: 10 }], hideLegend: true, @@ -57,7 +57,7 @@ describe('chartSelectors', () => { getResponseTimeSeries({ apmTimeseries, anomalyTimeseries: undefined }) ).toEqual([ { - color: '#3185fc', + color: '#6092c0', data: [ { x: 0, y: 100 }, { x: 1000, y: 200 } @@ -67,7 +67,7 @@ describe('chartSelectors', () => { type: 'linemark' }, { - color: '#e6c220', + color: '#fae181', data: [ { x: 0, y: 200 }, { x: 1000, y: 300 } @@ -77,7 +77,7 @@ describe('chartSelectors', () => { type: 'linemark' }, { - color: '#f98510', + color: '#f19f58', data: [ { x: 0, y: 300 }, { x: 1000, y: 400 } diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts index f49c1e022a0701..870b02fa7ba6de 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/breakdown/index.test.ts @@ -70,13 +70,13 @@ describe('getTransactionBreakdown', () => { expect(response.kpis[0]).toEqual({ name: 'app', - color: '#00b3a4', + color: '#5bbaa0', percentage: 0.5408550899466306 }); expect(response.kpis[3]).toEqual({ name: 'postgresql', - color: '#490092', + color: '#9170b8', percentage: 0.047366859295002 }); }); diff --git a/x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx b/x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx index 0e07c2b4960b72..29cdcfccfc756c 100644 --- a/x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx +++ b/x-pack/legacy/plugins/beats_management/public/components/inputs/input.tsx @@ -3,12 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { EuiFieldText, EuiFieldTextProps, EuiFormRow } from '@elastic/eui'; +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; import { CommonProps } from '@elastic/eui/src/components/common'; import { FormsyInputProps, withFormsy } from 'formsy-react'; import React, { Component, InputHTMLAttributes } from 'react'; -interface ComponentProps extends FormsyInputProps, CommonProps, EuiFieldTextProps { +interface ComponentProps + extends FormsyInputProps, + CommonProps, + Omit, 'onChange' | 'onBlur'> { instantValidation?: boolean; label: string; errorText: string; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx index 429eec19a47fa7..0c099135f631d4 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_icon.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ICON_TYPES, palettes, EuiIcon } from '@elastic/eui'; +import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui'; function stringToNum(s: string) { return Array.from(s).reduce((acc, ch) => acc + ch.charCodeAt(0), 1); @@ -23,7 +23,7 @@ function getIconForDataType(dataType: string) { export function getColorForDataType(type: string) { const iconType = getIconForDataType(type); - const { colors } = palettes.euiPaletteColorBlind; + const colors = euiPaletteColorBlind(); const colorIndex = stringToNum(iconType) % colors.length; return colors[colorIndex]; } diff --git a/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts b/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts index 855818886ab6fa..46fec39bfce06c 100644 --- a/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts +++ b/x-pack/legacy/plugins/graph/public/helpers/style_choices.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore -import { palettes } from '@elastic/eui/lib/services'; +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; export interface FontawesomeIcon { class: string; @@ -255,4 +255,4 @@ urlTemplateIconChoices.forEach(icon => { urlTemplateIconChoicesByClass[icon.class] = icon; }); -export const colorChoices = palettes.euiPaletteColorBlind.colors; +export const colorChoices = euiPaletteColorBlind(); diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts index 1861479f85f189..efef3d246ac98d 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts @@ -161,7 +161,7 @@ describe('deserialize', () => { }, Object { "aggregatable": true, - "color": "#CE0060", + "color": "#D36086", "hopSize": 5, "icon": Object { "class": "fa-folder-open-o", diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.test.tsx index 85c1deb0ea7e1a..e0e33ef03d3d19 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.test.tsx @@ -17,37 +17,37 @@ import { FieldIcon } from './field_icon'; describe('FieldIcon', () => { it('should render icons', () => { expect(shallow()).toMatchInlineSnapshot(` - - `); + + `); expect(shallow()).toMatchInlineSnapshot(` - - `); + + `); expect(shallow()).toMatchInlineSnapshot(` - - `); + + `); expect(shallow()).toMatchInlineSnapshot(` - - `); + + `); expect(shallow()).toMatchInlineSnapshot(` `); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.tsx index f1e8db04860a7d..796f200bffd972 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_icon.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ICON_TYPES, palettes, EuiIcon } from '@elastic/eui'; +import { ICON_TYPES, euiPaletteColorBlind, EuiIcon } from '@elastic/eui'; import classNames from 'classnames'; import { DataType } from '../types'; @@ -24,7 +24,7 @@ function getIconForDataType(dataType: string) { export function getColorForDataType(type: string) { const iconType = getIconForDataType(type); - const { colors } = palettes.euiPaletteColorBlind; + const colors = euiPaletteColorBlind(); const colorIndex = stringToNum(iconType) % colors.length; return colors[colorIndex]; } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.test.tsx index 7441083550706d..a470f5fc51cfb7 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.test.tsx @@ -20,10 +20,10 @@ test('LensFieldIcon renders properly', () => { test('LensFieldIcon getColorForDataType for a valid type', () => { const color = getColorForDataType('date'); - expect(color).toEqual('#B0916F'); + expect(color).toEqual('#F19F58'); }); test('LensFieldIcon getColorForDataType for an invalid type', () => { const color = getColorForDataType('invalid'); - expect(color).toEqual('#1EA593'); + expect(color).toEqual('#5BBAA0'); }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.tsx index cd2bb69f6e580b..2e6a5fcd8115fb 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/lens_field_icon.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { palettes } from '@elastic/eui'; +import { euiPaletteColorBlind } from '@elastic/eui'; import { FieldIcon, typeToEuiIconMap } from '../../../../../../src/plugins/kibana_react/public'; import { DataType } from '../types'; import { normalizeOperationDataType } from './utils'; @@ -15,7 +15,7 @@ export function getColorForDataType(type: string) { if (iconMap) { return iconMap.color; } - return palettes.euiPaletteColorBlind.colors[0]; + return euiPaletteColorBlind()[0]; } export function LensFieldIcon({ type }: { type: DataType }) { diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx index a891814bb04965..d21c6c74e10508 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/operations/definitions/terms.test.tsx @@ -354,7 +354,7 @@ describe('terms', () => { expect(select.prop('value')).toEqual('alphabetical'); - expect(select.prop('options').map(({ value }) => value)).toEqual([ + expect(select.prop('options')!.map(({ value }) => value)).toEqual([ 'column$$$col2', 'alphabetical', ]); @@ -423,7 +423,7 @@ describe('terms', () => { .find(EuiSelect); expect(select.prop('value')).toEqual('asc'); - expect(select.prop('options').map(({ value }) => value)).toEqual(['asc', 'desc']); + expect(select.prop('options')!.map(({ value }) => value)).toEqual(['asc', 'desc']); }); it('should update state with the order direction value', () => { diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap index 8df2d764c02084..76802e701b3871 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/__snapshots__/xy_expression.test.tsx.snap @@ -86,18 +86,18 @@ exports[`xy_expression XYChart component it renders area 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -272,18 +272,18 @@ exports[`xy_expression XYChart component it renders bar 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -458,18 +458,18 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -644,18 +644,18 @@ exports[`xy_expression XYChart component it renders line 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -830,18 +830,18 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -1020,18 +1020,18 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = ` "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { @@ -1210,18 +1210,18 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] = "top": 0, }, "colors": Object { - "defaultVizColor": "#3185FC", + "defaultVizColor": "#6092C0", "vizColors": Array [ - "#1EA593", - "#2B70F7", - "#CE0060", - "#38007E", - "#FCA5D3", - "#F37020", - "#E49E29", - "#B0916F", - "#7B000B", - "#34130C", + "#5BBAA0", + "#6092C0", + "#D36086", + "#9170B8", + "#EEAFCF", + "#FAE181", + "#CDBD9D", + "#F19F58", + "#B46F5F", + "#E7664C", ], }, "crosshair": Object { diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap b/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap index dc51c066a8cb9b..74f109df382cd2 100644 --- a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap +++ b/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap @@ -48,11 +48,9 @@ exports[`PipelineEditor component includes required error message for falsy pipe labelType="label" > `; diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap b/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap index 98e6fa0517f580..2dc355513ece24 100644 --- a/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap +++ b/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap @@ -13,8 +13,6 @@ exports[`should not render relation select when geo field is geo_point 1`] = ` > @@ -92,8 +90,6 @@ exports[`should not show "within" relation when filter geometry is not closed 1` > @@ -151,9 +147,6 @@ exports[`should not show "within" relation when filter geometry is not closed 1` > @@ -283,8 +274,6 @@ exports[`should render relation select when geo field is geo_shape 1`] = ` > @@ -342,9 +331,6 @@ exports[`should render relation select when geo field is geo_shape 1`] = ` > tinycolor(color) diff --git a/x-pack/legacy/plugins/ml/public/application/components/annotations/annotations_table/__snapshots__/annotations_table.test.js.snap b/x-pack/legacy/plugins/ml/public/application/components/annotations/annotations_table/__snapshots__/annotations_table.test.js.snap index 1d95c217e10f78..48cf53cf1ac016 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/annotations/annotations_table/__snapshots__/annotations_table.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/components/annotations/annotations_table/__snapshots__/annotations_table.test.js.snap @@ -89,6 +89,7 @@ exports[`AnnotationsTable Initialization with annotations prop. 1`] = ` }, } } + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap b/x-pack/legacy/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap index 217aa113fba4d9..f3c825a66ee2f2 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/components/kql_filter_bar/filter_bar/__snapshots__/filter_bar.test.js.snap @@ -23,6 +23,7 @@ exports[`FilterBar snapshot suggestions not shown 1`] = ` fullWidth={true} incremental={false} inputRef={[Function]} + isClearable={true} isLoading={false} onChange={[Function]} onClick={[Function]} @@ -88,6 +89,7 @@ exports[`FilterBar snapshot suggestions shown 1`] = ` fullWidth={true} incremental={false} inputRef={[Function]} + isClearable={true} isLoading={false} onChange={[Function]} onClick={[Function]} diff --git a/x-pack/legacy/plugins/ml/public/application/components/rule_editor/__snapshots__/condition_expression.test.js.snap b/x-pack/legacy/plugins/ml/public/application/components/rule_editor/__snapshots__/condition_expression.test.js.snap index 177ba5019fbe3d..43b4625e81f794 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/rule_editor/__snapshots__/condition_expression.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/components/rule_editor/__snapshots__/condition_expression.test.js.snap @@ -55,10 +55,6 @@ exports[`ConditionExpression renders with appliesTo, operator and value supplied } > @@ -495,9 +477,7 @@ exports[`CustomUrlEditor renders the editor for a discover type URL with valid t @@ -742,9 +714,7 @@ exports[`CustomUrlEditor renders the editor for a new dashboard type URL with no @@ -56,10 +53,8 @@ exports[`CustomUrlList renders a list of custom URLs 1`] = ` labelType="label" > @@ -214,10 +203,8 @@ exports[`CustomUrlList renders a list of custom URLs 1`] = ` labelType="label" > @@ -372,10 +353,8 @@ exports[`CustomUrlList renders a list of custom URLs 1`] = ` labelType="label" > `; @@ -166,6 +167,7 @@ exports[`EventsTable Renders events table with search bar 1`] = ` }, } } + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap index d689d1b8744621..8c518f42e0ec77 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/list/table/__snapshots__/table.test.js.snap @@ -108,6 +108,7 @@ exports[`CalendarsListTable renders the table with all calendars 1`] = ` }, } } + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap index 6513f2795ed3ab..f4fb2734a5e82c 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap @@ -46,9 +46,6 @@ exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = ` labelType="label" > diff --git a/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/header.test.js.snap b/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/header.test.js.snap index 7c0aa7951e951e..85a31fbcd91852 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/header.test.js.snap +++ b/x-pack/legacy/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/header.test.js.snap @@ -70,10 +70,7 @@ exports[`EditFilterListHeader renders the header when creating a new filter list labelType="label" > `; @@ -196,6 +197,7 @@ exports[`Filter Lists Table renders with filter lists supplied 1`] = ` }, } } + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap index 0bddf48d882785..b7d57b9bb5d9d0 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap @@ -41,5 +41,6 @@ exports[`Latest Active that latest active component renders normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap index e2fd9be796012f..0021bfae57d6eb 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap @@ -33,5 +33,6 @@ exports[`Latest Types that latest active component renders normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap index 65f6ae16033522..93ff1299323a04 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap @@ -29,5 +29,6 @@ exports[`Latest Versions that latest active component renders normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap index 37e0039c94ec4c..d54612b6f4f29a 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap @@ -131,6 +131,7 @@ exports[`Ccr that it renders normally 1`] = ` }, } } + tableLayout="fixed" /> diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap index 02e42b8647c03a..366c23135cc76b 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap @@ -45,6 +45,7 @@ exports[`CcrShard that is renders an exception properly 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap index 4affadd1275301..de02fb10a5937b 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap @@ -270,6 +270,7 @@ exports[`Logs should render normally 1`] = ` } noItemsMessage="No items found" responsive={true} + tableLayout="fixed" /> @@ -378,9 +376,7 @@ exports[`Flyout beats part one should render normally 1`] = ` labelType="label" > @@ -761,9 +757,7 @@ exports[`Flyout elasticsearch part one should render normally 1`] = ` labelType="label" > @@ -1104,9 +1098,7 @@ exports[`Flyout kibana part one should render normally 1`] = ` labelType="label" > @@ -1454,9 +1446,7 @@ exports[`Flyout logstash part one should render normally 1`] = ` labelType="label" > diff --git a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap b/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap index fe5625fa0dfa65..3b6d7a0b5f0032 100644 --- a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap +++ b/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap @@ -45,6 +45,7 @@ Array [ } } responsive={true} + tableLayout="fixed" >
`; diff --git a/x-pack/legacy/plugins/siem/public/components/autocomplete_field/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/autocomplete_field/__snapshots__/index.test.tsx.snap index 3fbf46760f3a55..dfd9612d524434 100644 --- a/x-pack/legacy/plugins/siem/public/components/autocomplete_field/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/autocomplete_field/__snapshots__/index.test.tsx.snap @@ -10,6 +10,7 @@ exports[`Autocomplete rendering it renders against snapshot 1`] = ` fullWidth={true} incremental={false} inputRef={[Function]} + isClearable={true} isInvalid={true} isLoading={false} onChange={[Function]} diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap index 983eb2409bd775..d8b8ae63cf41fc 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/__snapshots__/jobs_table.test.tsx.snap @@ -112,5 +112,6 @@ exports[`JobsTableComponent renders correctly against snapshot 1`] = ` } } responsive={false} + tableLayout="fixed" /> `; diff --git a/x-pack/legacy/plugins/siem/public/components/notes/note_card/__snapshots__/note_card_body.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/notes/note_card/__snapshots__/note_card_body.test.tsx.snap index e8ed09658c7880..11fd78d81052af 100644 --- a/x-pack/legacy/plugins/siem/public/components/notes/note_card/__snapshots__/note_card_body.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/notes/note_card/__snapshots__/note_card_body.test.tsx.snap @@ -167,16 +167,16 @@ exports[`NoteCardBody renders correctly against snapshot 1`] = ` "euiColorPrimary": "#1ba9f5", "euiColorSecondary": "#7de2d1", "euiColorSuccess": "#7de2d1", - "euiColorVis0": "#00b3a4", - "euiColorVis1": "#3185fc", - "euiColorVis2": "#db1374", - "euiColorVis3": "#490092", - "euiColorVis4": "#feb6db", - "euiColorVis5": "#e6c220", - "euiColorVis6": "#bfa180", - "euiColorVis7": "#f98510", - "euiColorVis8": "#461a0a", - "euiColorVis9": "#920000", + "euiColorVis0": "#5bbaa0", + "euiColorVis1": "#6092c0", + "euiColorVis2": "#d36086", + "euiColorVis3": "#9170b8", + "euiColorVis4": "#eeafcf", + "euiColorVis5": "#fae181", + "euiColorVis6": "#cdbd9d", + "euiColorVis7": "#f19f58", + "euiColorVis8": "#b46f5f", + "euiColorVis9": "#e7664c", "euiColorWarning": "#ffce7a", "euiContextMenuWidth": "256px", "euiControlBarBackground": "#000000", @@ -351,15 +351,17 @@ exports[`NoteCardBody renders correctly against snapshot 1`] = ` "warning": "#ffce7a", }, "euiSuggestItemColors": Object { - "tint0": "#00b3a4", - "tint1": "#3185fc", - "tint2": "#db1374", - "tint3": "#490092", - "tint4": "#e6c220", - "tint5": "#f98510", - "tint6": "#461a0a", - "tint7": "#920000", - "tint8": "#98a2b3", + "tint0": "#5bbaa0", + "tint1": "#6092c0", + "tint10": "#98a2b3", + "tint2": "#d36086", + "tint3": "#9170b8", + "tint4": "#eeafcf", + "tint5": "#fae181", + "tint6": "#cdbd9d", + "tint7": "#f19f58", + "tint8": "#b46f5f", + "tint9": "#e7664c", }, "euiSuperDatePickerButtonWidth": "118px", "euiSuperDatePickerWidth": "480px", @@ -505,11 +507,11 @@ exports[`NoteCardBody renders correctly against snapshot 1`] = ` "tokenTypes": Object { "tokenTint01": "#1ba9f5", "tokenTint02": "#f990c0", - "tokenTint03": "#490092", - "tokenTint04": "#f98510", - "tokenTint05": "#3185fc", + "tokenTint03": "#9170b8", + "tokenTint04": "#f19f58", + "tokenTint05": "#6092c0", "tokenTint06": "#e6c220", - "tokenTint07": "#00b3a4", + "tokenTint07": "#5bbaa0", "tokenTint08": "#920000", "tokenTint09": "#ff00ff", "tokenTint10": "#26ab00", diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/paginated_table/__snapshots__/index.test.tsx.snap index 98c9fc202dd6b1..82bcd62c77cbe4 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/__snapshots__/index.test.tsx.snap @@ -167,16 +167,16 @@ exports[`Paginated Table Component rendering it renders the default load more ta "euiColorPrimary": "#1ba9f5", "euiColorSecondary": "#7de2d1", "euiColorSuccess": "#7de2d1", - "euiColorVis0": "#00b3a4", - "euiColorVis1": "#3185fc", - "euiColorVis2": "#db1374", - "euiColorVis3": "#490092", - "euiColorVis4": "#feb6db", - "euiColorVis5": "#e6c220", - "euiColorVis6": "#bfa180", - "euiColorVis7": "#f98510", - "euiColorVis8": "#461a0a", - "euiColorVis9": "#920000", + "euiColorVis0": "#5bbaa0", + "euiColorVis1": "#6092c0", + "euiColorVis2": "#d36086", + "euiColorVis3": "#9170b8", + "euiColorVis4": "#eeafcf", + "euiColorVis5": "#fae181", + "euiColorVis6": "#cdbd9d", + "euiColorVis7": "#f19f58", + "euiColorVis8": "#b46f5f", + "euiColorVis9": "#e7664c", "euiColorWarning": "#ffce7a", "euiContextMenuWidth": "256px", "euiControlBarBackground": "#000000", @@ -351,15 +351,17 @@ exports[`Paginated Table Component rendering it renders the default load more ta "warning": "#ffce7a", }, "euiSuggestItemColors": Object { - "tint0": "#00b3a4", - "tint1": "#3185fc", - "tint2": "#db1374", - "tint3": "#490092", - "tint4": "#e6c220", - "tint5": "#f98510", - "tint6": "#461a0a", - "tint7": "#920000", - "tint8": "#98a2b3", + "tint0": "#5bbaa0", + "tint1": "#6092c0", + "tint10": "#98a2b3", + "tint2": "#d36086", + "tint3": "#9170b8", + "tint4": "#eeafcf", + "tint5": "#fae181", + "tint6": "#cdbd9d", + "tint7": "#f19f58", + "tint8": "#b46f5f", + "tint9": "#e7664c", }, "euiSuperDatePickerButtonWidth": "118px", "euiSuperDatePickerWidth": "480px", @@ -505,11 +507,11 @@ exports[`Paginated Table Component rendering it renders the default load more ta "tokenTypes": Object { "tokenTint01": "#1ba9f5", "tokenTint02": "#f990c0", - "tokenTint03": "#490092", - "tokenTint04": "#f98510", - "tokenTint05": "#3185fc", + "tokenTint03": "#9170b8", + "tokenTint04": "#f19f58", + "tokenTint05": "#6092c0", "tokenTint06": "#e6c220", - "tokenTint07": "#00b3a4", + "tokenTint07": "#5bbaa0", "tokenTint08": "#920000", "tokenTint09": "#ff00ff", "tokenTint10": "#26ab00", diff --git a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap index db9913ea7f0724..8930dedfa0035d 100644 --- a/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap @@ -56,10 +56,7 @@ exports[`ConfirmDeleteModal renders as expected 1`] = ` labelType="label" > diff --git a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap index 15fd1ae81a3474..2aec970def9df0 100644 --- a/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap +++ b/x-pack/legacy/plugins/spaces/public/management/edit_space/customize_space/__snapshots__/space_identifier.test.tsx.snap @@ -49,10 +49,8 @@ exports[`renders without crashing 1`] = ` labelType="label" > @@ -140,7 +141,7 @@ Array [ } > Minimal initialization 1`] = ` labelType="label" > diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/__snapshots__/popover_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/__snapshots__/popover_form.test.tsx.snap index 01114e3b9cfa3e..ba53bb2da57160 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/__snapshots__/popover_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/__snapshots__/popover_form.test.tsx.snap @@ -21,11 +21,8 @@ exports[`Transform: Group By Minimal initialization 1`] = ` labelType="label" > @@ -41,10 +38,6 @@ exports[`Transform: Group By Minimal initialization 1`] = ` labelType="label" > { // expected. test('render', () => { expect(shallowWithIntl()).toMatchInlineSnapshot(` - -`); + "hidePerPageOptions": true, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [], + "totalItemCount": 3, + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "asc", + "field": "index", + }, + } + } + tableLayout="fixed" + /> + `); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap index 0e6ea3662b97e5..af1ed12e5e8116 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap @@ -29,6 +29,7 @@ exports[`FilterPopover component does not show item list when loading 1`] = ` disabled={false} fullWidth={false} incremental={false} + isClearable={true} isLoading={false} onSearch={[Function]} placeholder="Loading..." @@ -66,6 +67,7 @@ exports[`FilterPopover component renders without errors for valid props 1`] = ` disabled={false} fullWidth={false} incremental={false} + isClearable={true} isLoading={false} onSearch={[Function]} placeholder="Search bar" diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap index dc98878f6fe085..e32771faf5912c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap @@ -63,6 +63,7 @@ exports[`MonitorList component renders a no items message when no data is provid loading={false} noItemsMessage="No uptime monitors found" responsive={true} + tableLayout="fixed" /> diff --git a/x-pack/package.json b/x-pack/package.json index 5951ac43241be7..61cb04de49386a 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -179,7 +179,7 @@ "@elastic/apm-rum-react": "^0.3.2", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "1.0.5", - "@elastic/eui": "17.3.1", + "@elastic/eui": "18.0.0", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.1.0", "@elastic/node-crypto": "^1.0.0", diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts index a9a89ee05f561e..a73ffcc51a301c 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts @@ -73,7 +73,7 @@ describe('SecurityNavControlService', () => {
Date: Tue, 14 Jan 2020 11:35:35 -0700 Subject: [PATCH 21/52] [kbn/ui-shared-deps] include polyfills, required by some deps (#54667) * [kbn/ui-shared-deps] include polyfills, required by some deps * remove unnecessary dep * replace abort-controller for server side use Co-authored-by: Elastic Machine --- package.json | 1 - packages/kbn-ui-shared-deps/entry.js | 3 +++ packages/kbn-ui-shared-deps/package.json | 8 +++++- packages/kbn-ui-shared-deps/polyfills.js | 26 +++++++++++++++++++ .../vendor/childnode_remove_polyfill.js | 19 +------------- .../tests_bundle/tests_entry_template.js | 9 ------- .../ui/ui_bundles/app_entry_template.js | 8 ------ 7 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 packages/kbn-ui-shared-deps/polyfills.js rename webpackShims/childnode-remove-polyfill.js => packages/kbn-ui-shared-deps/vendor/childnode_remove_polyfill.js (68%) diff --git a/package.json b/package.json index 7a8596da9d377c..a623b656ec9a1f 100644 --- a/package.json +++ b/package.json @@ -163,7 +163,6 @@ "compare-versions": "3.5.1", "core-js": "^3.2.1", "css-loader": "2.1.1", - "custom-event-polyfill": "^0.3.0", "d3": "3.5.17", "d3-cloud": "1.2.5", "deep-freeze-strict": "^1.1.1", diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 250abd162f91d7..7a15c3bb742c0a 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -17,6 +17,9 @@ * under the License. */ +// import global polyfills before everything else +require('./polyfills'); + // must load before angular export const Jquery = require('jquery'); window.$ = window.jQuery = Jquery; diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 2e62eb3504aca6..c9434f3ec1c38d 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -13,8 +13,11 @@ "@elastic/charts": "^16.1.0", "@kbn/dev-utils": "1.0.0", "@yarnpkg/lockfile": "^1.1.0", + "abortcontroller-polyfill": "^1.3.0", "angular": "^1.7.9", + "core-js": "^3.2.1", "css-loader": "^2.1.1", + "custom-event-polyfill": "^0.3.0", "del": "^5.1.0", "jquery": "^3.4.1", "mini-css-extract-plugin": "0.8.0", @@ -24,6 +27,9 @@ "react-intl": "^2.8.0", "react": "^16.12.0", "read-pkg": "^5.2.0", - "webpack": "4.41.0" + "regenerator-runtime": "^0.13.3", + "symbol-observable": "^1.2.0", + "webpack": "4.41.0", + "whatwg-fetch": "^3.0.0" } } \ No newline at end of file diff --git a/packages/kbn-ui-shared-deps/polyfills.js b/packages/kbn-ui-shared-deps/polyfills.js new file mode 100644 index 00000000000000..d2305d643e4d20 --- /dev/null +++ b/packages/kbn-ui-shared-deps/polyfills.js @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('core-js/stable'); +require('regenerator-runtime/runtime'); +require('custom-event-polyfill'); +require('whatwg-fetch'); +require('abortcontroller-polyfill/dist/polyfill-patch-fetch'); +require('./vendor/childnode_remove_polyfill'); +require('symbol-observable'); diff --git a/webpackShims/childnode-remove-polyfill.js b/packages/kbn-ui-shared-deps/vendor/childnode_remove_polyfill.js similarity index 68% rename from webpackShims/childnode-remove-polyfill.js rename to packages/kbn-ui-shared-deps/vendor/childnode_remove_polyfill.js index 26c21d1674b07d..d8818fe809ccbb 100644 --- a/webpackShims/childnode-remove-polyfill.js +++ b/packages/kbn-ui-shared-deps/vendor/childnode_remove_polyfill.js @@ -1,21 +1,4 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ +/* eslint-disable @kbn/eslint/require-license-header */ /* @notice * This product bundles childnode-remove which is available under a diff --git a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js index 02b1f5fec9c199..2f8a2264530d57 100644 --- a/src/legacy/core_plugins/tests_bundle/tests_entry_template.js +++ b/src/legacy/core_plugins/tests_bundle/tests_entry_template.js @@ -29,16 +29,7 @@ export const createTestEntryTemplate = defaultUiSettings => bundle => ` * */ -// import global polyfills before everything else -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; -import 'custom-event-polyfill'; -import 'whatwg-fetch'; -import 'abortcontroller-polyfill'; -import 'childnode-remove-polyfill'; import fetchMock from 'fetch-mock/es5/client'; -import Symbol_observable from 'symbol-observable'; - import { CoreSystem } from '__kibanaCore__'; // Fake uiCapabilities returned to Core in browser tests diff --git a/src/legacy/ui/ui_bundles/app_entry_template.js b/src/legacy/ui/ui_bundles/app_entry_template.js index f0ca97f473324d..a1c3a153a196cf 100644 --- a/src/legacy/ui/ui_bundles/app_entry_template.js +++ b/src/legacy/ui/ui_bundles/app_entry_template.js @@ -28,14 +28,6 @@ export const appEntryTemplate = bundle => ` * context: ${bundle.getContext()} */ -// import global polyfills -import Symbol_observable from 'symbol-observable'; -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; -import 'custom-event-polyfill'; -import 'whatwg-fetch'; -import 'abortcontroller-polyfill'; -import 'childnode-remove-polyfill'; ${apmImport()} import { i18n } from '@kbn/i18n'; import { CoreSystem } from '__kibanaCore__' From 573885538950d1d350c0c12dc8a50097222edef1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 14 Jan 2020 18:44:24 +0000 Subject: [PATCH 22/52] fix(NA): recursive delete function on watch cache directory (#54758) --- src/optimize/watch/watch_cache.ts | 34 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/optimize/watch/watch_cache.ts b/src/optimize/watch/watch_cache.ts index 15957210b3d43c..b6784c1734a174 100644 --- a/src/optimize/watch/watch_cache.ts +++ b/src/optimize/watch/watch_cache.ts @@ -170,22 +170,20 @@ export class WatchCache { * very large folders (with 84K+ files) cause a stack overflow. */ async function recursiveDelete(directory: string) { - const entries = await readdirAsync(directory, { withFileTypes: true }); - await Promise.all( - entries.map(entry => { - const absolutePath = path.join(directory, entry.name); - const result = entry.isDirectory() - ? recursiveDelete(absolutePath) - : unlinkAsync(absolutePath); - - // Ignore errors, if the file or directory doesn't exist. - return result.catch(e => { - if (e.code !== 'ENOENT') { - throw e; - } - }); - }) - ); - - return rmdirAsync(directory); + try { + const entries = await readdirAsync(directory, { withFileTypes: true }); + + await Promise.all( + entries.map(entry => { + const absolutePath = path.join(directory, entry.name); + return entry.isDirectory() ? recursiveDelete(absolutePath) : unlinkAsync(absolutePath); + }) + ); + + return rmdirAsync(directory); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } } From 1b076171f3c6171c0abebda6f3887568823efb6a Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 14 Jan 2020 19:52:58 +0100 Subject: [PATCH 23/52] [ML] Data Visualizer redesign (#54358) * [ML] change basic page structure * [ML] adjust search panel * [ML] adjust fields_panel.tsx * [ML] card icon styles * [ML] styles * [ML] adjust actions_panel.tsx * Update styling of panels, spacing * [ML] change basic page structure * [ML] adjust search panel * [ML] adjust fields_panel.tsx * [ML] card icon styles * [ML] styles * [ML] adjust actions_panel.tsx * [ML] fix i18n * [ML] fix styles * [ML] adjust top values styles * [ML] remove conflicts artifacts * Use EuiBorderColor * [ML] fix i18n * [ML] fix i18n * [ML] fix counters * [ML] fixed width for sample size select * [ML] fix layout for file viz * [ML] fix empty cards rendering * Update text styling and spacing * [ML] fix field stats card * [ML] fix counter for showAllFields * [ML] reset title for the badge * [ML] boolean_content.tsx with the bar chart * [ML] fix counters Co-authored-by: DeFazio Co-authored-by: Elastic Machine --- .../display_value/display_value.tsx | 4 +- .../field_title_bar/_field_title_bar.scss | 14 +- .../field_type_icon/_field_type_icon.scss | 14 +- .../fields_stats/_field_stats_card.scss | 74 ++++-- .../fields_stats/field_stats_card.js | 229 +++++++++--------- .../components/fields_stats/fields_stats.js | 11 +- .../actions_panel/actions_panel.tsx | 12 +- .../field_data_card/_field_data_card.scss | 70 ++++-- .../content_types/boolean_content.tsx | 123 +++++----- .../content_types/date_content.tsx | 26 +- .../content_types/geo_point_content.tsx | 46 ++-- .../content_types/ip_content.tsx | 44 ++-- .../content_types/keyword_content.tsx | 59 +++-- .../content_types/not_in_docs_content.tsx | 30 +-- .../content_types/number_content.tsx | 133 +++++----- .../content_types/other_content.tsx | 42 ++-- .../examples_list/examples_list.tsx | 18 +- .../field_data_card/field_data_card.tsx | 23 +- .../field_data_card/top_values/top_values.tsx | 6 +- .../components/fields_panel/fields_panel.tsx | 126 +++++----- .../components/search_panel/search_panel.tsx | 173 ++++++------- .../datavisualizer/index_based/page.tsx | 148 ++++++----- .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - 24 files changed, 803 insertions(+), 632 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/application/components/display_value/display_value.tsx b/x-pack/legacy/plugins/ml/public/application/components/display_value/display_value.tsx index cfe3d09a16320a..36225cb839704e 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/display_value/display_value.tsx +++ b/x-pack/legacy/plugins/ml/public/application/components/display_value/display_value.tsx @@ -13,11 +13,11 @@ export const DisplayValue: FC<{ value: any }> = ({ value }) => { const length = String(value).length; if (length <= MAX_CHARS) { - return value; + return {value}; } else { return ( - {value} + {value} ); } diff --git a/x-pack/legacy/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss b/x-pack/legacy/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss index 0fa087deacf91e..75118266d45dba 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss +++ b/x-pack/legacy/plugins/ml/public/application/components/field_title_bar/_field_title_bar.scss @@ -1,9 +1,14 @@ .ml-field-title-bar { - color: $euiColorEmptyShade; - @include euiFontSizeL; + @include euiFontSizeM; + font-family: Roboto Mono, serif; + font-style: normal; + font-weight: bold; + font-size: $euiFontSizeS; + border-radius: $euiBorderRadius $euiBorderRadius 0 0; + padding: $euiSizeXS; + margin: (-$euiSize) (-$euiSize) 0 (-$euiSize); + border-top: 3px solid; text-align: center; - border-radius: $euiBorderRadius $euiBorderRadius 0px 0px; - padding-bottom: $euiSizeXS/2; .field-type-icon { vertical-align: middle; @@ -18,5 +23,6 @@ padding-right: $euiSizeS; max-width: 290px; // SASSTODO: Calculate value display: inline-block; + margin-left: $euiSizeS; } } diff --git a/x-pack/legacy/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss b/x-pack/legacy/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss index ba318057bcae19..864df28f2c0554 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss +++ b/x-pack/legacy/plugins/ml/public/application/components/field_type_icon/_field_type_icon.scss @@ -1,8 +1,18 @@ +$icon-size: 20px; + .field-type-icon-container { - display: inline !important; + display: inline-block !important; + vertical-align: middle; + border: 1px solid; + border-radius: 4px; + width: $icon-size; + height: $icon-size; + line-height: $icon-size;; + text-align: center; .field-type-icon { - padding-right: $euiSizeXS / 2; + padding: 0; display: inline !important; + vertical-align: initial; } } diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss index 39a87ece68ac94..2702817a557492 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss @@ -8,56 +8,96 @@ // These styles should all be removed once the file data visualizer is using // the same field_data_card component as the index based data visualizer. height: 408px; + box-shadow: none; + border-color: $euiBorderColor; + // Note the names of these styles need to match the type of the field they are displaying. .boolean { - background-color: #e6c220; + color: $euiColorVis5; + border-color: $euiColorVis5; + + .field-type-icon-container { + background-color: rgba($euiColorVis5, 0.5); + } } .date { - background-color: #f98510; + color: $euiColorVis7; + border-color: $euiColorVis7; + + .field-type-icon-container { + background-color: rgba($euiColorVis7, 0.5); + } } .document_count { - background-color: #db1374; + color: $euiColorVis2; + border-color: $euiColorVis2; + + .field-type-icon-container { + background-color: rgba($euiColorVis2, 0.5); + } } .geo_point { - background-color: #461a0a; + color: $euiColorVis8; + border-color: $euiColorVis8; + + .field-type-icon-container { + background-color: rgba($euiColorVis8, 0.5); + } } .ip { - background-color: #490092; + color: $euiColorVis3; + border-color: $euiColorVis3; + + .field-type-icon-container { + background-color: rgba($euiColorVis3, 0.5); + } } .keyword { - background-color: #00b3a4; + color: $euiColorVis0; + border-color: $euiColorVis0; + + .field-type-icon-container { + background-color: rgba($euiColorVis0, 0.5); + } } .number { - background-color: #3185fc; + color: $euiColorVis1; + border-color: $euiColorVis1; + + .field-type-icon-container { + background-color: rgba($euiColorVis1, 0.5); + } } .text { - background-color: #920000; + color: $euiColorVis9; + border-color: $euiColorVis9; + + .field-type-icon-container { + background-color: rgba($euiColorVis9, 0.5); + } } .type-other, .unknown { - background-color: #bfa180; + color: $euiColorVis6; + border-color: $euiColorVis6; + + .field-type-icon-container { + background-color: rgba($euiColorVis6, 0.5); + } } // Use euiPanel styling @include euiPanel($selector: '.card-contents'); - .card-contents { - height: 378px; - line-height: 21px; - border-radius: 0px 0px $euiBorderRadius $euiBorderRadius; - overflow: hidden; - } - .stats { - padding: 10px 10px 0px 10px; text-align: center; } diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js index b1167266a5d29e..988fb653dd1ad4 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiSpacer, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiProgress } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { FieldTypeIcon } from '../../../../components/field_type_icon'; @@ -25,129 +25,136 @@ export function FieldStatsCard({ field }) { } return ( - -
-
-
- -
- {field.name} -
+ +
+
+ +
+ {field.name}
+
-
- {field.count > 0 && ( - -
-
-