Skip to content

Commit

Permalink
[Lens] Make incomplete switches possible (#83519)
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 authored Nov 24, 2020
1 parent ab8a2f7 commit 0bd9201
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 100 deletions.
123 changes: 82 additions & 41 deletions x-pack/plugins/lens/public/pie_visualization/suggestions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { PaletteOutput } from 'src/plugins/charts/public';
import { DataType } from '../types';
import { suggestions } from './suggestions';
import { PieVisualizationState } from './types';

describe('suggestions', () => {
describe('pie', () => {
Expand Down Expand Up @@ -82,7 +83,7 @@ describe('suggestions', () => {
).toHaveLength(0);
});

it('should reject any date operations', () => {
it('should reject date operations', () => {
expect(
suggestions({
table: {
Expand Down Expand Up @@ -111,7 +112,7 @@ describe('suggestions', () => {
).toHaveLength(0);
});

it('should reject any histogram operations', () => {
it('should reject histogram operations', () => {
expect(
suggestions({
table: {
Expand Down Expand Up @@ -140,36 +141,32 @@ describe('suggestions', () => {
).toHaveLength(0);
});

it('should reject when there are no buckets', () => {
it('should reject when there are too many buckets', () => {
expect(
suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'c',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
columnId: 'a',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'b',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
],
changeType: 'initial',
},
state: undefined,
keptLayerIds: ['first'],
})
).toHaveLength(0);
});

it('should reject when there are no metrics', () => {
expect(
suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'c',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: true },
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'd',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'e',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
},
],
changeType: 'initial',
Expand All @@ -180,7 +177,7 @@ describe('suggestions', () => {
).toHaveLength(0);
});

it('should reject when there are too many buckets', () => {
it('should reject when there are too many metrics', () => {
expect(
suggestions({
table: {
Expand All @@ -201,7 +198,7 @@ describe('suggestions', () => {
},
{
columnId: 'd',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false },
},
{
columnId: 'e',
Expand All @@ -216,42 +213,86 @@ describe('suggestions', () => {
).toHaveLength(0);
});

it('should reject when there are too many metrics', () => {
it('should reject if there are no buckets and it is not a specific chart type switch', () => {
expect(
suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'a',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'b',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'c',
operation: { label: 'Top 5', dataType: 'string' as DataType, isBucketed: true },
},
{
columnId: 'd',
operation: { label: 'Avg', dataType: 'number' as DataType, isBucketed: false },
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
},
],
changeType: 'initial',
},
state: {} as PieVisualizationState,
keptLayerIds: ['first'],
})
).toHaveLength(0);
});

it('should reject if there are no metrics and it is not a specific chart type switch', () => {
expect(
suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'e',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
columnId: 'c',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: true },
},
],
changeType: 'initial',
},
state: undefined,
state: {} as PieVisualizationState,
keptLayerIds: ['first'],
})
).toHaveLength(0);
});

it('should hide suggestions when there are no buckets', () => {
const currentSuggestions = suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'c',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: false },
},
],
changeType: 'initial',
},
state: undefined,
keptLayerIds: ['first'],
});
expect(currentSuggestions).toHaveLength(3);
expect(currentSuggestions.every((s) => s.hide)).toEqual(true);
});

it('should hide suggestions when there are no metrics', () => {
const currentSuggestions = suggestions({
table: {
layerId: 'first',
isMultiRow: true,
columns: [
{
columnId: 'c',
operation: { label: 'Count', dataType: 'number' as DataType, isBucketed: true },
},
],
changeType: 'initial',
},
state: undefined,
keptLayerIds: ['first'],
});
expect(currentSuggestions).toHaveLength(3);
expect(currentSuggestions.every((s) => s.hide)).toEqual(true);
});

it('should suggest a donut chart as initial state when only one bucket', () => {
const results = suggestions({
table: {
Expand Down
32 changes: 22 additions & 10 deletions x-pack/plugins/lens/public/pie_visualization/suggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function suggestions({
state,
keptLayerIds,
mainPalette,
subVisualizationId,
}: SuggestionRequest<PieVisualizationState>): Array<
VisualizationSuggestion<PieVisualizationState>
> {
Expand All @@ -33,11 +34,17 @@ export function suggestions({

const [groups, metrics] = partition(table.columns, (col) => col.operation.isBucketed);

if (
groups.length === 0 ||
metrics.length !== 1 ||
groups.length > Math.max(MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS)
) {
if (metrics.length > 1 || groups.length > Math.max(MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS)) {
return [];
}

const incompleteConfiguration = metrics.length === 0 || groups.length === 0;
const metricColumnId = metrics.length > 0 ? metrics[0].columnId : undefined;

if (incompleteConfiguration && state && !subVisualizationId) {
// reject incomplete configurations if the sub visualization isn't specifically requested
// this allows to switch chart types via switcher with incomplete configurations, but won't
// cause incomplete suggestions getting auto applied on dropped fields
return [];
}

Expand Down Expand Up @@ -65,12 +72,12 @@ export function suggestions({
...state.layers[0],
layerId: table.layerId,
groups: groups.map((col) => col.columnId),
metric: metrics[0].columnId,
metric: metricColumnId,
}
: {
layerId: table.layerId,
groups: groups.map((col) => col.columnId),
metric: metrics[0].columnId,
metric: metricColumnId,
numberDisplay: 'percent',
categoryDisplay: 'default',
legendDisplay: 'default',
Expand Down Expand Up @@ -117,7 +124,7 @@ export function suggestions({
...state.layers[0],
layerId: table.layerId,
groups: groups.map((col) => col.columnId),
metric: metrics[0].columnId,
metric: metricColumnId,
categoryDisplay:
state.layers[0].categoryDisplay === 'inside'
? 'default'
Expand All @@ -126,7 +133,7 @@ export function suggestions({
: {
layerId: table.layerId,
groups: groups.map((col) => col.columnId),
metric: metrics[0].columnId,
metric: metricColumnId,
numberDisplay: 'percent',
categoryDisplay: 'default',
legendDisplay: 'default',
Expand All @@ -140,5 +147,10 @@ export function suggestions({
});
}

return [...results].sort((a, b) => a.score - b.score);
return [...results]
.sort((a, b) => a.score - b.score)
.map((suggestion) => ({
...suggestion,
hide: incompleteConfiguration || suggestion.hide,
}));
}
51 changes: 32 additions & 19 deletions x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
*/

import { getSuggestions } from './xy_suggestions';
import {
TableSuggestionColumn,
VisualizationSuggestion,
DataType,
TableSuggestion,
} from '../types';
import { TableSuggestionColumn, VisualizationSuggestion, TableSuggestion } from '../types';
import { State, XYState, visualizationTypes } from './types';
import { generateId } from '../id_generator';
import { getXyVisualization } from './xy_visualization';
Expand Down Expand Up @@ -89,12 +84,7 @@ describe('xy_suggestions', () => {
jest.resetAllMocks();
});

test('ignores invalid combinations', () => {
const unknownCol = () => {
const str = strCol('foo');
return { ...str, operation: { ...str.operation, dataType: 'wonkies' as DataType } };
};

test('partially maps invalid combinations, but hides them', () => {
expect(
([
{
Expand All @@ -111,19 +101,41 @@ describe('xy_suggestions', () => {
},
{
isMultiRow: false,
columns: [strCol('foo'), numCol('bar')],
columns: [numCol('bar')],
layerId: 'first',
changeType: 'unchanged',
},
] as TableSuggestion[]).map((table) => {
const suggestions = getSuggestions({ table, keptLayerIds: [] });
expect(suggestions.every((suggestion) => suggestion.hide)).toEqual(true);
expect(suggestions).toHaveLength(10);
})
);
});

test('rejects incomplete configurations if there is a state already but no sub visualization id', () => {
expect(
([
{
isMultiRow: true,
columns: [unknownCol(), numCol('bar')],
columns: [dateCol('a')],
layerId: 'first',
changeType: 'unchanged',
changeType: 'reduced',
},
{
isMultiRow: false,
columns: [numCol('bar')],
layerId: 'first',
changeType: 'reduced',
},
] as TableSuggestion[]).map((table) =>
expect(getSuggestions({ table, keptLayerIds: [] })).toEqual([])
)
] as TableSuggestion[]).map((table) => {
const suggestions = getSuggestions({
table,
keptLayerIds: [],
state: {} as XYState,
});
expect(suggestions).toHaveLength(0);
})
);
});

Expand Down Expand Up @@ -915,8 +927,9 @@ describe('xy_suggestions', () => {
Object {
"seriesType": "bar_stacked",
"splitAccessor": undefined,
"x": "quantity",
"x": undefined,
"y": Array [
"quantity",
"price",
],
},
Expand Down
Loading

0 comments on commit 0bd9201

Please sign in to comment.