Skip to content

Commit

Permalink
[EuiDataGrid] InMemory Performance (#2374)
Browse files Browse the repository at this point in the history
* Automatically detect data schema for in-memory datagrid

* Merge in described schema for field formatting

* Better column type detection

* Tests for euidatagrid schema / column type

* refactor datagrid schema code, add datetime type detection

* some comments

* Allow extra type detectors for EuiDataGrid

* cleanup of docs and type formatting

* Fix datagrid unit test

* Update currency detector

* Allow EuiDataGrid's inMemory prop to be {true}

* Added ability to provide extra props for the containing cell div

* Added test for cell props

* Performance cleanups

* Clean up datagrid doc's inMemory selection

* Merged in feature branch

* EuiDataGrid in-memory options

* Performance refactor for in-memory values

* added a comment

* Fix sorting on in-memory and schema datagrid docs
  • Loading branch information
chandlerprall authored Sep 27, 2019
1 parent 18153cf commit 250b121
Show file tree
Hide file tree
Showing 13 changed files with 421 additions and 342 deletions.
215 changes: 150 additions & 65 deletions src-docs/src/views/datagrid/datagrid.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import React, { Component, Fragment, useEffect } from 'react';
import React, {
Fragment,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { fake } from 'faker';

import {
EuiButton,
EuiDataGrid,
EuiButtonIcon,
EuiLink,
EuiPopover,
} from '../../../../src/components/';
import { iconTypes } from '../../../../src-docs/src/views/icon/icons';
import { EuiRadioGroup } from '../../../../src/components/form/radio';

const columns = [
{
Expand Down Expand Up @@ -38,10 +47,10 @@ const columns = [
},
];

const data = [];
const raw_data = [];

for (let i = 1; i < 100; i++) {
data.push({
for (let i = 1; i < 1000; i++) {
raw_data.push({
name: fake('{{name.lastName}}, {{name.firstName}} {{name.suffix}}'),
email: <EuiLink href="">{fake('{{internet.email}}')}</EuiLink>,
location: (
Expand Down Expand Up @@ -72,22 +81,39 @@ for (let i = 1; i < 100; i++) {
});
}

export default class DataGridContainer extends Component {
constructor(props) {
super(props);

this.state = {
sortingColumns: [],
data,
pagination: {
pageIndex: 0,
pageSize: 50,
},
};
}
export default () => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

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

// ** Sorting config
const [sortingColumns, setSortingColumns] = useState([]);
const onSort = useCallback(
sortingColumns => {
setSortingColumns(sortingColumns);
},
[setSortingColumns]
);

setSorting = sortingColumns => {
const sortedData = [...data].sort((a, b) => {
const [inMemoryLevel, setInMemoryLevel] = useState('');

// Sort data
let data = useMemo(() => {
// the grid itself is responsible for sorting if inMemory is `sorting`
if (inMemoryLevel === 'sorting') {
return raw_data;
}

return [...raw_data].sort((a, b) => {
for (let i = 0; i < sortingColumns.length; i++) {
const column = sortingColumns[i];
const aValue = a[column.id];
Expand All @@ -99,58 +125,117 @@ export default class DataGridContainer extends Component {

return 0;
});
this.setState({ sortingColumns, data: sortedData });
};

setPageIndex = pageIndex =>
this.setState(({ pagination }) => ({
pagination: { ...pagination, pageIndex },
}));

setPageSize = pageSize =>
this.setState(({ pagination }) => ({
pagination: { ...pagination, pageSize },
}));

dummyIcon = () => (
<EuiButtonIcon
aria-label="dummy icon"
iconType={iconTypes[Math.floor(Math.random() * iconTypes.length)]}
/>
);
}, [raw_data, sortingColumns, inMemoryLevel]);

// Pagination
data = useMemo(() => {
// the grid itself is responsible for sorting if inMemory is sorting or pagination
if (inMemoryLevel === 'sorting' || inMemoryLevel === 'pagination') {
return data;
}

const rowStart = pagination.pageIndex * pagination.pageSize;
const rowEnd = Math.min(rowStart + pagination.pageSize, data.length);
return data.slice(rowStart, rowEnd);
}, [data, pagination, inMemoryLevel]);

const renderCellValue = useMemo(() => {
return ({ rowIndex, columnId, setCellProps }) => {
let adjustedRowIndex = rowIndex;

render() {
const { data, pagination, sortingColumns } = this.state;
// If we are doing the pagination (instead of leaving that to the grid)
// then the row index must be adjusted as `data` has already been pruned to the page size
if (inMemoryLevel !== 'sorting' && inMemoryLevel !== 'pagination') {
adjustedRowIndex =
rowIndex - pagination.pageIndex * pagination.pageSize;
}

useEffect(() => {
if (columnId === 'amount') {
if (data.hasOwnProperty(adjustedRowIndex)) {
const numeric = parseFloat(
data[adjustedRowIndex][columnId].match(/\d+\.\d+/)[0],
10
);
setCellProps({
style: {
backgroundColor: `rgba(0, ${(numeric / 1000) * 255}, 0, 0.2)`,
},
});
}
}
}, [adjustedRowIndex, columnId, setCellProps]);

return data.hasOwnProperty(adjustedRowIndex)
? data[adjustedRowIndex][columnId]
: null;
};
}, [data, inMemoryLevel]);

const inMemoryProps = {};
if (inMemoryLevel !== '') {
inMemoryProps.inMemory = {
level: inMemoryLevel,
skipColumns: ['actions'],
};
}

return (
<div>
<EuiPopover
isOpen={isPopoverOpen}
button={
<EuiButton onClick={() => setIsPopoverOpen(state => !state)}>
inMemory options
</EuiButton>
}
closePopover={() => setIsPopoverOpen(false)}>
<EuiRadioGroup
compressed={true}
options={[
{
id: '',
label: 'off',
value: '',
},
{
id: 'enhancements',
label: 'only enhancements',
value: 'enhancements',
},
{
id: 'pagination',
label: 'only pagination',
value: 'pagination',
},
{
id: 'sorting',
label: 'sorting and pagination',
value: 'sorting',
},
]}
idSelected={inMemoryLevel}
onChange={(id, value) => {
setInMemoryLevel(value === '' ? undefined : value);
setIsPopoverOpen(false);
}}
/>
</EuiPopover>

return (
<EuiDataGrid
aria-label="Data grid demo"
columns={columns}
rowCount={data.length}
renderCellValue={({ rowIndex, columnId, setCellProps }) => {
useEffect(() => {
if (columnId === 'amount') {
const numeric = parseFloat(
data[rowIndex][columnId].match(/\d+\.\d+/)[0],
10
);
setCellProps({
style: {
backgroundColor: `rgba(0, ${(numeric / 1000) * 255}, 0, 0.2)`,
},
});
}
}, [rowIndex, columnId, setCellProps]);
return data[rowIndex][columnId];
}}
sorting={{ columns: sortingColumns, onSort: this.setSorting }}
rowCount={raw_data.length}
renderCellValue={renderCellValue}
{...inMemoryProps}
sorting={{ columns: sortingColumns, onSort }}
pagination={{
...pagination,
pageSizeOptions: [5, 10, 25],
onChangeItemsPerPage: this.setPageSize,
onChangePage: this.setPageIndex,
pageSizeOptions: [10, 25, 50, 100],
onChangeItemsPerPage: onChangeItemsPerPage,
onChangePage: onChangePage,
}}
/>
);
}
}
</div>
);
};
2 changes: 1 addition & 1 deletion src-docs/src/views/datagrid/in_memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export default class InMemoryDataGrid extends Component {
const value = data[rowIndex][columnId];
return value;
}}
inMemory="sorting"
inMemory={{ level: 'sorting' }}
sorting={{ columns: sortingColumns, onSort: this.setSorting }}
pagination={{
...pagination,
Expand Down
17 changes: 16 additions & 1 deletion src-docs/src/views/datagrid/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,22 @@ export default class InMemoryDataGrid extends Component {
};
}

setSorting = sortingColumns => this.setState({ sortingColumns });
setSorting = sortingColumns => {
const data = [...this.state.data].sort((a, b) => {
for (let i = 0; i < sortingColumns.length; i++) {
const column = sortingColumns[i];
const aValue = a[column.id];
const bValue = b[column.id];

if (aValue < bValue) return column.direction === 'asc' ? -1 : 1;
if (aValue > bValue) return column.direction === 'asc' ? 1 : -1;
}

return 0;
});

this.setState({ data, sortingColumns });
};

setPageIndex = pageIndex =>
this.setState(({ pagination }) => ({
Expand Down
Loading

0 comments on commit 250b121

Please sign in to comment.