Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Lens] Fieldless operations #78080

Merged
merged 12 commits into from
Sep 28, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '@elastic/eui';
import { EuiFormLabel } from '@elastic/eui';
import { IndexPatternColumn, OperationType } from '../indexpattern';
import { IndexPatternDimensionEditorProps, OperationFieldSupportMatrix } from './dimension_panel';
import { IndexPatternDimensionEditorProps, OperationSupportMatrix } from './dimension_panel';
import {
operationDefinitionMap,
getOperationDisplay,
Expand All @@ -36,7 +36,7 @@ const operationPanels = getOperationDisplay();

export interface DimensionEditorProps extends IndexPatternDimensionEditorProps {
selectedColumn?: IndexPatternColumn;
operationFieldSupportMatrix: OperationFieldSupportMatrix;
operationSupportMatrix: OperationSupportMatrix;
currentIndexPattern: IndexPattern;
}

Expand Down Expand Up @@ -90,22 +90,24 @@ const LabelInput = ({ value, onChange }: { value: string; onChange: (value: stri
export function DimensionEditor(props: DimensionEditorProps) {
const {
selectedColumn,
operationFieldSupportMatrix,
operationSupportMatrix,
state,
columnId,
setState,
layerId,
currentIndexPattern,
hideGrouping,
} = props;
const { operationByField, fieldByOperation } = operationFieldSupportMatrix;
const { operationByField, fieldByOperation } = operationSupportMatrix;
const [
incompatibleSelectedOperationType,
setInvalidOperationType,
] = useState<OperationType | null>(null);

const ParamEditor =
selectedColumn && operationDefinitionMap[selectedColumn.operationType].paramEditor;
const selectedOperationDefinition =
selectedColumn && operationDefinitionMap[selectedColumn.operationType];

const ParamEditor = selectedOperationDefinition?.paramEditor;

const fieldMap: Record<string, IndexPatternField> = useMemo(() => {
const fields: Record<string, IndexPatternField> = {};
Expand All @@ -129,6 +131,10 @@ export function DimensionEditor(props: DimensionEditorProps) {
[
...asOperationOptions(validOperationTypes, true),
...asOperationOptions(possibleOperationTypes, false),
...asOperationOptions(
operationSupportMatrix.operationWithoutField,
!selectedColumn || !hasField(selectedColumn)
),
],
'operationType'
);
Expand Down Expand Up @@ -166,12 +172,30 @@ export function DimensionEditor(props: DimensionEditorProps) {
compatibleWithCurrentField ? '' : ' incompatible'
}`,
onClick() {
// todo: when moving from terms agg to filters, we want to create a filter `$field.name : *`
// it probably has to be re-thought when removing the field name.
const isTermsToFilters =
selectedColumn?.operationType === 'terms' && operationType === 'filters';

if (!selectedColumn || !compatibleWithCurrentField) {
if (operationDefinitionMap[operationType].input === 'none') {
// Clear invalid state because we are creating a valid column
setInvalidOperationType(null);
if (selectedColumn?.operationType === operationType) {
return;
}
setState(
changeColumn({
state,
layerId,
columnId,
newColumn: buildColumn({
columns: props.state.layers[props.layerId].columns,
suggestedPriority: props.suggestedPriority,
layerId: props.layerId,
op: operationType,
indexPattern: currentIndexPattern,
previousColumn: selectedColumn,
}),
})
);
trackUiEvent(`indexpattern_dimension_operation_${operationType}`);
return;
} else if (!selectedColumn || !compatibleWithCurrentField) {
const possibleFields = fieldByOperation[operationType] || [];

if (possibleFields.length === 1) {
Expand All @@ -197,19 +221,20 @@ export function DimensionEditor(props: DimensionEditorProps) {
trackUiEvent(`indexpattern_dimension_operation_${operationType}`);
return;
}
if (incompatibleSelectedOperationType && !isTermsToFilters) {
setInvalidOperationType(null);
}
if (selectedColumn.operationType === operationType) {

setInvalidOperationType(null);

if (selectedColumn?.operationType === operationType) {
return;
}

const newColumn: IndexPatternColumn = buildColumn({
columns: props.state.layers[props.layerId].columns,
suggestedPriority: props.suggestedPriority,
layerId: props.layerId,
op: operationType,
indexPattern: currentIndexPattern,
field: fieldMap[selectedColumn.sourceField],
field: hasField(selectedColumn) ? fieldMap[selectedColumn.sourceField] : undefined,
previousColumn: selectedColumn,
});

Expand Down Expand Up @@ -244,93 +269,101 @@ export function DimensionEditor(props: DimensionEditorProps) {
</div>
<EuiSpacer size="s" />
<div className="lnsIndexPatternDimensionEditor__section lnsIndexPatternDimensionEditor__section--shaded">
<EuiFormRow
data-test-subj="indexPattern-field-selection-row"
label={i18n.translate('xpack.lens.indexPattern.chooseField', {
defaultMessage: 'Choose a field',
})}
fullWidth
isInvalid={Boolean(incompatibleSelectedOperationType)}
error={
selectedColumn
? i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', {
defaultMessage: 'To use this function, select a different field.',
})
: undefined
}
>
<FieldSelect
currentIndexPattern={currentIndexPattern}
existingFields={state.existingFields}
fieldMap={fieldMap}
operationFieldSupportMatrix={operationFieldSupportMatrix}
selectedColumnOperationType={selectedColumn && selectedColumn.operationType}
selectedColumnSourceField={
selectedColumn && hasField(selectedColumn) ? selectedColumn.sourceField : undefined
{!selectedColumn ||
selectedOperationDefinition?.input === 'field' ||
(incompatibleSelectedOperationType &&
operationDefinitionMap[incompatibleSelectedOperationType].input === 'field') ? (
<EuiFormRow
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers: this is an indentation change, not a functional change inside this code.

data-test-subj="indexPattern-field-selection-row"
label={i18n.translate('xpack.lens.indexPattern.chooseField', {
defaultMessage: 'Choose a field',
})}
fullWidth
isInvalid={Boolean(incompatibleSelectedOperationType)}
error={
selectedColumn && incompatibleSelectedOperationType
? selectedOperationDefinition?.input === 'field'
? i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', {
defaultMessage: 'To use this function, select a different field.',
})
: i18n.translate('xpack.lens.indexPattern.chooseFieldLabel', {
defaultMessage: 'To use this function, select a field.',
})
: undefined
}
incompatibleSelectedOperationType={incompatibleSelectedOperationType}
onDeleteColumn={() => {
setState(
deleteColumn({
state,
layerId,
columnId,
})
);
}}
onChoose={(choice) => {
let column: IndexPatternColumn;
if (
!incompatibleSelectedOperationType &&
selectedColumn &&
'field' in choice &&
choice.operationType === selectedColumn.operationType
) {
// If we just changed the field are not in an error state and the operation didn't change,
// we use the operations onFieldChange method to calculate the new column.
column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]);
} else {
// Otherwise we'll use the buildColumn method to calculate a new column
const compatibleOperations =
('field' in choice &&
operationFieldSupportMatrix.operationByField[choice.field]) ||
[];
let operation;
if (compatibleOperations.length > 0) {
operation =
incompatibleSelectedOperationType &&
compatibleOperations.includes(incompatibleSelectedOperationType)
? incompatibleSelectedOperationType
: compatibleOperations[0];
} else if ('field' in choice) {
operation = choice.operationType;
}
column = buildColumn({
columns: props.state.layers[props.layerId].columns,
field: fieldMap[choice.field],
indexPattern: currentIndexPattern,
layerId: props.layerId,
suggestedPriority: props.suggestedPriority,
op: operation as OperationType,
previousColumn: selectedColumn,
});
>
<FieldSelect
currentIndexPattern={currentIndexPattern}
existingFields={state.existingFields}
fieldMap={fieldMap}
operationSupportMatrix={operationSupportMatrix}
selectedColumnOperationType={selectedColumn && selectedColumn.operationType}
selectedColumnSourceField={
selectedColumn && hasField(selectedColumn) ? selectedColumn.sourceField : undefined
}
incompatibleSelectedOperationType={incompatibleSelectedOperationType}
onDeleteColumn={() => {
setState(
deleteColumn({
state,
layerId,
columnId,
})
);
}}
onChoose={(choice) => {
let column: IndexPatternColumn;
if (
!incompatibleSelectedOperationType &&
selectedColumn &&
'field' in choice &&
choice.operationType === selectedColumn.operationType
) {
// If we just changed the field are not in an error state and the operation didn't change,
// we use the operations onFieldChange method to calculate the new column.
column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]);
} else {
// Otherwise we'll use the buildColumn method to calculate a new column
const compatibleOperations =
('field' in choice && operationSupportMatrix.operationByField[choice.field]) ||
[];
let operation;
if (compatibleOperations.length > 0) {
operation =
incompatibleSelectedOperationType &&
compatibleOperations.includes(incompatibleSelectedOperationType)
? incompatibleSelectedOperationType
: compatibleOperations[0];
} else if ('field' in choice) {
operation = choice.operationType;
}
column = buildColumn({
columns: props.state.layers[props.layerId].columns,
field: fieldMap[choice.field],
indexPattern: currentIndexPattern,
layerId: props.layerId,
suggestedPriority: props.suggestedPriority,
op: operation as OperationType,
previousColumn: selectedColumn,
});
}

setState(
changeColumn({
state,
layerId,
columnId,
newColumn: column,
keepParams: false,
})
);
setInvalidOperationType(null);
}}
/>
</EuiFormRow>
setState(
changeColumn({
state,
layerId,
columnId,
newColumn: column,
keepParams: false,
})
);
setInvalidOperationType(null);
}}
/>
</EuiFormRow>
) : null}

{!incompatibleSelectedOperationType && ParamEditor && (
{!incompatibleSelectedOperationType && selectedColumn && ParamEditor && (
<>
<ParamEditor
state={state}
Expand Down
Loading