diff --git a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx index f03ab50c5234b..518eaa51cd1f8 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view.test.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view.test.tsx @@ -1252,7 +1252,7 @@ describe('TableList', () => { it('reports the page data test subject', async () => { const setPageDataTestSubject = jest.fn(); - act(() => { + await act(async () => { setup({ setPageDataTestSubject }); }); diff --git a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx index a0d544de5687e..2a564b0004d5c 100644 --- a/packages/content-management/table_list_view_table/src/table_list_view_table.tsx +++ b/packages/content-management/table_list_view_table/src/table_list_view_table.tsx @@ -293,6 +293,15 @@ function TableListViewTableComp({ const isMounted = useRef(false); const fetchIdx = useRef(0); + /** + * The "onTableSearchChange()" handler has an async behavior. We want to be able to discard + * previsous search changes and only handle the last one. For that we keep a counter of the changes. + */ + const tableSearchChangeIdx = useRef(0); + /** + * We want to build the initial query + */ + const initialQueryInitialized = useRef(false); const { canEditAdvancedSettings, @@ -333,10 +342,7 @@ function TableListViewTableComp({ showDeleteModal: false, hasUpdatedAtMetadata: false, selectedIds: [], - searchQuery: - initialQuery !== undefined - ? { text: initialQuery, query: new Query(Ast.create([]), undefined, initialQuery) } - : { text: '', query: new Query(Ast.create([]), undefined, '') }, + searchQuery: { text: '', query: new Query(Ast.create([]), undefined, '') }, pagination: { pageIndex: 0, totalItemCount: 0, @@ -348,7 +354,7 @@ function TableListViewTableComp({ direction: 'asc', }, }), - [initialPageSize, initialQuery] + [initialPageSize] ); const [state, dispatch] = useReducer(reducer, initialState); @@ -618,12 +624,67 @@ function TableListViewTableComp({ // ------------ // Callbacks // ------------ + const buildQueryFromText = useCallback( + async (text: string) => { + let ast = Ast.create([]); + let termMatch = text; + + if (searchQueryParser) { + // Parse possible tags in the search text + const { + references, + referencesToExclude, + searchQuery: searchTerm, + } = await searchQueryParser(text); + + termMatch = searchTerm; + + if (references?.length || referencesToExclude?.length) { + const allTags = getTagList(); + + if (references?.length) { + references.forEach(({ id: refId }) => { + const tag = allTags.find(({ id }) => id === refId); + if (tag) { + ast = ast.addOrFieldValue('tag', tag.name, true, 'eq'); + } + }); + } + + if (referencesToExclude?.length) { + referencesToExclude.forEach(({ id: refId }) => { + const tag = allTags.find(({ id }) => id === refId); + if (tag) { + ast = ast.addOrFieldValue('tag', tag.name, false, 'eq'); + } + }); + } + } + } + + if (termMatch.trim() !== '') { + ast = ast.addClause({ type: 'term', value: termMatch, match: 'must' }); + } + + return new Query(ast, undefined, text); + }, + [getTagList, searchQueryParser] + ); + const onTableSearchChange = useCallback( (arg: { query: Query | null; queryText: string }) => { - const query = arg.query ?? new Query(Ast.create([]), undefined, arg.queryText); - updateQuery(query); + if (arg.query) { + updateQuery(arg.query); + } else { + const idx = tableSearchChangeIdx.current + 1; + buildQueryFromText(arg.queryText).then((query) => { + if (idx === tableSearchChangeIdx.current) { + updateQuery(query); + } + }); + } }, - [updateQuery] + [updateQuery, buildQueryFromText] ); const updateTableSortAndPagination = useCallback( @@ -809,47 +870,7 @@ function TableListViewTableComp({ // Update our Query instance based on the URL "s" text const updateQueryFromURL = async (text: string = '') => { - let ast = Ast.create([]); - let termMatch = text; - - if (searchQueryParser) { - // Parse possible tags in the search text - const { - references, - referencesToExclude, - searchQuery: searchTerm, - } = await searchQueryParser(text); - - termMatch = searchTerm; - - if (references?.length || referencesToExclude?.length) { - const allTags = getTagList(); - - if (references?.length) { - references.forEach(({ id: refId }) => { - const tag = allTags.find(({ id }) => id === refId); - if (tag) { - ast = ast.addOrFieldValue('tag', tag.name, true, 'eq'); - } - }); - } - - if (referencesToExclude?.length) { - referencesToExclude.forEach(({ id: refId }) => { - const tag = allTags.find(({ id }) => id === refId); - if (tag) { - ast = ast.addOrFieldValue('tag', tag.name, false, 'eq'); - } - }); - } - } - } - - if (termMatch.trim() !== '') { - ast = ast.addClause({ type: 'term', value: termMatch, match: 'must' }); - } - - const updatedQuery = new Query(ast, undefined, text); + const updatedQuery = await buildQueryFromText(text); dispatch({ type: 'onSearchQueryChange', @@ -879,7 +900,7 @@ function TableListViewTableComp({ updateQueryFromURL(urlState.s); updateSortFromURL(urlState.sort); - }, [urlState, searchQueryParser, getTagList, urlStateEnabled]); + }, [urlState, buildQueryFromText, urlStateEnabled]); useEffect(() => { isMounted.current = true; @@ -889,6 +910,13 @@ function TableListViewTableComp({ }; }, []); + useEffect(() => { + if (initialQuery && !initialQueryInitialized.current) { + initialQueryInitialized.current = true; + buildQueryFromText(initialQuery).then(updateQuery); + } + }, [initialQuery, buildQueryFromText, updateQuery]); + const PageTemplate = useMemo(() => { return withoutPageTemplateWrapper ? ((({ diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 57716ee8b525e..e39d323374fbc 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -7,7 +7,7 @@ */ import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; -import React, { PropsWithChildren, useCallback, useState } from 'react'; +import React, { PropsWithChildren, useCallback, useMemo, useState } from 'react'; import { type TableListViewKibanaDependencies, @@ -206,6 +206,14 @@ export const DashboardListing = ({ const { getEntityName, getTableListTitle, getEntityNamePlural } = dashboardListingTableStrings; + const savedObjectsTaggingFakePlugin = useMemo(() => { + return savedObjectsTagging.hasApi // TODO: clean up this logic once https://github.com/elastic/kibana/issues/140433 is resolved + ? ({ + ui: savedObjectsTagging, + } as TableListViewKibanaDependencies['savedObjectsTagging']) + : undefined; + }, [savedObjectsTagging]); + return (