diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx index 2f38f68b01f16d..d7cbcf59213be8 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -33,6 +33,7 @@ describe('field_manager', () => { selected: true, type: 'string', hopSize: 5, + aggregatable: true, }, { name: 'field2', @@ -42,6 +43,7 @@ describe('field_manager', () => { type: 'string', hopSize: 0, lastValidHopSize: 5, + aggregatable: false, }, { name: 'field3', @@ -50,6 +52,16 @@ describe('field_manager', () => { selected: false, type: 'string', hopSize: 5, + aggregatable: true, + }, + { + name: 'field4', + color: 'orange', + icon: getSuitableIcon('field4'), + selected: false, + type: 'string', + hopSize: 5, + aggregatable: false, }, ]) ); @@ -86,6 +98,17 @@ describe('field_manager', () => { ).toEqual('field2'); }); + it('should show selected non-aggregatable fields in picker, but hide unselected ones', () => { + expect( + getInstance() + .find(FieldPicker) + .dive() + .find(EuiSelectable) + .prop('options') + .map((option: { label: string }) => option.label) + ).toEqual(['field1', 'field2', 'field3']); + }); + it('should select fields from picker', () => { expect( getInstance() @@ -130,6 +153,25 @@ describe('field_manager', () => { expect(getInstance().find(FieldEditor).length).toEqual(1); }); + it('should show remove non-aggregatable fields from picker after deselection', () => { + act(() => { + getInstance() + .find(FieldEditor) + .at(1) + .dive() + .find(EuiContextMenu) + .prop('panels')![0].items![2].onClick!({} as any); + }); + expect( + getInstance() + .find(FieldPicker) + .dive() + .find(EuiSelectable) + .prop('options') + .map((option: { label: string }) => option.label) + ).toEqual(['field1', 'field3']); + }); + it('should disable field', () => { const toggleItem = getInstance() .find(FieldEditor) diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx index 6ad792defb6697..b38e3f8430980e 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_picker.tsx @@ -114,9 +114,26 @@ export function FieldPicker({ function toOptions( fields: WorkspaceField[] ): Array<{ label: string; checked?: 'on' | 'off'; prepend?: ReactNode }> { - return fields.map(field => ({ - label: field.name, - prepend: , - checked: field.selected ? 'on' : undefined, - })); + return ( + fields + // don't show non-aggregatable fields, except for the case when they are already selected. + // this is necessary to ensure backwards compatibility with existing workspaces that might + // contain non-aggregatable fields. + .filter(field => isExplorable(field) || field.selected) + .map(field => ({ + label: field.name, + prepend: , + checked: field.selected ? 'on' : undefined, + })) + ); +} + +const explorableTypes = ['string', 'number', 'date', 'ip', 'boolean']; + +function isExplorable(field: WorkspaceField) { + if (!field.aggregatable) { + return false; + } + + return explorableTypes.includes(field.type); } diff --git a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx b/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx index a615901f40e253..0109e1f5a5ac7b 100644 --- a/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/settings/settings.test.tsx @@ -112,6 +112,7 @@ describe('settings', () => { code: '1', label: 'test', }, + aggregatable: true, }, { selected: false, @@ -123,6 +124,7 @@ describe('settings', () => { code: '1', label: 'test', }, + aggregatable: true, }, ]) ); diff --git a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts index 3bfc868fcb06e9..79ff4debc7e825 100644 --- a/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/fetch_top_nodes.test.ts @@ -13,8 +13,24 @@ describe('fetch_top_nodes', () => { it('should build terms agg', async () => { const postMock = jest.fn(() => Promise.resolve({ resp: {} })); await fetchTopNodes(postMock as any, 'test', [ - { color: '', hopSize: 5, icon, name: 'field1', selected: false, type: 'string' }, - { color: '', hopSize: 5, icon, name: 'field2', selected: false, type: 'string' }, + { + color: '', + hopSize: 5, + icon, + name: 'field1', + selected: false, + type: 'string', + aggregatable: true, + }, + { + color: '', + hopSize: 5, + icon, + name: 'field2', + selected: false, + type: 'string', + aggregatable: true, + }, ]); expect(postMock).toHaveBeenCalledWith('../api/graph/searchProxy', { body: JSON.stringify({ @@ -65,8 +81,24 @@ describe('fetch_top_nodes', () => { }) ); const result = await fetchTopNodes(postMock as any, 'test', [ - { color: 'red', hopSize: 5, icon, name: 'field1', selected: false, type: 'string' }, - { color: 'blue', hopSize: 5, icon, name: 'field2', selected: false, type: 'string' }, + { + color: 'red', + hopSize: 5, + icon, + name: 'field1', + selected: false, + type: 'string', + aggregatable: true, + }, + { + color: 'blue', + hopSize: 5, + icon, + name: 'field2', + selected: false, + type: 'string', + aggregatable: true, + }, ]); expect(result.length).toEqual(4); expect(result[0]).toEqual({ diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts index d38c950a5986f2..1861479f85f189 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.test.ts @@ -119,9 +119,9 @@ describe('deserialize', () => { savedWorkspace, { getNonScriptedFields: () => [ - { name: 'field1', type: 'string' }, - { name: 'field2', type: 'string' }, - { name: 'field3', type: 'string' }, + { name: 'field1', type: 'string', aggregatable: true }, + { name: 'field2', type: 'string', aggregatable: true }, + { name: 'field3', type: 'string', aggregatable: true }, ], } as IndexPattern, workspace @@ -140,6 +140,7 @@ describe('deserialize', () => { expect(allFields).toMatchInlineSnapshot(` Array [ Object { + "aggregatable": true, "color": "black", "hopSize": undefined, "icon": undefined, @@ -149,6 +150,7 @@ describe('deserialize', () => { "type": "string", }, Object { + "aggregatable": true, "color": "black", "hopSize": undefined, "icon": undefined, @@ -158,6 +160,7 @@ describe('deserialize', () => { "type": "string", }, Object { + "aggregatable": true, "color": "#CE0060", "hopSize": 5, "icon": Object { diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts index af34b4f1a725b4..43425077cc174d 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/deserialize.ts @@ -89,6 +89,7 @@ export function mapFields(indexPattern: IndexPattern): WorkspaceField[] { color: colorChoices[index % colorChoices.length], selected: false, type: field.type, + aggregatable: Boolean(field.aggregatable), })) .sort((a, b) => { if (a.name < b.name) { diff --git a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts index 0e0c750383a71e..a3942eccfdac36 100644 --- a/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts +++ b/x-pack/legacy/plugins/graph/public/services/persistence/serialize.test.ts @@ -41,6 +41,7 @@ describe('serialize', () => { name: 'field1', selected: true, type: 'string', + aggregatable: true, }, { color: 'black', @@ -48,6 +49,7 @@ describe('serialize', () => { name: 'field2', selected: true, type: 'string', + aggregatable: true, }, ], selectedIndex: { diff --git a/x-pack/legacy/plugins/graph/public/types/app_state.ts b/x-pack/legacy/plugins/graph/public/types/app_state.ts index eef8060f07f5cd..876f2cf23b53a9 100644 --- a/x-pack/legacy/plugins/graph/public/types/app_state.ts +++ b/x-pack/legacy/plugins/graph/public/types/app_state.ts @@ -25,6 +25,7 @@ export interface WorkspaceField { icon: FontawesomeIcon; selected: boolean; type: string; + aggregatable: boolean; } export interface AdvancedSettings { diff --git a/x-pack/legacy/plugins/graph/public/types/persistence.ts b/x-pack/legacy/plugins/graph/public/types/persistence.ts index 7fc5e15d9ea72b..adb07605b61c46 100644 --- a/x-pack/legacy/plugins/graph/public/types/persistence.ts +++ b/x-pack/legacy/plugins/graph/public/types/persistence.ts @@ -37,7 +37,7 @@ export interface SerializedUrlTemplate extends Omit { +export interface SerializedField extends Omit { iconClass: string; }