From 5c21f3c2c2037fd2c8022feb5983df62b9fc2be4 Mon Sep 17 00:00:00 2001
From: Diana Derevyankina
Date: Fri, 19 Feb 2021 09:41:42 +0300
Subject: [PATCH] [TSVB] Show an indicator when using Last Value mode
---
...{interval_regexp.js => interval_regexp.ts} | 0
.../vis_type_timeseries/common/types.ts | 2 +-
.../components/data_time_range_mode_label.tsx | 102 ++++++++++++++++++
....js => create_interval_based_formatter.ts} | 12 ++-
.../lib/{get_interval.js => get_interval.ts} | 35 +++---
.../components/vis_editor_visualization.js | 72 ++++++-------
.../components/vis_types/timeseries/vis.js | 8 +-
7 files changed, 168 insertions(+), 63 deletions(-)
rename src/plugins/vis_type_timeseries/common/{interval_regexp.js => interval_regexp.ts} (100%)
create mode 100644 src/plugins/vis_type_timeseries/public/application/components/data_time_range_mode_label.tsx
rename src/plugins/vis_type_timeseries/public/application/components/lib/{create_xaxis_formatter.js => create_interval_based_formatter.ts} (66%)
rename src/plugins/vis_type_timeseries/public/application/components/lib/{get_interval.js => get_interval.ts} (70%)
diff --git a/src/plugins/vis_type_timeseries/common/interval_regexp.js b/src/plugins/vis_type_timeseries/common/interval_regexp.ts
similarity index 100%
rename from src/plugins/vis_type_timeseries/common/interval_regexp.js
rename to src/plugins/vis_type_timeseries/common/interval_regexp.ts
diff --git a/src/plugins/vis_type_timeseries/common/types.ts b/src/plugins/vis_type_timeseries/common/types.ts
index d72a50f667a706..9846422524c9a3 100644
--- a/src/plugins/vis_type_timeseries/common/types.ts
+++ b/src/plugins/vis_type_timeseries/common/types.ts
@@ -17,7 +17,7 @@ export type PanelSchema = TypeOf;
export type VisPayload = TypeOf;
export type FieldObject = TypeOf;
-interface PanelData {
+export interface PanelData {
id: string;
label: string;
data: Array<[number, number]>;
diff --git a/src/plugins/vis_type_timeseries/public/application/components/data_time_range_mode_label.tsx b/src/plugins/vis_type_timeseries/public/application/components/data_time_range_mode_label.tsx
new file mode 100644
index 00000000000000..6960c7ffc32d9e
--- /dev/null
+++ b/src/plugins/vis_type_timeseries/public/application/components/data_time_range_mode_label.tsx
@@ -0,0 +1,102 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiFlexItem, EuiLink, EuiText, EuiToolTip } from '@elastic/eui';
+import { getUISettings } from '../../services';
+import { convertIntervalIntoUnit, isAutoInterval, isGteInterval } from './lib/get_interval';
+import { createIntervalBasedFormatter } from '../components/lib/create_interval_based_formatter';
+import { TIME_RANGE_DATA_MODES } from '../../../common/timerange_data_modes';
+
+const lastValueFormattedMessage = (
+
+);
+
+interface DataTimeRangeModeLabelProps {
+ seriesData: Array>;
+ panelInterval: number;
+ modelInterval: string;
+ modelTimeRangeMode: TIME_RANGE_DATA_MODES;
+}
+
+export const DataTimeRangeModeLabel = ({
+ seriesData,
+ panelInterval,
+ modelInterval,
+ modelTimeRangeMode,
+}: DataTimeRangeModeLabelProps) => {
+ const hasShowPanelIntervalValue = () =>
+ isAutoInterval(modelInterval) || isGteInterval(modelInterval);
+
+ const getFormattedPanelInterval = () => {
+ const interval = convertIntervalIntoUnit(panelInterval, false);
+ return interval && `${interval.unitValue}${interval.unitString}`;
+ };
+
+ const getLastValueLabelWithTooltip = () => {
+ const dateFormat = getUISettings().get('dateFormat');
+ const scaledDataFormat = getUISettings().get('dateFormat:scaled');
+ const formatter = createIntervalBasedFormatter(panelInterval, scaledDataFormat, dateFormat);
+
+ const lastBucketDate = formatter(seriesData[seriesData.length - 1][0]);
+ const formattedPanelInterval = hasShowPanelIntervalValue() && getFormattedPanelInterval();
+ const tooltipContent = (
+
+
+
+ ),
+ }}
+ />
+ );
+
+ return (
+
+ {lastValueFormattedMessage}
+
+ );
+ };
+
+ const lastValueLabel =
+ seriesData && panelInterval ? getLastValueLabelWithTooltip() : lastValueFormattedMessage;
+
+ return (
+
+
+
+ ),
+ }}
+ />
+
+
+ );
+};
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/create_xaxis_formatter.js b/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts
similarity index 66%
rename from src/plugins/vis_type_timeseries/public/application/components/lib/create_xaxis_formatter.js
rename to src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts
index e2eea5281b197a..562aec31a08032 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/create_xaxis_formatter.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/create_interval_based_formatter.ts
@@ -8,17 +8,21 @@
import moment from 'moment';
-function getFormat(interval, rules = []) {
+function getFormat(interval: number, rules: string[][] = []) {
for (let i = rules.length - 1; i >= 0; i--) {
const rule = rules[i];
- if (!rule[0] || interval >= moment.duration(rule[0])) {
+ if (!rule[0] || interval >= Number(moment.duration(rule[0]))) {
return rule[1];
}
}
}
-export function createXaxisFormatter(interval, rules, dateFormat) {
- return (val) => {
+export function createIntervalBasedFormatter(
+ interval: number,
+ rules: string[][],
+ dateFormat: string
+) {
+ return (val: moment.MomentInput): string => {
return moment(val).format(getFormat(interval, rules) ?? dateFormat);
};
}
diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.js b/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts
similarity index 70%
rename from src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.js
rename to src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts
index dc297df39a870d..98052ea57dc73a 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_interval.ts
@@ -13,6 +13,8 @@ import { search } from '../../../../../../plugins/data/public';
const { parseEsInterval } = search.aggs;
import { GTE_INTERVAL_RE } from '../../../../common/interval_regexp';
import { AUTO_INTERVAL } from '../../../../common/constants';
+import { PanelData, TimeseriesVisData } from '../../../../common/types';
+import { TimeseriesVisParams } from '../../../metrics_fn';
export const unitLookup = {
s: i18n.translate('visTypeTimeseries.getInterval.secondsLabel', { defaultMessage: 'seconds' }),
@@ -24,9 +26,11 @@ export const unitLookup = {
y: i18n.translate('visTypeTimeseries.getInterval.yearsLabel', { defaultMessage: 'years' }),
};
-export const convertIntervalIntoUnit = (interval, hasTranslateUnitString = true) => {
+type TimeUnits = Array;
+
+export const convertIntervalIntoUnit = (interval: number, hasTranslateUnitString = true) => {
// Iterate units from biggest to smallest
- const units = Object.keys(unitLookup).reverse();
+ const units = Object.keys(unitLookup).reverse() as TimeUnits;
const duration = moment.duration(interval, 'ms');
for (let i = 0; i < units.length; i++) {
@@ -41,11 +45,16 @@ export const convertIntervalIntoUnit = (interval, hasTranslateUnitString = true)
}
};
-export const isGteInterval = (interval) => GTE_INTERVAL_RE.test(interval);
-export const isAutoInterval = (interval) => !interval || interval === AUTO_INTERVAL;
+export const isGteInterval = (interval: string) => GTE_INTERVAL_RE.test(interval);
+export const isAutoInterval = (interval: string) => !interval || interval === AUTO_INTERVAL;
+
+interface ValidationResult {
+ errorMessage: string;
+ isValid: boolean;
+}
-export const validateReInterval = (intervalValue) => {
- const validationResult = {};
+export const validateReInterval = (intervalValue: string) => {
+ const validationResult = {} as ValidationResult;
try {
parseEsInterval(intervalValue);
@@ -58,14 +67,12 @@ export const validateReInterval = (intervalValue) => {
return validationResult;
};
-export const getInterval = (visData, model) => {
- let series;
-
- if (model && model.type === 'table') {
- series = get(visData, `series[0].series`, []);
- } else {
- series = get(visData, `${model.id}.series`, []);
- }
+export const getInterval = (visData: TimeseriesVisData, model: TimeseriesVisParams) => {
+ const series = get(
+ visData,
+ model?.type === 'table' ? `series[0].series` : `${model.id}.series`,
+ []
+ ) as PanelData[];
return series.reduce((currentInterval, item) => {
if (item.data.length > 1) {
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js
index bfce125a7ed873..31e62416b78910 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_visualization.js
@@ -11,14 +11,10 @@ import React, { Component } from 'react';
import { get } from 'lodash';
import { keys, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
-import {
- getInterval,
- convertIntervalIntoUnit,
- isAutoInterval,
- isGteInterval,
-} from './lib/get_interval';
+import { getInterval } from './lib/get_interval';
import { AUTO_INTERVAL } from '../../../common/constants';
import { PANEL_TYPES } from '../../../common/panel_types';
+import { DataTimeRangeModeLabel } from './data_time_range_mode_label';
const MIN_CHART_HEIGHT = 300;
@@ -66,7 +62,15 @@ class VisEditorVisualizationUI extends Component {
this.props.eventEmitter.emit('embeddableRendered');
this._subscription = this._handler.handler.data$.subscribe((data) => {
- this.setPanelInterval(data.value.visData);
+ const visData = data.value?.visData;
+ const series = get(visData, `${this.props.model.id}.series`, []);
+ this.setState({
+ seriesData: series?.length
+ ? series[0].data
+ : visData?.series && visData.series[0]?.series[0]?.data,
+ });
+
+ this.setPanelInterval(visData);
onDataChange(data.value);
});
}
@@ -98,26 +102,16 @@ class VisEditorVisualizationUI extends Component {
}
};
- hasShowPanelIntervalValue() {
+ isAnyPanelTypeExceptTimeseries() {
const type = get(this.props, 'model.type', '');
- const interval = get(this.props, 'model.interval', AUTO_INTERVAL);
-
- return (
- [
- PANEL_TYPES.METRIC,
- PANEL_TYPES.TOP_N,
- PANEL_TYPES.GAUGE,
- PANEL_TYPES.MARKDOWN,
- PANEL_TYPES.TABLE,
- ].includes(type) &&
- (isAutoInterval(interval) || isGteInterval(interval))
- );
- }
- getFormattedPanelInterval() {
- const interval = convertIntervalIntoUnit(this.state.panelInterval, false);
-
- return interval ? `${interval.unitValue}${interval.unitString}` : null;
+ return [
+ PANEL_TYPES.METRIC,
+ PANEL_TYPES.TOP_N,
+ PANEL_TYPES.GAUGE,
+ PANEL_TYPES.MARKDOWN,
+ PANEL_TYPES.TABLE,
+ ].includes(type);
}
componentWillUnmount() {
@@ -147,15 +141,14 @@ class VisEditorVisualizationUI extends Component {
}
render() {
- const { dirty, autoApply, title, description, onToggleAutoApply, onCommit } = this.props;
- const style = { height: this.state.height };
+ const { dirty, autoApply, title, description, onToggleAutoApply, onCommit, model } = this.props;
+ const { seriesData, panelInterval, height, dragging } = this.state;
+ const style = { height };
- if (this.state.dragging) {
+ if (dragging) {
style.userSelect = 'none';
}
- const panelInterval = this.hasShowPanelIntervalValue() && this.getFormattedPanelInterval();
-
let applyMessage = (
- {panelInterval && (
-
-
-
-
-
-
-
+ {this.isAnyPanelTypeExceptTimeseries() && (
+
)}
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js
index fb99b890d23276..1c16726a1e9e64 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/vis.js
@@ -19,7 +19,7 @@ import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public
import { replaceVars } from '../../lib/replace_vars';
import { getAxisLabelString } from '../../lib/get_axis_label_string';
import { getInterval } from '../../lib/get_interval';
-import { createXaxisFormatter } from '../../lib/create_xaxis_formatter';
+import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter';
import { STACKED_OPTIONS } from '../../../visualizations/constants';
import { getCoreStart } from '../../../../services';
@@ -35,7 +35,11 @@ class TimeseriesVisualization extends Component {
dateFormat = this.props.getConfig('dateFormat');
xAxisFormatter = (interval) => (val) => {
- const formatter = createXaxisFormatter(interval, this.scaledDataFormat, this.dateFormat);
+ const formatter = createIntervalBasedFormatter(
+ interval,
+ this.scaledDataFormat,
+ this.dateFormat
+ );
return formatter(val);
};