Skip to content

Commit

Permalink
[DataGridPro] Fix missing row hover styles (#10252)
Browse files Browse the repository at this point in the history
  • Loading branch information
cherniavskii authored Sep 22, 2023
1 parent 20a3d3c commit 2577f6b
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
gridColumnsTotalWidthSelector,
gridColumnPositionsSelector,
gridVisibleColumnFieldsSelector,
gridClasses,
useGridApiMethod,
useGridApiEventHandler,
GridEventListener,
Expand Down Expand Up @@ -71,69 +70,6 @@ export const useGridColumnPinning = (
): void => {
const pinnedColumns = useGridSelector(apiRef, gridPinnedColumnsSelector);
const theme = useTheme();
// Each visible row (not to be confused with a filter result) is composed of a central .MuiDataGrid-row element
// and up to two additional .MuiDataGrid-row's, one for the columns pinned to the left and another
// for those on the right side. When hovering any of these elements, the :hover styles are applied only to
// the row element that was actually hovered, not its additional siblings. To make it look like a contiguous row,
// this method adds/removes the .Mui-hovered class to all of the row elements inside one visible row.
const updateHoveredClassOnSiblingRows = React.useCallback(
(event: React.MouseEvent<HTMLElement>) => {
if (props.disableColumnPinning) {
return;
}

if (!Array.isArray(pinnedColumns.left) && !Array.isArray(pinnedColumns.right)) {
return;
}

const nbLeftPinnedColumns = pinnedColumns.left?.length ?? 0;
const nbRightPinnedColumns = pinnedColumns.right?.length ?? 0;
if (nbLeftPinnedColumns + nbRightPinnedColumns === 0) {
return;
}

const rowContainer = apiRef.current.virtualScrollerRef!.current!;
if (!rowContainer) {
return;
}

const index = event.currentTarget.dataset.rowindex;
const rowElements = rowContainer.querySelectorAll(
`.${gridClasses.row}[data-rowindex="${index}"]`,
);
rowElements.forEach((row) => {
// Ignore rows from other grid inside the hovered row
if (
row.closest(`.${gridClasses.virtualScroller}`) ===
apiRef.current.virtualScrollerRef!.current!
) {
if (event.type === 'mouseenter') {
row.classList.add('Mui-hovered');
} else {
row.classList.remove('Mui-hovered');
}
}
});
},
[apiRef, pinnedColumns.left, pinnedColumns.right, props.disableColumnPinning],
);

const handleMouseEnter = React.useCallback<GridEventListener<'rowMouseEnter'>>(
(params, event) => {
updateHoveredClassOnSiblingRows(event);
},
[updateHoveredClassOnSiblingRows],
);

const handleMouseLeave = React.useCallback<GridEventListener<'rowMouseLeave'>>(
(params, event) => {
updateHoveredClassOnSiblingRows(event);
},
[updateHoveredClassOnSiblingRows],
);

useGridApiEventHandler(apiRef, 'rowMouseEnter', handleMouseEnter);
useGridApiEventHandler(apiRef, 'rowMouseLeave', handleMouseLeave);

/**
* PRE-PROCESSING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DataGridProProps,
gridClasses,
GridPinnedPosition,
GRID_CHECKBOX_SELECTION_FIELD,
} from '@mui/x-data-grid-pro';
import { useBasicDemoData, getBasicGridData } from '@mui/x-data-grid-generator';
import {
Expand Down Expand Up @@ -64,6 +65,9 @@ describe('<DataGridPro /> - Column pinning', () => {
disconnect: () => {
clearTimeout(timeout);
},
unobserve: () => {
clearTimeout(timeout);
},
};
}

Expand Down Expand Up @@ -142,6 +146,34 @@ describe('<DataGridPro /> - Column pinning', () => {
expect(renderZone!.querySelector('[data-rowindex="0"]')).not.to.have.class('Mui-hovered');
});

// https://github.com/mui/mui-x/issues/10176
it('should keep .Mui-hovered on the entire row when row is selected and deselected', () => {
render(
<TestCase
initialState={{
pinnedColumns: { left: [GRID_CHECKBOX_SELECTION_FIELD], right: ['price16M'] },
}}
checkboxSelection
/>,
);
const leftColumns = document.querySelector(`.${gridClasses['pinnedColumns--left']}`);
const rightColumns = document.querySelector(`.${gridClasses['pinnedColumns--right']}`);
const renderZone = document.querySelector(`.${gridClasses.virtualScrollerRenderZone}`);
const cell = getCell(0, 0);

fireEvent.mouseEnter(cell);
expect(leftColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');
expect(rightColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');
expect(renderZone!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');

const checkbox = cell.querySelector('input[type="checkbox"]')!;
fireEvent.click(checkbox);
fireEvent.click(checkbox);
expect(leftColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');
expect(rightColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');
expect(renderZone!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered');
});

it('should update the render zone offset after resize', function test() {
if (isJSDOM) {
// Need layouting
Expand Down
4 changes: 3 additions & 1 deletion packages/grid/x-data-grid/src/components/GridRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function EmptyCell({ width }: { width: number }) {
const GridRow = React.forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props, refProp) {
const {
selected,
hovered,
rowId,
row,
index,
Expand Down Expand Up @@ -141,6 +142,7 @@ const GridRow = React.forwardRef<HTMLDivElement, GridRowProps>(function GridRow(

const ownerState = {
selected,
hovered,
isLastVisible,
classes: rootProps.classes,
editing: apiRef.current.getRowMode(rowId) === GridRowModes.Edit,
Expand Down Expand Up @@ -456,7 +458,7 @@ const GridRow = React.forwardRef<HTMLDivElement, GridRowProps>(function GridRow(
data-id={rowId}
data-rowindex={index}
role="row"
className={clsx(...rowClassNames, classes.root, className)}
className={clsx(...rowClassNames, classes.root, hovered ? 'Mui-hovered' : null, className)}
aria-rowindex={ariaRowIndex}
aria-selected={selected}
style={style}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
height: null,
});
const prevTotalWidth = React.useRef(columnsTotalWidth);
// Each visible row (not to be confused with a filter result) is composed of a central row element
// and up to two additional row elements for pinned columns (left and right).
// When hovering any of these elements, the :hover styles are applied only to the row element that
// was actually hovered, not its additional siblings. To make it look like a contiguous row,
// we add/remove the .Mui-hovered class to all of the row elements inside one visible row.
const [hoveredRowId, setHoveredRowId] = React.useState<GridRowId | null>(null);

const rowStyleCache = React.useRef<Record<GridRowId, any>>(Object.create(null));
const prevGetRowProps = React.useRef<UseGridVirtualScrollerProps['getRowProps']>();
Expand Down Expand Up @@ -498,6 +504,13 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
return -1;
}, [cellFocus, currentPage.rows]);

useGridApiEventHandler(apiRef, 'rowMouseEnter', (params) => {
setHoveredRowId(params.id ?? null);
});
useGridApiEventHandler(apiRef, 'rowMouseLeave', () => {
setHoveredRowId(null);
});

const getRows = (
params: {
renderContext: GridRenderContext | null;
Expand Down Expand Up @@ -704,6 +717,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
position={position}
{...rowProps}
{...rootRowProps}
hovered={hoveredRowId === id}
style={rowStyleCache.current[id]}
/>,
);
Expand Down

0 comments on commit 2577f6b

Please sign in to comment.