Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiDataGrid] Virtualization #4170

Merged
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
b0d616f
in-progress virtualization
chandlerprall Oct 22, 2020
8125fe2
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Nov 2, 2020
3795c28
Fix leading/trailing column focus management
chandlerprall Nov 2, 2020
d4060e5
Re-enabled datagrid header focus
chandlerprall Nov 3, 2020
37c6146
Fix cell tabIndex enable/disabling
chandlerprall Nov 4, 2020
70cec09
Re-enabled initial grid focus when tabbing into it
chandlerprall Nov 5, 2020
3d2ac34
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Nov 5, 2020
89ee454
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Nov 5, 2020
639f3bb
removed outdated code
chandlerprall Nov 5, 2020
653a313
restructure cell focus update calls to avoid render side effect
chandlerprall Nov 6, 2020
3889cc7
respond to changes in cell height
chandlerprall Nov 6, 2020
d574ee3
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Nov 10, 2020
9571bc0
Add optional height&width props to EuiDataGrid
chandlerprall Nov 10, 2020
f588432
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Nov 12, 2020
e81c56d
Respect height/width props and DOM constraints
chandlerprall Nov 12, 2020
b61114a
updated snapshots
chandlerprall Nov 12, 2020
bef5073
grid cells styling fixes
snide Dec 2, 2020
1c2a571
Merge remote-tracking branch 'upstream/master' into feature/virtualiz…
snide Dec 2, 2020
1449d02
fix borders and stripes
snide Dec 3, 2020
300801e
remove data_row, since it's no longer in use
snide Dec 3, 2020
8656fc8
fix prop and type for style
snide Dec 3, 2020
83254c1
remove console log
snide Dec 4, 2020
87c97ac
Merge remote-tracking branch 'upstream/master' into feature/virtualiz…
snide Dec 4, 2020
6281a5c
let popover in header act as a toggle
snide Dec 8, 2020
0ca6b5a
Merge remote-tracking branch 'upstream/master' into feature/virtualiz…
snide Dec 8, 2020
c4790bc
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Dec 18, 2020
00ed696
recompute grid height when rowCount changes
chandlerprall Dec 18, 2020
8b4ed63
Dynamically detect header row height
chandlerprall Dec 18, 2020
99e5346
Make useMutationObserver actually function
chandlerprall Dec 18, 2020
5328347
fix double-clicky and getting-stuck header cell popovers
chandlerprall Dec 18, 2020
1179b6f
small header height refactor
chandlerprall Dec 18, 2020
52798b3
fix grid height detection to wait until header height is known
chandlerprall Dec 21, 2020
458d25f
Fix footer column widths
chandlerprall Dec 21, 2020
037c979
Down to 2 failing unit tests
chandlerprall Dec 22, 2020
adeb437
Fix unit tests
chandlerprall Dec 23, 2020
42a0bb1
Quick react hook dependency cleanup
chandlerprall Dec 23, 2020
33176bc
only show buttons and popover if necessary
flash1293 Jan 12, 2021
1f3ba20
improve
flash1293 Jan 12, 2021
3c47649
make sure buttons and popover are only rendered if necessary
flash1293 Jan 12, 2021
f45009e
Refactor grid container height/width detection, ignore datagrid pages…
chandlerprall Jan 12, 2021
4e5111b
Fix datagrid setCellProps with a style object
chandlerprall Jan 12, 2021
1b2788b
remove an inaccurate comment
chandlerprall Jan 12, 2021
b54b4d7
Merge branch 'feature/virtualized-datagrid' into flash1293/datagrid-n…
flash1293 Jan 13, 2021
c05d493
clean up
flash1293 Jan 14, 2021
5dadd41
reduce rerenders
flash1293 Jan 14, 2021
c0d6c4e
remove console logs
flash1293 Jan 14, 2021
d205559
fix tests
flash1293 Jan 14, 2021
0d91454
convert to single mutation observer
flash1293 Jan 15, 2021
ba3eed5
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Jan 15, 2021
c75f81a
Small PR feedback cleanup
chandlerprall Jan 15, 2021
b1b9ae2
Fix fullscreen experience; revert change to default example which dup…
chandlerprall Jan 15, 2021
3f14321
Merge remote-tracking branch 'chandlerprall/feature/virtualized-datag…
flash1293 Jan 18, 2021
dfe0a90
reduce number of wrapper elements if not necessary
flash1293 Jan 18, 2021
43712b9
fix some bugs
flash1293 Jan 18, 2021
b7e1685
resolve some issues
flash1293 Jan 20, 2021
39816b5
resolve some issues
flash1293 Jan 20, 2021
8e3f6e1
resolve some issues
flash1293 Jan 20, 2021
10e2ac4
fix leftover problems
flash1293 Jan 20, 2021
60f9bd1
fix prettier / ts issues
snide Jan 20, 2021
5286e1f
Merge pull request #1 from snide/cleanup/less-wrappers
flash1293 Jan 21, 2021
2b99526
remove unnecessary checks
flash1293 Jan 21, 2021
b30faf1
remove unnecessary parameter
flash1293 Jan 21, 2021
624446f
Move datagrid's InnerElement to a stand alone component
chandlerprall Jan 21, 2021
bd0e2c6
Removed devDep on @types/resize-observer-browser in anticipation of T…
chandlerprall Jan 21, 2021
0f532df
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Jan 26, 2021
80915fd
Merge pull request #18 from flash1293/flash1293/less-wrappers
chandlerprall Jan 26, 2021
fa04eb1
Documentation
chandlerprall Jan 27, 2021
1e345a1
Fix a unit test setup
chandlerprall Jan 28, 2021
ca01966
chanagelog
chandlerprall Jan 28, 2021
d02a6b3
Merge branch 'master' into feature/virtualized-datagrid
chandlerprall Jan 28, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@
"@types/react-dom": "^16.9.6",
"@types/react-is": "^16.7.1",
"@types/react-router-dom": "^5.1.5",
"@types/resize-observer-browser": "^0.1.3",
"@types/tabbable": "^3.1.0",
"@types/url-parse": "^1.4.3",
"@types/uuid": "^8.3.0",
Expand Down
8 changes: 8 additions & 0 deletions scripts/a11y-testing.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ const docsPages = async (root, page) => {
`${root}#/forms/date-picker`,
`${root}#/forms/suggest`,
`${root}#/forms/super-date-picker`,
`${root}#/tabular-content/data-grid`,
`${root}#/tabular-content/data-grid-in-memory-settings`,
`${root}#/tabular-content/data-grid-schemas-and-popovers`,
`${root}#/tabular-content/data-grid-focus`,
`${root}#/tabular-content/data-grid-styling-and-control`,
`${root}#/tabular-content/data-grid-control-columns`,
`${root}#/tabular-content/data-grid-footer-row`,
`${root}#/tabular-content/data-grid-virtualization`,
`${root}#/elastic-charts/creating-charts`,
`${root}#/elastic-charts/part-to-whole-comparisons`,
`${root}#/utilities/css-utility-classes`,
Expand Down
2 changes: 2 additions & 0 deletions src-docs/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import { DataGridFocusExample } from './views/datagrid/datagrid_focus_example';
import { DataGridStylingExample } from './views/datagrid/datagrid_styling_example';
import { DataGridControlColumnsExample } from './views/datagrid/datagrid_controlcolumns_example';
import { DataGridFooterRowExample } from './views/datagrid/datagrid_footer_row_example';
import { DataGridVirtualizationExample } from './views/datagrid/datagrid_virtualization_example';

import { DatePickerExample } from './views/date_picker/date_picker_example';

Expand Down Expand Up @@ -368,6 +369,7 @@ const navigation = [
DataGridStylingExample,
DataGridControlColumnsExample,
DataGridFooterRowExample,
DataGridVirtualizationExample,
TableExample,
TableInMemoryExample,
].map((example) => createExample(example)),
Expand Down
11 changes: 7 additions & 4 deletions src-docs/src/views/datagrid/datagrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, {
useState,
createContext,
useContext,
useRef,
} from 'react';
import { fake } from 'faker';

Expand Down Expand Up @@ -191,7 +192,7 @@ const trailingControlColumns = [
ownFocus={true}>
<EuiPopoverTitle>Actions</EuiPopoverTitle>
<div style={{ width: 150 }}>
<button onClick={() => {}} component="span">
<button onClick={() => {}}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been bugging me a bunch in our docs. Why do we have a button (icon) within a <button> instead of just using a context menu?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a whole bunch of console error because of the nested <button>s.

<EuiFlexGroup
alignItems="center"
component="span"
Expand Down Expand Up @@ -293,6 +294,10 @@ export default () => {
};
}, []);

const onColumnResize = useRef((eventData) => {
console.log(eventData);
});

return (
<DataContext.Provider value={raw_data}>
<EuiDataGrid
Expand All @@ -310,9 +315,7 @@ export default () => {
onChangeItemsPerPage: onChangeItemsPerPage,
onChangePage: onChangePage,
}}
onColumnResize={(eventData) => {
console.log(eventData);
}}
onColumnResize={onColumnResize.current}
/>
</DataContext.Provider>
);
Expand Down
86 changes: 86 additions & 0 deletions src-docs/src/views/datagrid/datagrid_virtualization_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { Fragment } from 'react';

import { renderToHtml } from '../../services';
import { EuiCallOut, EuiCode } from '../../../../src/components';
import { GuideSectionTypes } from '../../components';

import DataGridVirtualization from './virtualization';
const dataGridVirtualizationSource = require('!!raw-loader!./virtualization');
const dataGridVirtualizationHtml = renderToHtml(DataGridVirtualization);

import DataGridVirtualizationConstrained from './virtualization_constrained';
const dataGridVirtualizationConstrainedSource = require('!!raw-loader!./virtualization_constrained');
const dataGridVirtualizationConstrainedHtml = renderToHtml(
DataGridVirtualizationConstrained
);

export const DataGridVirtualizationExample = {
title: 'Data grid virtualization',
sections: [
{
source: [
{
type: GuideSectionTypes.JS,
code: dataGridVirtualizationSource,
},
{
type: GuideSectionTypes.HTML,
code: dataGridVirtualizationHtml,
},
],
text: (
<Fragment>
<p>
Creating a lot of DOM nodes is computationally expensive, and{' '}
<strong>EuiDataGrid</strong> uses a couple wrapping divs to build
each cell. To help offset the cost of larger tables, cell
virtualization can be opted into by constraining the grid&apos;s
height and/or width. There are two ways to enable this
functionality. First, <EuiCode>height</EuiCode> and/or{' '}
<EuiCode>width</EuiCode> can be passed as props, which are applied
to the grid&apos;s container style. Alternatively, if{' '}
<strong>EuiDataGrid</strong> is unable to render at the full
dimensions it needs due to screen real estate or other DOM
constraints, it will overflow within a scrollable container and only
render the visible cells.
</p>

<EuiCallOut
title={
<>
Never toggle the height between a value and{' '}
<EuiCode>undefined</EuiCode>.
</>
}
color="warning">
<p>
Similar to React&apos;s rule of not switching between a controlled
and uncontrolled input, <EuiCode>EuiDataGrid</EuiCode> does not
accommodate for its height switching to or from{' '}
<EuiCode>undefined</EuiCode>. For demonstration purposes, the
example below uses a <EuiCode>key</EuiCode> to force{' '}
<strong>EuiDataGrid</strong> to completely remount when its height
changes between constrained & constrained heights.
</p>
</EuiCallOut>
</Fragment>
),
components: { DataGridVirtualization },
demo: <DataGridVirtualization />,
},
{
title: 'Constrained by DOM',
source: [
{
type: GuideSectionTypes.JS,
code: dataGridVirtualizationConstrainedSource,
},
{
type: GuideSectionTypes.HTML,
code: dataGridVirtualizationConstrainedHtml,
},
],
demo: <DataGridVirtualizationConstrained />,
},
],
};
200 changes: 200 additions & 0 deletions src-docs/src/views/datagrid/virtualization.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import React, {
Fragment,
useCallback,
useState,
createContext,
useContext,
useMemo,
useEffect,
} from 'react';
import { fake } from 'faker';

import {
EuiDataGrid,
EuiLink,
EuiText,
EuiSpacer,
EuiButtonGroup,
} from '../../../../src/components/';
import { EuiFormRow } from '../../../../src/components/form/form_row';

const DataContext = createContext();

const raw_data = [];

for (let i = 1; i < 10000; i++) {
const email = fake('{{internet.email}}');
const name = fake('{{name.lastName}}, {{name.firstName}}');
const suffix = fake('{{name.suffix}}');
raw_data.push({
name: `${name} ${suffix}`,
email: <EuiLink href="">{email}</EuiLink>,
location: (
<Fragment>
{`${fake('{{address.city}}')}, `}
<EuiLink href="https://google.com">
{fake('{{address.country}}')}
</EuiLink>
</Fragment>
),
date: fake('{{date.past}}'),
account: fake('{{finance.account}}'),
amount: fake('${{commerce.price}}'),
phone: fake('{{phone.phoneNumber}}'),
version: fake('{{system.semver}}'),
});
}

const columns = [
{
id: 'name',
displayAsText: 'Name',
defaultSortDirection: 'asc',
},
{
id: 'email',
},
{
id: 'location',
},
{
id: 'account',
},
{
id: 'date',
defaultSortDirection: 'desc',
},
{
id: 'amount',
},
{
id: 'phone',
isSortable: false,
},
{
id: 'version',
defaultSortDirection: 'desc',
initialWidth: 65,
isResizable: false,
},
];

function RenderCellValue({ rowIndex, columnId }) {
const { data, adjustMountedCellCount } = useContext(DataContext);

useEffect(() => {
adjustMountedCellCount(1);
return () => adjustMountedCellCount(-1);
}, [adjustMountedCellCount]);

return data.hasOwnProperty(rowIndex) ? data[rowIndex][columnId] : null;
}

const dimensionSizes = {
'height-300px': 300,
'height-600px': 600,

'width-200px': 200,
'width-50%': '50%',
'width-unconstrained': undefined,
};

export default () => {
// ** Pagination config
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 50 });
const onChangeItemsPerPage = useCallback(
(pageSize) =>
setPagination((pagination) => ({
...pagination,
pageSize,
pageIndex: 0,
})),
[setPagination]
);
const onChangePage = useCallback(
(pageIndex) =>
setPagination((pagination) => ({ ...pagination, pageIndex })),
[setPagination]
);

// Column visibility
const [visibleColumns, setVisibleColumns] = useState(() =>
columns.map(({ id }) => id)
); // initialize to the full set of columns

const [mountedCellCount, setMountedCellCount] = useState(0);

const dataContext = useMemo(
() => ({
data: raw_data,
adjustMountedCellCount: (adjustment) =>
setMountedCellCount(
(mountedCellCount) => mountedCellCount + adjustment
),
}),
[]
);

const [height, setHeight] = useState('height-300px');
const [width, setWidth] = useState('width-50%');

return (
<>
<EuiFormRow label="Height">
<EuiButtonGroup
legend="Set a height for the following grid"
options={[
{ id: 'height-300px', label: '300px' },
{ id: 'height-600px', label: '600px' },
{ id: 'height-unconstrained', label: 'Unconstrained' },
]}
idSelected={height}
onChange={setHeight}
/>
</EuiFormRow>

<EuiFormRow label="Width">
<EuiButtonGroup
legend="Set a width for the following grid"
options={[
{ id: 'width-200px', label: '200px' },
{ id: 'width-50%', label: '50%' },
{ id: 'width-unconstrained', label: 'Unconstrained' },
]}
idSelected={width}
onChange={setWidth}
/>
</EuiFormRow>

<EuiSpacer />

<EuiText>
<p>There are {mountedCellCount} rendered cells</p>
</EuiText>

<DataContext.Provider value={dataContext}>
<EuiDataGrid
// completely reset the grid when switching between controlled & uncontrolled heights
// otherwise, going from constrained->unconstrained is ignored.
// this is for example only, don't switch between controlled & uncontrolled heights
key={
height === 'height-unconstrained' ? 'unconstrained' : 'constrained'
}
aria-label="Virtualized data grid demo"
height={dimensionSizes[height]}
width={dimensionSizes[width]}
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
rowCount={raw_data.length}
renderCellValue={RenderCellValue}
pagination={{
...pagination,
pageSizeOptions: [50, 250, 1000],
onChangeItemsPerPage: onChangeItemsPerPage,
onChangePage: onChangePage,
}}
/>
</DataContext.Provider>
</>
);
};
Loading