From fe60f827312b7ba082bed1f18ebb51fb029fc1e0 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 11 Aug 2021 21:56:50 -0300 Subject: [PATCH] allow to disable virtualization --- docs/pages/api-docs/data-grid/data-grid.md | 1 + .../data-grid/grid-virtualization-api.json | 21 ----- docs/pages/api-docs/data-grid/x-grid.md | 1 + docs/scripts/buildApi.ts | 1 - .../virtualization/VirtualizationApiNoSnap.js | 7 -- .../virtualization/virtualization.md | 8 +- .../virtualization/useGridNoVirtualization.ts | 81 +++++++++++++++++++ .../virtualization/useGridVirtualization.ts | 15 +++- .../grid/hooks/root/useGridContainerProps.ts | 12 ++- .../_modules_/grid/models/gridOptions.tsx | 5 ++ .../src/tests/keyboard.DataGrid.test.tsx | 23 +++--- .../data-grid/src/useDataGridComponent.tsx | 2 + .../grid/x-grid/src/tests/rows.XGrid.test.tsx | 33 ++++++++ .../grid/x-grid/src/useXGridComponent.tsx | 2 + .../src/stories/grid-rows.stories.tsx | 15 ++++ 15 files changed, 179 insertions(+), 48 deletions(-) delete mode 100644 docs/pages/api-docs/data-grid/grid-virtualization-api.json delete mode 100644 docs/src/pages/components/data-grid/virtualization/VirtualizationApiNoSnap.js create mode 100644 packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts diff --git a/docs/pages/api-docs/data-grid/data-grid.md b/docs/pages/api-docs/data-grid/data-grid.md index ec45227713f57..8640609513b8b 100644 --- a/docs/pages/api-docs/data-grid/data-grid.md +++ b/docs/pages/api-docs/data-grid/data-grid.md @@ -33,6 +33,7 @@ import { DataGrid } from '@material-ui/data-grid'; | disableDensitySelector | boolean | false | If `true`, the density selector is disabled. | | disableExtendRowFullWidth | boolean | false | If `true`, rows will not be extended to fill the full width of the grid container. | | disableSelectionOnClick | boolean | false | If `true`, the selection on click on a row or cell is disabled. | +| disableVirtualization | boolean | false | If `true`, the virtualization is disabled. | | error | any | | An error that will turn the grid into its error state and display the error component. | | editRowsModel | GridEditRowsModel | undefined | Set the edit rows model of the grid. | | filterMode | GridFeatureMode | 'client' | Filtering can be processed on the server or client-side. Set it to `server` if you would like to handle filtering on the server-side. | diff --git a/docs/pages/api-docs/data-grid/grid-virtualization-api.json b/docs/pages/api-docs/data-grid/grid-virtualization-api.json deleted file mode 100644 index 2913901bbd01e..0000000000000 --- a/docs/pages/api-docs/data-grid/grid-virtualization-api.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "GridVirtualizationApi", - "description": "The virtualization API interface that is available in the grid apiRef.", - "properties": [ - { - "name": "getScrollPosition", - "description": "Returns the current scroll position.", - "type": "() => GridScrollParams" - }, - { - "name": "scroll", - "description": "Triggers the viewport to scroll to the given positions (in pixels).", - "type": "(params: Partial) => void" - }, - { - "name": "scrollToIndexes", - "description": "Triggers the viewport to scroll to the cell at indexes given by params.\nReturns true if the grid had to scroll to reach the target.", - "type": "(params: Partial) => boolean" - } - ] -} diff --git a/docs/pages/api-docs/data-grid/x-grid.md b/docs/pages/api-docs/data-grid/x-grid.md index bd75687bdc4da..0478658029488 100644 --- a/docs/pages/api-docs/data-grid/x-grid.md +++ b/docs/pages/api-docs/data-grid/x-grid.md @@ -40,6 +40,7 @@ import { XGrid } from '@material-ui/x-grid'; | disableMultipleColumnsSorting | boolean | false | If `true`, sorting with multiple columns is disabled. | | disableMultipleSelection | boolean | false | If `true`, multiple selection using the CTRL or CMD key is disabled. | | disableSelectionOnClick | boolean | false | If `true`, the selection on click on a row or cell is disabled. | +| disableVirtualization | boolean | false | If `true`, the virtualization is disabled. | | error | any | | An error that will turn the grid into its error state and display the error component. | | editRowsModel | GridEditRowsModel | undefined | Set the edit rows model of the grid. | | filterMode | GridFeatureMode | 'client' | Filtering can be processed on the server or client-side. Set it to `server` if you would like to handle filtering on the server-side. | diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index e8e3914570b23..6f73f834d5119 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -256,7 +256,6 @@ function run(argv: { outputDirectory?: string }) { 'GridFilterApi', 'GridCsvExportApi', 'GridExportCsvOptions', - 'GridVirtualizationApi', 'GridEvents', ]; diff --git a/docs/src/pages/components/data-grid/virtualization/VirtualizationApiNoSnap.js b/docs/src/pages/components/data-grid/virtualization/VirtualizationApiNoSnap.js deleted file mode 100644 index 7a9274542a6ee..0000000000000 --- a/docs/src/pages/components/data-grid/virtualization/VirtualizationApiNoSnap.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import ApiDocs from 'docsx/src/modules/components/ApiDocs'; -import api from 'docsx/pages/api-docs/data-grid/grid-virtualization-api.json'; - -export default function VirtualizationApiNoSnap() { - return ; -} diff --git a/docs/src/pages/components/data-grid/virtualization/virtualization.md b/docs/src/pages/components/data-grid/virtualization/virtualization.md index 293d6366515c2..f59d65381afc2 100644 --- a/docs/src/pages/components/data-grid/virtualization/virtualization.md +++ b/docs/src/pages/components/data-grid/virtualization/virtualization.md @@ -33,9 +33,13 @@ By default, 2 columns are rendered outside of the viewport. You can change this You can disable column virtualization by setting the column buffer to a higher number than the number of rendered columns, e.g. with `columnBuffer={columns.length}` or `columnBuffer={Number.MAX_SAFE_INTEGER}`. -## apiRef [](https://material-ui.com/store/items/material-ui-pro/) +## Disable virtualization -{{"demo": "pages/components/data-grid/virtualization/VirtualizationApiNoSnap.js", "bg": "inline", "hideToolbar": true}} +The virtualization can be disabled entirely using the `disableVirtualization` prop. + +```tsx + +``` ## API diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts new file mode 100644 index 0000000000000..26ee7ed23df6b --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts @@ -0,0 +1,81 @@ +import * as React from 'react'; +import { GridComponentProps } from '../../../GridComponentProps'; +import { GridApiRef } from '../../../models/api/gridApiRef'; +import { useNativeEventListener } from '../../root/useNativeEventListener'; +import { useGridScrollFn } from '../../utils/useGridScrollFn'; +import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; +import { useGridSelector } from '../core'; +import { useGridState } from '../core/useGridState'; +import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; + +export const useGridNoVirtualization = ( + apiRef: GridApiRef, + props: Pick, +): void => { + const windowRef = apiRef.current.windowRef; + const columnsHeaderRef = apiRef.current.columnHeadersElementRef; + const renderingZoneRef = apiRef.current.renderingZoneRef; + const [gridState, setGridState, forceUpdate] = useGridState(apiRef); + const [scrollTo] = useGridScrollFn(renderingZoneRef!, columnsHeaderRef!); + const paginationState = useGridSelector(apiRef, gridPaginationSelector); + const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + + const syncState = React.useCallback(() => { + if (!gridState.containerSizes || !windowRef?.current) { + return; + } + + let firstRowIdx = 0; + const { page, pageSize } = paginationState; + if (props.pagination && pageSize != null && props.paginationMode === 'client') { + firstRowIdx = pageSize * page; + } + const lastRowIdx = firstRowIdx + gridState.containerSizes.virtualRowsCount; + const lastColIdx = visibleColumns.length > 0 ? visibleColumns.length - 1 : 0; + const renderContext = { firstRowIdx, lastRowIdx, firstColIdx: 0, lastColIdx }; + + const scrollParams = { + top: windowRef.current!.scrollTop, + left: windowRef.current!.scrollLeft, + }; + + setGridState((state) => ({ + ...state, + rendering: { + ...state.rendering, + virtualPage: 0, + renderContext, + realScroll: scrollParams, + renderingZoneScroll: scrollParams, + }, + })); + forceUpdate(); + }, [ + gridState.containerSizes, + paginationState, + props.pagination, + props.paginationMode, + setGridState, + forceUpdate, + visibleColumns.length, + windowRef, + ]); + + React.useEffect(() => { + if (!props.disableVirtualization) { + return; + } + syncState(); + }, [props.disableVirtualization, syncState]); + + const handleScroll = React.useCallback(() => { + if (!props.disableVirtualization || !windowRef?.current) { + return; + } + const { scrollLeft, scrollTop } = windowRef.current; + scrollTo({ top: scrollTop, left: scrollLeft }); + syncState(); + }, [props.disableVirtualization, scrollTo, windowRef, syncState]); + + useNativeEventListener(apiRef, windowRef!, 'scroll', handleScroll, { passive: true }); +}; diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts index 745364a6ae00d..775700aec3117 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts @@ -229,6 +229,10 @@ export const useGridVirtualization = (apiRef: GridApiRef): void => { const updateViewport = React.useCallback( (forceReRender = false) => { + if (options.disableVirtualization) { + return; + } + const lastState = apiRef.current.getState(); const containerProps = lastState.containerSizes; if (!windowRef || !windowRef.current || !containerProps) { @@ -290,6 +294,7 @@ export const useGridVirtualization = (apiRef: GridApiRef): void => { setRenderingState, updateRenderedCols, windowRef, + options.disableVirtualization, ], ); @@ -306,6 +311,10 @@ export const useGridVirtualization = (apiRef: GridApiRef): void => { const scrollingTimeout = React.useRef(null); const handleScroll = React.useCallback(() => { + if (options.disableVirtualization) { + return; + } + // On iOS the inertia scrolling allows to return negative values. if (windowRef.current!.scrollLeft < 0 || windowRef.current!.scrollTop < 0) return; @@ -322,7 +331,7 @@ export const useGridVirtualization = (apiRef: GridApiRef): void => { if (apiRef.current.updateViewport) { apiRef.current.updateViewport(); } - }, [windowRef, apiRef, setGridState, forceUpdate]); + }, [options.disableVirtualization, windowRef, apiRef, setGridState, forceUpdate]); const getContainerPropsState = React.useCallback( () => gridState.containerSizes, @@ -334,6 +343,10 @@ export const useGridVirtualization = (apiRef: GridApiRef): void => { }, [gridState.rendering.renderContext]); useEnhancedEffect(() => { + if (options.disableVirtualization) { + return; + } + if (renderingZoneRef && renderingZoneRef.current) { logger.debug('applying scrollTop ', gridState.rendering.renderingZoneScroll.top); scrollTo(gridState.rendering.renderingZoneScroll); diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index 4e7d345419f9f..8b02d1d57c3ef 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -132,7 +132,7 @@ export const useGridContainerProps = (apiRef: GridApiRef) => { const requiredSize = rowsCount * rowHeight; const diff = requiredSize - windowSizesRef.current.height; // we activate virtualization when we have more than 2 rows outside the viewport - const isVirtualized = diff > rowHeight * 2; + const isVirtualized = diff > rowHeight * 2 && !options.disableVirtualization; if (options.autoPageSize || options.autoHeight || !isVirtualized) { const viewportFitHeightSize = Math.floor(viewportSizes.height / rowHeight); @@ -212,7 +212,15 @@ export const useGridContainerProps = (apiRef: GridApiRef) => { logger.debug('virtualized container props', indexes); return indexes; }, - [windowRef, columnsTotalWidth, rowHeight, options.autoPageSize, options.autoHeight, logger], + [ + windowRef, + columnsTotalWidth, + rowHeight, + options.disableVirtualization, + options.autoPageSize, + options.autoHeight, + logger, + ], ); const updateStateIfChanged = React.useCallback( diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index ed41a913573b1..5e798816ae9b9 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -126,6 +126,11 @@ export interface GridOptions { * @default false */ disableSelectionOnClick?: boolean; + /** + * If `true`, the virtualization is disabled. + * @default false + */ + disableVirtualization?: boolean; /** * Set the edit rows model of the grid. */ diff --git a/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx index bceee00e3b786..48f94a3f11365 100644 --- a/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx @@ -128,7 +128,11 @@ describe(' - Keyboard', () => { expect(handleKeyDown.returnValues).to.deep.equal([true]); }); - const KeyboardTest = (props: { nbRows?: number; checkboxSelection?: boolean }) => { + const KeyboardTest = (props: { + nbRows?: number; + checkboxSelection?: boolean; + disableVirtualization?: boolean; + }) => { const data = useData(props.nbRows || 100, 20); const transformColSizes = (columns: GridColumns) => columns.map((column) => ({ ...column, width: 60 })); @@ -140,6 +144,7 @@ describe(' - Keyboard', () => { rows={data.rows} columns={transformColSizes(data.columns)} checkboxSelection={props.checkboxSelection} + disableVirtualization={props.disableVirtualization} /> ); @@ -191,16 +196,11 @@ describe(' - Keyboard', () => { }); it('Space only should go to the bottom of the page', function test() { - if (isJSDOM) { - // Need layouting for row virtualization - this.skip(); - } - - render(); + render(); getCell(0, 0).focus(); expect(getActiveCell()).to.equal('0-0'); fireEvent.keyDown(document.activeElement!, { key: ' ' }); - expect(getActiveCell()).to.equal('4-0'); + expect(getActiveCell()).to.equal('99-0'); }); it('Space only should go to the bottom of the page even with small number of rows', () => { @@ -212,12 +212,7 @@ describe(' - Keyboard', () => { }); it('Home / End navigation', async function test() { - if (isJSDOM) { - // Need layouting for column virtualization - this.skip(); - } - - render(); + render(); getCell(1, 1).focus(); expect(getActiveCell()).to.equal('1-1'); fireEvent.keyDown(document.activeElement!, { key: 'Home' }); diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index c6fb7e3dba2ae..dc40ba18d53cc 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -23,6 +23,7 @@ import { useGridSelection } from '../../_modules_/grid/hooks/features/selection/ import { useGridSorting } from '../../_modules_/grid/hooks/features/sorting/useGridSorting'; import { useGridComponents } from '../../_modules_/grid/hooks/features/useGridComponents'; import { useGridVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridVirtualization'; +import { useGridNoVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridNoVirtualization'; import { useGridScroll } from '../../_modules_/grid/hooks/features/scroll/useGridScroll'; import { useApi } from '../../_modules_/grid/hooks/root/useApi'; import { useEvents } from '../../_modules_/grid/hooks/root/useEvents'; @@ -62,6 +63,7 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useGridContainerProps(apiRef); useGridDensity(apiRef, props); useGridScroll(apiRef, props); + useGridNoVirtualization(apiRef, props); useGridVirtualization(apiRef); useGridColumnResize(apiRef, props); useGridPageSize(apiRef, props); diff --git a/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx b/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx index d83a78363d28d..f5317153bd40c 100644 --- a/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx @@ -564,6 +564,39 @@ describe(' - Rows', () => { }); }); + describe('no virtualization', () => { + let apiRef: GridApiRef; + + const TestCase = (props: Partial & { nbRows?: number; nbCols?: number }) => { + apiRef = useGridApiRef(); + const data = useData(props.nbRows || 100, props.nbCols || 10); + return ( +
+ +
+ ); + }; + + it('should allow to disable virtualization', () => { + render(); + expect(document.querySelectorAll('[role="row"][data-rowindex]')).to.have.length(100); + expect(document.querySelectorAll('[role="cell"]')).to.have.length(100 * 10); + }); + + it('should render the correct rows when changing pages', () => { + render(); + expect(document.querySelectorAll('[role="row"][data-rowindex]')).to.have.length(100); + apiRef.current.setPage(1); + expect(document.querySelectorAll('[role="row"][data-rowindex]')).to.have.length(50); + }); + }); + describe('Cell focus', () => { let apiRef: GridApiRef; diff --git a/packages/grid/x-grid/src/useXGridComponent.tsx b/packages/grid/x-grid/src/useXGridComponent.tsx index 888497aad545c..084d4bdf8b2da 100644 --- a/packages/grid/x-grid/src/useXGridComponent.tsx +++ b/packages/grid/x-grid/src/useXGridComponent.tsx @@ -24,6 +24,7 @@ import { useGridSelection } from '../../_modules_/grid/hooks/features/selection/ import { useGridSorting } from '../../_modules_/grid/hooks/features/sorting/useGridSorting'; import { useGridComponents } from '../../_modules_/grid/hooks/features/useGridComponents'; import { useGridVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridVirtualization'; +import { useGridNoVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridNoVirtualization'; import { useGridScroll } from '../../_modules_/grid/hooks/features/scroll/useGridScroll'; import { useApi } from '../../_modules_/grid/hooks/root/useApi'; import { useEvents } from '../../_modules_/grid/hooks/root/useEvents'; @@ -63,6 +64,7 @@ export const useXGridComponent = (apiRef: GridApiRef, props: GridComponentProps) useGridContainerProps(apiRef); useGridDensity(apiRef, props); useGridScroll(apiRef, props); + useGridNoVirtualization(apiRef, props); useGridVirtualization(apiRef); useGridColumnReorder(apiRef, props); useGridColumnResize(apiRef, props); diff --git a/packages/storybook/src/stories/grid-rows.stories.tsx b/packages/storybook/src/stories/grid-rows.stories.tsx index c19a919ee4bcf..06721b200906f 100644 --- a/packages/storybook/src/stories/grid-rows.stories.tsx +++ b/packages/storybook/src/stories/grid-rows.stories.tsx @@ -974,6 +974,21 @@ export function SwitchVirtualization() { ); } + +export function DisableVirtualization() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 200, + maxColumns: 4, + }); + + return ( +
+ +
+ ); +} + export function DeferRendering() { const [deferRows, setRows] = React.useState([]); const [deferColumns] = React.useState([{ field: 'id', headerName: 'Id', width: 100 }]);