[Lens] Fix column order issues (#98261)

This commit is contained in:
Joe Reuter 2021-04-27 14:14:16 +02:00 committed by GitHub
parent b544be6ff2
commit 61d57370fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 165 additions and 33 deletions

View file

@ -1150,6 +1150,83 @@ describe('IndexPatternDimensionEditorPanel', () => {
});
});
it('respects groups on moving operations if some columns are not listed in groups', () => {
// config:
// a: col1,
// b: col2, col3
// c: col4
// col5, col6 not in visualization groups
// dragging col3 onto col1 in group a
onDrop({
...defaultProps,
columnId: 'col1',
droppedItem: draggingCol3,
state: {
...testState,
layers: {
first: {
...testState.layers.first,
columnOrder: ['col1', 'col2', 'col3', 'col4', 'col5', 'col6'],
columns: {
...testState.layers.first.columns,
col5: {
dataType: 'number',
operationType: 'count',
label: '',
isBucketed: false,
sourceField: 'Records',
},
col6: {
dataType: 'number',
operationType: 'count',
label: '',
isBucketed: false,
sourceField: 'Records',
},
},
},
},
},
groupId: 'a',
dimensionGroups: [
{ ...dimensionGroups[0], accessors: [{ columnId: 'col1' }] },
{ ...dimensionGroups[1], accessors: [{ columnId: 'col2' }, { columnId: 'col3' }] },
{ ...dimensionGroups[2] },
],
dropType: 'move_compatible',
});
expect(setState).toBeCalledTimes(1);
expect(setState).toHaveBeenCalledWith({
...testState,
layers: {
first: {
...testState.layers.first,
columnOrder: ['col1', 'col2', 'col4', 'col5', 'col6'],
columns: {
col1: testState.layers.first.columns.col3,
col2: testState.layers.first.columns.col2,
col4: testState.layers.first.columns.col4,
col5: {
dataType: 'number',
operationType: 'count',
label: '',
isBucketed: false,
sourceField: 'Records',
},
col6: {
dataType: 'number',
operationType: 'count',
label: '',
isBucketed: false,
sourceField: 'Records',
},
},
},
},
});
});
it('respects groups on duplicating operations between compatible groups with overwrite', () => {
// config:
// a: col1,

View file

@ -147,9 +147,9 @@ function onMoveCompatible(
columns: newColumns,
};
const updatedColumnOrder = getColumnOrder(newLayer);
let updatedColumnOrder = getColumnOrder(newLayer);
reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId);
updatedColumnOrder = reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId);
// Time to replace
setState(
@ -342,8 +342,8 @@ function onSwapCompatible({
newColumns[targetId] = sourceColumn;
newColumns[sourceId] = targetColumn;
const updatedColumnOrder = swapColumnOrder(layer.columnOrder, sourceId, targetId);
reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId);
let updatedColumnOrder = swapColumnOrder(layer.columnOrder, sourceId, targetId);
updatedColumnOrder = reorderByGroups(dimensionGroups, groupId, updatedColumnOrder, columnId);
// Time to replace
setState(

View file

@ -860,6 +860,44 @@ describe('IndexPattern Data Source', () => {
expect(operationDefinitionMap.testReference.toExpression).toHaveBeenCalled();
expect(ast.chain[2]).toEqual('mock');
});
it('should keep correct column mapping keys with reference columns present', async () => {
const queryBaseState: IndexPatternBaseState = {
currentIndexPatternId: '1',
layers: {
first: {
indexPatternId: '1',
columnOrder: ['col2', 'col1'],
columns: {
col1: {
label: 'Count of records',
dataType: 'date',
isBucketed: false,
sourceField: 'timefield',
operationType: 'unique_count',
},
col2: {
label: 'Reference',
dataType: 'number',
isBucketed: false,
// @ts-expect-error not a valid type
operationType: 'testReference',
references: ['col1'],
},
},
},
},
};
const state = enrichBaseState(queryBaseState);
const ast = indexPatternDatasource.toExpression(state, 'first') as Ast;
expect(JSON.parse(ast.chain[1].arguments.idMap[0] as string)).toEqual({
'col-0-col1': expect.objectContaining({
id: 'col1',
}),
});
});
});
});

View file

@ -1106,11 +1106,11 @@ describe('IndexPattern Data Source suggestions', () => {
operation: expect.objectContaining({ dataType: 'date', isBucketed: true }),
},
{
columnId: 'newid',
columnId: 'ref',
operation: expect.objectContaining({ dataType: 'number', isBucketed: false }),
},
{
columnId: 'ref',
columnId: 'newid',
operation: expect.objectContaining({ dataType: 'number', isBucketed: false }),
},
],
@ -1158,15 +1158,6 @@ describe('IndexPattern Data Source suggestions', () => {
table: expect.objectContaining({
changeType: 'extended',
columns: [
{
columnId: 'newid',
operation: {
dataType: 'number',
isBucketed: false,
label: 'Count of records',
scale: 'ratio',
},
},
{
columnId: 'ref',
operation: {
@ -1176,6 +1167,15 @@ describe('IndexPattern Data Source suggestions', () => {
scale: undefined,
},
},
{
columnId: 'newid',
operation: {
dataType: 'number',
isBucketed: false,
label: 'Count of records',
scale: 'ratio',
},
},
],
}),
})

View file

@ -712,7 +712,12 @@ function addBucket(
// they already had, with an extra level of detail.
updatedColumnOrder = [...buckets, addedColumnId, ...metrics, ...references];
}
reorderByGroups(visualizationGroups, targetGroup, updatedColumnOrder, addedColumnId);
updatedColumnOrder = reorderByGroups(
visualizationGroups,
targetGroup,
updatedColumnOrder,
addedColumnId
);
const tempLayer = {
...resetIncomplete(layer, addedColumnId),
columns: { ...layer.columns, [addedColumnId]: column },
@ -749,16 +754,24 @@ export function reorderByGroups(
});
const columnGroupIndex: Record<string, number> = {};
updatedColumnOrder.forEach((columnId) => {
columnGroupIndex[columnId] = orderedVisualizationGroups.findIndex(
const groupIndex = orderedVisualizationGroups.findIndex(
(group) =>
(columnId === addedColumnId && group.groupId === targetGroup) ||
group.accessors.some((acc) => acc.columnId === columnId)
);
if (groupIndex !== -1) {
columnGroupIndex[columnId] = groupIndex;
} else {
// referenced columns won't show up in visualization groups - put them in the back of the list. This will work as they are always metrics
columnGroupIndex[columnId] = updatedColumnOrder.length;
}
});
updatedColumnOrder.sort((a, b) => {
return [...updatedColumnOrder].sort((a, b) => {
return columnGroupIndex[a] - columnGroupIndex[b];
});
} else {
return updatedColumnOrder;
}
}
@ -899,12 +912,8 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] {
}
});
const [direct, referenceBased] = _.partition(
entries,
([, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference'
);
// If a reference has another reference as input, put it last in sort order
referenceBased.sort(([idA, a], [idB, b]) => {
entries.sort(([idA, a], [idB, b]) => {
if ('references' in a && a.references.includes(idB)) {
return 1;
}
@ -913,12 +922,9 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] {
}
return 0;
});
const [aggregations, metrics] = _.partition(direct, ([, col]) => col.isBucketed);
const [aggregations, metrics] = _.partition(entries, ([, col]) => col.isBucketed);
return aggregations
.map(([id]) => id)
.concat(metrics.map(([id]) => id))
.concat(referenceBased.map(([id]) => id));
return aggregations.map(([id]) => id).concat(metrics.map(([id]) => id));
}
// Splits existing columnOrder into the three categories

View file

@ -6,6 +6,7 @@
*/
import type { IUiSettingsClient } from 'kibana/public';
import { partition } from 'lodash';
import {
AggFunctionsMapping,
EsaggsExpressionFunctionDefinition,
@ -57,14 +58,24 @@ function getExpressionForLayer(
const columnEntries = columnOrder.map((colId) => [colId, columns[colId]] as const);
if (columnEntries.length) {
const [referenceEntries, esAggEntries] = partition(
columnEntries,
([, col]) => operationDefinitionMap[col.operationType]?.input === 'fullReference'
);
if (referenceEntries.length || esAggEntries.length) {
const aggs: ExpressionAstExpressionBuilder[] = [];
const expressions: ExpressionAstFunction[] = [];
columnEntries.forEach(([colId, col]) => {
referenceEntries.forEach(([colId, col]) => {
const def = operationDefinitionMap[col.operationType];
if (def.input === 'fullReference') {
expressions.push(...def.toExpression(layer, colId, indexPattern));
} else {
}
});
esAggEntries.forEach(([colId, col]) => {
const def = operationDefinitionMap[col.operationType];
if (def.input !== 'fullReference') {
const wrapInFilter = Boolean(def.filterable && col.filter);
let aggAst = def.toEsAggsFn(
col,
@ -101,8 +112,8 @@ function getExpressionForLayer(
}
});
const idMap = columnEntries.reduce((currentIdMap, [colId, column], index) => {
const esAggsId = `col-${columnEntries.length === 1 ? 0 : index}-${colId}`;
const idMap = esAggEntries.reduce((currentIdMap, [colId, column], index) => {
const esAggsId = `col-${index}-${colId}`;
return {
...currentIdMap,
[esAggsId]: {