diff --git a/CHANGELOG.md b/CHANGELOG.md
index c463d6725f9..3e3f7647849 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
- Added `iconProps` prop to `EuiCollapsibleNavGroup` to extend the props passed to the rendered `EuiIcon` ([#3365](https://github.com/elastic/eui/pull/3365))
- Added `closeButtonProps` to `EuiCollapsibleNav` ([#3398](https://github.com/elastic/eui/pull/3398))
- Added `buffer` prop to `EuiPopover` for altering minimum distance to container edges ([#3398](https://github.com/elastic/eui/pull/3398))
+- Allowed `search` prop changes to update `EuiInMemoryTable` internal query state ([#3371](https://github.com/elastic/eui/pull/3371))
**Bug Fixes**
diff --git a/src-docs/src/views/tables/in_memory/in_memory_search.js b/src-docs/src/views/tables/in_memory/in_memory_search.js
index 7c351793c80..0df220ad5eb 100644
--- a/src-docs/src/views/tables/in_memory/in_memory_search.js
+++ b/src-docs/src/views/tables/in_memory/in_memory_search.js
@@ -85,25 +85,6 @@ export const Table = () => {
return {label};
},
},
- {
- field: 'nationality',
- name: 'Nationality',
- render: countryCode => {
- const country = store.getCountry(countryCode);
- return `${country.flag} ${country.name}`;
- },
- },
- {
- field: 'online',
- name: 'Online',
- dataType: 'boolean',
- render: online => {
- const color = online ? 'success' : 'danger';
- const label = online ? 'Online' : 'Offline';
- return {label};
- },
- sortable: true,
- },
];
const search = {
diff --git a/src-docs/src/views/tables/in_memory/in_memory_search_external.js b/src-docs/src/views/tables/in_memory/in_memory_search_external.js
new file mode 100644
index 00000000000..3cee956bc59
--- /dev/null
+++ b/src-docs/src/views/tables/in_memory/in_memory_search_external.js
@@ -0,0 +1,276 @@
+import React, { Fragment, useState } from 'react';
+import { formatDate } from '../../../../../src/services/format';
+import { createDataStore } from '../data_store';
+import {
+ EuiInMemoryTable,
+ EuiLink,
+ EuiHealth,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiCheckableCard,
+} from '../../../../../src/components';
+
+/*
+Example user object:
+
+{
+ id: '1',
+ firstName: 'john',
+ lastName: 'doe',
+ github: 'johndoe',
+ dateOfBirth: Date.now(),
+ nationality: 'NL',
+ online: true
+}
+
+Example country object:
+
+{
+ code: 'NL',
+ name: 'Netherlands',
+ flag: '🇳🇱'
+}
+*/
+
+const initialState = {
+ eu: false,
+ na: false,
+ oc: false,
+ as: false,
+ af: false,
+ sa: false,
+};
+
+const store = createDataStore();
+
+export const Table = () => {
+ const [query, setQuery] = useState('');
+ const [state, setState] = useState(initialState);
+
+ const columns = [
+ {
+ field: 'firstName',
+ name: 'First Name',
+ sortable: true,
+ truncateText: true,
+ },
+ {
+ field: 'lastName',
+ name: 'Last Name',
+ truncateText: true,
+ },
+ {
+ field: 'github',
+ name: 'Github',
+ render: username => (
+
+ {username}
+
+ ),
+ },
+ {
+ field: 'dateOfBirth',
+ name: 'Date of Birth',
+ dataType: 'date',
+ render: date => formatDate(date, 'dobLong'),
+ sortable: true,
+ },
+ {
+ field: 'nationality',
+ name: 'Nationality',
+ render: countryCode => {
+ const country = store.getCountry(countryCode);
+ return `${country.flag} ${country.name}`;
+ },
+ },
+ {
+ field: 'online',
+ name: 'Online',
+ dataType: 'boolean',
+ render: online => {
+ const color = online ? 'success' : 'danger';
+ const label = online ? 'Online' : 'Offline';
+ return {label};
+ },
+ },
+ ];
+
+ const handleOnChange = search => {
+ setState(initialState);
+ setQuery(search.queryText);
+ return true;
+ };
+
+ const handleCheckbox = e => {
+ switch (e.target.id) {
+ case 'eu': {
+ const isChecked = !state.eu;
+ setQuery(
+ isChecked ? 'nationality:(NL or CZ or NO or IT or GB or GR)' : ''
+ );
+ setState({
+ ...initialState,
+ eu: isChecked,
+ });
+ break;
+ }
+ case 'na': {
+ const isChecked = !state.na;
+ setQuery(isChecked ? 'nationality:(US or CA or MX or HT)' : '');
+ setState({
+ ...initialState,
+ na: isChecked,
+ });
+ break;
+ }
+ case 'oc': {
+ const isChecked = !state.oc;
+ setQuery(isChecked ? 'nationality:(AU or FJ)' : '');
+ setState({
+ ...initialState,
+ oc: isChecked,
+ });
+ break;
+ }
+ case 'as': {
+ const isChecked = !state.as;
+ setQuery(isChecked ? 'nationality:(IL or LB)' : '');
+ setState({
+ ...initialState,
+ as: isChecked,
+ });
+ break;
+ }
+ case 'af': {
+ const isChecked = !state.af;
+ setQuery(isChecked ? 'nationality:(ZA or CG)' : '');
+ setState({
+ ...initialState,
+ af: isChecked,
+ });
+ break;
+ }
+ case 'sa': {
+ const isChecked = !state.sa;
+ setQuery(isChecked ? 'nationality:(CL)' : '');
+ setState({
+ ...initialState,
+ sa: isChecked,
+ });
+ break;
+ }
+ default:
+ }
+ };
+
+ const search = {
+ query,
+ onChange: handleOnChange,
+ box: {
+ schema: true,
+ },
+ filters: [
+ {
+ type: 'is',
+ field: 'online',
+ name: 'Online',
+ negatedName: 'Offline',
+ },
+ {
+ type: 'field_value_selection',
+ field: 'nationality',
+ name: 'Nationality',
+ multiSelect: 'or',
+ options: store.countries.map(country => ({
+ value: country.code,
+ name: country.name,
+ view: `${country.flag} ${country.name}`,
+ })),
+ },
+ ],
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src-docs/src/views/tables/in_memory/in_memory_search_external_section.js b/src-docs/src/views/tables/in_memory/in_memory_search_external_section.js
new file mode 100644
index 00000000000..1ab71f5477f
--- /dev/null
+++ b/src-docs/src/views/tables/in_memory/in_memory_search_external_section.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { GuideSectionTypes } from '../../../components';
+import { renderToHtml } from '../../../services';
+
+import { Table } from './in_memory_search_external';
+import { propsInfo } from './props_info';
+
+const source = require('!!raw-loader!./in_memory_search_external');
+const html = renderToHtml(Table);
+
+export const searchExternalSection = {
+ title: 'In-memory table with search and external state',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: source,
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: html,
+ },
+ ],
+ text: (
+
+
+ The example shows how to configure EuiInMemoryTable{' '}
+ when both external and internal search/filter states are in use.
+
+
+ ),
+ props: propsInfo,
+ demo: ,
+};
diff --git a/src-docs/src/views/tables/in_memory/index.js b/src-docs/src/views/tables/in_memory/index.js
index 6783e9f408a..f7b303a0e9b 100644
--- a/src-docs/src/views/tables/in_memory/index.js
+++ b/src-docs/src/views/tables/in_memory/index.js
@@ -1,5 +1,6 @@
export { section } from './in_memory_section';
export { selectionSection } from './in_memory_selection_section';
export { searchSection } from './in_memory_search_section';
+export { searchExternalSection } from './in_memory_search_external_section';
export { searchCallbackSection } from './in_memory_search_callback_section';
export { customSortingSection } from './in_memory_custom_sorting_section';
diff --git a/src-docs/src/views/tables/tables_in_memory_example.js b/src-docs/src/views/tables/tables_in_memory_example.js
index d21ebc745c8..204dfd4663e 100644
--- a/src-docs/src/views/tables/tables_in_memory_example.js
+++ b/src-docs/src/views/tables/tables_in_memory_example.js
@@ -2,6 +2,7 @@ import {
section as inMemorySection,
selectionSection as inMemorySelectionSection,
searchSection as inMemorySearchSection,
+ searchExternalSection as inMemorySearchExternalSection,
searchCallbackSection as inMemorySearchCallbackSection,
customSortingSection as inMemoryCustomSortingSection,
} from './in_memory';
@@ -13,6 +14,7 @@ export const TableInMemoryExample = {
inMemorySelectionSection,
inMemorySearchSection,
inMemorySearchCallbackSection,
+ inMemorySearchExternalSection,
inMemoryCustomSortingSection,
],
};
diff --git a/src/components/basic_table/in_memory_table.tsx b/src/components/basic_table/in_memory_table.tsx
index 3708112caad..08a9daa8bc2 100644
--- a/src/components/basic_table/in_memory_table.tsx
+++ b/src/components/basic_table/in_memory_table.tsx
@@ -108,7 +108,9 @@ interface State {
items: T[];
sortName: ReactNode;
sortDirection?: Direction;
+ search?: Search;
};
+ search?: Search;
query: Query | null;
pageIndex: number;
pageSize?: number;
@@ -119,12 +121,20 @@ interface State {
hidePerPageOptions: boolean | undefined;
}
-const getInitialQuery = (search: Search | undefined) => {
+const getQueryFromSearch = (
+ search: Search | undefined,
+ defaultQuery: boolean
+) => {
let query: Query | string;
if (!search) {
query = '';
} else {
- query = (search as EuiSearchBarProps).defaultQuery || '';
+ query =
+ (defaultQuery
+ ? (search as EuiSearchBarProps).defaultQuery ||
+ (search as EuiSearchBarProps).query ||
+ ''
+ : (search as EuiSearchBarProps).query) || '';
}
return isString(query) ? EuiSearchBar.Query.parse(query) : query;
@@ -258,6 +268,19 @@ export class EuiInMemoryTable extends Component<
sortDirection,
};
}
+
+ if (
+ (nextProps.search as EuiSearchBarProps) !==
+ (prevState.prevProps.search as EuiSearchBarProps)
+ ) {
+ return {
+ prevProps: {
+ ...prevState.prevProps,
+ search: nextProps.search,
+ },
+ query: getQueryFromSearch(nextProps.search, false),
+ };
+ }
return null;
}
@@ -278,8 +301,10 @@ export class EuiInMemoryTable extends Component<
items: props.items,
sortName,
sortDirection,
+ search,
},
- query: getInitialQuery(search),
+ search: search,
+ query: getQueryFromSearch(search, true),
pageIndex: pageIndex || 0,
pageSize,
pageSizeOptions,
@@ -369,10 +394,14 @@ export class EuiInMemoryTable extends Component<
}
// Reset pagination state.
- this.setState({
+ this.setState(state => ({
+ prevProps: {
+ ...state.prevProps,
+ search,
+ },
query,
pageIndex: 0,
- });
+ }));
};
renderSearchBar() {
diff --git a/src/components/search_bar/search_bar.tsx b/src/components/search_bar/search_bar.tsx
index fbaa4d369a7..2e3de4d787d 100644
--- a/src/components/search_bar/search_bar.tsx
+++ b/src/components/search_bar/search_bar.tsx
@@ -134,7 +134,7 @@ export class EuiSearchBar extends Component {
prevState: State
): State | null {
if (
- nextProps.query &&
+ (nextProps.query || nextProps.query === '') &&
(!prevState.query ||
(typeof nextProps.query !== 'string' &&
nextProps.query.text !== prevState.query.text) ||