[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:
Diana Derevyankina 2021-04-30 11:48:10 +03:00 committed by GitHub
parent e297fec23e
commit 6b6ad111c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 98 additions and 11 deletions

View file

@ -113,7 +113,7 @@ export const IndexPattern = ({
const defaults = {
[indexPatternName]: '',
[intervalName]: AUTO_INTERVAL,
[dropBucketName]: 1,
[dropBucketName]: 0,
[maxBarsName]: config.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
[TIME_RANGE_MODE_KEY]: timeRangeOptions[0].value,
};

View file

@ -406,6 +406,7 @@ export class TimeseriesPanelConfig extends Component<
<EuiTab
isSelected={selectedTab === PANEL_CONFIG_TABS.DATA}
onClick={() => this.switchTab(PANEL_CONFIG_TABS.DATA)}
data-test-subj="timeSeriesEditorDataBtn"
>
<FormattedMessage
id="visTypeTimeseries.timeseries.dataTab.dataButtonLabel"

View file

@ -17,7 +17,6 @@ import { createTickFormatter } from '../../lib/tick_formatter';
import { TimeSeries } from '../../../visualizations/views/timeseries';
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 { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter';
import { STACKED_OPTIONS } from '../../../visualizations/constants';
@ -235,11 +234,15 @@ class TimeseriesVisualization extends Component {
legend={Boolean(model.show_legend)}
legendPosition={model.legend_position}
tooltipMode={model.tooltip_mode}
xAxisLabel={getAxisLabelString(interval)}
xAxisFormatter={this.xAxisFormatter(interval)}
annotations={this.prepareAnnotations()}
syncColors={syncColors}
palettesService={palettesService}
interval={interval}
isLastBucketDropped={Boolean(
model.drop_last_bucket ||
model.series.some((series) => series.series_drop_last_bucket)
)}
/>
</div>
</div>

View file

@ -32,6 +32,9 @@ import { getStackAccessors } from './utils/stack_format';
import { getBaseTheme, getChartClasses } from './utils/theme';
import { emptyLabel } from '../../../../../common/empty_label';
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) =>
values.map(({ key, docs }) => ({
@ -54,7 +57,6 @@ export const TimeSeries = ({
legend,
legendPosition,
tooltipMode,
xAxisLabel,
series,
yAxis,
onBrush,
@ -62,6 +64,8 @@ export const TimeSeries = ({
annotations,
syncColors,
palettesService,
interval,
isLastBucketDropped,
}) => {
const chartRef = useRef();
// 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 timeZone = getTimezone(uiSettings);
const hasBarChart = series.some(({ bars }) => bars?.show);
@ -281,7 +295,7 @@ export const TimeSeries = ({
<Axis
id="bottom"
position={Position.Bottom}
title={xAxisLabel}
title={getAxisLabelString(interval)}
tickFormat={xAxisFormatter}
gridLine={{
...GRID_LINE_CONFIG,
@ -303,10 +317,11 @@ TimeSeries.propTypes = {
showGrid: PropTypes.bool,
legend: PropTypes.bool,
legendPosition: PropTypes.string,
xAxisLabel: PropTypes.string,
series: PropTypes.array,
yAxis: PropTypes.array,
onBrush: PropTypes.func,
xAxisFormatter: PropTypes.func,
annotations: PropTypes.array,
interval: PropTypes.number,
isLastBucketDropped: PropTypes.bool,
};

View file

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

View file

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

View file

@ -62,6 +62,7 @@ export const metricsVisDefinition = {
show_legend: 1,
show_grid: 1,
tooltip_mode: 'show_all',
drop_last_bucket: 0,
},
},
editorConfig: {

View file

@ -14,8 +14,9 @@ export function dropLastBucket(resp, panel, series) {
const shouldDropLastBucket = isLastValueTimerangeMode(panel, series);
if (shouldDropLastBucket) {
const seriesDropLastBucket = get(series, 'override_drop_last_bucket', 1);
const dropLastBucket = get(panel, 'drop_last_bucket', seriesDropLastBucket);
const dropLastBucket = series.override_index_pattern
? get(series, 'series_drop_last_bucket', 0)
: get(panel, 'drop_last_bucket', 0);
if (dropLastBucket) {
results.forEach((item) => {

View file

@ -45,6 +45,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visualBuilder.checkMetricTabIsPresent();
await PageObjects.visualBuilder.clickPanelOptions('metric');
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
await PageObjects.visualBuilder.setDropLastBucket(true);
await PageObjects.visualBuilder.clickDataTab('metric');
});
@ -106,6 +107,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visualBuilder.checkTopNTabIsPresent();
await PageObjects.visualBuilder.clickPanelOptions('topN');
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
await PageObjects.visualBuilder.setDropLastBucket(true);
await PageObjects.visualBuilder.clickDataTab('topN');
});
@ -129,6 +131,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visualBuilder.checkMetricTabIsPresent();
await PageObjects.visualBuilder.clickPanelOptions('metric');
await PageObjects.visualBuilder.setMetricsDataTimerangeMode('Last value');
await PageObjects.visualBuilder.setDropLastBucket(true);
await PageObjects.visualBuilder.clickDataTab('metric');
await PageObjects.timePicker.setAbsoluteRange(
'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'];
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.visChart.waitForVisualizationRenderingStabilized();
const legendItems1 = await PageObjects.visualBuilder.getLegendItemsContent();

View file

@ -39,6 +39,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
);
await visualBuilder.markdownSwitchSubTab('options');
await visualBuilder.setMetricsDataTimerangeMode('Last value');
await visualBuilder.setDropLastBucket(true);
await visualBuilder.markdownSwitchSubTab('markdown');
});

View file

@ -26,6 +26,7 @@ export default function ({ getPageObjects }: FtrProviderContext) {
await visualBuilder.checkTableTabIsPresent();
await visualBuilder.clickPanelOptions('table');
await visualBuilder.setMetricsDataTimerangeMode('Last value');
await visualBuilder.setDropLastBucket(true);
await visualBuilder.clickDataTab('table');
await visualBuilder.selectGroupByField('machine.os.raw');
await visualBuilder.setColumnLabelValue('OS');

View file

@ -26,6 +26,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('Time Series', () => {
beforeEach(async () => {
await visualBuilder.resetPage();
await visualBuilder.clickPanelOptions('timeSeries');
await visualBuilder.setDropLastBucket(true);
await visualBuilder.clickDataTab('timeSeries');
});
it('should render all necessary components', async () => {

View file

@ -69,8 +69,8 @@ export default function ({ getService }: FtrProviderContext) {
expect(series).to.have.property('id', 'user');
expect(series).to.have.property('data');
const datapoint = last(series.data) as any;
expect(datapoint).to.have.property('timestamp', 1547571720000);
expect(datapoint).to.have.property('value', 0.0018333333333333333);
expect(datapoint).to.have.property('timestamp', 1547571780000);
expect(datapoint).to.have.property('value', 0.0015);
});
});