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] InMemory Performance #2374

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8fe2cd6
Automatically detect data schema for in-memory datagrid
chandlerprall Sep 13, 2019
d7d68e7
Merge in described schema for field formatting
chandlerprall Sep 13, 2019
1818acb
Better column type detection
chandlerprall Sep 16, 2019
dd88556
Tests for euidatagrid schema / column type
chandlerprall Sep 17, 2019
0fa2017
refactor datagrid schema code, add datetime type detection
chandlerprall Sep 18, 2019
be62bf6
some comments
chandlerprall Sep 18, 2019
d43c422
Allow extra type detectors for EuiDataGrid
chandlerprall Sep 18, 2019
f56201e
cleanup of docs and type formatting
snide Sep 19, 2019
692ed6e
Fix datagrid unit test
chandlerprall Sep 19, 2019
b4a83f2
Update currency detector
chandlerprall Sep 19, 2019
66a4a55
Allow EuiDataGrid's inMemory prop to be {true}
chandlerprall Sep 19, 2019
8aa38f5
Added ability to provide extra props for the containing cell div
chandlerprall Sep 19, 2019
2054460
Added test for cell props
chandlerprall Sep 20, 2019
e79e150
Performance cleanups
chandlerprall Sep 23, 2019
6a2f174
Clean up datagrid doc's inMemory selection
chandlerprall Sep 23, 2019
17897a1
Merge branch 'feature/euidatagrid' into feature/euidatagrid-api-cleanup
chandlerprall Sep 23, 2019
b8f174b
Merged in feature branch
chandlerprall Sep 23, 2019
04562b3
EuiDataGrid in-memory options
chandlerprall Sep 24, 2019
800ea28
Performance refactor for in-memory values
chandlerprall Sep 25, 2019
c23a0b9
added a comment
chandlerprall Sep 25, 2019
7be8174
Merge branch 'feature/euidatagrid' into feature/euidatagrid-api-cleanup
snide Sep 25, 2019
ea09f89
Fix sorting on in-memory and schema datagrid docs
chandlerprall Sep 26, 2019
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
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for all the comments 💟

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