Auto interval on date histogram is getting displayed as timestamp per… (#59171)

* 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 <elasticmachine@users.noreply.github.com>
This commit is contained in:
DziyanaDzeraviankina 2020-03-31 17:17:09 +03:00 committed by GitHub
parent d73671e2b5
commit f29a5dd05b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 13 deletions

View file

@ -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<AggsAction>;
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)) {

View file

@ -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}
/>
)}
</EuiDraggable>

View file

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

View file

@ -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 ? (
<strong className="eui-displayBlock">
<strong data-test-subj="currentlyScaledText" className="eui-displayBlock">
<FormattedMessage
id="visDefaultEditor.controls.timeInterval.scaledHelpText"
defaultMessage="Currently scaled to {bucketDescription}"

View file

@ -39,6 +39,7 @@ import {
} from './state';
import { AddSchema, ReorderAggs, DefaultEditorAggCommonProps } from '../agg_common_props';
import { ISchemas } from '../../schemas';
import { TimeRange } from '../../../../../../plugins/data/public';
import { EditorVisState } from './state/reducers';
export interface DefaultEditorDataTabProps {
@ -51,6 +52,7 @@ export interface DefaultEditorDataTabProps {
setTouched(isTouched: boolean): void;
setValidity(modelName: string, value: boolean): void;
setStateValue: DefaultEditorAggCommonProps['setStateParamValue'];
timeRange: TimeRange;
}
function DefaultEditorDataTab({
@ -62,6 +64,7 @@ function DefaultEditorDataTab({
setTouched,
setValidity,
setStateValue,
timeRange,
}: DefaultEditorDataTabProps) {
const lastParentPipelineAgg = useMemo(
() =>
@ -128,6 +131,7 @@ function DefaultEditorDataTab({
<DefaultEditorAggGroup
groupName={AggGroupNames.Buckets}
schemas={schemas.buckets}
timeRange={timeRange}
{...commonProps}
/>
</>

View file

@ -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({
<Editor
isTabSelected={isTabSelected}
{...(name === 'data' ? dataTabProps : optionTabProps)}
timeRange={timeRange}
/>
</div>
);

View file

@ -92,6 +92,7 @@ function DefaultEditor({
uiState={uiState}
isLinkedSearch={linked}
savedSearch={savedSearch}
timeRange={timeRange}
eventEmitter={eventEmitter}
/>
</Panel>

View file

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

View file

@ -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": {