mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
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:
parent
d73671e2b5
commit
f29a5dd05b
9 changed files with 132 additions and 13 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -92,6 +92,7 @@ function DefaultEditor({
|
|||
uiState={uiState}
|
||||
isLinkedSearch={linked}
|
||||
savedSearch={savedSearch}
|
||||
timeRange={timeRange}
|
||||
eventEmitter={eventEmitter}
|
||||
/>
|
||||
</Panel>
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue