mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[TableListView] Fix tag selection when passing initialFilter (#160871)
This commit is contained in:
parent
fd81b12e62
commit
aec3a4bee3
3 changed files with 132 additions and 57 deletions
|
@ -781,6 +781,49 @@ describe('TableListView', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('initialFilter', () => {
|
||||
const setupInitialFilter = registerTestBed<string, TableListViewTableProps>(
|
||||
WithServices<TableListViewTableProps>(TableListViewTable, {
|
||||
getTagList: () => [
|
||||
{ id: 'id-tag-foo', name: 'foo', type: 'tag', description: '', color: '' },
|
||||
],
|
||||
}),
|
||||
{
|
||||
defaultProps: { ...requiredProps },
|
||||
memoryRouter: { wrapComponent: true },
|
||||
}
|
||||
);
|
||||
|
||||
test('should filter by tag passed as in initialFilter prop', async () => {
|
||||
let testBed: TestBed;
|
||||
|
||||
const initialFilter = 'tag:(tag-1)';
|
||||
const findItems = jest.fn().mockResolvedValue({ total: 0, hits: [] });
|
||||
|
||||
await act(async () => {
|
||||
testBed = await setupInitialFilter({
|
||||
findItems,
|
||||
initialFilter,
|
||||
urlStateEnabled: false,
|
||||
});
|
||||
});
|
||||
|
||||
const { component, find } = testBed!;
|
||||
component.update();
|
||||
|
||||
const getSearchBoxValue = () => find('tableListSearchBox').props().defaultValue;
|
||||
|
||||
const getLastCallArgsFromFindItems = () =>
|
||||
findItems.mock.calls[findItems.mock.calls.length - 1];
|
||||
|
||||
// The search bar should be updated
|
||||
const expected = initialFilter;
|
||||
const [searchTerm] = getLastCallArgsFromFindItems();
|
||||
expect(getSearchBoxValue()).toBe(expected);
|
||||
expect(searchTerm).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('url state', () => {
|
||||
let router: Router | undefined;
|
||||
|
||||
|
@ -1265,7 +1308,7 @@ describe('TableList', () => {
|
|||
it('reports the page data test subject', async () => {
|
||||
const setPageDataTestSubject = jest.fn();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
setup({ setPageDataTestSubject });
|
||||
});
|
||||
|
||||
|
|
|
@ -293,6 +293,15 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
|
|||
|
||||
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<T extends UserContentCommonSchema>({
|
|||
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<T extends UserContentCommonSchema>({
|
|||
direction: 'asc',
|
||||
},
|
||||
}),
|
||||
[initialPageSize, initialQuery]
|
||||
[initialPageSize]
|
||||
);
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
@ -614,12 +620,67 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
|
|||
// ------------
|
||||
// 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(
|
||||
|
@ -815,47 +876,7 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
|
|||
|
||||
// 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',
|
||||
|
@ -885,7 +906,7 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
|
|||
|
||||
updateQueryFromURL(urlState.s);
|
||||
updateSortFromURL(urlState.sort);
|
||||
}, [urlState, searchQueryParser, getTagList, urlStateEnabled]);
|
||||
}, [urlState, buildQueryFromText, urlStateEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
|
@ -895,6 +916,13 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialQuery && !initialQueryInitialized.current) {
|
||||
initialQueryInitialized.current = true;
|
||||
buildQueryFromText(initialQuery).then(updateQuery);
|
||||
}
|
||||
}, [initialQuery, buildQueryFromText, updateQuery]);
|
||||
|
||||
const PageTemplate = useMemo<typeof KibanaPageTemplate>(() => {
|
||||
return withoutPageTemplateWrapper
|
||||
? ((({
|
||||
|
|
|
@ -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 (
|
||||
<I18nProvider>
|
||||
<TableListViewKibanaProvider
|
||||
|
@ -217,11 +225,7 @@ export const DashboardListing = ({
|
|||
http,
|
||||
},
|
||||
toMountPoint,
|
||||
savedObjectsTagging: savedObjectsTagging.hasApi // TODO: clean up this logic once https://github.com/elastic/kibana/issues/140433 is resolved
|
||||
? ({
|
||||
ui: savedObjectsTagging,
|
||||
} as TableListViewKibanaDependencies['savedObjectsTagging'])
|
||||
: undefined,
|
||||
savedObjectsTagging: savedObjectsTaggingFakePlugin,
|
||||
FormattedRelative,
|
||||
}}
|
||||
>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue