[TSVB] Timeseries Drop last bucket set default to false (#97257)
* [TSVB] Timeseries Drop last bucket should default to false * Rename isLastBucketDropped prop and move series domain calculation to a separate file * Fix failing tests because of wrong default value * update drop_last_bucket.js * Refactor drop_last_bucket and some functional tests * Change infra metrics test values because of last bucket value changed * Refactor series_domain_calculation and related code * Update series_domain_calculations.test * Update series_domain_calculations.test * Fix tooltip showing wrong time * Refactor index Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Alexey Antonov <alexwizp@gmail.com>
This commit is contained in:
parent
e297fec23e
commit
6b6ad111c0
|
@ -113,7 +113,7 @@ export const IndexPattern = ({
|
||||||
const defaults = {
|
const defaults = {
|
||||||
[indexPatternName]: '',
|
[indexPatternName]: '',
|
||||||
[intervalName]: AUTO_INTERVAL,
|
[intervalName]: AUTO_INTERVAL,
|
||||||
[dropBucketName]: 1,
|
[dropBucketName]: 0,
|
||||||
[maxBarsName]: config.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
|
[maxBarsName]: config.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
|
||||||
[TIME_RANGE_MODE_KEY]: timeRangeOptions[0].value,
|
[TIME_RANGE_MODE_KEY]: timeRangeOptions[0].value,
|
||||||
};
|
};
|
||||||
|
|
|
@ -406,6 +406,7 @@ export class TimeseriesPanelConfig extends Component<
|
||||||
<EuiTab
|
<EuiTab
|
||||||
isSelected={selectedTab === PANEL_CONFIG_TABS.DATA}
|
isSelected={selectedTab === PANEL_CONFIG_TABS.DATA}
|
||||||
onClick={() => this.switchTab(PANEL_CONFIG_TABS.DATA)}
|
onClick={() => this.switchTab(PANEL_CONFIG_TABS.DATA)}
|
||||||
|
data-test-subj="timeSeriesEditorDataBtn"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="visTypeTimeseries.timeseries.dataTab.dataButtonLabel"
|
id="visTypeTimeseries.timeseries.dataTab.dataButtonLabel"
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { createTickFormatter } from '../../lib/tick_formatter';
|
||||||
import { TimeSeries } from '../../../visualizations/views/timeseries';
|
import { TimeSeries } from '../../../visualizations/views/timeseries';
|
||||||
import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public';
|
import { MarkdownSimple } from '../../../../../../../plugins/kibana_react/public';
|
||||||
import { replaceVars } from '../../lib/replace_vars';
|
import { replaceVars } from '../../lib/replace_vars';
|
||||||
import { getAxisLabelString } from '../../lib/get_axis_label_string';
|
|
||||||
import { getInterval } from '../../lib/get_interval';
|
import { getInterval } from '../../lib/get_interval';
|
||||||
import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter';
|
import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter';
|
||||||
import { STACKED_OPTIONS } from '../../../visualizations/constants';
|
import { STACKED_OPTIONS } from '../../../visualizations/constants';
|
||||||
|
@ -235,11 +234,15 @@ class TimeseriesVisualization extends Component {
|
||||||
legend={Boolean(model.show_legend)}
|
legend={Boolean(model.show_legend)}
|
||||||
legendPosition={model.legend_position}
|
legendPosition={model.legend_position}
|
||||||
tooltipMode={model.tooltip_mode}
|
tooltipMode={model.tooltip_mode}
|
||||||
xAxisLabel={getAxisLabelString(interval)}
|
|
||||||
xAxisFormatter={this.xAxisFormatter(interval)}
|
xAxisFormatter={this.xAxisFormatter(interval)}
|
||||||
annotations={this.prepareAnnotations()}
|
annotations={this.prepareAnnotations()}
|
||||||
syncColors={syncColors}
|
syncColors={syncColors}
|
||||||
palettesService={palettesService}
|
palettesService={palettesService}
|
||||||
|
interval={interval}
|
||||||
|
isLastBucketDropped={Boolean(
|
||||||
|
model.drop_last_bucket ||
|
||||||
|
model.series.some((series) => series.series_drop_last_bucket)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,6 +32,9 @@ import { getStackAccessors } from './utils/stack_format';
|
||||||
import { getBaseTheme, getChartClasses } from './utils/theme';
|
import { getBaseTheme, getChartClasses } from './utils/theme';
|
||||||
import { emptyLabel } from '../../../../../common/empty_label';
|
import { emptyLabel } from '../../../../../common/empty_label';
|
||||||
import { getSplitByTermsColor } from '../../../lib/get_split_by_terms_color';
|
import { getSplitByTermsColor } from '../../../lib/get_split_by_terms_color';
|
||||||
|
import { renderEndzoneTooltip } from '../../../../../../charts/public';
|
||||||
|
import { getAxisLabelString } from '../../../components/lib/get_axis_label_string';
|
||||||
|
import { calculateDomainForSeries } from './utils/series_domain_calculation';
|
||||||
|
|
||||||
const generateAnnotationData = (values, formatter) =>
|
const generateAnnotationData = (values, formatter) =>
|
||||||
values.map(({ key, docs }) => ({
|
values.map(({ key, docs }) => ({
|
||||||
|
@ -54,7 +57,6 @@ export const TimeSeries = ({
|
||||||
legend,
|
legend,
|
||||||
legendPosition,
|
legendPosition,
|
||||||
tooltipMode,
|
tooltipMode,
|
||||||
xAxisLabel,
|
|
||||||
series,
|
series,
|
||||||
yAxis,
|
yAxis,
|
||||||
onBrush,
|
onBrush,
|
||||||
|
@ -62,6 +64,8 @@ export const TimeSeries = ({
|
||||||
annotations,
|
annotations,
|
||||||
syncColors,
|
syncColors,
|
||||||
palettesService,
|
palettesService,
|
||||||
|
interval,
|
||||||
|
isLastBucketDropped,
|
||||||
}) => {
|
}) => {
|
||||||
const chartRef = useRef();
|
const chartRef = useRef();
|
||||||
// const [palettesRegistry, setPalettesRegistry] = useState(null);
|
// const [palettesRegistry, setPalettesRegistry] = useState(null);
|
||||||
|
@ -80,7 +84,17 @@ export const TimeSeries = ({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const tooltipFormatter = decorateFormatter(xAxisFormatter);
|
let tooltipFormatter = decorateFormatter(xAxisFormatter);
|
||||||
|
if (!isLastBucketDropped) {
|
||||||
|
const domainBounds = calculateDomainForSeries(series);
|
||||||
|
tooltipFormatter = renderEndzoneTooltip(
|
||||||
|
interval,
|
||||||
|
domainBounds?.domainStart,
|
||||||
|
domainBounds?.domainEnd,
|
||||||
|
xAxisFormatter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const uiSettings = getUISettings();
|
const uiSettings = getUISettings();
|
||||||
const timeZone = getTimezone(uiSettings);
|
const timeZone = getTimezone(uiSettings);
|
||||||
const hasBarChart = series.some(({ bars }) => bars?.show);
|
const hasBarChart = series.some(({ bars }) => bars?.show);
|
||||||
|
@ -281,7 +295,7 @@ export const TimeSeries = ({
|
||||||
<Axis
|
<Axis
|
||||||
id="bottom"
|
id="bottom"
|
||||||
position={Position.Bottom}
|
position={Position.Bottom}
|
||||||
title={xAxisLabel}
|
title={getAxisLabelString(interval)}
|
||||||
tickFormat={xAxisFormatter}
|
tickFormat={xAxisFormatter}
|
||||||
gridLine={{
|
gridLine={{
|
||||||
...GRID_LINE_CONFIG,
|
...GRID_LINE_CONFIG,
|
||||||
|
@ -303,10 +317,11 @@ TimeSeries.propTypes = {
|
||||||
showGrid: PropTypes.bool,
|
showGrid: PropTypes.bool,
|
||||||
legend: PropTypes.bool,
|
legend: PropTypes.bool,
|
||||||
legendPosition: PropTypes.string,
|
legendPosition: PropTypes.string,
|
||||||
xAxisLabel: PropTypes.string,
|
|
||||||
series: PropTypes.array,
|
series: PropTypes.array,
|
||||||
yAxis: PropTypes.array,
|
yAxis: PropTypes.array,
|
||||||
onBrush: PropTypes.func,
|
onBrush: PropTypes.func,
|
||||||
xAxisFormatter: PropTypes.func,
|
xAxisFormatter: PropTypes.func,
|
||||||
annotations: PropTypes.array,
|
annotations: PropTypes.array,
|
||||||
|
interval: PropTypes.number,
|
||||||
|
isLastBucketDropped: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* 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 { PanelData } from '../../../../../../common/types';
|
||||||
|
|
||||||
|
export const calculateDomainForSeries = (series: PanelData[]) => {
|
||||||
|
const seriesData = series[0]?.data || [];
|
||||||
|
|
||||||
|
return seriesData?.length
|
||||||
|
? {
|
||||||
|
domainStart: seriesData[0][0],
|
||||||
|
domainEnd: seriesData[Math.max(seriesData.length - 1, 0)][0],
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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 { calculateDomainForSeries } from './series_domain_calculation';
|
||||||
|
import { PanelData } from 'src/plugins/vis_type_timeseries/common/types';
|
||||||
|
|
||||||
|
describe('calculateDomainForSeries', () => {
|
||||||
|
it('should return 0 for domainStart and 3 for domainEnd', () => {
|
||||||
|
const series = [
|
||||||
|
{
|
||||||
|
data: [
|
||||||
|
[0, 0],
|
||||||
|
[1, 1],
|
||||||
|
[2, 2],
|
||||||
|
[3, 3],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as PanelData[];
|
||||||
|
const domainBounds = calculateDomainForSeries(series);
|
||||||
|
|
||||||
|
expect(domainBounds?.domainStart).toBe(0);
|
||||||
|
expect(domainBounds?.domainEnd).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined when series is empty', () => {
|
||||||
|
const domainBounds = calculateDomainForSeries([]);
|
||||||
|
|
||||||
|
expect(domainBounds).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -62,6 +62,7 @@ export const metricsVisDefinition = {
|
||||||
show_legend: 1,
|
show_legend: 1,
|
||||||
show_grid: 1,
|
show_grid: 1,
|
||||||
tooltip_mode: 'show_all',
|
tooltip_mode: 'show_all',
|
||||||
|
drop_last_bucket: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
editorConfig: {
|
editorConfig: {
|
||||||
|
|
|
@ -14,8 +14,9 @@ export function dropLastBucket(resp, panel, series) {
|
||||||
const shouldDropLastBucket = isLastValueTimerangeMode(panel, series);
|
const shouldDropLastBucket = isLastValueTimerangeMode(panel, series);
|
||||||
|
|
||||||
if (shouldDropLastBucket) {
|
if (shouldDropLastBucket) {
|
||||||
const seriesDropLastBucket = get(series, 'override_drop_last_bucket', 1);
|
const dropLastBucket = series.override_index_pattern
|
||||||
const dropLastBucket = get(panel, 'drop_last_bucket', seriesDropLastBucket);
|
? get(series, 'series_drop_last_bucket', 0)
|
||||||
|
: get(panel, 'drop_last_bucket', 0);
|
||||||
|
|
||||||
if (dropLastBucket) {
|
if (dropLastBucket) {
|
||||||
results.forEach((item) => {
|
results.forEach((item) => {
|
||||||
|
|
|
@ -45,6 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
await PageObjects.visualBuilder.checkMetricTabIsPresent();
|
await PageObjects.visualBuilder.checkMetricTabIsPresent();
|
||||||
await PageObjects.visualBuilder.clickPanelOptions('metric');
|
await PageObjects.visualBuilder.clickPanelOptions('metric');
|
||||||
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
||||||
|
await PageObjects.visualBuilder.setDropLastBucket(true);
|
||||||
await PageObjects.visualBuilder.clickDataTab('metric');
|
await PageObjects.visualBuilder.clickDataTab('metric');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -106,6 +107,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
await PageObjects.visualBuilder.checkTopNTabIsPresent();
|
await PageObjects.visualBuilder.checkTopNTabIsPresent();
|
||||||
await PageObjects.visualBuilder.clickPanelOptions('topN');
|
await PageObjects.visualBuilder.clickPanelOptions('topN');
|
||||||
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
||||||
|
await PageObjects.visualBuilder.setDropLastBucket(true);
|
||||||
await PageObjects.visualBuilder.clickDataTab('topN');
|
await PageObjects.visualBuilder.clickDataTab('topN');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,6 +131,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
await PageObjects.visualBuilder.checkMetricTabIsPresent();
|
await PageObjects.visualBuilder.checkMetricTabIsPresent();
|
||||||
await PageObjects.visualBuilder.clickPanelOptions('metric');
|
await PageObjects.visualBuilder.clickPanelOptions('metric');
|
||||||
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
|
||||||
|
await PageObjects.visualBuilder.setDropLastBucket(true);
|
||||||
await PageObjects.visualBuilder.clickDataTab('metric');
|
await PageObjects.visualBuilder.clickDataTab('metric');
|
||||||
await PageObjects.timePicker.setAbsoluteRange(
|
await PageObjects.timePicker.setAbsoluteRange(
|
||||||
'Sep 22, 2019 @ 00:00:00.000',
|
'Sep 22, 2019 @ 00:00:00.000',
|
||||||
|
@ -215,6 +218,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||||
const finalLegendItems = ['jpg: 106', 'css: 22', 'png: 14', 'gif: 8', 'php: 6'];
|
const finalLegendItems = ['jpg: 106', 'css: 22', 'png: 14', 'gif: 8', 'php: 6'];
|
||||||
|
|
||||||
log.debug('Group metrics by terms: extension.raw');
|
log.debug('Group metrics by terms: extension.raw');
|
||||||
|
await PageObjects.visualBuilder.clickPanelOptions('timeSeries');
|
||||||
|
await PageObjects.visualBuilder.setDropLastBucket(true);
|
||||||
|
await PageObjects.visualBuilder.clickDataTab('timeSeries');
|
||||||
await PageObjects.visualBuilder.setMetricsGroupByTerms('extension.raw');
|
await PageObjects.visualBuilder.setMetricsGroupByTerms('extension.raw');
|
||||||
await PageObjects.visChart.waitForVisualizationRenderingStabilized();
|
await PageObjects.visChart.waitForVisualizationRenderingStabilized();
|
||||||
const legendItems1 = await PageObjects.visualBuilder.getLegendItemsContent();
|
const legendItems1 = await PageObjects.visualBuilder.getLegendItemsContent();
|
||||||
|
|
|
@ -39,6 +39,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||||
);
|
);
|
||||||
await visualBuilder.markdownSwitchSubTab('options');
|
await visualBuilder.markdownSwitchSubTab('options');
|
||||||
await visualBuilder.setMetricsDataTimerangeMode('Last value');
|
await visualBuilder.setMetricsDataTimerangeMode('Last value');
|
||||||
|
await visualBuilder.setDropLastBucket(true);
|
||||||
await visualBuilder.markdownSwitchSubTab('markdown');
|
await visualBuilder.markdownSwitchSubTab('markdown');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
||||||
await visualBuilder.checkTableTabIsPresent();
|
await visualBuilder.checkTableTabIsPresent();
|
||||||
await visualBuilder.clickPanelOptions('table');
|
await visualBuilder.clickPanelOptions('table');
|
||||||
await visualBuilder.setMetricsDataTimerangeMode('Last value');
|
await visualBuilder.setMetricsDataTimerangeMode('Last value');
|
||||||
|
await visualBuilder.setDropLastBucket(true);
|
||||||
await visualBuilder.clickDataTab('table');
|
await visualBuilder.clickDataTab('table');
|
||||||
await visualBuilder.selectGroupByField('machine.os.raw');
|
await visualBuilder.selectGroupByField('machine.os.raw');
|
||||||
await visualBuilder.setColumnLabelValue('OS');
|
await visualBuilder.setColumnLabelValue('OS');
|
||||||
|
|
|
@ -26,6 +26,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||||
describe('Time Series', () => {
|
describe('Time Series', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await visualBuilder.resetPage();
|
await visualBuilder.resetPage();
|
||||||
|
await visualBuilder.clickPanelOptions('timeSeries');
|
||||||
|
await visualBuilder.setDropLastBucket(true);
|
||||||
|
await visualBuilder.clickDataTab('timeSeries');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render all necessary components', async () => {
|
it('should render all necessary components', async () => {
|
||||||
|
|
|
@ -69,8 +69,8 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
expect(series).to.have.property('id', 'user');
|
expect(series).to.have.property('id', 'user');
|
||||||
expect(series).to.have.property('data');
|
expect(series).to.have.property('data');
|
||||||
const datapoint = last(series.data) as any;
|
const datapoint = last(series.data) as any;
|
||||||
expect(datapoint).to.have.property('timestamp', 1547571720000);
|
expect(datapoint).to.have.property('timestamp', 1547571780000);
|
||||||
expect(datapoint).to.have.property('value', 0.0018333333333333333);
|
expect(datapoint).to.have.property('value', 0.0015);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue