From 0d50ea332f5a58ae54048a864a87b70019bb11a5 Mon Sep 17 00:00:00 2001 From: DziyanaDzeraviankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Wed, 1 Apr 2020 00:22:31 +0300 Subject: [PATCH] =?UTF-8?q?Auto=20interval=20on=20date=20histogram=20is=20?= =?UTF-8?q?getting=20displayed=20as=20timestamp=20per=E2=80=A6=20(#59171)?= =?UTF-8?q?=20(#61992)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Auto interval on date histogram is getting displayed as timestamp per 0 milliseconds when x-axis bucket is collapsed Closes #57822 * Fixed incorrect interval label displaying while scaling the chart. * Updated agg.test.tsx snapshot * Got rid of context and refactored agg hooks * Fixed agg.type.name check * Added functional tests to cover the date histogram interval editing * Fixed some expected values in tests * Updated some test cases * Added a new visualization to visualize archive * Added testSubjects service to replace find where possible * Updated tests to match updated behavior Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../public/components/agg.tsx | 21 +++--- .../public/components/agg_group.tsx | 4 ++ .../public/components/agg_params_helper.ts | 15 +++- .../components/controls/time_interval.tsx | 4 +- .../public/components/sidebar/data_tab.tsx | 4 ++ .../public/components/sidebar/sidebar.tsx | 4 ++ .../public/default_editor.tsx | 1 + test/functional/apps/visualize/_area_chart.js | 71 +++++++++++++++++++ .../fixtures/es_archiver/visualize/data.json | 21 ++++++ 9 files changed, 132 insertions(+), 13 deletions(-) diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx index 2a452732076233..83fbf70c9099ef 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx @@ -35,6 +35,8 @@ import { AGGS_ACTION_KEYS, AggsAction } from './agg_group_state'; import { RowsOrColumnsControl } from './controls/rows_or_columns'; import { RadiusRatioOptionControl } from './controls/radius_ratio_option'; import { getSchemaByName } from '../schemas'; +import { TimeRange } from '../../../../../plugins/data/public'; +import { buildAggDescription } from './agg_params_helper'; export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { agg: IAggConfig; @@ -46,6 +48,7 @@ export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { isLastBucket: boolean; isRemovable: boolean; setAggsState: React.Dispatch; + timeRange?: TimeRange; } function DefaultEditorAgg({ @@ -69,6 +72,7 @@ function DefaultEditorAgg({ removeAgg, setAggsState, schemas, + timeRange, }: DefaultEditorAggProps) { const [isEditorOpen, setIsEditorOpen] = useState((agg as any).brandNew); const [validState, setValidState] = useState(true); @@ -103,18 +107,15 @@ function DefaultEditorAgg({ } } - // A description of the aggregation, for displaying in the collapsed agg header - let aggDescription = ''; + const [aggDescription, setAggDescription] = useState(buildAggDescription(agg)); - if (agg.type && agg.type.makeLabel) { - try { - aggDescription = agg.type.makeLabel(agg); - } catch (e) { - // Date Histogram's `makeLabel` implementation invokes 'write' method for each param, including interval's 'write', - // which throws an error when interval is undefined. - aggDescription = ''; + // This useEffect is required to update the timeRange value and initiate rerender to keep labels up to date (Issue #57822). + useEffect(() => { + if (timeRange && aggName === 'date_histogram') { + agg.aggConfigs.setTimeRange(timeRange); } - } + setAggDescription(buildAggDescription(agg)); + }, [agg, aggName, timeRange]); useEffect(() => { if (isLastBucketAgg && ['date_histogram', 'histogram'].includes(aggName)) { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx index 08b69ef37f5286..f50abc3ebb599a 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx @@ -42,6 +42,7 @@ import { } from './agg_group_helper'; import { aggGroupReducer, initAggsState, AGGS_ACTION_KEYS } from './agg_group_state'; import { Schema, getSchemasByGroup } from '../schemas'; +import { TimeRange } from '../../../../../plugins/data/public'; export interface DefaultEditorAggGroupProps extends DefaultEditorAggCommonProps { schemas: Schema[]; @@ -49,6 +50,7 @@ export interface DefaultEditorAggGroupProps extends DefaultEditorAggCommonProps reorderAggs: ReorderAggs; setValidity(modelName: string, value: boolean): void; setTouched(isTouched: boolean): void; + timeRange?: TimeRange; } function DefaultEditorAggGroup({ @@ -67,6 +69,7 @@ function DefaultEditorAggGroup({ reorderAggs, setTouched, setValidity, + timeRange, }: DefaultEditorAggGroupProps) { const groupNameLabel = (search.aggs.aggGroupNamesMap() as any)[groupName]; // e.g. buckets can have no aggs @@ -185,6 +188,7 @@ function DefaultEditorAggGroup({ removeAgg={removeAgg} setAggsState={setAggsState} schemas={schemas} + timeRange={timeRange} /> )} diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts index 10590e1a59f4ac..073cb7d5ac66c2 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts +++ b/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts @@ -174,4 +174,17 @@ function isInvalidParamsTouched( return invalidParams.every(param => param.touched); } -export { getAggParamsToRender, getAggTypeOptions, isInvalidParamsTouched }; +function buildAggDescription(agg: IAggConfig) { + let description = ''; + if (agg.type && agg.type.makeLabel) { + try { + description = agg.type.makeLabel(agg); + } catch (e) { + // Date Histogram's `makeLabel` implementation invokes 'write' method for each param, including interval's 'write', + // which throws an error when interval is undefined. + } + } + return description; +} + +export { getAggParamsToRender, getAggTypeOptions, isInvalidParamsTouched, buildAggDescription }; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx index 9aacd6a10262fa..971a62faf7d7c9 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx @@ -61,7 +61,7 @@ function validateInterval( timeBase?: string ) { if (definedOption) { - return { isValid: true }; + return { isValid: true, interval: agg.buckets?.getInterval() }; } if (!value) { @@ -131,7 +131,7 @@ function TimeIntervalParamEditor({ const scaledHelpText = interval && interval.scaled ? ( - + @@ -128,6 +131,7 @@ function DefaultEditorDataTab({ diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index 29039715066be8..071e10682e22c2 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -33,6 +33,7 @@ import { PersistedState } from '../../../../../../plugins/visualizations/public' import { SavedSearch } from '../../../../../../plugins/discover/public'; import { AggGroupNames } from '../../../../../../plugins/data/public'; import { getSchemasByGroup } from '../../schemas'; +import { TimeRange } from '../../../../../../plugins/data/public'; interface DefaultEditorSideBarProps { isCollapsed: boolean; @@ -43,6 +44,7 @@ interface DefaultEditorSideBarProps { isLinkedSearch: boolean; eventEmitter: EventEmitter; savedSearch?: SavedSearch; + timeRange: TimeRange; } function DefaultEditorSideBar({ @@ -54,6 +56,7 @@ function DefaultEditorSideBar({ isLinkedSearch, eventEmitter, savedSearch, + timeRange, }: DefaultEditorSideBarProps) { const [selectedTab, setSelectedTab] = useState(optionTabs[0].name); const [isDirty, setDirty] = useState(false); @@ -214,6 +217,7 @@ function DefaultEditorSideBar({ ); diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx index b504dfd6a55e95..899b9c1b5fd6eb 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx +++ b/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx @@ -92,6 +92,7 @@ function DefaultEditor({ uiState={uiState} isLinkedSearch={linked} savedSearch={savedSearch} + timeRange={timeRange} eventEmitter={eventEmitter} /> diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index ebbeb01cbc9177..8f2012d7f184d0 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -554,5 +554,76 @@ export default function({ getService, getPageObjects }) { }); }); }); + + describe('date histogram interval', () => { + before(async () => { + await PageObjects.visualize.loadSavedVisualization('Visualization AreaChart'); + await PageObjects.visChart.waitForVisualization(); + }); + + beforeEach(async () => { + const fromTime = 'Sep 20, 2015 @ 00:00:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }); + + it('should update collapsed accordion label when time range is changed', async () => { + const accordionLabel = await find.byCssSelector( + '[data-test-subj="visEditorAggAccordion2"] .visEditorSidebar__aggGroupAccordionButtonContent' + ); + let accordionLabelText = await accordionLabel.getVisibleText(); + expect(accordionLabelText).to.include.string('per 30 minutes'); + const fromTime = 'Sep 20, 2015 @ 08:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + accordionLabelText = await accordionLabel.getVisibleText(); + expect(accordionLabelText).to.include.string('per 10 minutes'); + }); + + describe('expanded accordion', () => { + before(async () => await PageObjects.visEditor.toggleAccordion('visEditorAggAccordion2')); + + it('should update label inside the opened accordion when scaled to milliseconds', async () => { + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(false); + await PageObjects.visEditor.setInterval('Millisecond'); + const helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + }); + + it('should display updated scaled label text after time range is changed', async () => { + await PageObjects.visEditor.setInterval('Millisecond'); + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(true); + let helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + const fromTime = 'Sep 20, 2015 @ 22:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 30 seconds'); + }); + + it('should update scaled label text after custom interval is set and time range is changed', async () => { + await PageObjects.visEditor.setInterval('10s', { type: 'custom' }); + await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + const isHelperScaledLabelExists = await find.existsByCssSelector( + '[data-test-subj="currentlyScaledText"]' + ); + expect(isHelperScaledLabelExists).to.be(true); + let helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to 10 minutes'); + const fromTime = 'Sep 20, 2015 @ 21:30:00.000'; + const toTime = 'Sep 20, 2015 @ 23:30:00.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + helperScaledLabelText = await testSubjects.getVisibleText('currentlyScaledText'); + expect(helperScaledLabelText).to.include.string('to minute'); + }); + }); + }); }); } diff --git a/test/functional/fixtures/es_archiver/visualize/data.json b/test/functional/fixtures/es_archiver/visualize/data.json index 845e9a5e088257..abca5a98bf7fd2 100644 --- a/test/functional/fixtures/es_archiver/visualize/data.json +++ b/test/functional/fixtures/es_archiver/visualize/data.json @@ -69,6 +69,27 @@ } } +{ + "type": "doc", + "value": { + "id": "visualization:Visualization-AreaChart", + "index": ".kibana", + "source": { + "type": "visualization", + "visualization": { + "description": "AreaChart", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}" + }, + "title": "Visualization AreaChart", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"Visualization AreaChart\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"labels\":{}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"timeRange\":{\"from\":\"now-15m\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"auto\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{}}}]}" + } + } + } +} + { "type": "doc", "value": {