Skip to content

Commit

Permalink
Merge branch 'lens/invalid-state' into lens/reference-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
wylieconlon committed Dec 1, 2020
2 parents baf8fae + 5b84ce1 commit c486a56
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ export function LayerPanel(
<>
{activeGroup && activeId && (
<NativeRenderer
render={props.datasourceMap[datasourceId].renderDimensionEditor}
render={layerDatasource.renderDimensionEditor}
nativeProps={{
...layerDatasourceConfigProps,
core: props.core,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@elastic/eui';
import { IndexPatternDimensionEditorProps } from './dimension_panel';
import { OperationSupportMatrix } from './operation_support';
import { IndexPatternColumn, OperationType } from '../indexpattern';
import { IndexPatternColumn } from '../indexpattern';
import {
operationDefinitionMap,
getOperationDisplay,
Expand All @@ -27,6 +27,8 @@ import {
deleteColumn,
updateColumnParam,
resetIncomplete,
FieldBasedIndexPatternColumn,
OperationType,
} from '../operations';
import { mergeLayer } from '../state_helpers';
import { FieldSelect } from './field_select';
Expand Down Expand Up @@ -113,7 +115,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
selectedColumn && operationDefinitionMap[selectedColumn.operationType];

const incompleteInfo = (state.layers[layerId].incompleteColumns ?? {})[columnId];
const incompleteOperation = (incompleteInfo?.operationType as OperationType) ?? null;
const incompleteOperation = incompleteInfo?.operationType;
const incompleteField = incompleteInfo?.sourceField ?? null;

const ParamEditor = selectedOperationDefinition?.paramEditor;
Expand Down Expand Up @@ -187,6 +189,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
operationDefinitionMap[operationType].input === 'none' ||
operationDefinitionMap[operationType].input === 'fullReference'
) {
// Clear invalid state because we are reseting to a valid column
if (selectedColumn?.operationType === operationType) {
if (incompleteInfo) {
setState(
Expand Down Expand Up @@ -226,7 +229,6 @@ export function DimensionEditor(props: DimensionEditorProps) {
})
);
} else {
// setInvalidOperationType(operationType);
setState(
mergeLayer({
state,
Expand Down Expand Up @@ -336,8 +338,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
isInvalid={Boolean(incompleteOperation || currentFieldIsInvalid)}
error={getErrorMessage(
selectedColumn,
incompleteOperation,
incompleteField,
Boolean(incompleteOperation),
selectedOperationDefinition?.input,
currentFieldIsInvalid
)}
Expand All @@ -347,12 +348,17 @@ export function DimensionEditor(props: DimensionEditorProps) {
currentIndexPattern={currentIndexPattern}
existingFields={state.existingFields}
operationSupportMatrix={operationSupportMatrix}
// selectedColumnOperationType={incompleteOperation || selectedColumn?.operationType}
selectedColumnOperationType={selectedColumn?.operationType}
selectedColumnSourceField={
selectedColumn && hasField(selectedColumn) ? selectedColumn.sourceField : undefined
selectedOperationType={
// Allows operation to be selected before creating a valid column
selectedColumn ? selectedColumn.operationType : incompleteOperation
}
incompatibleSelectedOperationType={incompleteOperation}
selectedField={
// Allows field to be selected
incompleteField
? incompleteField
: (selectedColumn as FieldBasedIndexPatternColumn)?.sourceField
}
incompleteOperation={incompleteOperation}
onDeleteColumn={() => {
setState(
mergeLayer({
Expand All @@ -363,59 +369,19 @@ export function DimensionEditor(props: DimensionEditorProps) {
);
}}
onChoose={(choice) => {
let newLayer: IndexPatternLayer;
if (
incompleteOperation
// choice.operationType === incompleteOperation
// operationSupportMatrix.fieldByOperation[incompleteOperation] &&
// operationSupportMatrix.fieldByOperation[incompleteOperation]!.has(choice.field)
) {
newLayer = insertOrReplaceColumn({
layer: state.layers[layerId],
columnId,
indexPattern: currentIndexPattern,
op: incompleteOperation,
field: currentIndexPattern.getFieldByName(choice.field),
});
} else if (
!incompleteOperation &&
selectedColumn &&
'field' in choice &&
choice.operationType === selectedColumn.operationType
) {
// Replaces just the field
newLayer = replaceColumn({
layer: state.layers[layerId],
columnId,
indexPattern: currentIndexPattern,
op: choice.operationType,
field: currentIndexPattern.getFieldByName(choice.field)!,
});
} else {
// Finds a new operation
const compatibleOperations =
('field' in choice && operationSupportMatrix.operationByField[choice.field]) ||
new Set();
let operation;
if (compatibleOperations.size > 0) {
operation =
// incompleteOperation && compatibleOperations.has(incompleteOperation)
// ? incompleteOperation
compatibleOperations.values().next().value;
} else if ('field' in choice) {
operation = choice.operationType;
}
newLayer = insertOrReplaceColumn({
layer: state.layers[layerId],
columnId,
field: currentIndexPattern.getFieldByName(choice.field),
indexPattern: currentIndexPattern,
op: operation as OperationType,
});
}

setState(mergeLayer({ state, layerId, newLayer }));
// setInvalidOperationType(null);
setState(
mergeLayer({
state,
layerId,
newLayer: insertOrReplaceColumn({
layer: state.layers[layerId],
columnId,
indexPattern: currentIndexPattern,
op: choice.operationType,
field: currentIndexPattern.getFieldByName(choice.field),
}),
})
);
}}
/>
</EuiFormRow>
Expand Down Expand Up @@ -514,8 +480,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
}
function getErrorMessage(
selectedColumn: IndexPatternColumn | undefined,
incompleteOperation: OperationType | null,
incompleteField: string | null,
incompleteOperation: boolean,
input: 'none' | 'field' | 'fullReference' | undefined,
fieldInvalid: boolean
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ describe('IndexPatternDimensionEditorPanel', () => {
},
};

setState = jest.fn();
setState = jest.fn().mockImplementation((newState) => {
if (wrapper instanceof ReactWrapper) {
wrapper.setProps({ state: newState });
}
});

defaultProps = {
state,
Expand Down Expand Up @@ -546,13 +550,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
});

describe('transient invalid state', () => {
beforeEach(() => {
setState.mockImplementation((newState) => {
wrapper.setProps({ state: newState });
});
});

it('should update the state when selecting an operation incompatible with the current field', () => {
it('should set the state if selecting an operation incompatible with the current field', () => {
wrapper = mount(<IndexPatternDimensionEditorComponent {...defaultProps} />);

act(() => {
Expand Down Expand Up @@ -683,6 +681,17 @@ describe('IndexPatternDimensionEditorPanel', () => {
wrapper = mount(<IndexPatternDimensionEditorComponent {...defaultProps} columnId={'col2'} />);

wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click');
expect(setState).toHaveBeenCalledWith({
...state,
layers: {
first: {
...state.layers.first,
incompleteColumns: {
col2: { operationType: 'avg' },
},
},
},
});

const comboBox = wrapper
.find(EuiComboBox)
Expand All @@ -694,7 +703,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
comboBox.prop('onChange')!([options![1].options![2]]);
});

expect(setState).toHaveBeenCalledWith({
expect(setState).toHaveBeenLastCalledWith({
...state,
layers: {
first: {
Expand Down Expand Up @@ -778,11 +787,9 @@ describe('IndexPatternDimensionEditorPanel', () => {
it('should set datasource state if compatible field is selected for operation', () => {
wrapper = mount(<IndexPatternDimensionEditorComponent {...defaultProps} />);

act(() => {
wrapper
.find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]')
.simulate('click');
});
wrapper
.find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]')
.simulate('click');

const comboBox = wrapper
.find(EuiComboBox)
Expand All @@ -793,7 +800,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
comboBox.prop('onChange')!([option]);
});

expect(setState).toHaveBeenCalledWith({
expect(setState).toHaveBeenLastCalledWith({
...state,
layers: {
first: {
Expand Down Expand Up @@ -1259,6 +1266,9 @@ describe('IndexPatternDimensionEditorPanel', () => {
});

it('should add a column on selection of a field', () => {
// Prevents field format from being loaded
setState.mockImplementation(() => {});

wrapper = mount(<IndexPatternDimensionEditorComponent {...defaultProps} columnId={'col2'} />);

const comboBox = wrapper
Expand All @@ -1278,6 +1288,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
columns: {
...state.layers.first.columns,
col2: expect.objectContaining({
operationType: 'range',
sourceField: 'bytes',
// Other parts of this don't matter for this test
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ import { fieldExists } from '../pure_helpers';
export interface FieldChoice {
type: 'field';
field: string;
operationType?: OperationType;
operationType: OperationType;
}

export interface FieldSelectProps extends EuiComboBoxProps<{}> {
currentIndexPattern: IndexPattern;
incompatibleSelectedOperationType: OperationType | null;
selectedColumnOperationType?: OperationType;
selectedColumnSourceField?: string;
selectedOperationType?: OperationType;
selectedField?: string;
incompleteOperation?: OperationType;
operationSupportMatrix: OperationSupportMatrix;
onChoose: (choice: FieldChoice) => void;
onDeleteColumn: () => void;
Expand All @@ -45,9 +45,9 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> {

export function FieldSelect({
currentIndexPattern,
incompatibleSelectedOperationType,
selectedColumnOperationType,
selectedColumnSourceField,
incompleteOperation,
selectedOperationType,
selectedField,
operationSupportMatrix,
onChoose,
onDeleteColumn,
Expand All @@ -59,14 +59,10 @@ export function FieldSelect({
const memoizedFieldOptions = useMemo(() => {
const fields = Object.keys(operationByField).sort();

const currentOperationType = incompleteOperation ?? selectedOperationType;

function isCompatibleWithCurrentOperation(fieldName: string) {
if (incompatibleSelectedOperationType) {
return operationByField[fieldName]!.has(incompatibleSelectedOperationType);
}
return (
!selectedColumnOperationType ||
operationByField[fieldName]!.has(selectedColumnOperationType)
);
return !currentOperationType || operationByField[fieldName]!.has(currentOperationType);
}

const [specialFields, normalFields] = _.partition(
Expand All @@ -81,20 +77,25 @@ export function FieldSelect({
function fieldNamesToOptions(items: string[]) {
return items
.filter((field) => currentIndexPattern.getFieldByName(field)?.displayName)
.map((field) => ({
label: currentIndexPattern.getFieldByName(field)?.displayName,
value: {
type: 'field',
field,
dataType: currentIndexPattern.getFieldByName(field)?.type,
operationType:
selectedColumnOperationType && isCompatibleWithCurrentOperation(field)
? selectedColumnOperationType
: undefined,
},
exists: containsData(field),
compatible: isCompatibleWithCurrentOperation(field),
}))
.map((field) => {
return {
label: currentIndexPattern.getFieldByName(field)?.displayName,
value: {
type: 'field',
field,
dataType: currentIndexPattern.getFieldByName(field)?.type,
// Use the operation directly, or choose the first compatible operation.
// All fields are guaranteed to have at least one operation because they
// won't appear in the list otherwise
operationType:
currentOperationType && isCompatibleWithCurrentOperation(field)
? currentOperationType
: operationByField[field]!.values().next().value,
},
exists: containsData(field),
compatible: isCompatibleWithCurrentOperation(field),
};
})
.sort((a, b) => {
if (a.compatible && !b.compatible) {
return -1;
Expand Down Expand Up @@ -157,8 +158,8 @@ export function FieldSelect({
metaFieldsOptions,
].filter(Boolean);
}, [
incompatibleSelectedOperationType,
selectedColumnOperationType,
incompleteOperation,
selectedOperationType,
currentIndexPattern,
operationByField,
existingFields,
Expand All @@ -174,15 +175,15 @@ export function FieldSelect({
defaultMessage: 'Field',
})}
options={(memoizedFieldOptions as unknown) as EuiComboBoxOptionOption[]}
isInvalid={Boolean(incompatibleSelectedOperationType || fieldIsInvalid)}
isInvalid={Boolean(incompleteOperation || fieldIsInvalid)}
selectedOptions={
((selectedColumnOperationType && selectedColumnSourceField
((selectedOperationType && selectedField
? [
{
label: fieldIsInvalid
? selectedColumnSourceField
: currentIndexPattern.getFieldByName(selectedColumnSourceField)?.displayName,
value: { type: 'field', field: selectedColumnSourceField },
? selectedField
: currentIndexPattern.getFieldByName(selectedField)?.displayName,
value: { type: 'field', field: selectedField },
},
]
: []) as unknown) as EuiComboBoxOptionOption[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1002,8 +1002,8 @@ describe('IndexPattern Data Source', () => {
columnOrder: [],
columns: {},
incompleteColumns: {
col1: { operationType: 'avg' },
col2: { operationType: 'sum' },
col1: { operationType: 'avg' as const },
col2: { operationType: 'sum' as const },
},
},
},
Expand Down
Loading

0 comments on commit c486a56

Please sign in to comment.