Skip to content

Commit

Permalink
[DataGridPremium] Fix auto-scroll not working when selecting cell ran…
Browse files Browse the repository at this point in the history
…ge (#12267)

Co-authored-by: Bilal Shafi <[email protected]>
  • Loading branch information
cherniavskii and MBilalShafi authored Mar 12, 2024
1 parent 3d604b0 commit 2afd9cf
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 27 deletions.
6 changes: 6 additions & 0 deletions docs/pages/x/api/data-grid/selectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@
"description": "",
"supportsApiRef": true
},
{
"name": "gridHeaderFilteringEnabledSelector",
"returnType": "boolean",
"description": "",
"supportsApiRef": true
},
{
"name": "gridHeaderFilteringMenuSelector",
"returnType": "string | null",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ownerDocument, useEventCallback } from '@mui/material/utils';
import {
GridPipeProcessor,
GridStateInitializer,
getTotalHeaderHeight,
isNavigationKey,
serializeCellValue,
useGridRegisterPipeProcessor,
Expand All @@ -25,6 +26,7 @@ import {
GRID_REORDER_COL_DEF,
useGridSelector,
gridSortedRowIdsSelector,
gridDimensionsSelector,
} from '@mui/x-data-grid-pro';
import { gridCellSelectionStateSelector } from './gridCellSelectionSelector';
import { GridCellSelectionApi } from './gridCellSelectionInterfaces';
Expand Down Expand Up @@ -56,6 +58,7 @@ export const useGridCellSelection = (
| 'paginationMode'
| 'ignoreValueFormatterDuringExport'
| 'clipboardCopyCellDelimiter'
| 'columnHeaderHeight'
>,
) => {
const visibleRows = useGridVisibleRows(apiRef, props);
Expand All @@ -64,6 +67,8 @@ export const useGridCellSelection = (
const mousePosition = React.useRef<{ x: number; y: number } | null>(null);
const autoScrollRAF = React.useRef<number | null>();
const sortedRowIds = useGridSelector(apiRef, gridSortedRowIdsSelector);
const dimensions = useGridSelector(apiRef, gridDimensionsSelector);
const totalHeaderHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight);

const ignoreValueFormatterProp = props.ignoreValueFormatterDuringExport;
const ignoreValueFormatter =
Expand Down Expand Up @@ -265,26 +270,18 @@ export const useGridCellSelection = (
return;
}

const virtualScrollerRect = apiRef.current.virtualScrollerRef?.current?.getBoundingClientRect();

if (!virtualScrollerRect) {
return;
}

function autoScroll() {
if (!mousePosition.current || !apiRef.current.virtualScrollerRef?.current) {
return;
}

const { x: mouseX, y: mouseY } = mousePosition.current;
const { height, width } = virtualScrollerRect;
const { height, width } = dimensions.viewportInnerSize;

let deltaX = 0;
let deltaY = 0;
let factor = 0;

const dimensions = apiRef.current.getRootDimensions();

if (mouseY <= AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollY) {
// When scrolling up, the multiplier increases going closer to the top edge
factor = (AUTO_SCROLL_SENSITIVITY - mouseY) / -AUTO_SCROLL_SENSITIVITY;
Expand Down Expand Up @@ -316,7 +313,7 @@ export const useGridCellSelection = (
}

autoScroll();
}, [apiRef]);
}, [apiRef, dimensions]);

const handleCellMouseOver = React.useCallback<GridEventListener<'cellMouseOver'>>(
(params, event) => {
Expand All @@ -339,9 +336,10 @@ export const useGridCellSelection = (
return;
}

const { height, width, x, y } = virtualScrollerRect;
const { x, y } = virtualScrollerRect;
const { height, width } = dimensions.viewportInnerSize;
const mouseX = event.clientX - x;
const mouseY = event.clientY - y;
const mouseY = event.clientY - y - totalHeaderHeight;
mousePosition.current = { x: mouseX, y: mouseY };

const hasEnteredVerticalSensitivityArea =
Expand All @@ -361,7 +359,7 @@ export const useGridCellSelection = (
stopAutoScroll();
}
},
[apiRef, startAutoScroll, stopAutoScroll],
[apiRef, startAutoScroll, stopAutoScroll, totalHeaderHeight, dimensions],
);

const handleCellClick = useEventCallback<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { stub, SinonStub } from 'sinon';
import { expect } from 'chai';
import { spyApi, getCell } from 'test/utils/helperFn';
import { spyApi, getCell, grid } from 'test/utils/helperFn';
import { createRenderer, fireEvent, act, userEvent, screen } from '@mui-internal/test-utils';
import {
DataGridPremium,
Expand Down Expand Up @@ -388,17 +388,23 @@ describe('<DataGridPremium /> - Cell selection', () => {
fireEvent.click(cell71);

const virtualScroller = document.querySelector(`.${gridClasses.virtualScroller}`)!;
const rect = virtualScroller.getBoundingClientRect();
const gridRect = grid('root')!.getBoundingClientRect();

virtualScroller.scrollTop = 30;
virtualScroller.dispatchEvent(new Event('scroll'));
expect(virtualScroller.scrollTop).to.equal(30);

const cell11 = getCell(1, 1);
fireEvent.mouseOver(cell11, { clientX: rect.x, clientY: rect.y + 25 }); // 25=half speed
fireEvent.mouseOver(cell11, {
clientX: gridRect.x,
clientY: gridRect.y + border + columnHeaderHeight + 25, // 25=half speed
});
expect(virtualScroller.scrollTop).to.equal(20);

fireEvent.mouseOver(cell11, { clientX: rect.x, clientY: rect.y }); // 0=full speed
fireEvent.mouseOver(cell11, {
clientX: gridRect.x,
clientY: gridRect.y + border + columnHeaderHeight + 0, // 0=full speed
});
expect(virtualScroller.scrollTop).to.equal(0);

(window.requestAnimationFrame as SinonStub).restore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { clamp } from '../../../utils/utils';
import { GridApiCommon } from '../../../models/api/gridApiCommon';
import { GridRowEntry } from '../../../models/gridRows';
import { gridDensityFactorSelector } from '../density/densitySelector';
import { gridHeaderFilteringEnabledSelector } from '../headerFiltering/gridHeaderFilteringSelectors';
import { gridColumnGroupsHeaderMaxDepthSelector } from '../columnGrouping/gridColumnGroupsSelector';

export const COLUMNS_DIMENSION_PROPERTIES = ['maxWidth', 'minWidth', 'width', 'flex'] as const;
Expand Down Expand Up @@ -464,5 +465,7 @@ export function getTotalHeaderHeight(
) {
const densityFactor = gridDensityFactorSelector(apiRef);
const maxDepth = gridColumnGroupsHeaderMaxDepthSelector(apiRef);
return Math.floor(headerHeight * densityFactor) * ((maxDepth ?? 0) + 1);
const isHeaderFilteringEnabled = gridHeaderFilteringEnabledSelector(apiRef);
const multiplicationFactor = isHeaderFilteringEnabled ? 2 : 1;
return Math.floor(headerHeight * densityFactor) * ((maxDepth ?? 0) + multiplicationFactor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,7 @@ export function useGridDimensions(
const rowHeight = Math.floor(props.rowHeight * densityFactor);
const headerHeight = Math.floor(props.columnHeaderHeight * densityFactor);
const columnsTotalWidth = roundToDecimalPlaces(gridColumnsTotalWidthSelector(apiRef), 6);
// XXX: The `props as any` below is not resilient to change.
const hasHeaderFilters = Boolean((props as any).headerFilters);
const headersTotalHeight =
getTotalHeaderHeight(apiRef, props.columnHeaderHeight) +
Number(hasHeaderFilters) * headerHeight;
const headersTotalHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight);

const leftPinnedWidth = pinnedColumns.left.reduce((w, col) => w + col.computedWidth, 0);
const rightPinnedWidth = pinnedColumns.right.reduce((w, col) => w + col.computedWidth, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { GridStateCommunity } from '../../../models/gridStateCommunity';
export const gridHeaderFilteringStateSelector = (state: GridStateCommunity) =>
state.headerFiltering;

export const gridHeaderFilteringEnabledSelector = createSelector(
gridHeaderFilteringStateSelector,
// No initialization in MIT, so we need to default to false to be used by `getTotalHeaderHeight`
(headerFilteringState) => headerFilteringState?.enabled ?? false,
);

export const gridHeaderFilteringEditFieldSelector = createSelector(
gridHeaderFilteringStateSelector,
(headerFilteringState) => headerFilteringState.editing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ import {
GridHeaderFilteringPrivateApi,
} from '../../../models/api/gridHeaderFilteringApi';

export const headerFilteringStateInitializer: GridStateInitializer = (state) => ({
export const headerFilteringStateInitializer: GridStateInitializer = (state, props) => ({
...state,
headerFiltering: { editing: null, menuOpen: null },
// @ts-expect-error Access `Pro` prop in MIT
headerFiltering: { enabled: props.headerFilters ?? false, editing: null, menuOpen: null },
});

export const useGridHeaderFiltering = (
apiRef: React.MutableRefObject<GridPrivateApiCommunity>,
props: Pick<DataGridProcessedProps, 'signature'>,
) => {
const logger = useGridLogger(apiRef, 'useGridHeaderFiltering');

// @ts-expect-error Access `Pro` prop in MIT
const isHeaderFilteringEnabled = props.headerFilters ?? false;
const setHeaderFilterState = React.useCallback(
(headerFilterState: Partial<GridHeaderFilteringState>) => {
apiRef.current.setState((state) => {
Expand All @@ -37,14 +39,15 @@ export const useGridHeaderFiltering = (
return {
...state,
headerFiltering: {
enabled: isHeaderFilteringEnabled ?? false,
editing: headerFilterState.editing ?? null,
menuOpen: headerFilterState.menuOpen ?? null,
},
};
});
apiRef.current.forceUpdate();
},
[apiRef, props.signature],
[apiRef, props.signature, isHeaderFilteringEnabled],
);

const startHeaderFilterEditMode = React.useCallback<
Expand Down Expand Up @@ -117,4 +120,16 @@ export const useGridHeaderFiltering = (

useGridApiMethod(apiRef, headerFilterApi, 'public');
useGridApiMethod(apiRef, headerFilterPrivateApi, 'private');

/*
* EFFECTS
*/
const isFirstRender = React.useRef(true);
React.useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
} else {
apiRef.current.setHeaderFilterState({ enabled: isHeaderFilteringEnabled });
}
}, [apiRef, isHeaderFilteringEnabled]);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { GridColDef } from './colDef';

export type GridHeaderFilteringState = {
enabled: boolean;
editing: GridColDef['field'] | null;
menuOpen: GridColDef['field'] | null;
};
1 change: 1 addition & 0 deletions scripts/x-data-grid-premium.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
{ "name": "GridHeaderFilterCellProps", "kind": "Interface" },
{ "name": "GridHeaderFilterEventLookup", "kind": "Interface" },
{ "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringStateSelector", "kind": "Variable" },
{ "name": "GridHeaderFilterMenu", "kind": "Function" },
Expand Down
1 change: 1 addition & 0 deletions scripts/x-data-grid-pro.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@
{ "name": "GridHeaderFilterCellProps", "kind": "Interface" },
{ "name": "GridHeaderFilterEventLookup", "kind": "Interface" },
{ "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringStateSelector", "kind": "Variable" },
{ "name": "GridHeaderFilterMenu", "kind": "Function" },
Expand Down
1 change: 1 addition & 0 deletions scripts/x-data-grid.exports.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
{ "name": "GridHeaderCheckbox", "kind": "Variable" },
{ "name": "GridHeaderFilterEventLookup", "kind": "Interface" },
{ "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" },
{ "name": "gridHeaderFilteringStateSelector", "kind": "Variable" },
{ "name": "GridHeaderSelectionCheckboxParams", "kind": "Interface" },
Expand Down

0 comments on commit 2afd9cf

Please sign in to comment.