Skip to content

Commit 7440eea

Browse files
authored
[Lens] Use accordion menus in field list for available and empty fields (#68871)
1 parent 3ac5bc5 commit 7440eea

28 files changed

+784
-421
lines changed

x-pack/plugins/lens/public/indexpattern_datasource/__mocks__/loader.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export function loadInitialState() {
1919
[restricted.id]: restricted,
2020
},
2121
layers: {},
22-
showEmptyFields: false,
2322
};
2423
return result;
2524
}

x-pack/plugins/lens/public/indexpattern_datasource/__snapshots__/no_fields_callout.test.tsx.snap

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
@import 'datapanel';
21
@import 'field_item';

x-pack/plugins/lens/public/indexpattern_datasource/_datapanel.scss renamed to x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
line-height: $euiSizeXXL;
1717
}
1818

19-
.lnsInnerIndexPatternDataPanel__filterWrapper {
20-
flex-grow: 0;
21-
}
22-
2319
/**
2420
* 1. Don't cut off the shadow of the field items
2521
*/
@@ -41,11 +37,9 @@
4137
right: $euiSizeXS; /* 1 */
4238
}
4339

44-
.lnsInnerIndexPatternDataPanel__filterButton {
45-
width: 100%;
46-
color: $euiColorPrimary;
47-
padding-left: $euiSizeS;
48-
padding-right: $euiSizeS;
40+
.lnsInnerIndexPatternDataPanel__fieldItems {
41+
// Quick fix for making sure the shadow and focus rings are visible outside the accordion bounds
42+
padding: $euiSizeXS $euiSizeXS 0;
4943
}
5044

5145
.lnsInnerIndexPatternDataPanel__textField {
@@ -54,7 +48,9 @@
5448
}
5549

5650
.lnsInnerIndexPatternDataPanel__filterType {
51+
font-size: $euiFontSizeS;
5752
padding: $euiSizeS;
53+
border-bottom: 1px solid $euiColorLightestShade;
5854
}
5955

6056
.lnsInnerIndexPatternDataPanel__filterTypeInner {

x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx

Lines changed: 99 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@ import { createMockedDragDropContext } from './mocks';
99
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
1010
import { InnerIndexPatternDataPanel, IndexPatternDataPanel, MemoizedDataPanel } from './datapanel';
1111
import { FieldItem } from './field_item';
12+
import { NoFieldsCallout } from './no_fields_callout';
1213
import { act } from 'react-dom/test-utils';
1314
import { coreMock } from 'src/core/public/mocks';
1415
import { IndexPatternPrivateState } from './types';
1516
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
1617
import { ChangeIndexPattern } from './change_indexpattern';
17-
import { EuiProgress } from '@elastic/eui';
18+
import { EuiProgress, EuiLoadingSpinner } from '@elastic/eui';
1819
import { documentField } from './document_field';
1920

2021
const initialState: IndexPatternPrivateState = {
2122
indexPatternRefs: [],
2223
existingFields: {},
2324
currentIndexPatternId: '1',
24-
showEmptyFields: false,
2525
layers: {
2626
first: {
2727
indexPatternId: '1',
@@ -229,8 +229,6 @@ describe('IndexPattern Data Panel', () => {
229229
},
230230
query: { query: '', language: 'lucene' },
231231
filters: [],
232-
showEmptyFields: false,
233-
onToggleEmptyFields: jest.fn(),
234232
};
235233
});
236234

@@ -303,7 +301,6 @@ describe('IndexPattern Data Panel', () => {
303301
state: {
304302
indexPatternRefs: [],
305303
existingFields: {},
306-
showEmptyFields: false,
307304
currentIndexPatternId: 'a',
308305
indexPatterns: {
309306
a: { id: 'a', title: 'aaa', timeFieldName: 'atime', fields: [] },
@@ -534,42 +531,97 @@ describe('IndexPattern Data Panel', () => {
534531
});
535532
});
536533

537-
describe('while showing empty fields', () => {
538-
it('should list all supported fields in the pattern sorted alphabetically', async () => {
539-
const wrapper = shallowWithIntl(
540-
<InnerIndexPatternDataPanel {...defaultProps} showEmptyFields={true} />
534+
describe('displaying field list', () => {
535+
let props: Parameters<typeof InnerIndexPatternDataPanel>[0];
536+
beforeEach(() => {
537+
props = {
538+
...defaultProps,
539+
existingFields: {
540+
idx1: {
541+
bytes: true,
542+
memory: true,
543+
},
544+
},
545+
};
546+
});
547+
it('should list all supported fields in the pattern sorted alphabetically in groups', async () => {
548+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
549+
expect(wrapper.find(FieldItem).first().prop('field').name).toEqual('Records');
550+
expect(
551+
wrapper
552+
.find('[data-test-subj="lnsIndexPatternAvailableFields"]')
553+
.find(FieldItem)
554+
.map((fieldItem) => fieldItem.prop('field').name)
555+
).toEqual(['bytes', 'memory']);
556+
wrapper
557+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
558+
.find('button')
559+
.first()
560+
.simulate('click');
561+
expect(
562+
wrapper
563+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
564+
.find(FieldItem)
565+
.map((fieldItem) => fieldItem.prop('field').name)
566+
).toEqual(['client', 'source', 'timestamp']);
567+
});
568+
569+
it('should display NoFieldsCallout when all fields are empty', async () => {
570+
const wrapper = mountWithIntl(
571+
<InnerIndexPatternDataPanel {...defaultProps} existingFields={{ idx1: {} }} />
541572
);
573+
expect(wrapper.find(NoFieldsCallout).length).toEqual(1);
574+
expect(
575+
wrapper
576+
.find('[data-test-subj="lnsIndexPatternAvailableFields"]')
577+
.find(FieldItem)
578+
.map((fieldItem) => fieldItem.prop('field').name)
579+
).toEqual([]);
580+
wrapper
581+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
582+
.find('button')
583+
.first()
584+
.simulate('click');
585+
expect(
586+
wrapper
587+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
588+
.find(FieldItem)
589+
.map((fieldItem) => fieldItem.prop('field').name)
590+
).toEqual(['bytes', 'client', 'memory', 'source', 'timestamp']);
591+
});
542592

543-
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
544-
'Records',
545-
'bytes',
546-
'client',
547-
'memory',
548-
'source',
549-
'timestamp',
550-
]);
593+
it('should display spinner for available fields accordion if existing fields are not loaded yet', async () => {
594+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...defaultProps} />);
595+
expect(
596+
wrapper.find('[data-test-subj="lnsIndexPatternAvailableFields"]').find(EuiLoadingSpinner)
597+
.length
598+
).toEqual(1);
599+
wrapper.setProps({ existingFields: { idx1: {} } });
600+
expect(wrapper.find(NoFieldsCallout).length).toEqual(1);
551601
});
552602

553603
it('should filter down by name', () => {
554-
const wrapper = shallowWithIntl(
555-
<InnerIndexPatternDataPanel {...defaultProps} showEmptyFields={true} />
556-
);
557-
604+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
558605
act(() => {
559606
wrapper.find('[data-test-subj="lnsIndexPatternFieldSearch"]').prop('onChange')!({
560-
target: { value: 'mem' },
607+
target: { value: 'me' },
561608
} as ChangeEvent<HTMLInputElement>);
562609
});
563610

611+
wrapper
612+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
613+
.find('button')
614+
.first()
615+
.simulate('click');
616+
564617
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
565618
'memory',
619+
'timestamp',
566620
]);
567621
});
568622

569623
it('should filter down by type', () => {
570-
const wrapper = mountWithIntl(
571-
<InnerIndexPatternDataPanel {...defaultProps} showEmptyFields={true} />
572-
);
624+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
573625

574626
wrapper.find('[data-test-subj="lnsIndexPatternFiltersToggle"]').first().simulate('click');
575627

@@ -581,112 +633,55 @@ describe('IndexPattern Data Panel', () => {
581633
]);
582634
});
583635

584-
it('should toggle type if clicked again', () => {
585-
const wrapper = mountWithIntl(
586-
<InnerIndexPatternDataPanel {...defaultProps} showEmptyFields={true} />
587-
);
636+
it('should display no fields in groups when filtered by type Record', () => {
637+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
588638

589639
wrapper.find('[data-test-subj="lnsIndexPatternFiltersToggle"]').first().simulate('click');
590640

591-
wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click');
592-
wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click');
641+
wrapper.find('[data-test-subj="typeFilter-document"]').first().simulate('click');
593642

594643
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
595644
'Records',
596-
'bytes',
597-
'client',
598-
'memory',
599-
'source',
600-
'timestamp',
601645
]);
646+
expect(wrapper.find(NoFieldsCallout).length).toEqual(2);
602647
});
603648

604-
it('should filter down by type and by name', () => {
605-
const wrapper = mountWithIntl(
606-
<InnerIndexPatternDataPanel {...defaultProps} showEmptyFields={true} />
607-
);
608-
609-
act(() => {
610-
wrapper.find('[data-test-subj="lnsIndexPatternFieldSearch"]').prop('onChange')!({
611-
target: { value: 'mem' },
612-
} as ChangeEvent<HTMLInputElement>);
613-
});
614-
649+
it('should toggle type if clicked again', () => {
650+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
615651
wrapper.find('[data-test-subj="lnsIndexPatternFiltersToggle"]').first().simulate('click');
616652

617653
wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click');
618-
619-
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
620-
'memory',
621-
]);
622-
});
623-
});
624-
625-
describe('filtering out empty fields', () => {
626-
let emptyFieldsTestProps: typeof defaultProps;
627-
628-
beforeEach(() => {
629-
emptyFieldsTestProps = {
630-
...defaultProps,
631-
indexPatterns: {
632-
...defaultProps.indexPatterns,
633-
'1': {
634-
...defaultProps.indexPatterns['1'],
635-
fields: defaultProps.indexPatterns['1'].fields.map((field) => ({
636-
...field,
637-
exists: field.type === 'number',
638-
})),
639-
},
640-
},
641-
onToggleEmptyFields: jest.fn(),
642-
};
643-
});
644-
645-
it('should list all supported fields in the pattern sorted alphabetically', async () => {
646-
const props = {
647-
...emptyFieldsTestProps,
648-
existingFields: {
649-
idx1: {
650-
bytes: true,
651-
memory: true,
652-
},
653-
},
654-
};
655-
const wrapper = shallowWithIntl(<InnerIndexPatternDataPanel {...props} />);
656-
654+
wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click');
655+
wrapper
656+
.find('[data-test-subj="lnsIndexPatternEmptyFields"]')
657+
.find('button')
658+
.first()
659+
.simulate('click');
657660
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
658661
'Records',
659662
'bytes',
660663
'memory',
664+
'client',
665+
'source',
666+
'timestamp',
661667
]);
662668
});
663669

664-
it('should filter down by name', () => {
665-
const wrapper = shallowWithIntl(
666-
<InnerIndexPatternDataPanel {...emptyFieldsTestProps} showEmptyFields={true} />
667-
);
668-
670+
it('should filter down by type and by name', () => {
671+
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...props} />);
669672
act(() => {
670673
wrapper.find('[data-test-subj="lnsIndexPatternFieldSearch"]').prop('onChange')!({
671-
target: { value: 'mem' },
674+
target: { value: 'me' },
672675
} as ChangeEvent<HTMLInputElement>);
673676
});
674677

675-
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
676-
'memory',
677-
]);
678-
});
679-
680-
it('should allow removing the filter for data', () => {
681-
const wrapper = mountWithIntl(<InnerIndexPatternDataPanel {...emptyFieldsTestProps} />);
682-
683678
wrapper.find('[data-test-subj="lnsIndexPatternFiltersToggle"]').first().simulate('click');
684679

685-
wrapper.find('[data-test-subj="lnsEmptyFilter"]').first().prop('onChange')!(
686-
{} as ChangeEvent
687-
);
680+
wrapper.find('[data-test-subj="typeFilter-number"]').first().simulate('click');
688681

689-
expect(emptyFieldsTestProps.onToggleEmptyFields).toHaveBeenCalled();
682+
expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([
683+
'memory',
684+
]);
690685
});
691686
});
692687
});

0 commit comments

Comments
 (0)