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); };