Skip to content

Commit

Permalink
Update logic for showing references in dimension editor, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
wylieconlon committed Dec 22, 2020
1 parent 7ac5dde commit 1cf5cd8
Show file tree
Hide file tree
Showing 21 changed files with 341 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -400,20 +400,25 @@ export const InnerVisualizationWrapper = ({
showExtraErrors = localState.configurationValidationError
.slice(1)
.map(({ longMessage }) => (
<EuiFlexItem key={longMessage} className="eui-textBreakAll">
<EuiFlexItem
key={longMessage}
className="eui-textBreakAll"
data-test-subj="configuration-failure-error"
>
{longMessage}
</EuiFlexItem>
));
} else {
showExtraErrors = (
<EuiFlexItem data-test-subj="configuration-failure-more-errors">
<EuiFlexItem>
<EuiButtonEmpty
onClick={() => {
setLocalState((prevState: WorkspaceState) => ({
...prevState,
expandError: !prevState.expandError,
}));
}}
data-test-subj="configuration-failure-more-errors"
>
{i18n.translate('xpack.lens.editorFrame.configurationFailureMoreErrors', {
defaultMessage: ` +{errors} {errors, plural, one {error} other {errors}}`,
Expand Down Expand Up @@ -445,7 +450,7 @@ export const InnerVisualizationWrapper = ({
</EuiTextColor>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem className="eui-textBreakAll">
<EuiFlexItem className="eui-textBreakAll" data-test-subj="configuration-failure-error">
{localState.configurationValidationError[0].longMessage}
</EuiFlexItem>
{showExtraErrors}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EuiListGroupItemProps,
EuiFormLabel,
EuiToolTip,
EuiText,
} from '@elastic/eui';
import { IndexPatternDimensionEditorProps } from './dimension_panel';
import { OperationSupportMatrix } from './operation_support';
Expand Down Expand Up @@ -184,7 +185,15 @@ export function DimensionEditor(props: DimensionEditorProps) {
}

let label: EuiListGroupItemProps['label'] = operationPanels[operationType].displayName;
if (disabledStatus) {
if (isActive && disabledStatus) {
label = (
<EuiToolTip content={disabledStatus} display="block" position="left">
<EuiText color="danger" size="s">
<strong>{operationPanels[operationType].displayName}</strong>
</EuiText>
</EuiToolTip>
);
} else if (disabledStatus) {
label = (
<EuiToolTip content={disabledStatus} display="block" position="left">
<span>{operationPanels[operationType].displayName}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1581,4 +1581,64 @@ describe('IndexPatternDimensionEditorPanel', () => {

expect(wrapper.find('ReferenceEditor')).toHaveLength(0);
});

it('should show a warning when the current dimension is no longer configurable', () => {
const stateWithInvalidCol: IndexPatternPrivateState = getStateWithColumns({
col1: {
label: 'Invalid derivative',
dataType: 'number',
isBucketed: false,
operationType: 'derivative',
references: ['ref1'],
},
});

wrapper = mount(
<IndexPatternDimensionEditorComponent {...defaultProps} state={stateWithInvalidCol} />
);

expect(
wrapper
.find('[data-test-subj="lns-indexPatternDimension-derivative incompatible"]')
.find('EuiText[color="danger"]')
.first()
).toBeTruthy();
});

it('should remove options to select references when there are no time fields', () => {
const stateWithoutTime: IndexPatternPrivateState = {
...getStateWithColumns({
col1: {
label: 'Avg',
dataType: 'number',
isBucketed: false,
operationType: 'avg',
sourceField: 'bytes',
},
}),
indexPatterns: {
1: {
id: '1',
title: 'my-fake-index-pattern',
hasRestrictions: false,
fields,
getFieldByName: getFieldByNameFactory([
{
name: 'bytes',
displayName: 'bytes',
type: 'number',
aggregatable: true,
searchable: true,
},
]),
},
},
};

wrapper = mount(
<IndexPatternDimensionEditorComponent {...defaultProps} state={stateWithoutTime} />
);

expect(wrapper.find('[data-test-subj="lns-indexPatternDimension-derivative"]')).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> {
onDeleteColumn: () => void;
existingFields: IndexPatternPrivateState['existingFields'];
fieldIsInvalid: boolean;
markAllFieldsCompatible?: boolean;
}

export function FieldSelect({
Expand All @@ -53,6 +54,7 @@ export function FieldSelect({
onDeleteColumn,
existingFields,
fieldIsInvalid,
markAllFieldsCompatible,
...rest
}: FieldSelectProps) {
const { operationByField } = operationSupportMatrix;
Expand Down Expand Up @@ -93,7 +95,7 @@ export function FieldSelect({
: operationByField[field]!.values().next().value,
},
exists: containsData(field),
compatible: isCompatibleWithCurrentOperation(field),
compatible: markAllFieldsCompatible || isCompatibleWithCurrentOperation(field),
};
})
.sort((a, b) => {
Expand Down Expand Up @@ -163,6 +165,7 @@ export function FieldSelect({
currentIndexPattern,
operationByField,
existingFields,
markAllFieldsCompatible,
]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ describe('reference editor', () => {
expect(fieldSelect.prop('selectedField')).toEqual('bytes');
expect(fieldSelect.prop('selectedOperationType')).toEqual('avg');
expect(fieldSelect.prop('incompleteOperation')).toEqual('max');
expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(false);
});

it('should pass the incomplete field info to FieldSelect', () => {
Expand Down Expand Up @@ -363,6 +364,7 @@ describe('reference editor', () => {
expect(fieldSelect.prop('selectedField')).toBeUndefined();
expect(fieldSelect.prop('selectedOperationType')).toBeUndefined();
expect(fieldSelect.prop('incompleteOperation')).toBeUndefined();
expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(true);
});

it('should show the ParamEditor for functions that offer one', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ export function ReferenceEditor(props: ReferenceEditorProps) {
.forEach((op) => {
if (op.input === 'field') {
const allFields = currentIndexPattern.fields.filter((field) =>
isOperationAllowedAsReference({ operationType: op.type, validation, field })
isOperationAllowedAsReference({
operationType: op.type,
validation,
field,
indexPattern: currentIndexPattern,
})
);
if (allFields.length) {
operationTypes.add(op.type);
Expand All @@ -98,7 +103,13 @@ export function ReferenceEditor(props: ReferenceEditorProps) {
operationByField[field.name]?.add(op.type);
});
}
} else if (isOperationAllowedAsReference({ operationType: op.type, validation })) {
} else if (
isOperationAllowedAsReference({
operationType: op.type,
validation,
indexPattern: currentIndexPattern,
})
) {
operationTypes.add(op.type);
operationWithoutField.add(op.type);
}
Expand All @@ -111,8 +122,9 @@ export function ReferenceEditor(props: ReferenceEditorProps) {
};
}, [currentIndexPattern, validation]);

const functionOptions: Array<EuiComboBoxOptionOption<OperationType>> = [];
operationSupportMatrix.operationTypes.forEach((operationType) => {
const functionOptions: Array<EuiComboBoxOptionOption<OperationType>> = Array.from(
operationSupportMatrix.operationTypes
).map((operationType) => {
const def = operationDefinitionMap[operationType];
const label = operationPanels[operationType].displayName;
const isCompatible =
Expand All @@ -123,14 +135,14 @@ export function ReferenceEditor(props: ReferenceEditorProps) {
operationSupportMatrix.fieldByOperation[operationType]?.has(column.sourceField)) ||
(column && !hasField(column) && def.input !== 'field');

functionOptions.push({
return {
label,
value: operationType,
className: 'lnsIndexPatternDimensionEditor__operation',
'data-test-subj': `lns-indexPatternDimension-${operationType}${
isCompatible ? '' : ' incompatible'
}`,
});
};
});

function onChooseFunction(operationType: OperationType) {
Expand Down Expand Up @@ -256,6 +268,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) {
: (column as FieldBasedIndexPatternColumn)?.sourceField
}
incompleteOperation={incompleteOperation}
markAllFieldsCompatible={selectionStyle === 'field'}
onDeleteColumn={() => {
updateLayer(deleteColumn({ layer, columnId, indexPattern: currentIndexPattern }));
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ export const counterRateOperation: OperationDefinition<
validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed,
},
],
getPossibleOperation: () => {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
getPossibleOperation: (indexPattern) => {
if (hasDateField(indexPattern)) {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
}
},
getDefaultLabel: (column, indexPattern, columns) => {
const ref = columns[column.references[0]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
checkForDateHistogram,
getErrorsForDateReference,
dateBasedOperationToExpression,
hasDateField,
} from './utils';
import { OperationDefinition } from '..';

Expand Down Expand Up @@ -50,12 +51,14 @@ export const cumulativeSumOperation: OperationDefinition<
validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed,
},
],
getPossibleOperation: () => {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
getPossibleOperation: (indexPattern) => {
if (hasDateField(indexPattern)) {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
}
},
getDefaultLabel: (column, indexPattern, columns) => {
const ref = columns[column.references[0]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ export const derivativeOperation: OperationDefinition<
validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed,
},
],
getPossibleOperation: () => {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
getPossibleOperation: (indexPattern) => {
if (hasDateField(indexPattern)) {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
}
},
getDefaultLabel: (column, indexPattern, columns) => {
const ref = columns[column.references[0]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ export const movingAverageOperation: OperationDefinition<
validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed,
},
],
getPossibleOperation: () => {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
getPossibleOperation: (indexPattern) => {
if (hasDateField(indexPattern)) {
return {
dataType: 'number',
isBucketed: false,
scale: 'ratio',
};
}
},
getDefaultLabel: (column, indexPattern, columns) => {
return ofName(columns[column.references[0]]?.label, column.timeScale);
Expand Down Expand Up @@ -120,6 +122,19 @@ export const movingAverageOperation: OperationDefinition<
timeScalingMode: 'optional',
};

function isValidNumber(input: string) {
if (input === '') return false;
try {
const val = parseFloat(input);
if (isNaN(val)) return false;
if (val < 1) return false;
if (val.toString().includes('.')) return false;
} catch (e) {
return false;
}
return true;
}

function MovingAverageParamEditor({
layer,
updateLayer,
Expand All @@ -130,10 +145,8 @@ function MovingAverageParamEditor({

useDebounceWithOptions(
() => {
if (inputValue === '') {
return;
}
const inputNumber = Number(inputValue);
if (!isValidNumber(inputValue)) return;
const inputNumber = parseInt(inputValue, 10);
updateLayer(
updateColumnParam({
layer,
Expand All @@ -147,19 +160,23 @@ function MovingAverageParamEditor({
256,
[inputValue]
);

return (
<EuiFormRow
label={i18n.translate('xpack.lens.indexPattern.movingAverage.window', {
defaultMessage: 'Window size',
})}
display="columnCompressed"
fullWidth
isInvalid={!isValidNumber(inputValue)}
>
<EuiFieldNumber
compressed
value={inputValue}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setInputValue(e.target.value)}
min={1}
step={1}
isInvalid={!isValidNumber(inputValue)}
/>
</EuiFormRow>
);
Expand Down
Loading

0 comments on commit 1cf5cd8

Please sign in to comment.