Fix auto time interval preselection if disabled (#85937) (#86115)

* Hide auto interval when it is filtered

* Add unit tests

* Fix types
This commit is contained in:
Daniil 2020-12-17 11:00:56 +03:00 committed by GitHub
parent f041891336
commit 01135f6a4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 228 additions and 11 deletions

View file

@ -0,0 +1,60 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TimeIntervalParamEditor should match snapshot 1`] = `
<EuiFormRow
describedByIds={Array []}
display="rowCompressed"
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
helpText={
<React.Fragment>
<FormattedMessage
defaultMessage="Select an option or create a custom value. Examples: 30s, 20m, 24h, 2d, 1w, 1M"
id="visDefaultEditor.controls.timeInterval.selectOptionHelpText"
values={Object {}}
/>
</React.Fragment>
}
isInvalid={true}
label="Minimum interval"
labelType="label"
>
<EuiComboBox
async={false}
compressed={true}
data-test-subj="visEditorInterval"
fullWidth={true}
isClearable={true}
isInvalid={true}
noSuggestions={false}
onBlur={[MockFunction]}
onChange={[Function]}
onCreateOption={[Function]}
options={
Array [
Object {
"key": "auto",
"label": "Auto",
},
Object {
"key": "ms",
"label": "Millisecond",
},
Object {
"key": "s",
"label": "Second",
},
]
}
placeholder="Select an interval"
selectedOptions={Array []}
singleSelection={
Object {
"asPlainText": true,
}
}
sortMatchesBy="none"
/>
</EuiFormRow>
`;

View file

@ -0,0 +1,155 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { shallow } from 'enzyme';
import { TimeIntervalParamEditor } from './time_interval';
import { aggParamCommonPropsMock } from './test_utils';
import { AggParamEditorProps } from '../agg_param_props';
jest.mock('../../../../data/public', () => ({
search: {
aggs: {
isValidInterval: jest.fn().mockReturnValue(true),
parseEsInterval: jest.fn(),
InvalidEsCalendarIntervalError: class {},
},
},
}));
import { search } from '../../../../data/public';
describe('TimeIntervalParamEditor', () => {
let props: AggParamEditorProps<string>;
beforeEach(() => {
props = {
...aggParamCommonPropsMock,
showValidation: true,
setTouched: jest.fn(),
setValidity: jest.fn(),
setValue: jest.fn(),
};
props.aggParam.options = [
{
display: 'Auto',
enabled: jest.fn().mockReturnValue(true),
val: 'auto',
},
{
display: 'Millisecond',
val: 'ms',
},
{
display: 'Second',
val: 's',
},
];
});
test('should match snapshot', () => {
const comp = shallow(<TimeIntervalParamEditor {...props} />);
expect(comp).toMatchSnapshot();
});
describe('interval "auto" value', () => {
test('should convert interval options into combobox options', () => {
props.value = 'auto';
const comp = shallow(<TimeIntervalParamEditor {...props} />);
expect(comp.children().prop('selectedOptions')).toEqual([
{
key: 'auto',
label: 'Auto',
},
]);
expect(comp.children().prop('options')).toEqual([
{
key: 'auto',
label: 'Auto',
},
{
key: 'ms',
label: 'Millisecond',
},
{
key: 's',
label: 'Second',
},
]);
expect(comp.prop('isInvalid')).toBeFalsy();
});
test('should filter out "auto" interval value if it is disabled in options and mark as invalid', () => {
props.aggParam.options[0].enabled = jest.fn().mockReturnValue(false);
props.value = 'auto';
const comp = shallow(<TimeIntervalParamEditor {...props} />);
expect(props.aggParam.options[0].enabled).toHaveBeenCalledWith(props.agg);
expect(comp.prop('isInvalid')).toBeTruthy();
expect(comp.children().prop('selectedOptions')).toEqual([]);
expect(comp.children().prop('options')).toEqual([
{
key: 'ms',
label: 'Millisecond',
},
{
key: 's',
label: 'Second',
},
]);
});
});
describe('custom interval value', () => {
test('should have valid "2h" interval selected', () => {
props.value = '2h';
// @ts-ignore
props.agg.buckets = {
getInterval: jest.fn().mockReturnValue({
expression: '2h',
}),
};
const comp = shallow(<TimeIntervalParamEditor {...props} />);
expect(comp.prop('isInvalid')).toBeFalsy();
expect(comp.prop('error')).toBeUndefined();
expect(comp.children().prop('selectedOptions')).toEqual([{ label: '2h', key: 'custom' }]);
});
test('should have invalid calendar interval "3w"', () => {
props.value = '3w';
// @ts-ignore
props.agg.buckets = {
getInterval: jest.fn().mockReturnValue({
expression: '3w',
}),
};
// @ts-expect-error
search.aggs.isValidInterval.mockReturnValue(false);
const comp = shallow(<TimeIntervalParamEditor {...props} />);
expect(comp.prop('isInvalid')).toBeTruthy();
expect(comp.prop('error')).toBeDefined();
expect(comp.children().prop('selectedOptions')).toEqual([{ label: '3w', key: 'custom' }]);
});
});
});

View file

@ -53,6 +53,7 @@ const errorMessage = i18n.translate(
defaultMessage: 'Invalid interval format.',
}
);
const autoInterval = 'auto';
function validateInterval(
agg: any,
@ -64,7 +65,7 @@ function validateInterval(
return { isValid: true, interval: agg.buckets?.getInterval() };
}
if (!value) {
if (!value || (value === autoInterval && !definedOption)) {
return { isValid: false };
}
@ -110,21 +111,22 @@ function TimeIntervalParamEditor({
const timeBase: string = get(editorConfig, 'interval.timeBase') as string;
const options = timeBase
? []
: ((aggParam as any).options || []).reduce(
(filtered: ComboBoxOption[], option: AggParamOption) => {
if (option.enabled ? option.enabled(agg) : true) {
filtered.push({ label: option.display, key: option.val });
}
return filtered;
},
[] as ComboBoxOption[]
);
: (aggParam.options || []).reduce((filtered: ComboBoxOption[], option: AggParamOption) => {
if (option.enabled ? option.enabled(agg) : true) {
filtered.push({ label: option.display, key: option.val });
}
return filtered;
}, []);
let selectedOptions: ComboBoxOption[] = [];
let definedOption: ComboBoxOption | undefined;
if (value) {
definedOption = find(options, { key: value });
selectedOptions = definedOption ? [definedOption] : [{ label: value, key: 'custom' }];
selectedOptions = definedOption
? [definedOption]
: value === autoInterval
? []
: [{ label: value, key: 'custom' }];
}
const { isValid, error, interval } = validateInterval(agg, value, definedOption, timeBase);