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) ||