[Lens] Refactor param editor to use layers (#86499) (#86656)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Wylie Conlon 2020-12-21 13:57:14 -05:00 committed by GitHub
parent 0c872a70a3
commit 0f77d66489
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 808 additions and 998 deletions

View file

@ -107,7 +107,15 @@ export function DimensionEditor(props: DimensionEditorProps) {
layerId,
currentIndexPattern,
hideGrouping,
dateRange,
} = props;
const services = {
data: props.data,
uiSettings: props.uiSettings,
savedObjectsClient: props.savedObjectsClient,
http: props.http,
storage: props.storage,
};
const { fieldByOperation, operationWithoutField } = operationSupportMatrix;
const setStateWrapper = (layer: IndexPatternLayer) => {
@ -346,17 +354,13 @@ export function DimensionEditor(props: DimensionEditorProps) {
{!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ParamEditor && (
<>
<ParamEditor
state={state}
setState={setState}
layer={state.layers[layerId]}
updateLayer={setStateWrapper}
columnId={columnId}
currentColumn={state.layers[layerId].columns[columnId]}
storage={props.storage}
uiSettings={props.uiSettings}
savedObjectsClient={props.savedObjectsClient}
layerId={layerId}
http={props.http}
dateRange={props.dateRange}
data={props.data}
dateRange={dateRange}
indexPattern={currentIndexPattern}
{...services}
/>
</>
)}
@ -407,12 +411,15 @@ export function DimensionEditor(props: DimensionEditorProps) {
selectedColumn={selectedColumn}
onChange={(newFormat) => {
setState(
updateColumnParam({
mergeLayer({
state,
layerId,
currentColumn: selectedColumn,
paramName: 'format',
value: newFormat,
newLayer: updateColumnParam({
layer: state.layers[layerId],
columnId,
paramName: 'format',
value: newFormat,
}),
})
);
}}

View file

@ -111,10 +111,10 @@ export const movingAverageOperation: OperationDefinition<
};
function MovingAverageParamEditor({
state,
setState,
layer,
updateLayer,
currentColumn,
layerId,
columnId,
}: ParamEditorProps<MovingAverageIndexPatternColumn>) {
const [inputValue, setInputValue] = useState(String(currentColumn.params.window));
@ -124,11 +124,10 @@ function MovingAverageParamEditor({
return;
}
const inputNumber = Number(inputValue);
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'window',
value: inputNumber,
})

View file

@ -5,19 +5,19 @@
*/
import React from 'react';
import { DateHistogramIndexPatternColumn } from './date_histogram';
import type { DateHistogramIndexPatternColumn } from './date_histogram';
import { dateHistogramOperation } from './index';
import { shallow } from 'enzyme';
import { EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import type { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { UI_SETTINGS } from '../../../../../../../src/plugins/data/public';
import {
dataPluginMock,
getCalculateAutoTimeExpression,
} from '../../../../../../../src/plugins/data/public/mocks';
import { createMockedIndexPattern } from '../../mocks';
import { IndexPatternPrivateState } from '../../types';
import type { IndexPatternLayer, IndexPattern } from '../../types';
import { getFieldByNameFactory } from '../../pure_helpers';
const dataStart = dataPluginMock.createStartContract();
@ -29,6 +29,60 @@ dataStart.search.aggs.calculateAutoTimeExpression = getCalculateAutoTimeExpressi
}
);
const indexPattern1: IndexPattern = {
id: '1',
title: 'Mock Indexpattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
displayName: 'timestampLabel',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
],
getFieldByName: getFieldByNameFactory([
{
name: 'timestamp',
displayName: 'timestampLabel',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
]),
};
const indexPattern2: IndexPattern = {
id: '2',
title: 'Mock Indexpattern 2',
hasRestrictions: false,
fields: [
{
name: 'other_timestamp',
displayName: 'other_timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
],
getFieldByName: getFieldByNameFactory([
{
name: 'other_timestamp',
displayName: 'other_timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
]),
};
const defaultOptions = {
storage: {} as IStorageWrapper,
uiSettings: {} as IUiSettingsClient,
@ -39,150 +93,47 @@ const defaultOptions = {
},
data: dataStart,
http: {} as HttpSetup,
indexPattern: indexPattern1,
};
describe('date_histogram', () => {
let state: IndexPatternPrivateState;
let layer: IndexPatternLayer;
const InlineOptions = dateHistogramOperation.paramEditor!;
beforeEach(() => {
state = {
indexPatternRefs: [],
existingFields: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
indexPatterns: {
1: {
id: '1',
title: 'Mock Indexpattern',
timeFieldName: 'timestamp',
hasRestrictions: false,
fields: [
{
name: 'timestamp',
displayName: 'timestampLabel',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
],
layer = {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
getFieldByName: getFieldByNameFactory([
{
name: 'timestamp',
displayName: 'timestampLabel',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
]),
},
2: {
id: '2',
title: 'Mock Indexpattern 2',
hasRestrictions: false,
fields: [
{
name: 'other_timestamp',
displayName: 'other_timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
],
getFieldByName: getFieldByNameFactory([
{
name: 'other_timestamp',
displayName: 'other_timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
},
]),
},
},
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: '42w',
},
sourceField: 'timestamp',
},
},
},
second: {
indexPatternId: '2',
columnOrder: ['col2'],
columns: {
col2: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: 'd',
},
sourceField: 'other_timestamp',
},
},
},
third: {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: 'auto',
},
sourceField: 'timestamp',
},
// Private
operationType: 'date_histogram',
params: {
interval: '42w',
},
sourceField: 'timestamp',
},
},
};
});
function stateWithInterval(interval: string) {
function layerWithInterval(interval: string) {
return ({
...state,
layers: {
...state.layers,
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
interval,
},
},
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
interval,
},
},
},
} as unknown) as IndexPatternPrivateState;
} as unknown) as IndexPatternLayer;
}
describe('buildColumn', () => {
@ -246,9 +197,9 @@ describe('date_histogram', () => {
describe('toEsAggsFn', () => {
it('should reflect params correctly', () => {
const esAggsFn = dateHistogramOperation.toEsAggsFn(
state.layers.first.columns.col1 as DateHistogramIndexPatternColumn,
layer.columns.col1 as DateHistogramIndexPatternColumn,
'col1',
state.indexPatterns['1']
indexPattern1
);
expect(esAggsFn).toEqual(
expect.objectContaining({
@ -263,10 +214,10 @@ describe('date_histogram', () => {
it('should not use normalized es interval for rollups', () => {
const esAggsFn = dateHistogramOperation.toEsAggsFn(
state.layers.first.columns.col1 as DateHistogramIndexPatternColumn,
layer.columns.col1 as DateHistogramIndexPatternColumn,
'col1',
{
...state.indexPatterns['1'],
...indexPattern1,
fields: [
{
name: 'timestamp',
@ -465,15 +416,14 @@ describe('date_histogram', () => {
describe('param editor', () => {
it('should render current value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
@ -482,15 +432,34 @@ describe('date_histogram', () => {
});
it('should render current value for other index pattern', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const secondLayer: IndexPatternLayer = {
indexPatternId: '2',
columnOrder: ['col2'],
columns: {
col2: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: 'd',
},
sourceField: 'other_timestamp',
},
},
};
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={secondLayer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={state.layers.second.columns.col2 as DateHistogramIndexPatternColumn}
layerId="second"
currentColumn={secondLayer.columns.col2 as DateHistogramIndexPatternColumn}
indexPattern={indexPattern2}
/>
);
@ -499,14 +468,33 @@ describe('date_histogram', () => {
});
it('should render disabled switch and no time interval control for auto interval', () => {
const thirdLayer: IndexPatternLayer = {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: 'auto',
},
sourceField: 'timestamp',
},
},
};
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={jest.fn()}
layer={thirdLayer}
updateLayer={jest.fn()}
columnId="col1"
currentColumn={state.layers.third.columns.col1 as DateHistogramIndexPatternColumn}
layerId="third"
currentColumn={thirdLayer.columns.col1 as DateHistogramIndexPatternColumn}
indexPattern={indexPattern1}
/>
);
expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy();
@ -515,35 +503,52 @@ describe('date_histogram', () => {
});
it('should allow switching to manual interval', () => {
const setStateSpy = jest.fn();
const thirdLayer: IndexPatternLayer = {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: {
label: 'Value of timestamp',
dataType: 'date',
isBucketed: true,
// Private
operationType: 'date_histogram',
params: {
interval: 'auto',
},
sourceField: 'timestamp',
},
},
};
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={thirdLayer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="third"
currentColumn={state.layers.third.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={thirdLayer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
instance.find(EuiSwitch).prop('onChange')!({
target: { checked: true },
} as EuiSwitchEvent);
expect(setStateSpy).toHaveBeenCalled();
const newState = setStateSpy.mock.calls[0][0];
expect(newState).toHaveProperty('layers.third.columns.col1.params.interval', '30d');
expect(updateLayerSpy).toHaveBeenCalled();
const newLayer = updateLayerSpy.mock.calls[0][0];
expect(newLayer).toHaveProperty('columns.col1.params.interval', '30d');
});
it('should force calendar values to 1', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({
@ -551,67 +556,63 @@ describe('date_histogram', () => {
value: '2',
},
} as React.ChangeEvent<HTMLInputElement>);
expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('1w'));
expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('1w'));
});
it('should display error if an invalid interval is specified', () => {
const setStateSpy = jest.fn();
const testState = stateWithInterval('4quid');
const updateLayerSpy = jest.fn();
const testLayer = layerWithInterval('4quid');
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={testState}
setState={setStateSpy}
layer={testLayer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={testState.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy();
});
it('should not display error if interval value is blank', () => {
const setStateSpy = jest.fn();
const testState = stateWithInterval('d');
const updateLayerSpy = jest.fn();
const testLayer = layerWithInterval('d');
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={testState}
setState={setStateSpy}
layer={testLayer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={testState.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeFalsy();
});
it('should display error if interval value is 0', () => {
const setStateSpy = jest.fn();
const testState = stateWithInterval('0d');
const updateLayerSpy = jest.fn();
const testLayer = layerWithInterval('0d');
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={testState}
setState={setStateSpy}
layer={testLayer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={testState.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy();
});
it('should update the unit', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('onChange')!({
@ -619,21 +620,20 @@ describe('date_histogram', () => {
value: 'd',
},
} as React.ChangeEvent<HTMLInputElement>);
expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('42d'));
expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('42d'));
});
it('should update the value', () => {
const setStateSpy = jest.fn();
const testState = stateWithInterval('42d');
const updateLayerSpy = jest.fn();
const testLayer = layerWithInterval('42d');
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={testState}
setState={setStateSpy}
layer={testLayer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={testState.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={testLayer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);
instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({
@ -641,50 +641,48 @@ describe('date_histogram', () => {
value: '9',
},
} as React.ChangeEvent<HTMLInputElement>);
expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('9d'));
expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('9d'));
});
it('should not render options if they are restricted', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const indexPattern = {
...indexPattern1,
fields: [
{
...indexPattern1.fields[0],
aggregationRestrictions: {
date_histogram: {
agg: 'date_histogram',
time_zone: 'UTC',
calendar_interval: '1h',
},
},
},
],
getFieldByName: getFieldByNameFactory([
{
...indexPattern1.fields[0],
aggregationRestrictions: {
date_histogram: {
agg: 'date_histogram',
time_zone: 'UTC',
calendar_interval: '1h',
},
},
},
]),
};
const instance = shallow(
<InlineOptions
{...defaultOptions}
state={{
...state,
indexPatterns: {
1: {
...state.indexPatterns[1],
fields: [
{
...state.indexPatterns[1].fields[0],
aggregationRestrictions: {
date_histogram: {
agg: 'date_histogram',
time_zone: 'UTC',
calendar_interval: '1h',
},
},
},
],
getFieldByName: getFieldByNameFactory([
{
...state.indexPatterns[1].fields[0],
aggregationRestrictions: {
date_histogram: {
agg: 'date_histogram',
time_zone: 'UTC',
calendar_interval: '1h',
},
},
},
]),
},
},
}}
setState={setStateSpy}
layer={layer}
indexPattern={indexPattern}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as DateHistogramIndexPatternColumn}
currentColumn={layer.columns.col1 as DateHistogramIndexPatternColumn}
/>
);

View file

@ -143,12 +143,8 @@ export const dateHistogramOperation: OperationDefinition<
extended_bounds: JSON.stringify({}),
}).toAst();
},
paramEditor: ({ state, setState, currentColumn, layerId, dateRange, data }) => {
const field =
currentColumn &&
state.indexPatterns[state.layers[layerId].indexPatternId].getFieldByName(
currentColumn.sourceField
);
paramEditor: ({ layer, columnId, currentColumn, updateLayer, dateRange, data, indexPattern }) => {
const field = currentColumn && indexPattern.getFieldByName(currentColumn.sourceField);
const intervalIsRestricted =
field!.aggregationRestrictions && field!.aggregationRestrictions.date_histogram;
@ -167,14 +163,14 @@ export const dateHistogramOperation: OperationDefinition<
const value = ev.target.checked
? data.search.aggs.calculateAutoTimeExpression({ from: fromDate, to: toDate }) || '1h'
: autoInterval;
setState(updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value }));
updateLayer(updateColumnParam({ layer, columnId, paramName: 'interval', value }));
}
const setInterval = (newInterval: typeof interval) => {
const isCalendarInterval = calendarOnlyIntervals.has(newInterval.unit);
const value = `${isCalendarInterval ? '1' : newInterval.value}${newInterval.unit || 'd'}`;
setState(updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value }));
updateLayer(updateColumnParam({ layer, columnId, paramName: 'interval', value }));
};
return (

View file

@ -7,12 +7,13 @@
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import type { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';
import { FiltersIndexPatternColumn } from '.';
import type { FiltersIndexPatternColumn } from '.';
import { filtersOperation } from '../index';
import { IndexPatternPrivateState } from '../../../types';
import type { IndexPatternLayer } from '../../../types';
import { createMockedIndexPattern } from '../../../mocks';
import { FilterPopover } from './filter_popover';
const defaultProps = {
@ -22,6 +23,7 @@ const defaultProps = {
dateRange: { fromDate: 'now-1d', toDate: 'now' },
data: dataPluginMock.createStartContract(),
http: {} as HttpSetup,
indexPattern: createMockedIndexPattern(),
};
// mocking random id generator function
@ -38,49 +40,40 @@ jest.mock('@elastic/eui', () => {
});
describe('filters', () => {
let state: IndexPatternPrivateState;
let layer: IndexPatternLayer;
const InlineOptions = filtersOperation.paramEditor!;
beforeEach(() => {
state = {
indexPatternRefs: [],
indexPatterns: {},
existingFields: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'filters',
dataType: 'document',
operationType: 'filters',
scale: 'ordinal',
isBucketed: true,
params: {
filters: [
{
input: { query: 'bytes >= 1', language: 'kuery' },
label: 'More than one',
},
{
input: { query: 'src : 2', language: 'kuery' },
label: '',
},
],
layer = {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'filters',
dataType: 'document',
operationType: 'filters',
scale: 'ordinal',
isBucketed: true,
params: {
filters: [
{
input: { query: 'bytes >= 1', language: 'kuery' },
label: 'More than one',
},
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
{
input: { query: 'src : 2', language: 'kuery' },
label: '',
},
],
},
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
},
};
});
@ -88,9 +81,9 @@ describe('filters', () => {
describe('toEsAggsFn', () => {
it('should reflect params correctly', () => {
const esAggsFn = filtersOperation.toEsAggsFn(
state.layers.first.columns.col1 as FiltersIndexPatternColumn,
layer.columns.col1 as FiltersIndexPatternColumn,
'col1',
state.indexPatterns['1']
createMockedIndexPattern()
);
expect(esAggsFn).toEqual(
expect.objectContaining({
@ -133,15 +126,14 @@ describe('filters', () => {
}));
it('should update state when changing a filter', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as FiltersIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as FiltersIndexPatternColumn}
/>
);
@ -156,34 +148,29 @@ describe('filters', () => {
});
});
instance.update();
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
filters: [
{
input: {
query: 'dest : 5',
language: 'lucene',
},
label: 'Dest5',
},
{
input: {
language: 'kuery',
query: 'src : 2',
},
label: '',
},
],
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
filters: [
{
input: {
query: 'dest : 5',
language: 'lucene',
},
label: 'Dest5',
},
},
{
input: {
language: 'kuery',
query: 'src : 2',
},
label: '',
},
],
},
},
},
@ -192,15 +179,14 @@ describe('filters', () => {
describe('Modify filters', () => {
it('should correctly show existing filters ', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as FiltersIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as FiltersIndexPatternColumn}
/>
);
expect(
@ -218,15 +204,14 @@ describe('filters', () => {
});
it('should remove filter', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as FiltersIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as FiltersIndexPatternColumn}
/>
);
@ -234,27 +219,22 @@ describe('filters', () => {
.find('[data-test-subj="lns-customBucketContainer-remove"]')
.at(2)
.simulate('click');
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
filters: [
{
input: {
language: 'kuery',
query: 'bytes >= 1',
},
label: 'More than one',
},
],
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
filters: [
{
input: {
language: 'kuery',
query: 'bytes >= 1',
},
label: 'More than one',
},
},
],
},
},
},

View file

@ -128,16 +128,14 @@ export const filtersOperation: OperationDefinition<FiltersIndexPatternColumn, 'n
}).toAst();
},
paramEditor: ({ state, setState, currentColumn, layerId, data }) => {
const indexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
paramEditor: ({ layer, columnId, currentColumn, indexPattern, updateLayer, data }) => {
const filters = currentColumn.params.filters;
const setFilters = (newFilters: Filter[]) =>
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'filters',
value: newFilters,
})

View file

@ -36,12 +36,7 @@ import { countOperation, CountIndexPatternColumn } from './count';
import { lastValueOperation, LastValueIndexPatternColumn } from './last_value';
import { OperationMetadata } from '../../../types';
import type { BaseIndexPatternColumn, ReferenceBasedIndexPatternColumn } from './column_types';
import {
IndexPatternPrivateState,
IndexPattern,
IndexPatternField,
IndexPatternLayer,
} from '../../types';
import { IndexPattern, IndexPatternField, IndexPatternLayer } from '../../types';
import { DateRange } from '../../../../common';
import { ExpressionAstFunction } from '../../../../../../../src/plugins/expressions/public';
import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public';
@ -115,10 +110,10 @@ export {
*/
export interface ParamEditorProps<C> {
currentColumn: C;
state: IndexPatternPrivateState;
setState: (newState: IndexPatternPrivateState) => void;
layer: IndexPatternLayer;
updateLayer: (newLayer: IndexPatternLayer) => void;
columnId: string;
layerId: string;
indexPattern: IndexPattern;
uiSettings: IUiSettingsClient;
storage: IStorageWrapper;
savedObjectsClient: SavedObjectsClientContract;

View file

@ -13,7 +13,7 @@ import { dataPluginMock } from '../../../../../../../src/plugins/data/public/moc
import { createMockedIndexPattern } from '../../mocks';
import { LastValueIndexPatternColumn } from './last_value';
import { lastValueOperation } from './index';
import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../types';
import type { IndexPattern, IndexPatternLayer } from '../../types';
const defaultProps = {
storage: {} as IStorageWrapper,
@ -22,52 +22,41 @@ const defaultProps = {
dateRange: { fromDate: 'now-1d', toDate: 'now' },
data: dataPluginMock.createStartContract(),
http: {} as HttpSetup,
indexPattern: {
...createMockedIndexPattern(),
hasRestrictions: false,
} as IndexPattern,
};
describe('last_value', () => {
let state: IndexPatternPrivateState;
let layer: IndexPatternLayer;
const InlineOptions = lastValueOperation.paramEditor!;
beforeEach(() => {
const indexPattern = createMockedIndexPattern();
state = {
indexPatternRefs: [],
indexPatterns: {
'1': {
...indexPattern,
hasRestrictions: false,
} as IndexPattern,
},
existingFields: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'Top value of category',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
params: {
orderBy: { type: 'alphabetical' },
size: 3,
orderDirection: 'asc',
},
sourceField: 'category',
},
col2: {
label: 'Last value of a',
dataType: 'number',
isBucketed: false,
sourceField: 'a',
operationType: 'last_value',
params: {
sortField: 'datefield',
},
},
layer = {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'Top value of category',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
params: {
orderBy: { type: 'alphabetical' },
size: 3,
orderDirection: 'asc',
},
sourceField: 'category',
},
col2: {
label: 'Last value of a',
dataType: 'number',
isBucketed: false,
sourceField: 'a',
operationType: 'last_value',
params: {
sortField: 'datefield',
},
},
},
@ -76,7 +65,7 @@ describe('last_value', () => {
describe('toEsAggsFn', () => {
it('should reflect params correctly', () => {
const lastValueColumn = state.layers.first.columns.col2 as LastValueIndexPatternColumn;
const lastValueColumn = layer.columns.col2 as LastValueIndexPatternColumn;
const esAggsFn = lastValueOperation.toEsAggsFn(
{ ...lastValueColumn, params: { ...lastValueColumn.params } },
'col1',
@ -345,15 +334,14 @@ describe('last_value', () => {
describe('param editor', () => {
it('should render current sortField', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col2 as LastValueIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col2 as LastValueIndexPatternColumn}
/>
);
@ -363,15 +351,14 @@ describe('last_value', () => {
});
it('should update state when changing sortField', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col2 as LastValueIndexPatternColumn}
layerId="first"
layer={layer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={layer.columns.col2 as LastValueIndexPatternColumn}
/>
);
@ -380,20 +367,15 @@ describe('last_value', () => {
.find(EuiComboBox)
.prop('onChange')!([{ label: 'datefield2', value: 'datefield2' }]);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col2: {
...state.layers.first.columns.col2,
params: {
...(state.layers.first.columns.col2 as LastValueIndexPatternColumn).params,
sortField: 'datefield2',
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col2: {
...layer.columns.col2,
params: {
...(layer.columns.col2 as LastValueIndexPatternColumn).params,
sortField: 'datefield2',
},
},
},
@ -403,10 +385,10 @@ describe('last_value', () => {
describe('getErrorMessage', () => {
let indexPattern: IndexPattern;
let layer: IndexPatternLayer;
let errorLayer: IndexPatternLayer;
beforeEach(() => {
indexPattern = createMockedIndexPattern();
layer = {
errorLayer = {
columns: {
col1: {
dataType: 'boolean',
@ -423,53 +405,55 @@ describe('last_value', () => {
};
});
it('returns undefined if sourceField exists and sortField is of type date ', () => {
expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual(undefined);
expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual(
undefined
);
});
it('shows error message if the sourceField does not exist in index pattern', () => {
layer = {
...layer,
errorLayer = {
...errorLayer,
columns: {
col1: {
...layer.columns.col1,
...errorLayer.columns.col1,
sourceField: 'notExisting',
} as LastValueIndexPatternColumn,
},
};
expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([
expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([
'Field notExisting was not found',
]);
});
it('shows error message if the sortField does not exist in index pattern', () => {
layer = {
...layer,
errorLayer = {
...errorLayer,
columns: {
col1: {
...layer.columns.col1,
...errorLayer.columns.col1,
params: {
...layer.columns.col1.params,
...errorLayer.columns.col1.params,
sortField: 'notExisting',
},
} as LastValueIndexPatternColumn,
},
};
expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([
expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([
'Field notExisting was not found',
]);
});
it('shows error message if the sortField is not date', () => {
layer = {
...layer,
errorLayer = {
...errorLayer,
columns: {
col1: {
...layer.columns.col1,
...errorLayer.columns.col1,
params: {
...layer.columns.col1.params,
...errorLayer.columns.col1.params,
sortField: 'bytes',
},
} as LastValueIndexPatternColumn,
},
};
expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([
expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([
'Field bytes is not a date field and cannot be used for sorting',
]);
});

View file

@ -186,12 +186,11 @@ export const lastValueOperation: OperationDefinition<LastValueIndexPatternColumn
);
},
paramEditor: ({ state, setState, currentColumn, layerId }) => {
const currentIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
const dateFields = getDateFields(currentIndexPattern);
paramEditor: ({ layer, updateLayer, columnId, currentColumn, indexPattern }) => {
const dateFields = getDateFields(indexPattern);
const isSortFieldInvalid = !!getInvalidSortFieldMessage(
currentColumn.params.sortField,
currentIndexPattern
indexPattern
);
return (
<>
@ -228,11 +227,10 @@ export const lastValueOperation: OperationDefinition<LastValueIndexPatternColumn
if (choices.length === 0) {
return;
}
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'sortField',
value: choices[0].value,
})
@ -243,8 +241,8 @@ export const lastValueOperation: OperationDefinition<LastValueIndexPatternColumn
? [
{
label:
currentIndexPattern.getFieldByName(currentColumn.params.sortField)
?.displayName || currentColumn.params.sortField,
indexPattern.getFieldByName(currentColumn.params.sortField)?.displayName ||
currentColumn.params.sortField,
value: currentColumn.params.sortField,
},
]

View file

@ -17,7 +17,7 @@ import {
} from '@elastic/eui';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { IndexPatternPrivateState, IndexPattern } from '../../../types';
import type { IndexPatternLayer, IndexPattern } from '../../../types';
import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';
import { rangeOperation } from '../index';
import { RangeIndexPatternColumn } from './ranges';
@ -51,6 +51,7 @@ dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(({ p
type ReactMouseEvent = React.MouseEvent<HTMLAnchorElement, MouseEvent> &
React.MouseEvent<HTMLButtonElement, MouseEvent>;
const sourceField = 'MyField';
const defaultOptions = {
storage: {} as IStorageWrapper,
// need this for MAX_HISTOGRAM value
@ -64,75 +65,64 @@ const defaultOptions = {
},
data: dataPluginMockValue,
http: {} as HttpSetup,
indexPattern: {
id: '1',
title: 'my_index_pattern',
hasRestrictions: false,
fields: [{ name: sourceField, type: 'number', displayName: sourceField }],
getFieldByName: getFieldByNameFactory([
{ name: sourceField, type: 'number', displayName: sourceField },
]),
},
};
describe('ranges', () => {
let state: IndexPatternPrivateState;
let layer: IndexPatternLayer;
const InlineOptions = rangeOperation.paramEditor!;
const sourceField = 'MyField';
const MAX_HISTOGRAM_VALUE = 100;
const GRANULARITY_DEFAULT_VALUE = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / 2;
const GRANULARITY_STEP = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / SLICES;
function setToHistogramMode() {
const column = state.layers.first.columns.col1 as RangeIndexPatternColumn;
const column = layer.columns.col1 as RangeIndexPatternColumn;
column.dataType = 'number';
column.scale = 'interval';
column.params.type = MODES.Histogram;
}
function setToRangeMode() {
const column = state.layers.first.columns.col1 as RangeIndexPatternColumn;
const column = layer.columns.col1 as RangeIndexPatternColumn;
column.dataType = 'string';
column.scale = 'ordinal';
column.params.type = MODES.Range;
}
function getDefaultState(): IndexPatternPrivateState {
function getDefaultLayer(): IndexPatternLayer {
return {
indexPatternRefs: [],
indexPatterns: {
'1': {
id: '1',
title: 'my_index_pattern',
hasRestrictions: false,
fields: [{ name: sourceField, type: 'number', displayName: sourceField }],
getFieldByName: getFieldByNameFactory([
{ name: sourceField, type: 'number', displayName: sourceField },
]),
},
},
existingFields: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
// Start with the histogram type
col1: {
label: sourceField,
dataType: 'number',
operationType: 'range',
scale: 'interval',
isBucketed: true,
sourceField,
params: {
type: MODES.Histogram,
ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }],
maxBars: 'auto',
},
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
// Start with the histogram type
col1: {
label: sourceField,
dataType: 'number',
operationType: 'range',
scale: 'interval',
isBucketed: true,
sourceField,
params: {
type: MODES.Histogram,
ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }],
maxBars: 'auto',
},
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
},
};
}
@ -142,7 +132,7 @@ describe('ranges', () => {
});
beforeEach(() => {
state = getDefaultState();
layer = getDefaultLayer();
});
describe('toEsAggsFn', () => {
@ -150,7 +140,7 @@ describe('ranges', () => {
it('should reflect params correctly', () => {
const esAggsFn = rangeOperation.toEsAggsFn(
state.layers.first.columns.col1 as RangeIndexPatternColumn,
layer.columns.col1 as RangeIndexPatternColumn,
'col1',
{} as IndexPattern
);
@ -189,10 +179,10 @@ describe('ranges', () => {
});
it('should set maxBars param if provided', () => {
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.maxBars = 10;
(layer.columns.col1 as RangeIndexPatternColumn).params.maxBars = 10;
const esAggsFn = rangeOperation.toEsAggsFn(
state.layers.first.columns.col1 as RangeIndexPatternColumn,
layer.columns.col1 as RangeIndexPatternColumn,
'col1',
{} as IndexPattern
);
@ -211,7 +201,7 @@ describe('ranges', () => {
setToRangeMode();
const esAggsFn = rangeOperation.toEsAggsFn(
state.layers.first.columns.col1 as RangeIndexPatternColumn,
layer.columns.col1 as RangeIndexPatternColumn,
'col1',
{} as IndexPattern
);
@ -225,12 +215,12 @@ describe('ranges', () => {
it('should include custom labels', () => {
setToRangeMode();
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges = [
(layer.columns.col1 as RangeIndexPatternColumn).params.ranges = [
{ from: 0, to: 100, label: 'customlabel' },
];
const esAggsFn = rangeOperation.toEsAggsFn(
state.layers.first.columns.col1 as RangeIndexPatternColumn,
layer.columns.col1 as RangeIndexPatternColumn,
'col1',
{} as IndexPattern
);
@ -276,36 +266,30 @@ describe('ranges', () => {
describe('paramEditor', () => {
describe('Modify intervals in basic mode', () => {
beforeEach(() => {
state = getDefaultState();
layer = getDefaultLayer();
});
it('should start update the state with the default maxBars value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE,
},
},
},
@ -313,16 +297,15 @@ describe('ranges', () => {
});
it('should update state when changing Max bars number', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -343,20 +326,15 @@ describe('ranges', () => {
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
});
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
maxBars: MAX_HISTOGRAM_VALUE,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
maxBars: MAX_HISTOGRAM_VALUE,
},
},
},
@ -364,16 +342,15 @@ describe('ranges', () => {
});
it('should update the state using the plus or minus buttons by the step amount', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -389,20 +366,15 @@ describe('ranges', () => {
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
});
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE - GRANULARITY_STEP,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE - GRANULARITY_STEP,
},
},
},
@ -417,25 +389,19 @@ describe('ranges', () => {
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
});
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
maxBars: GRANULARITY_DEFAULT_VALUE,
},
},
},
});
// });
});
});
@ -446,16 +412,15 @@ describe('ranges', () => {
beforeEach(() => setToRangeMode());
it('should show one range interval to start with', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -463,16 +428,15 @@ describe('ranges', () => {
});
it('should add a new range', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -495,23 +459,18 @@ describe('ranges', () => {
} as React.ChangeEvent<HTMLInputElement>);
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
ranges: [
{ from: 0, to: DEFAULT_INTERVAL, label: '' },
{ from: 50, to: Infinity, label: '' },
],
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
ranges: [
{ from: 0, to: DEFAULT_INTERVAL, label: '' },
{ from: 50, to: Infinity, label: '' },
],
},
},
},
@ -520,16 +479,15 @@ describe('ranges', () => {
});
it('should add a new range with custom label', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -552,23 +510,18 @@ describe('ranges', () => {
} as React.ChangeEvent<HTMLInputElement>);
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
ranges: [
{ from: 0, to: DEFAULT_INTERVAL, label: '' },
{ from: DEFAULT_INTERVAL, to: Infinity, label: 'customlabel' },
],
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
ranges: [
{ from: 0, to: DEFAULT_INTERVAL, label: '' },
{ from: DEFAULT_INTERVAL, to: Infinity, label: 'customlabel' },
],
},
},
},
@ -577,16 +530,15 @@ describe('ranges', () => {
});
it('should open a popover to edit an existing range', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -607,20 +559,15 @@ describe('ranges', () => {
} as React.ChangeEvent<HTMLInputElement>);
jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...state.layers.first.columns.col1.params,
ranges: [{ from: 0, to: 50, label: '' }],
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...layer.columns.col1.params,
ranges: [{ from: 0, to: 50, label: '' }],
},
},
},
@ -629,16 +576,15 @@ describe('ranges', () => {
});
it('should not accept invalid ranges', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -670,10 +616,10 @@ describe('ranges', () => {
});
it('should be possible to remove a range if multiple', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
// Add an extra range
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({
(layer.columns.col1 as RangeIndexPatternColumn).params.ranges.push({
from: DEFAULT_INTERVAL,
to: 2 * DEFAULT_INTERVAL,
label: '',
@ -682,11 +628,10 @@ describe('ranges', () => {
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -709,10 +654,10 @@ describe('ranges', () => {
});
it('should handle correctly open ranges when saved', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
// Add an extra open range:
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({
(layer.columns.col1 as RangeIndexPatternColumn).params.ranges.push({
from: null,
to: null,
label: '',
@ -721,11 +666,10 @@ describe('ranges', () => {
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -749,21 +693,21 @@ describe('ranges', () => {
});
it('should correctly handle the default formatter for the field', () => {
const setStateSpy = jest.fn();
// set a default formatter for the sourceField used
state.indexPatterns['1'].fieldFormatMap = {
MyField: { id: 'custom', params: {} },
};
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
indexPattern={{
...defaultOptions.indexPattern,
fieldFormatMap: {
MyField: { id: 'custom', params: {} },
},
}}
/>
);
@ -773,15 +717,10 @@ describe('ranges', () => {
});
it('should correctly pick the dimension formatter for the field', () => {
const setStateSpy = jest.fn();
// set a default formatter for the sourceField used
state.indexPatterns['1'].fieldFormatMap = {
MyField: { id: 'custom', params: {} },
};
const updateLayerSpy = jest.fn();
// now set a format on the range operation
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.format = {
(layer.columns.col1 as RangeIndexPatternColumn).params.format = {
id: 'bytes',
params: { decimals: 0 },
};
@ -789,11 +728,16 @@ describe('ranges', () => {
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
indexPattern={{
...defaultOptions.indexPattern,
fieldFormatMap: {
MyField: { id: 'custom', params: {} },
},
}}
/>
);
@ -803,25 +747,24 @@ describe('ranges', () => {
});
it('should not update the state on mount', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
expect(setStateSpy.mock.calls.length).toBe(0);
expect(updateLayerSpy.mock.calls.length).toBe(0);
});
it('should not reset formatters when switching between custom ranges and auto histogram', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
// now set a format on the range operation
(state.layers.first.columns.col1 as RangeIndexPatternColumn).params.format = {
(layer.columns.col1 as RangeIndexPatternColumn).params.format = {
id: 'custom',
params: { decimals: 3 },
};
@ -829,11 +772,10 @@ describe('ranges', () => {
const instance = mount(
<InlineOptions
{...defaultOptions}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as RangeIndexPatternColumn}
/>
);
@ -842,7 +784,7 @@ describe('ranges', () => {
instance.find(EuiLink).first().prop('onClick')!({} as ReactMouseEvent);
});
expect(setStateSpy.mock.calls[1][0].layers.first.columns.col1.params.format).toEqual({
expect(updateLayerSpy.mock.calls[1][0].columns.col1.params.format).toEqual({
id: 'custom',
params: { decimals: 3 },
});

View file

@ -16,7 +16,6 @@ import { RangeEditor } from './range_editor';
import { OperationDefinition } from '../index';
import { FieldBasedIndexPatternColumn } from '../column_types';
import { updateColumnParam } from '../../layer_helpers';
import { mergeLayer } from '../../../state_helpers';
import { supportedFormats } from '../../../format_column';
import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants';
import { IndexPattern, IndexPatternField } from '../../../types';
@ -173,8 +172,15 @@ export const rangeOperation: OperationDefinition<RangeIndexPatternColumn, 'field
extended_bounds: JSON.stringify({ min: '', max: '' }),
}).toAst();
},
paramEditor: ({ state, setState, currentColumn, layerId, columnId, uiSettings, data }) => {
const indexPattern = state.indexPatterns[state.layers[layerId].indexPatternId];
paramEditor: ({
layer,
columnId,
currentColumn,
updateLayer,
indexPattern,
uiSettings,
data,
}) => {
const currentField = indexPattern.getFieldByName(currentColumn.sourceField);
const numberFormat = currentColumn.params.format;
const numberFormatterPattern =
@ -198,11 +204,10 @@ export const rangeOperation: OperationDefinition<RangeIndexPatternColumn, 'field
// Used to change one param at the time
const setParam: UpdateParamsFnType = (paramName, value) => {
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName,
value,
})
@ -217,29 +222,24 @@ export const rangeOperation: OperationDefinition<RangeIndexPatternColumn, 'field
newMode === MODES.Range
? { id: 'range', params: { template: 'arrow_right', replaceInfinity: true } }
: undefined;
setState(
mergeLayer({
state,
layerId,
newLayer: {
columns: {
...state.layers[layerId].columns,
[columnId]: {
...currentColumn,
scale,
dataType,
params: {
type: newMode,
ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }],
maxBars: maxBarsDefaultValue,
format: currentColumn.params.format,
parentFormat,
},
},
updateLayer({
...layer,
columns: {
...layer.columns,
[columnId]: {
...currentColumn,
scale,
dataType,
params: {
type: newMode,
ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }],
maxBars: maxBarsDefaultValue,
format: currentColumn.params.format,
parentFormat,
},
},
})
);
},
});
};
return (
<RangeEditor

View file

@ -169,8 +169,7 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
}
return currentColumn;
},
paramEditor: function ParamEditor({ state, setState, currentColumn, layerId }) {
const indexPattern = currentColumn && state.indexPatterns[state.layers[layerId].indexPatternId];
paramEditor: function ParamEditor({ layer, updateLayer, currentColumn, columnId, indexPattern }) {
const hasRestrictions = indexPattern.hasRestrictions;
const [popoverOpen, setPopoverOpen] = useState(false);
@ -194,11 +193,11 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
};
}
const orderOptions = Object.entries(state.layers[layerId].columns)
.filter(([_columnId, column]) => isSortableByColumn(column))
.map(([columnId, column]) => {
const orderOptions = Object.entries(layer.columns)
.filter(([sortId, column]) => isSortableByColumn(column))
.map(([sortId, column]) => {
return {
value: toValue({ type: 'column', columnId }),
value: toValue({ type: 'column', columnId: sortId }),
text: column.label,
};
});
@ -220,11 +219,10 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
<ValuesRangeInput
value={currentColumn.params.size}
onChange={(value) => {
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'size',
value,
})
@ -263,11 +261,10 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
data-test-subj="indexPattern-terms-other-bucket"
checked={Boolean(currentColumn.params.otherBucket)}
onChange={(e: EuiSwitchEvent) =>
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'otherBucket',
value: e.target.checked,
})
@ -284,11 +281,10 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
data-test-subj="indexPattern-terms-missing-bucket"
checked={Boolean(currentColumn.params.missingBucket)}
onChange={(e: EuiSwitchEvent) =>
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'missingBucket',
value: e.target.checked,
})
@ -312,11 +308,10 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
options={orderOptions}
value={toValue(currentColumn.params.orderBy)}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'orderBy',
value: fromValue(e.target.value),
})
@ -353,11 +348,10 @@ export const termsOperation: OperationDefinition<TermsIndexPatternColumn, 'field
]}
value={currentColumn.params.orderDirection}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setState(
updateLayer(
updateColumnParam({
state,
layerId,
currentColumn,
layer,
columnId,
paramName: 'orderDirection',
value: e.target.value as 'asc' | 'desc',
})

View file

@ -8,14 +8,14 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { shallow, mount } from 'enzyme';
import { EuiRange, EuiSelect, EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import type { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';
import { createMockedIndexPattern } from '../../../mocks';
import { ValuesRangeInput } from './values_range_input';
import { TermsIndexPatternColumn } from '.';
import type { TermsIndexPatternColumn } from '.';
import { termsOperation } from '../index';
import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../../types';
import { IndexPattern, IndexPatternLayer } from '../../../types';
const defaultProps = {
storage: {} as IStorageWrapper,
@ -24,48 +24,36 @@ const defaultProps = {
dateRange: { fromDate: 'now-1d', toDate: 'now' },
data: dataPluginMock.createStartContract(),
http: {} as HttpSetup,
indexPattern: createMockedIndexPattern(),
};
describe('terms', () => {
let state: IndexPatternPrivateState;
let layer: IndexPatternLayer;
const InlineOptions = termsOperation.paramEditor!;
beforeEach(() => {
state = {
indexPatternRefs: [],
indexPatterns: {
'1': {
hasRestrictions: false,
} as IndexPattern,
},
existingFields: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'Top value of category',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
params: {
orderBy: { type: 'alphabetical' },
size: 3,
orderDirection: 'asc',
},
sourceField: 'category',
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
layer = {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'Top value of category',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
params: {
orderBy: { type: 'alphabetical' },
size: 3,
orderDirection: 'asc',
},
sourceField: 'category',
},
col2: {
label: 'Count',
dataType: 'number',
isBucketed: false,
sourceField: 'Records',
operationType: 'count',
},
},
};
@ -73,7 +61,7 @@ describe('terms', () => {
describe('toEsAggsFn', () => {
it('should reflect params correctly', () => {
const termsColumn = state.layers.first.columns.col1 as TermsIndexPatternColumn;
const termsColumn = layer.columns.col1 as TermsIndexPatternColumn;
const esAggsFn = termsOperation.toEsAggsFn(
{ ...termsColumn, params: { ...termsColumn.params, otherBucket: true } },
'col1',
@ -92,7 +80,7 @@ describe('terms', () => {
});
it('should not enable missing bucket if other bucket is not set', () => {
const termsColumn = state.layers.first.columns.col1 as TermsIndexPatternColumn;
const termsColumn = layer.columns.col1 as TermsIndexPatternColumn;
const esAggsFn = termsOperation.toEsAggsFn(
{
...termsColumn,
@ -370,7 +358,7 @@ describe('terms', () => {
it('should use the default size when there is an existing bucket', () => {
const termsColumn = termsOperation.buildColumn({
indexPattern: createMockedIndexPattern(),
layer: state.layers.first,
layer,
field: {
aggregatable: true,
searchable: true,
@ -526,15 +514,14 @@ describe('terms', () => {
describe('param editor', () => {
it('should render current other bucket value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -546,15 +533,18 @@ describe('terms', () => {
});
it('should hide other bucket setting for rollups', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={{ ...state, indexPatterns: { '1': { hasRestrictions: true } as IndexPattern } }}
setState={setStateSpy}
layer={layer}
indexPattern={{
...createMockedIndexPattern(),
hasRestrictions: true,
}}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -562,15 +552,14 @@ describe('terms', () => {
});
it('should disable missing bucket setting as long as other bucket is not set', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -582,23 +571,22 @@ describe('terms', () => {
});
it('should enable missing bucket setting as long as other bucket is set', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={
{
...state.layers.first.columns.col1,
...layer.columns.col1,
params: {
...state.layers.first.columns.col1.params,
...layer.columns.col1.params,
otherBucket: true,
},
} as TermsIndexPatternColumn
}
layerId="first"
/>
);
@ -610,15 +598,14 @@ describe('terms', () => {
});
it('should update state when clicking other bucket toggle', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -631,20 +618,15 @@ describe('terms', () => {
},
} as EuiSwitchEvent);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
otherBucket: true,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...(layer.columns.col1 as TermsIndexPatternColumn).params,
otherBucket: true,
},
},
},
@ -652,15 +634,14 @@ describe('terms', () => {
});
it('should render current order by value and options', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -675,15 +656,14 @@ describe('terms', () => {
});
it('should update state with the order by value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
layerId="first"
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -696,22 +676,17 @@ describe('terms', () => {
},
} as React.ChangeEvent<HTMLSelectElement>);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
orderBy: {
type: 'column',
columnId: 'col2',
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...(layer.columns.col1 as TermsIndexPatternColumn).params,
orderBy: {
type: 'column',
columnId: 'col2',
},
},
},
@ -720,15 +695,14 @@ describe('terms', () => {
});
it('should render current order direction value and options', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -741,15 +715,14 @@ describe('terms', () => {
});
it('should update state with the order direction value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = shallow(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -762,20 +735,15 @@ describe('terms', () => {
},
} as React.ChangeEvent<HTMLSelectElement>);
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
orderDirection: 'desc',
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...(layer.columns.col1 as TermsIndexPatternColumn).params,
orderDirection: 'desc',
},
},
},
@ -783,15 +751,14 @@ describe('terms', () => {
});
it('should render current size value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -799,15 +766,14 @@ describe('terms', () => {
});
it('should update state with the size value', () => {
const setStateSpy = jest.fn();
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
{...defaultProps}
state={state}
setState={setStateSpy}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col1"
layerId="first"
currentColumn={state.layers.first.columns.col1 as TermsIndexPatternColumn}
currentColumn={layer.columns.col1 as TermsIndexPatternColumn}
/>
);
@ -815,20 +781,15 @@ describe('terms', () => {
instance.find(ValuesRangeInput).prop('onChange')!(7);
});
expect(setStateSpy).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
columns: {
...state.layers.first.columns,
col1: {
...state.layers.first.columns.col1,
params: {
...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params,
size: 7,
},
},
expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
...layer.columns,
col1: {
...layer.columns.col1,
params: {
...(layer.columns.col1 as TermsIndexPatternColumn).params,
size: 7,
},
},
},
@ -837,7 +798,6 @@ describe('terms', () => {
});
describe('getErrorMessage', () => {
let indexPattern: IndexPattern;
let layer: IndexPatternLayer;
beforeEach(() => {
indexPattern = createMockedIndexPattern();
layer = {

View file

@ -18,7 +18,7 @@ import { operationDefinitionMap, OperationType } from '../operations';
import { TermsIndexPatternColumn } from './definitions/terms';
import { DateHistogramIndexPatternColumn } from './definitions/date_histogram';
import { AvgIndexPatternColumn } from './definitions/metrics';
import type { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from '../types';
import type { IndexPattern, IndexPatternLayer } from '../types';
import { documentField } from '../document_field';
import { getFieldByNameFactory } from '../pure_helpers';
import { generateId } from '../../id_generator';
@ -1266,31 +1266,19 @@ describe('state_helpers', () => {
sourceField: 'timestamp',
};
const state: IndexPatternPrivateState = {
indexPatternRefs: [],
existingFields: {},
indexPatterns: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
expect(
updateColumnParam({
layer: {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: currentColumn,
},
},
},
};
expect(
updateColumnParam({
state,
layerId: 'first',
currentColumn,
columnId: 'col1',
paramName: 'interval',
value: 'M',
}).layers.first.columns.col1
}).columns.col1
).toEqual({
...currentColumn,
params: { interval: 'M' },
@ -1307,31 +1295,19 @@ describe('state_helpers', () => {
sourceField: 'bytes',
};
const state: IndexPatternPrivateState = {
indexPatternRefs: [],
existingFields: {},
indexPatterns: {},
currentIndexPatternId: '1',
isFirstExistenceFetch: false,
layers: {
first: {
expect(
updateColumnParam({
layer: {
indexPatternId: '1',
columnOrder: ['col1'],
columns: {
col1: currentColumn,
},
},
},
};
expect(
updateColumnParam({
state,
layerId: 'first',
currentColumn,
columnId: 'col1',
paramName: 'format',
value: { id: 'bytes' },
}).layers.first.columns.col1
}).columns.col1
).toEqual({
...currentColumn,
params: { format: { id: 'bytes' } },

View file

@ -13,14 +13,8 @@ import {
IndexPatternColumn,
RequiredReference,
} from './definitions';
import type {
IndexPattern,
IndexPatternField,
IndexPatternLayer,
IndexPatternPrivateState,
} from '../types';
import type { IndexPattern, IndexPatternField, IndexPatternLayer } from '../types';
import { getSortScoreByPriority } from './operations';
import { mergeLayer } from '../state_helpers';
import { generateId } from '../../id_generator';
import { ReferenceBasedIndexPatternColumn } from './definitions/column_types';
@ -390,40 +384,29 @@ export function getMetricOperationTypes(field: IndexPatternField) {
}
export function updateColumnParam<C extends IndexPatternColumn>({
state,
layerId,
currentColumn,
layer,
columnId,
paramName,
value,
}: {
state: IndexPatternPrivateState;
layerId: string;
currentColumn: C;
layer: IndexPatternLayer;
columnId: string;
paramName: string;
value: unknown;
}): IndexPatternPrivateState {
const columnId = Object.entries(state.layers[layerId].columns).find(
([_columnId, column]) => column === currentColumn
)![0];
const layer = state.layers[layerId];
return mergeLayer({
state,
layerId,
newLayer: {
columns: {
...layer.columns,
[columnId]: {
...currentColumn,
params: {
...currentColumn.params,
[paramName]: value,
},
}): IndexPatternLayer {
return {
...layer,
columns: {
...layer.columns,
[columnId]: {
...layer.columns[columnId],
params: {
...layer.columns[columnId].params,
[paramName]: value,
},
},
},
});
} as Record<string, IndexPatternColumn>,
};
}
function adjustColumnReferencesForChangedColumn(