diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5f95295961..cf083d33e87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
- Added `onToggle` callback to `EuiAccordion` ([#1974](https://github.com/elastic/eui/pull/1974))
- Removed `options` `defaultProps` value from `EuiSuperSelect` ([#1975](https://github.com/elastic/eui/pull/1975))
- Removed TSlint and will perform all linting through ESLint ([#1950](https://github.com/elastic/eui/pull/1950))
+- Added new component `EuiDelayRender` ([#1876](https://github.com/elastic/eui/pull/1876))
- Replaced `EuiColorPicker` with custom, customizable component ([#1914](https://github.com/elastic/eui/pull/1914))
**Bug fixes**
diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js
index 67882fc71cc..81080050344 100644
--- a/src-docs/src/routes.js
+++ b/src-docs/src/routes.js
@@ -68,6 +68,7 @@ import { CopyExample } from './views/copy/copy_example';
import { DatePickerExample } from './views/date_picker/date_picker_example';
import { DelayHideExample } from './views/delay_hide/delay_hide_example';
+import { DelayRenderExample } from './views/delay_render/delay_render_example';
import { DescriptionListExample } from './views/description_list/description_list_example';
@@ -366,6 +367,7 @@ const navigation = [
CopyExample,
UtilityClassesExample,
DelayHideExample,
+ DelayRenderExample,
ErrorBoundaryExample,
FocusTrapExample,
HighlightExample,
diff --git a/src-docs/src/views/delay_render/delay_render.js b/src-docs/src/views/delay_render/delay_render.js
new file mode 100644
index 00000000000..504dbf450c8
--- /dev/null
+++ b/src-docs/src/views/delay_render/delay_render.js
@@ -0,0 +1,59 @@
+import React, { Component, Fragment } from 'react';
+import {
+ EuiDelayRender,
+ EuiFlexItem,
+ EuiCheckbox,
+ EuiFormRow,
+ EuiFieldNumber,
+ EuiLoadingSpinner,
+} from '../../../../src/components';
+
+export default class extends Component {
+ state = {
+ minimumDelay: 3000,
+ render: false,
+ };
+
+ onChangeMinimumDelay = event => {
+ this.setState({ minimumDelay: parseInt(event.target.value, 10) });
+ };
+
+ onChangeHide = event => {
+ this.setState({ render: event.target.checked });
+ };
+
+ render() {
+ const status = this.state.render ? 'showing' : 'hidden';
+ const label = `Child (${status})`;
+ return (
+
+
+
+
+
+
+
+
+
+
+ {this.state.render ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+ );
+ }
+}
diff --git a/src-docs/src/views/delay_render/delay_render_example.js b/src-docs/src/views/delay_render/delay_render_example.js
new file mode 100644
index 00000000000..268e5f9dfa8
--- /dev/null
+++ b/src-docs/src/views/delay_render/delay_render_example.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import DelayRender from './delay_render';
+import { GuideSectionTypes } from '../../components';
+import { EuiCode, EuiDelayRender } from '../../../../src/components';
+import { renderToHtml } from '../../services';
+
+const delayRenderSource = require('!!raw-loader!./delay_render');
+const delayRenderHtml = renderToHtml(DelayRender);
+
+export const DelayRenderExample = {
+ title: 'Delay Render',
+ sections: [
+ {
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: delayRenderSource,
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: delayRenderHtml,
+ },
+ ],
+ text: (
+
+ EuiDelayRender is a component for conditionally
+ toggling the visibility of a child component. It will ensure that the
+ child is hidden for at least 500ms (default). This allows delay UI
+ rendering. That is helpful when you need to update aria live region(s)
+ repeatedly.
+
+ ),
+ props: { EuiDelayRender },
+ demo: ,
+ },
+ ],
+};
diff --git a/src/components/basic_table/__snapshots__/basic_table.test.js.snap b/src/components/basic_table/__snapshots__/basic_table.test.js.snap
index 91358ae05b6..0fb11ef045d 100644
--- a/src/components/basic_table/__snapshots__/basic_table.test.js.snap
+++ b/src/components/basic_table/__snapshots__/basic_table.test.js.snap
@@ -28,15 +28,19 @@ exports[`EuiBasicTable cellProps renders cells with custom props from a callback
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -149,15 +153,19 @@ exports[`EuiBasicTable cellProps renders rows with custom props from an object 1
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -272,15 +280,19 @@ exports[`EuiBasicTable empty is rendered 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -348,15 +360,19 @@ exports[`EuiBasicTable empty renders a node as a custom message 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -432,15 +448,19 @@ exports[`EuiBasicTable empty renders a string as a custom message 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -508,15 +528,19 @@ exports[`EuiBasicTable footers do not render without a column footer definition
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -745,15 +769,19 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1051,15 +1079,19 @@ exports[`EuiBasicTable itemIdToExpandedRowMap renders an expanded row 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1183,15 +1215,19 @@ exports[`EuiBasicTable rowProps renders rows with custom props from a callback 1
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1310,15 +1346,19 @@ exports[`EuiBasicTable rowProps renders rows with custom props from an object 1`
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1437,15 +1477,19 @@ exports[`EuiBasicTable with pagination - 2nd page 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1543,15 +1587,19 @@ exports[`EuiBasicTable with pagination 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1666,15 +1714,19 @@ exports[`EuiBasicTable with pagination and error 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1754,15 +1806,19 @@ exports[`EuiBasicTable with pagination and selection 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -1921,15 +1977,19 @@ exports[`EuiBasicTable with pagination, hiding the per page options 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -2066,15 +2126,19 @@ exports[`EuiBasicTable with pagination, selection and sorting 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -2257,15 +2321,19 @@ exports[`EuiBasicTable with pagination, selection, sorting and a single record a
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -2562,15 +2630,19 @@ exports[`EuiBasicTable with pagination, selection, sorting and column dataType 1
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -2753,15 +2825,19 @@ exports[`EuiBasicTable with pagination, selection, sorting and column renderer 1
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -2944,15 +3020,19 @@ exports[`EuiBasicTable with pagination, selection, sorting and multiple record a
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -3267,15 +3347,19 @@ exports[`EuiBasicTable with pagination, selection, sorting, column renderer and
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -3437,15 +3521,19 @@ exports[`EuiBasicTable with sortable columns and sorting disabled 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
@@ -3563,15 +3651,19 @@ exports[`EuiBasicTable with sorting 1`] = `
aria-relevant="text"
role="status"
>
-
+
+ />
+
diff --git a/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap b/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
index fe572ae4865..e825c9f5490 100644
--- a/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
+++ b/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
@@ -139,17 +139,9 @@ exports[`EuiInMemoryTable behavior pagination 1`] = `
className="euiScreenReaderOnly"
role="status"
>
-
- Below is a table of 2 items.
-
+
diff --git a/src/components/basic_table/basic_table.js b/src/components/basic_table/basic_table.js
index 65f2dff7d2c..1d5b2dee2d3 100644
--- a/src/components/basic_table/basic_table.js
+++ b/src/components/basic_table/basic_table.js
@@ -38,6 +38,7 @@ import { EuiTableSortMobile } from '../table/mobile/table_sort_mobile';
import { withRequiredProp } from '../../utils/prop_types/with_required_prop';
import { EuiScreenReaderOnly, EuiKeyboardAccessible } from '../accessibility';
import { EuiI18n } from '../i18n';
+import { EuiDelayRender } from '../delay_render';
import makeId from '../form/form_row/make_id';
const dataTypesProfiles = {
@@ -460,11 +461,13 @@ export class EuiBasicTable extends Component {
return (
-
+
+
+
);
diff --git a/src/components/delay_render/delay_render.tsx b/src/components/delay_render/delay_render.tsx
new file mode 100644
index 00000000000..49ceff33b50
--- /dev/null
+++ b/src/components/delay_render/delay_render.tsx
@@ -0,0 +1,63 @@
+import { Component } from 'react';
+
+interface EuiDelayRenderProps {
+ delay: number;
+}
+
+interface EuiDelayRenderState {
+ toggle: boolean;
+}
+
+export class EuiDelayRender extends Component<
+ EuiDelayRenderProps,
+ EuiDelayRenderState
+> {
+ static defaultProps = {
+ delay: 500,
+ };
+
+ private delayID: number | undefined;
+ private toBeDelayed: boolean = true;
+
+ constructor(props: EuiDelayRenderProps) {
+ super(props);
+ this.state = {
+ toggle: false,
+ };
+ }
+
+ shouldUpdate() {
+ this.setState(({ toggle }) => ({ toggle: !toggle }));
+ }
+
+ startDelaying = () => {
+ window.clearTimeout(this.delayID);
+ this.toBeDelayed = true;
+ this.delayID = window.setTimeout(this.stopDelaying, this.props.delay);
+ };
+ stopDelaying = () => {
+ window.clearTimeout(this.delayID);
+ this.toBeDelayed = false;
+ this.shouldUpdate();
+ };
+
+ componentDidMount() {
+ this.startDelaying();
+ }
+ shouldComponentUpdate() {
+ if (this.toBeDelayed) {
+ this.startDelaying();
+ }
+ return true;
+ }
+ componentWillUnmount() {
+ this.stopDelaying();
+ }
+ componentDidUpdate() {
+ this.toBeDelayed = true;
+ }
+
+ render() {
+ return !this.toBeDelayed ? this.props.children : null;
+ }
+}
diff --git a/src/components/delay_render/index.ts b/src/components/delay_render/index.ts
new file mode 100644
index 00000000000..6d29dbddf4f
--- /dev/null
+++ b/src/components/delay_render/index.ts
@@ -0,0 +1 @@
+export { EuiDelayRender } from './delay_render';
diff --git a/src/components/index.js b/src/components/index.js
index 139ee7f57a0..f6008bc1596 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -53,6 +53,7 @@ export {
} from './date_picker';
export { EuiDelayHide } from './delay_hide';
+export { EuiDelayRender } from './delay_render';
export {
EuiDescriptionList,
diff --git a/src/components/search_bar/filters/__snapshots__/field_value_toggle_filter.test.js.snap b/src/components/search_bar/filters/__snapshots__/field_value_toggle_filter.test.js.snap
index 3a281e49ba3..3796cb5c880 100644
--- a/src/components/search_bar/filters/__snapshots__/field_value_toggle_filter.test.js.snap
+++ b/src/components/search_bar/filters/__snapshots__/field_value_toggle_filter.test.js.snap
@@ -2,6 +2,7 @@
exports[`FieldValueToggleFilter render - active 1`] = `
,
,
,
,
+
{name}
);
diff --git a/src/components/search_bar/filters/field_value_toggle_group_filter.js b/src/components/search_bar/filters/field_value_toggle_group_filter.js
index 66c944259c5..6a08fcc1c17 100644
--- a/src/components/search_bar/filters/field_value_toggle_group_filter.js
+++ b/src/components/search_bar/filters/field_value_toggle_group_filter.js
@@ -75,6 +75,8 @@ export class FieldValueToggleGroupFilter extends Component {
key={key}
onClick={onClick}
hasActiveFilters={active}
+ noDivider={!isLastItem}
+ aria-pressed={!!active}
withNext={!isLastItem}>
{name}
diff --git a/src/components/search_bar/filters/is_filter.js b/src/components/search_bar/filters/is_filter.js
index 27bf341e3cc..304d6f1e2d3 100644
--- a/src/components/search_bar/filters/is_filter.js
+++ b/src/components/search_bar/filters/is_filter.js
@@ -56,7 +56,10 @@ export class IsFilter extends Component {
this.valueChanged(config.field, checked);
};
return (
-
+
{name}
);