Skip to content

Commit

Permalink
[InMemoryTable] Add enabled option to executeQueryOptions (elastic#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga authored and chandlerprall committed Nov 1, 2022
1 parent aa96323 commit 3883a47
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 11 deletions.
49 changes: 49 additions & 0 deletions src/components/basic_table/in_memory_table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { requiredProps } from '../../test';
import { EuiInMemoryTable, EuiInMemoryTableProps } from './in_memory_table';
import { keys, SortDirection } from '../../services';
import { SearchFilterConfig } from '../search_bar/filters';
import { Query } from '../search_bar/query';

interface BasicItem {
id: number | string;
Expand Down Expand Up @@ -1314,4 +1315,52 @@ describe('EuiInMemoryTable', () => {
expect(component.find('td').at(3).text()).toBe('Index3');
});
});

describe('controlled search query', () => {
it('execute the Query and filters the table items', () => {
const items = [{ title: 'foo' }, { title: 'bar' }, { title: 'baz' }];
const columns = [{ field: 'title', name: 'Title' }];
const query = Query.parse('baz');

const component = mount(
<EuiInMemoryTable
items={items}
search={{ query }}
columns={columns}
executeQueryOptions={{ defaultFields: ['title'] }}
/>
);

const tableContent = component.find(
'.euiTableRowCell .euiTableCellContent'
);

expect(tableContent.length).toBe(1); // only 1 match
expect(tableContent.at(0).text()).toBe('baz');
});

it('does not execute the Query and renders the items passed as is', () => {
const items = [{ title: 'foo' }, { title: 'bar' }, { title: 'baz' }];
const columns = [{ field: 'title', name: 'Title' }];
const query = Query.parse('baz');

const component = mount(
<EuiInMemoryTable
items={items}
search={{ query }}
columns={columns}
executeQueryOptions={{ defaultFields: ['title'], enabled: false }}
/>
);

const tableContent = component.find(
'.euiTableRowCell .euiTableCellContent'
);

expect(tableContent.length).toBe(3);
expect(tableContent.at(0).text()).toBe('foo');
expect(tableContent.at(1).text()).toBe('bar');
expect(tableContent.at(2).text()).toBe('baz');
});
});
});
12 changes: 9 additions & 3 deletions src/components/basic_table/in_memory_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ type InMemoryTableProps<T> = Omit<
defaultFields?: string[];
isClauseMatcher?: (...args: any) => boolean;
explain?: boolean;
/**
* When the search bar Query is controlled and passed to the `search` prop it is by default executed against the items passed to the table to filter them out.
* If the filtering is already done before passing the `items` to the table we can disable the execution by setting `enabled` to `false`.
*/
enabled?: boolean;
};
/**
* Insert content between the search bar and table components.
Expand Down Expand Up @@ -577,9 +582,10 @@ export class EuiInMemoryTable<T> extends Component<

const { query, sortName, pageIndex, pageSize } = this.state;

const matchingItems = query
? EuiSearchBar.Query.execute(query, items, executeQueryOptions)
: items;
const matchingItems =
query !== null && executeQueryOptions?.enabled !== false
? EuiSearchBar.Query.execute(query, items, executeQueryOptions)
: items;

const sortedItems = sortName
? matchingItems
Expand Down
36 changes: 28 additions & 8 deletions src/components/search_bar/query/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ const arrayIncludesValue = (array: any[], value: any) => {
return array.some((item) => valuesEqual(item, value));
};

const mustToMatch = (must: boolean) =>
must === true ? Match.MUST : Match.MUST_NOT;

/**
* The AST structure is an array of clauses. There are 3 types of clauses that are supported:
*
Expand Down Expand Up @@ -367,13 +370,24 @@ export class _AST {
return isNil(value) || arrayIncludesValue(clause.value as Value[], value);
}

getOrFieldClause(field: string, value?: Value) {
return this.getFieldClause(
field,
(clause) =>
isArray(clause.value) &&
(isNil(value) || arrayIncludesValue(clause.value, value))
);
getOrFieldClause(
field: string,
value?: Value,
must?: boolean,
operator?: OperatorType
) {
return this.getFieldClause(field, (clause) => {
if (!isArray(clause.value)) {
return false;
}

const matchValue =
isNil(value) || arrayIncludesValue(clause.value, value);
const matchMust = isNil(must) || mustToMatch(must) === clause.match;
const matchOperator = isNil(operator) || operator === clause.operator;

return matchValue && matchMust && matchOperator;
});
}

addOrFieldValue(
Expand All @@ -382,7 +396,13 @@ export class _AST {
must = true,
operator: OperatorType = Operator.EQ
) {
const existingClause = this.getOrFieldClause(field);
const existingClause = this.getOrFieldClause(
field,
undefined,
must,
operator
);

if (!existingClause) {
const newClause = must
? Field.must[operator](field, [value])
Expand Down
40 changes: 40 additions & 0 deletions src/components/search_bar/query/default_syntax.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,46 @@ describe('defaultSyntax', () => {
expect(printedQuery).toBe(query);
});

test('field or clause with negation', () => {
const query = 'field:(foo or bar) -field:(baz)';
const ast = defaultSyntax.parse(query);

expect(ast).toBeDefined();
expect(ast.clauses).toHaveLength(2);

const clauseMust: Clause = ast.getOrFieldClause('field')!;
expect(clauseMust).toBeDefined();
expect(clauseMust.value).toHaveLength(2);
expect(clauseMust.value).toEqual(['foo', 'bar']);

const clauseMustNot: Clause = ast.getOrFieldClause(
'field',
undefined,
false
)!;
expect(clauseMustNot).toBeDefined();
expect(clauseMustNot.value).toHaveLength(1);
expect(clauseMustNot.value).toEqual(['baz']);
});

test('addOrFieldValue()', () => {
const query = 'field:(foo) -field:(baz)';
let ast = defaultSyntax.parse(query);
expect(ast.getOrFieldClause('field')!.value).toEqual(['foo']);
expect(ast.getOrFieldClause('field', undefined, false)!.value).toEqual([
'baz',
]);

ast = ast.addOrFieldValue('field', 'foo2');
ast = ast.addOrFieldValue('field', 'baz2', false);

expect(ast.getOrFieldClause('field')!.value).toEqual(['foo', 'foo2']);
expect(ast.getOrFieldClause('field', undefined, false)!.value).toEqual([
'baz',
'baz2',
]);
});

test('field or & and clause & phrases', () => {
const query = 'field1:(foo or "bar baz") -field2:baz';
const ast = defaultSyntax.parse(query);
Expand Down
1 change: 1 addition & 0 deletions upcoming_changelogs/6284.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added the `enabled` option to the `<EuiInMemoryTable />` `executeQueryOptions` prop. This option prevents the Query from being executed when controlled by the consumer.

0 comments on commit 3883a47

Please sign in to comment.