[Graph] Only show explorable fields (#54101)

This commit is contained in:
Joe Reuter 2020-01-10 20:34:15 +01:00 committed by GitHub
parent 07278aba37
commit 51e07f27f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 113 additions and 13 deletions

View file

@ -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)

View file

@ -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: <FieldIcon type={field.type} size="m" useColor />,
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: <FieldIcon type={field.type} size="m" useColor />,
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);
}

View file

@ -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,
},
])
);

View file

@ -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({

View file

@ -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 {

View file

@ -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) {

View file

@ -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: {

View file

@ -25,6 +25,7 @@ export interface WorkspaceField {
icon: FontawesomeIcon;
selected: boolean;
type: string;
aggregatable: boolean;
}
export interface AdvancedSettings {

View file

@ -37,7 +37,7 @@ export interface SerializedUrlTemplate extends Omit<UrlTemplate, 'encoder' | 'ic
encoderID: string;
iconClass?: string;
}
export interface SerializedField extends Omit<WorkspaceField, 'icon' | 'type'> {
export interface SerializedField extends Omit<WorkspaceField, 'icon' | 'type' | 'aggregatable'> {
iconClass: string;
}