Skip to content

Commit

Permalink
TSVB display interval information when building (elastic#32117)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexwizp authored and PhilippBaranovskiy committed Mar 7, 2019
1 parent 3ad1c09 commit c93e170
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const IndexPattern = props => {
disabled={props.disabled}
onChange={handleTextChange(intervalName, 'auto')}
value={model[intervalName]}
placeholder={'auto'}
/>
</EuiFormRow>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/

import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options';
import _ from 'lodash';
import moment from 'moment';
import { convertIntervalIntoUnit } from './get_interval';
import { i18n } from '@kbn/i18n';

const unitLookup = {
s: i18n.translate('tsvb.axisLabelOptions.secondsLabel', { defaultMessage: 'seconds' }),
m: i18n.translate('tsvb.axisLabelOptions.minutesLabel', { defaultMessage: 'minutes' }),
h: i18n.translate('tsvb.axisLabelOptions.hoursLabel', { defaultMessage: 'hours' }),
d: i18n.translate('tsvb.axisLabelOptions.daysLabel', { defaultMessage: 'days' }),
w: i18n.translate('tsvb.axisLabelOptions.weeksLabel', { defaultMessage: 'weeks' }),
M: i18n.translate('tsvb.axisLabelOptions.monthsLabel', { defaultMessage: 'months' }),
y: i18n.translate('tsvb.axisLabelOptions.yearsLabel', { defaultMessage: 'years' })
};
export function getAxisLabelString(interval) {
const units = _.pluck(_.clone(relativeOptions).reverse(), 'value')
.filter(s => /^[smhdwMy]$/.test(s));
const duration = moment.duration(interval, 'ms');
for (let i = 0; i < units.length; i++) {
const as = duration.as(units[i]);
if (Math.abs(as) > 1) {
const unitValue = Math.round(Math.abs(as));
const unitString = unitLookup[units[i]];
return i18n.translate('tsvb.axisLabelOptions.axisLabel',
{ defaultMessage: 'per {unitValue} {unitString}', values: { unitValue, unitString } });
}
const convertedValue = convertIntervalIntoUnit(interval);

if (convertedValue) {
return i18n.translate('tsvb.axisLabelOptions.axisLabel',
{
defaultMessage: 'per {unitValue} {unitString}',
values: {
unitValue: convertedValue.unitValue,
unitString: convertedValue.unitString,
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.
*/
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { pluck, get, clone, isString } from 'lodash';
import { relativeOptions } from '../../../../../ui/public/timepicker/relative_options';

import { GTE_INTERVAL_RE, INTERVAL_STRING_RE } from '../../../common/interval_regexp';

export const unitLookup = {
s: i18n.translate('tsvb.getInterval.secondsLabel', { defaultMessage: 'seconds' }),
m: i18n.translate('tsvb.getInterval.minutesLabel', { defaultMessage: 'minutes' }),
h: i18n.translate('tsvb.getInterval.hoursLabel', { defaultMessage: 'hours' }),
d: i18n.translate('tsvb.getInterval.daysLabel', { defaultMessage: 'days' }),
w: i18n.translate('tsvb.getInterval.weeksLabel', { defaultMessage: 'weeks' }),
M: i18n.translate('tsvb.getInterval.monthsLabel', { defaultMessage: 'months' }),
y: i18n.translate('tsvb.getInterval.yearsLabel', { defaultMessage: 'years' })
};

export const convertIntervalIntoUnit = (interval, hasTranslateUnitString = true) => {
const units = pluck(clone(relativeOptions).reverse(), 'value')
.filter(s => /^[smhdwMy]$/.test(s));
const duration = moment.duration(interval, 'ms');

for (let i = 0; i < units.length; i++) {
const as = duration.as(units[i]);

if (Math.abs(as) > 1) {
return {
unitValue: Math.round(Math.abs(as)),
unitString: hasTranslateUnitString ? unitLookup[units[i]] : units[i]
};
}
}
};
export const isGteInterval = (interval) => GTE_INTERVAL_RE.test(interval);

export const isIntervalValid = (interval) => {
return isString(interval) &&
(interval === 'auto' || INTERVAL_STRING_RE.test(interval) || isGteInterval(interval));
};

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`, []);
}

return series.reduce((currentInterval, item) => {
if (item.data.length > 1) {
const seriesInterval = item.data[1][0] - item.data[0][0];
if (!currentInterval || seriesInterval < currentInterval) return seriesInterval;
}
return currentInterval;
}, 0);
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { get } from 'lodash';
import { keyCodes, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui';
import { getVisualizeLoader } from 'ui/visualize/loader/visualize_loader';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import { getInterval, convertIntervalIntoUnit, isIntervalValid, isGteInterval } from './lib/get_interval';

const MIN_CHART_HEIGHT = 250;

Expand All @@ -30,7 +31,8 @@ class VisEditorVisualization extends Component {
super(props);
this.state = {
height: MIN_CHART_HEIGHT,
dragging: false
dragging: false,
panelInterval: 0,
};

this.handleMouseUp = this.handleMouseUp.bind(this);
Expand All @@ -53,7 +55,7 @@ class VisEditorVisualization extends Component {
this.handleMouseMove = (event) => {
if (this.state.dragging) {
this.setState((prevState) => ({
height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY)
height: Math.max(MIN_CHART_HEIGHT, prevState.height + event.movementY),
}));
}
};
Expand All @@ -67,16 +69,16 @@ class VisEditorVisualization extends Component {
if (this._handler) {
this._handler.destroy();
}
if(this._subscription) {
if (this._subscription) {
this._subscription.unsubscribe();
}
}

onUpdate = () => {
this._handler.update({
timeRange: this.props.timeRange
timeRange: this.props.timeRange,
});
}
};

_loadVisualization() {
getVisualizeLoader().then(loader => {
Expand All @@ -94,6 +96,7 @@ class VisEditorVisualization extends Component {
});

this._subscription = this._handler.data$.subscribe((data) => {
this.setPanelInterval(data.visData);
this.props.onDataChange(data);
});

Expand All @@ -103,6 +106,14 @@ class VisEditorVisualization extends Component {
});
}

setPanelInterval(visData) {
const panelInterval = getInterval(visData, this.props.model);

if (this.state.panelInterval !== panelInterval) {
this.setState({ panelInterval });
}
}

componentDidUpdate() {
if (!this._handler) {
this._handlerUpdateHasAlreadyBeenTriggered = true;
Expand All @@ -115,6 +126,7 @@ class VisEditorVisualization extends Component {
componentDidMount() {
this._loadVisualization();
}

/**
* Resize the chart height when pressing up/down while the drag handle
* for resizing has the focus.
Expand All @@ -128,19 +140,49 @@ class VisEditorVisualization extends Component {
this.setState((prevState) => {
const newHeight = prevState.height + (keyCode === keyCodes.UP ? -15 : 15);
return {
height: Math.max(MIN_CHART_HEIGHT, newHeight)
height: Math.max(MIN_CHART_HEIGHT, newHeight),
};
});
}
}

hasShowPanelIntervalValue() {
const type = get(this.props, 'model.type', '');

return [
'metric',
'top_n',
'gauge',
'markdown',
'table',
].includes(type);
}

getFormattedPanelInterval() {
const interval = get(this.props, 'model.interval') || 'auto';
const isValid = isIntervalValid(interval);
const shouldShowActualInterval = interval === 'auto' || isGteInterval(interval);

if (shouldShowActualInterval || !isValid) {
const autoInterval = convertIntervalIntoUnit(this.state.panelInterval, false);

if (autoInterval) {
return `${autoInterval.unitValue}${autoInterval.unitString}`;
}
} else {
return interval;
}
}

render() {
const { dirty, autoApply } = this.props;
const style = { height: this.state.height };
if (this.state.dragging) {
style.userSelect = 'none';
}

const panelInterval = this.hasShowPanelIntervalValue() && this.getFormattedPanelInterval();

let applyMessage = (<FormattedMessage
id="tsvb.visEditorVisualization.changesSuccessfullyAppliedMessage"
defaultMessage="The latest changes have been applied."
Expand Down Expand Up @@ -171,6 +213,22 @@ class VisEditorVisualization extends Component {
/>
</EuiFlexItem>

{panelInterval &&
<EuiFlexItem grow={false}>
<EuiText color="default" size="xs">
<p>
<FormattedMessage
id="tsvb.visEditorVisualization.panelInterval"
defaultMessage="Interval: {panelInterval}"
values={{
panelInterval: panelInterval,
}}
/>
</p>
</EuiText>
</EuiFlexItem>
}

<EuiFlexItem grow={false}>
<EuiText color={dirty ? 'default' : 'subdued'} size="xs">
<p>
Expand All @@ -180,14 +238,14 @@ class VisEditorVisualization extends Component {
</EuiFlexItem>

{!autoApply &&
<EuiFlexItem grow={false}>
<EuiButton iconType="play" fill size="s" onClick={this.props.onCommit} disabled={!dirty}>
<FormattedMessage
id="tsvb.visEditorVisualization.applyChangesLabel"
defaultMessage="Apply changes"
/>
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton iconType="play" fill size="s" onClick={this.props.onCommit} disabled={!dirty}>
<FormattedMessage
id="tsvb.visEditorVisualization.applyChangesLabel"
defaultMessage="Apply changes"
/>
</EuiButton>
</EuiFlexItem>
}
</EuiFlexGroup>
);
Expand All @@ -213,10 +271,10 @@ class VisEditorVisualization extends Component {
onKeyDown={this.onSizeHandleKeyDown}
aria-label={this.props.intl.formatMessage({
id: 'tsvb.colorRules.adjustChartSizeAriaLabel',
defaultMessage: 'Press up/down to adjust the chart size'
defaultMessage: 'Press up/down to adjust the chart size',
})}
>
<i className="fa fa-ellipsis-h" />
<i className="fa fa-ellipsis-h"/>
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import _ from 'lodash';
import Timeseries from '../../../visualizations/components/timeseries';
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';

function hasSeparateAxis(row) {
Expand All @@ -35,20 +36,10 @@ function hasSeparateAxis(row) {

class TimeseriesVisualization extends Component {

constructor(props) {
super(props);
}

getInterval = () => {
const { visData, model } = this.props;
const series = _.get(visData, `${model.id}.series`, []);
return series.reduce((currentInterval, item) => {
if (item.data.length > 1) {
const seriesInterval = item.data[1][0] - item.data[0][0];
if (!currentInterval || seriesInterval < currentInterval) return seriesInterval;
}
return currentInterval;
}, 0);

return getInterval(visData, model);
}

xaxisFormatter = (val) => {
Expand Down
14 changes: 7 additions & 7 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2808,13 +2808,13 @@
"tsvb.annotationsEditor.rowTemplateLabel": "行模板(必需)",
"tsvb.annotationsEditor.timeFieldLabel": "时间字段(必需)",
"tsvb.axisLabelOptions.axisLabel": "每 {unitValue} {unitString}",
"tsvb.axisLabelOptions.daysLabel": "天",
"tsvb.axisLabelOptions.hoursLabel": "小时",
"tsvb.axisLabelOptions.minutesLabel": "分钟",
"tsvb.axisLabelOptions.monthsLabel": "个月",
"tsvb.axisLabelOptions.secondsLabel": "秒",
"tsvb.axisLabelOptions.weeksLabel": "周",
"tsvb.axisLabelOptions.yearsLabel": "年",
"tsvb.getInterval.daysLabel": "天",
"tsvb.getInterval.hoursLabel": "小时",
"tsvb.getInterval.minutesLabel": "分钟",
"tsvb.getInterval.monthsLabel": "个月",
"tsvb.getInterval.secondsLabel": "秒",
"tsvb.getInterval.weeksLabel": "周",
"tsvb.getInterval.yearsLabel": "年",
"tsvb.calculateLabel.bucketScriptsLabel": "桶脚本",
"tsvb.calculateLabel.countLabel": "计数",
"tsvb.calculateLabel.filterRatioLabel": "筛选比",
Expand Down

0 comments on commit c93e170

Please sign in to comment.