diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx index f7b9d18a24358..bcd16c8580698 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/anomaly_chart/anomaly_chart.tsx @@ -14,6 +14,7 @@ import { Scatter } from './scatter'; import { Axes } from '../common/axes'; import { getXRange } from '../common/utils'; import { LineChartPoint } from '../../../../common/chart_loader'; +import { LoadingWrapper } from '../loading_wrapper'; export enum CHART_TYPE { LINE, @@ -27,32 +28,32 @@ interface Props { anomalyData: Anomaly[]; height: string; width: string; + loading?: boolean; } export const AnomalyChart: FC = ({ chartType, - chartData, + chartData = [], modelData, anomalyData, height, width, + loading = false, }) => { const data = chartType === CHART_TYPE.SCATTER ? flattenData(chartData) : chartData; - if (data.length === 0) { - return null; - } - const xDomain = getXRange(data); return (
- - - - - - {chartType === CHART_TYPE.LINE && } - {chartType === CHART_TYPE.SCATTER && } - + 0} loading={loading}> + + + + + + {chartType === CHART_TYPE.LINE && } + {chartType === CHART_TYPE.SCATTER && } + +
); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/common/utils.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/common/utils.ts index 74d01b00f9254..91f4c08c72f7a 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/common/utils.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/common/utils.ts @@ -14,7 +14,11 @@ export function getCustomColor(specId: string, color: string): CustomSeriesColor return new Map([[lineDataSeriesColorValues, color]]); } -export function getYRange(chartData: any) { +export function getYRange(chartData: any[]) { + if (chartData.length === 0) { + return { min: 0, max: 0 }; + } + let max: number = Number.MIN_VALUE; let min: number = Number.MAX_VALUE; chartData.forEach((r: any) => { @@ -32,7 +36,10 @@ export function getYRange(chartData: any) { }; } -export function getXRange(lineChartData: any) { +export function getXRange(lineChartData: any[]) { + if (lineChartData.length === 0) { + return { min: 0, max: 0 }; + } return { min: lineChartData[0].time, max: lineChartData[lineChartData.length - 1].time, diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx index ad806f8af243c..030d622bed61b 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/event_rate_chart/event_rate_chart.tsx @@ -10,33 +10,43 @@ import { Axes } from '../common/axes'; import { getCustomColor } from '../common/utils'; import { LineChartPoint } from '../../../../common/chart_loader'; import { EVENT_RATE_COLOR } from '../common/settings'; +import { LoadingWrapper } from '../loading_wrapper'; interface Props { eventRateChartData: LineChartPoint[]; height: string; width: string; showAxis?: boolean; + loading?: boolean; } const SPEC_ID = 'event_rate'; -export const EventRateChart: FC = ({ eventRateChartData, height, width, showAxis }) => { +export const EventRateChart: FC = ({ + eventRateChartData, + height, + width, + showAxis, + loading = false, +}) => { return (
- - {showAxis === true && } + 0} loading={loading}> + + {showAxis === true && } - - - + + + +
); }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/index.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/index.ts new file mode 100644 index 0000000000000..cf617363033ca --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/index.ts @@ -0,0 +1,6 @@ +/* + * 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 { LoadingWrapper } from './loading_wrapper'; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/loading_wrapper.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/loading_wrapper.tsx new file mode 100644 index 0000000000000..1087fdc8d0fce --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/charts/loading_wrapper/loading_wrapper.tsx @@ -0,0 +1,43 @@ +/* + * 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, { FC, Fragment } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; + +interface Props { + hasData: boolean; + height: string; + loading?: boolean; +} + +export const LoadingWrapper: FC = ({ hasData, loading = false, height, children }) => { + const opacity = loading === true ? (hasData === true ? 0.3 : 0) : 1; + + return ( + +
+ {children} +
+ {loading === true && ( + + + + + + )} +
+ ); +}; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/chart_grid.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/chart_grid.tsx index 691527513b804..e92a6b77b8aa8 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/chart_grid.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/chart_grid.tsx @@ -27,6 +27,7 @@ interface ChartGridProps { deleteDetector?: (index: number) => void; jobType: JOB_TYPE; animate?: boolean; + loading?: boolean; } export const ChartGrid: FC = ({ @@ -39,6 +40,7 @@ export const ChartGrid: FC = ({ anomalyData, deleteDetector, jobType, + loading = false, }) => { const animateSplit = useAnimateSplit(); @@ -53,25 +55,24 @@ export const ChartGrid: FC = ({ {aggFieldPairList.map((af, i) => ( - {lineChartsData[i] !== undefined && ( - - - - - )} + + + + ))} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx index da477193e35ab..c14e2e59a3387 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx @@ -43,6 +43,7 @@ export const MultiMetricDetectors: FC = ({ isActive, setIsValid }) => { jobCreator.aggFieldPairs ); const [lineChartsData, setLineChartsData] = useState({}); + const [loadingData, setLoadingData] = useState(false); const [modelData, setModelData] = useState>([]); const [anomalyData, setAnomalyData] = useState>([]); const [start, setStart] = useState(jobCreator.start); @@ -152,6 +153,7 @@ export const MultiMetricDetectors: FC = ({ isActive, setIsValid }) => { setChartSettings(cs); if (aggFieldPairList.length > 0) { + setLoadingData(true); const resp: LineChartData = await chartLoader.loadLineCharts( jobCreator.start, jobCreator.end, @@ -162,24 +164,25 @@ export const MultiMetricDetectors: FC = ({ isActive, setIsValid }) => { ); setLineChartsData(resp); + setLoadingData(false); } } return ( - {lineChartsData && ( - - )} + + {isActive && ( void; jobType: JOB_TYPE; fieldValuesPerDetector: DetectorFieldValues; + loading?: boolean; } export const ChartGrid: FC = ({ @@ -41,6 +42,7 @@ export const ChartGrid: FC = ({ deleteDetector, jobType, fieldValuesPerDetector, + loading = false, }) => { const animateSplit = useAnimateSplit(); @@ -48,40 +50,39 @@ export const ChartGrid: FC = ({ {aggFieldPairList.map((af, i) => ( - {lineChartsData[i] !== undefined && ( - - - - - - - {deleteDetector !== undefined && } - - - - + + + - - - )} + + + {deleteDetector !== undefined && } + + + + + + ))} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/population_view/metric_selection.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/population_view/metric_selection.tsx index 684401f7280dc..d47b592b5a4da 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/population_view/metric_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/population_view/metric_selection.tsx @@ -48,6 +48,7 @@ export const PopulationDetectors: FC = ({ isActive, setIsValid }) => { jobCreator.aggFieldPairs ); const [lineChartsData, setLineChartsData] = useState({}); + const [loadingData, setLoadingData] = useState(false); const [modelData, setModelData] = useState>([]); const [anomalyData, setAnomalyData] = useState>([]); const [start, setStart] = useState(jobCreator.start); @@ -175,6 +176,7 @@ export const PopulationDetectors: FC = ({ isActive, setIsValid }) => { setChartSettings(cs); if (aggFieldPairList.length > 0) { + setLoadingData(true); const resp: LineChartData = await chartLoader.loadPopulationCharts( jobCreator.start, jobCreator.end, @@ -184,6 +186,7 @@ export const PopulationDetectors: FC = ({ isActive, setIsValid }) => { ); setLineChartsData(resp); + setLoadingData(false); } } @@ -238,7 +241,7 @@ export const PopulationDetectors: FC = ({ isActive, setIsValid }) => { )} - {lineChartsData && splitField !== null && ( + {splitField !== null && ( = ({ isActive, setIsValid }) => { deleteDetector={isActive ? deleteDetector : undefined} jobType={jobCreator.type} fieldValuesPerDetector={fieldValuesPerDetector} + loading={loadingData} /> )} {isActive === true && splitField !== null && ( diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx index 4bd7602798bd0..c5024ea7690af 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx @@ -45,6 +45,7 @@ export const SingleMetricDetectors: FC = ({ isActive, setIsValid }) => { ]); const [aggFieldPair, setAggFieldPair] = useState(jobCreator.aggFieldPair); const [lineChartsData, setLineChartData] = useState([]); + const [loadingData, setLoadingData] = useState(false); const [modelData, setModelData] = useState([]); const [anomalyData, setAnomalyData] = useState([]); const [start, setStart] = useState(jobCreator.start); @@ -100,6 +101,7 @@ export const SingleMetricDetectors: FC = ({ isActive, setIsValid }) => { async function loadChart() { if (aggFieldPair !== null) { + setLoadingData(true); const resp: LineChartData = await chartLoader.loadLineCharts( jobCreator.start, jobCreator.end, @@ -111,6 +113,7 @@ export const SingleMetricDetectors: FC = ({ isActive, setIsValid }) => { if (resp[DTR_IDX] !== undefined) { setLineChartData(resp); } + setLoadingData(false); } } @@ -124,7 +127,7 @@ export const SingleMetricDetectors: FC = ({ isActive, setIsValid }) => { removeOptions={[]} /> )} - {lineChartsData[DTR_IDX] !== undefined && ( + {(lineChartsData[DTR_IDX] !== undefined || loadingData === true) && ( = ({ isActive, setIsValid }) => { anomalyData={anomalyData} height="300px" width="100%" + loading={loadingData} /> )} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/time_range_step/time_range.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/time_range_step/time_range.tsx index 282eba35de4af..9eda91a08a04d 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/time_range_step/time_range.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/time_range_step/time_range.tsx @@ -39,14 +39,17 @@ export const TimeRangeStep: FC = ({ setCurrentStep, isCurrentStep }) end: jobCreator.end, }); const [eventRateChartData, setEventRateChartData] = useState([]); + const [loadingData, setLoadingData] = useState(false); async function loadChart() { + setLoadingData(true); const resp = await chartLoader.loadEventRateChart( jobCreator.start, jobCreator.end, chartInterval.getInterval().asMilliseconds() ); setEventRateChartData(resp); + setLoadingData(false); } useEffect(() => { @@ -104,6 +107,7 @@ export const TimeRangeStep: FC = ({ setCurrentStep, isCurrentStep }) height="300px" width="100%" showAxis={true} + loading={loadingData} /> setCurrentStep(WIZARD_STEPS.PICK_FIELDS)} nextActive={true} />