From 771830d6d4a82b8a95866b1d102483324f18053c Mon Sep 17 00:00:00 2001 From: Philipp B Date: Mon, 3 Jun 2019 10:33:01 +0300 Subject: [PATCH] EuiDelayRender (#1876) New EuiDelayRender component is introduced, please have a look at the documentation to find out how to use it. This is a HOC so you can just wrap with it what you want and get delayed rendering. Set a delay period in ms. Default is 500. {...} --- CHANGELOG.md | 1 + src-docs/src/routes.js | 2 + .../src/views/delay_render/delay_render.js | 59 +++ .../delay_render/delay_render_example.js | 37 ++ .../__snapshots__/basic_table.test.js.snap | 460 +++++++++++------- .../in_memory_table.test.js.snap | 14 +- src/components/basic_table/basic_table.js | 13 +- src/components/delay_render/delay_render.tsx | 63 +++ src/components/delay_render/index.ts | 1 + src/components/index.js | 1 + .../field_value_toggle_filter.test.js.snap | 4 + ...eld_value_toggle_group_filter.test.js.snap | 16 + .../__snapshots__/is_filter.test.js.snap | 1 + .../filters/field_value_toggle_filter.js | 5 +- .../field_value_toggle_group_filter.js | 2 + .../search_bar/filters/is_filter.js | 5 +- 16 files changed, 482 insertions(+), 202 deletions(-) create mode 100644 src-docs/src/views/delay_render/delay_render.js create mode 100644 src-docs/src/views/delay_render/delay_render_example.js create mode 100644 src/components/delay_render/delay_render.tsx create mode 100644 src/components/delay_render/index.ts 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} );