[Unified Search] Hide ES|QL ad hoc data views from the data view picker (#177109)

## Summary

This PR uses the new `esql` data view `type` to hide ES|QL ad hoc data
views from the Unified Search data view picker since they are an
implementation detail and should not be visible to users.

Resolves #170098.
Resolves #166911.
Partially addresses #176873.

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Davis McPhee 2024-02-22 20:40:23 -04:00 committed by GitHub
parent f006fc1a5e
commit b207ff4534
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 145 additions and 45 deletions

View file

@ -7,6 +7,5 @@
*/
export enum TextBasedLanguages {
SQL = 'SQL',
ESQL = 'ESQL',
}

View file

@ -17,7 +17,7 @@ import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/d
import { TextBasedLanguages } from '@kbn/esql-utils';
import { ChangeDataView } from './change_dataview';
import { DataViewSelector } from './data_view_selector';
import { dataViewMock } from './mocks/dataview';
import { dataViewMock, dataViewMockEsql } from './mocks/dataview';
import { DataViewPickerPropsExtended } from './data_view_picker';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@ -157,8 +157,8 @@ describe('DataView component', () => {
{
...props,
onDataViewCreated: jest.fn(),
textBasedLanguages: [TextBasedLanguages.ESQL, TextBasedLanguages.SQL],
textBasedLanguage: TextBasedLanguages.SQL,
textBasedLanguages: [TextBasedLanguages.ESQL],
textBasedLanguage: TextBasedLanguages.ESQL,
},
false
)
@ -168,35 +168,7 @@ describe('DataView component', () => {
expect(props.onTextLangQuerySubmit).toHaveBeenCalled();
});
it('should not propagate the adHoc dataviews for text based mode', async () => {
const component = mount(
wrapDataViewComponentInContext(
{
...props,
onDataViewCreated: jest.fn(),
textBasedLanguages: [TextBasedLanguages.ESQL, TextBasedLanguages.SQL],
textBasedLanguage: TextBasedLanguages.ESQL,
savedDataViews: [
{
id: 'dataview-1',
title: 'dataview-1',
},
],
adHocDataViews: [dataViewMock],
},
false
)
);
findTestSubject(component, 'dataview-trigger').simulate('click');
expect(component.find(DataViewSelector).prop('dataViewsList')).toStrictEqual([
{
id: 'dataview-1',
title: 'dataview-1',
},
]);
});
it('should propagate the adHoc dataviews for dataview mode', async () => {
it('should properly handle ad hoc data views', async () => {
const component = mount(
wrapDataViewComponentInContext(
{
@ -223,6 +195,40 @@ describe('DataView component', () => {
id: 'the-data-view-id',
title: 'the-data-view-title',
name: 'the-data-view',
type: 'default',
isAdhoc: true,
},
]);
});
it('should properly handle ES|QL ad hoc data views', async () => {
const component = mount(
wrapDataViewComponentInContext(
{
...props,
onDataViewCreated: jest.fn(),
savedDataViews: [
{
id: 'dataview-1',
title: 'dataview-1',
},
],
adHocDataViews: [dataViewMockEsql],
},
false
)
);
findTestSubject(component, 'dataview-trigger').simulate('click');
expect(component.find(DataViewSelector).prop('dataViewsList')).toStrictEqual([
{
id: 'dataview-1',
title: 'dataview-1',
},
{
id: 'the-data-view-esql-id',
title: 'the-data-view-esql-title',
name: 'the-data-view-esql',
type: 'esql',
isAdhoc: true,
},
]);

View file

@ -55,11 +55,12 @@ export const TextBasedLanguagesTransitionModal = (
</React.Suspense>
);
const mapAdHocDataView = (adHocDataView: DataView) => {
const mapAdHocDataView = (adHocDataView: DataView): DataViewListItemEnhanced => {
return {
title: adHocDataView.title,
name: adHocDataView.name,
id: adHocDataView.id!,
type: adHocDataView.type,
isAdhoc: true,
};
};
@ -112,9 +113,8 @@ export function ChangeDataView({
const savedDataViewRefs: DataViewListItemEnhanced[] = savedDataViews
? savedDataViews
: await data.dataViews.getIdsWithTitle();
// not propagate the adHoc dataviews on the list for text based languages
const adHocDataViewRefs: DataViewListItemEnhanced[] =
(!isTextBasedLangSelected && adHocDataViews?.map(mapAdHocDataView)) || [];
adHocDataViews?.map(mapAdHocDataView) ?? [];
setDataViewsList(savedDataViewRefs.concat(adHocDataViewRefs));
};

View file

@ -11,7 +11,8 @@ import { EuiSelectable } from '@elastic/eui';
import { act } from 'react-dom/test-utils';
import { ShallowWrapper } from 'enzyme';
import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers';
import { DataViewsList, DataViewsListProps } from './dataview_list';
import { DataViewListItemEnhanced, DataViewsList, DataViewsListProps } from './dataview_list';
import { ESQL_TYPE } from '@kbn/data-view-utils';
function getDataViewPickerList(instance: ShallowWrapper) {
return instance.find(EuiSelectable).first();
@ -47,6 +48,7 @@ describe('DataView list component', () => {
];
const changeDataViewSpy = jest.fn();
let props: DataViewsListProps;
beforeEach(() => {
props = {
currentDataViewId: 'dataview-1',
@ -55,6 +57,7 @@ describe('DataView list component', () => {
isTextBasedLangSelected: false,
};
});
it('should trigger the onChangeDataView if a new dataview is selected', async () => {
const component = shallow(<DataViewsList {...props} />);
await act(async () => {
@ -63,7 +66,7 @@ describe('DataView list component', () => {
expect(changeDataViewSpy).toHaveBeenCalled();
});
it('should list all dataviiew', () => {
it('should list all dataviews', () => {
const component = shallow(<DataViewsList {...props} />);
expect(getDataViewPickerOptions(component)!.map((option: any) => option.label)).toEqual([
@ -77,4 +80,54 @@ describe('DataView list component', () => {
expect(getDataViewPickerOptions(component)!.map((option: any) => option.append)).not.toBeNull();
});
describe('ad hoc data views', () => {
const runAdHocDataViewTest = (
esqlMode: boolean,
esqlDataViews: DataViewListItemEnhanced[] = []
) => {
const dataViewList = [
...list,
{
id: 'dataview-3',
title: 'dataview-3',
isAdhoc: true,
},
...esqlDataViews,
];
const component = shallow(
<DataViewsList {...props} dataViewsList={dataViewList} isTextBasedLangSelected={esqlMode} />
);
expect(getDataViewPickerOptions(component)!.map((option: any) => option.label)).toEqual([
'dataview-1',
'dataview-2',
'dataview-3',
]);
};
const esqlDataViews: DataViewListItemEnhanced[] = [
{
id: 'dataview-4',
title: 'dataview-4',
type: ESQL_TYPE,
isAdhoc: true,
},
];
it('should show ad hoc data views for text based mode', () => {
runAdHocDataViewTest(true);
});
it('should show ad hoc data views for data view mode', () => {
runAdHocDataViewTest(false);
});
it('should not show ES|QL ad hoc data views for text based mode', () => {
runAdHocDataViewTest(true, esqlDataViews);
});
it('should not show ES|QL ad hoc data views for data view mode', () => {
runAdHocDataViewTest(false, esqlDataViews);
});
});
});

View file

@ -19,9 +19,8 @@ import {
} from '@elastic/eui';
import type { DataViewListItem } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { ESQL_TYPE } from '@kbn/data-view-utils';
import { SortingService } from './sorting_service';
import { MIDDLE_TRUNCATION_PROPS } from '../filter_bar/filter_editor/lib/helpers';
@ -89,9 +88,13 @@ export function DataViewsList({
[]
);
const [sortedDataViewsList, setSortedDataViewsList] = useState<DataViewListItemEnhanced[]>(
sortingService.sortData(dataViewsList)
);
const [sortedDataViewsList, setSortedDataViewsList] = useState<DataViewListItemEnhanced[]>(() => {
// Don't show ES|QL ad hoc data views in the data view list
const filteredDataViewsList = dataViewsList.filter(
(dataView) => !dataView.isAdhoc || dataView.type !== ESQL_TYPE
);
return sortingService.sortData(filteredDataViewsList);
});
const sortOrderOptions = useMemo(
() =>

View file

@ -7,6 +7,7 @@
*/
import { DataView } from '@kbn/data-views-plugin/public';
import { ESQL_TYPE } from '@kbn/data-view-utils';
const fields = [
{
@ -77,10 +78,12 @@ export const buildDataViewMock = ({
name,
fields: definedFields,
timeFieldName,
type = 'default',
}: {
name: string;
fields: DataView['fields'];
timeFieldName?: string;
type?: string;
}): DataView => {
const dataViewFields = [...definedFields] as DataView['fields'];
@ -98,7 +101,7 @@ export const buildDataViewMock = ({
name,
metaFields: ['_index', '_score'],
fields: dataViewFields,
type: 'default',
type,
getName: () => name,
getComputedFields: () => ({ docvalueFields: [], scriptFields: {} }),
getSourceFiltering: () => ({}),
@ -126,5 +129,10 @@ export const dataViewMockWithTimefield = buildDataViewMock({
name: 'the-data-view-with-timefield',
fields,
});
export const dataViewMockEsql = buildDataViewMock({
name: 'the-data-view-esql',
fields,
type: ESQL_TYPE,
});
export const dataViewMockList = [dataViewMock, dataViewMockWithTimefield];

View file

@ -45,6 +45,7 @@
"@kbn/code-editor",
"@kbn/calculate-width-from-char-count",
"@kbn/react-kibana-context-render",
"@kbn/data-view-utils",
"@kbn/esql-utils"
],
"exclude": [

View file

@ -13,6 +13,7 @@ import type { DataView } from '@kbn/data-views-plugin/public';
import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks';
import { DataViewSelector } from '@kbn/unified-search-plugin/public';
import { act } from 'react-dom/test-utils';
import { ESQL_TYPE } from '@kbn/data-view-utils';
const selectedDataView = {
id: 'mock-data-logs-id',
@ -23,7 +24,13 @@ const selectedDataView = {
getName: () => 'kibana_sample_data_logs',
} as unknown as DataView;
const dataViewIds = ['mock-data-logs-id', 'mock-ecommerce-id', 'mock-test-id', 'mock-ad-hoc-id'];
const dataViewIds = [
'mock-data-logs-id',
'mock-ecommerce-id',
'mock-test-id',
'mock-ad-hoc-id',
'mock-ad-hoc-esql-id',
];
const dataViewOptions = [
selectedDataView,
@ -62,6 +69,16 @@ const dataViewOptions = [
isPersisted: jest.fn(() => false),
getName: () => 'ad-hoc data view',
},
{
id: 'mock-ad-hoc-esql-id',
namespaces: ['default'],
title: 'ad-hoc data view esql',
type: ESQL_TYPE,
typeMeta: {},
isTimeBased: jest.fn(),
isPersisted: jest.fn(() => false),
getName: () => 'ad-hoc data view esql',
},
];
const mount = () => {
@ -119,24 +136,35 @@ describe('DataViewSelectPopover', () => {
"isAdhoc": false,
"name": undefined,
"title": "kibana_sample_data_logs",
"type": undefined,
},
Object {
"id": "mock-ecommerce-id",
"isAdhoc": false,
"name": undefined,
"title": "kibana_sample_data_ecommerce",
"type": undefined,
},
Object {
"id": "mock-test-id",
"isAdhoc": false,
"name": undefined,
"title": "test",
"type": undefined,
},
Object {
"id": "mock-ad-hoc-id",
"isAdhoc": true,
"name": undefined,
"title": "ad-hoc data view",
"type": undefined,
},
Object {
"id": "mock-ad-hoc-esql-id",
"isAdhoc": true,
"name": undefined,
"title": "ad-hoc data view esql",
"type": "esql",
},
]
`);

View file

@ -46,6 +46,7 @@ const toDataViewListItem = (dataView: DataView): DataViewListItemEnhanced => {
id: dataView.id!,
title: dataView.title,
name: dataView.name,
type: dataView.type,
isAdhoc: !dataView.isPersisted(),
};
};

View file

@ -49,6 +49,7 @@
"@kbn/react-field",
"@kbn/code-editor",
"@kbn/esql-utils",
"@kbn/data-view-utils",
],
"exclude": [
"target/**/*",