Skip to content

Commit

Permalink
[DataGrid] Store the outlined cell in the state (#7111)
Browse files Browse the repository at this point in the history
  • Loading branch information
m4theushw committed Jan 6, 2023
1 parent 4610080 commit 0203346
Show file tree
Hide file tree
Showing 30 changed files with 245 additions and 103 deletions.
2 changes: 1 addition & 1 deletion docs/data/data-grid/demo/PopularFeaturesDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ export default function PopularFeaturesDemo() {
[`& .${gridClasses.detailPanel}`]: {
background: 'transparent',
},
[`& .${gridClasses.cell}:focus, & .${gridClasses.cell}:focus-within`]: {
[`& .${gridClasses['cell--outlined']}`]: {
outline: 'none',
},
[`& .${gridClasses.columnHeader}:focus, & .${gridClasses.columnHeader}:focus-within`]:
Expand Down
2 changes: 1 addition & 1 deletion docs/data/data-grid/demo/PopularFeaturesDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ export default function PopularFeaturesDemo() {
[`& .${gridClasses.detailPanel}`]: {
background: 'transparent',
},
[`& .${gridClasses.cell}:focus, & .${gridClasses.cell}:focus-within`]: {
[`& .${gridClasses['cell--outlined']}`]: {
outline: 'none',
},
[`& .${gridClasses.columnHeader}:focus, & .${gridClasses.columnHeader}:focus-within`]:
Expand Down
14 changes: 14 additions & 0 deletions docs/data/data-grid/events/events.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
"params": "GridAggregationModel",
"event": "MuiEvent<{}>"
},
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "cellBlur",
"description": "Fired when a <code>blur</code> event happens in a cell.",
"params": "GridCellParams",
"event": "MuiEvent<React.FocusEvent<HTMLElement>>"
},
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "cellClick",
Expand Down Expand Up @@ -38,6 +45,13 @@
"event": "MuiEvent<MuiBaseEvent>",
"componentProp": "onCellEditStop"
},
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "cellFocus",
"description": "Fired when a <code>focus</code> event happens in a cell.",
"params": "GridCellParams",
"event": "MuiEvent<React.FocusEvent<HTMLElement>>"
},
{
"projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"],
"name": "cellFocusIn",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,16 @@ Most of this breaking change is handled by `preset-safe` codemod but some furthe

### CSS classes

- To update the outline style of a focused cell, use the `.MuiDataGrid-cell--outlined` class instead of the `:focus-within` selector.
```diff
-'.MuiDataGrid-cell:focus-within': {
+'.MuiDataGrid-cell--outlined': {
```
The new class name is also available in `gridClasses`:
```diff
-`.${gridClasses.cell}:focus-within`: {
+`.${gridClasses['cell--outlined']}`: {
```
- Some CSS classes were removed or renamed

| MUI X v5 classes | MUI X v6 classes | Note |
Expand Down
3 changes: 2 additions & 1 deletion docs/pages/x/api/data-grid/data-grid-premium.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"experimentalFeatures": {
"type": {
"name": "shape",
"description": "{ columnGrouping?: bool, lazyLoading?: bool, rowPinning?: bool, warnIfFocusStateIsNotSynced?: bool }"
"description": "{ columnGrouping?: bool, lazyLoading?: bool, rowPinning?: bool }"
}
},
"filterMode": {
Expand Down Expand Up @@ -370,6 +370,7 @@
"cell--textLeft",
"cell--textRight",
"cell--withRenderer",
"cell--outlined",
"cell--rangeTop",
"cell--rangeBottom",
"cell--rangeLeft",
Expand Down
3 changes: 2 additions & 1 deletion docs/pages/x/api/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"experimentalFeatures": {
"type": {
"name": "shape",
"description": "{ columnGrouping?: bool, lazyLoading?: bool, rowPinning?: bool, warnIfFocusStateIsNotSynced?: bool }"
"description": "{ columnGrouping?: bool, lazyLoading?: bool, rowPinning?: bool }"
}
},
"filterMode": {
Expand Down Expand Up @@ -339,6 +339,7 @@
"cell--textLeft",
"cell--textRight",
"cell--withRenderer",
"cell--outlined",
"cell--rangeTop",
"cell--rangeBottom",
"cell--rangeLeft",
Expand Down
6 changes: 2 additions & 4 deletions docs/pages/x/api/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@
},
"error": { "type": { "name": "any" } },
"experimentalFeatures": {
"type": {
"name": "shape",
"description": "{ columnGrouping?: bool, warnIfFocusStateIsNotSynced?: bool }"
}
"type": { "name": "shape", "description": "{ columnGrouping?: bool }" }
},
"filterMode": {
"type": { "name": "enum", "description": "'client'<br>&#124;&nbsp;'server'" },
Expand Down Expand Up @@ -276,6 +273,7 @@
"cell--textLeft",
"cell--textRight",
"cell--withRenderer",
"cell--outlined",
"cell--rangeTop",
"cell--rangeBottom",
"cell--rangeLeft",
Expand Down
5 changes: 5 additions & 0 deletions docs/translations/api-docs/data-grid/data-grid-premium.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@
"nodeName": "the cell element",
"conditions": "the cell has a custom renderer"
},
"cell--outlined": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
"conditions": "the cell is outlined"
},
"cell--rangeTop": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
Expand Down
5 changes: 5 additions & 0 deletions docs/translations/api-docs/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@
"nodeName": "the cell element",
"conditions": "the cell has a custom renderer"
},
"cell--outlined": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
"conditions": "the cell is outlined"
},
"cell--rangeTop": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
Expand Down
5 changes: 5 additions & 0 deletions docs/translations/api-docs/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@
"nodeName": "the cell element",
"conditions": "the cell has a custom renderer"
},
"cell--outlined": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
"conditions": "the cell is outlined"
},
"cell--rangeTop": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the cell element",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ DataGridPremiumRaw.propTypes = {
columnGrouping: PropTypes.bool,
lazyLoading: PropTypes.bool,
rowPinning: PropTypes.bool,
warnIfFocusStateIsNotSynced: PropTypes.bool,
}),
/**
* Filtering can be processed on the server or client-side.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ DataGridProRaw.propTypes = {
columnGrouping: PropTypes.bool,
lazyLoading: PropTypes.bool,
rowPinning: PropTypes.bool,
warnIfFocusStateIsNotSynced: PropTypes.bool,
}),
/**
* Filtering can be processed on the server or client-side.
Expand Down
1 change: 0 additions & 1 deletion packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ DataGridRaw.propTypes = {
*/
experimentalFeatures: PropTypes.shape({
columnGrouping: PropTypes.bool,
warnIfFocusStateIsNotSynced: PropTypes.bool,
}),
/**
* Filtering can be processed on the server or client-side.
Expand Down
5 changes: 3 additions & 2 deletions packages/grid/x-data-grid/src/components/GridRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
GridEditingState,
GridCellModes,
} from '../models/gridEditRowModel';
import { useGridApiContext } from '../hooks/utils/useGridApiContext';
import { getDataGridUtilityClass, gridClasses } from '../constants/gridClasses';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import { DataGridProcessedProps } from '../models/props/DataGridProps';
Expand All @@ -33,6 +32,7 @@ import { gridRowMaximumTreeDepthSelector } from '../hooks/features/rows/gridRows
import { gridColumnGroupsHeaderMaxDepthSelector } from '../hooks/features/columnGrouping/gridColumnGroupsSelector';
import { randomNumberBetween } from '../utils/utils';
import { GridCellProps } from './cell/GridCell';
import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';

export interface GridRowProps {
rowId: GridRowId;
Expand Down Expand Up @@ -122,7 +122,7 @@ const GridRow = React.forwardRef<
onMouseLeave,
...other
} = props;
const apiRef = useGridApiContext();
const apiRef = useGridPrivateApiContext();
const ref = React.useRef<HTMLDivElement>(null);
const rootProps = useGridRootProps();
const currentPage = useGridVisibleRows(apiRef, rootProps);
Expand Down Expand Up @@ -362,6 +362,7 @@ const GridRow = React.forwardRef<
cellMode={cellParams.cellMode}
colIndex={cellProps.indexRelativeToAllColumns}
isEditable={cellParams.isEditable}
isOutlined={apiRef.current.isCellOutlined(rowId, column.field)}
isSelected={isSelected}
hasFocus={hasFocus}
tabIndex={tabIndex}
Expand Down
58 changes: 21 additions & 37 deletions packages/grid/x-data-grid/src/components/cell/GridCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
import { GridAlignment } from '../../models/colDef/gridColDef';
import { useGridApiContext } from '../../hooks/utils/useGridApiContext';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import { gridFocusCellSelector } from '../../hooks/features/focus/gridFocusStateSelector';
import { DataGridProcessedProps } from '../../models/props/DataGridProps';
import { FocusElement } from '../../models/params/gridCellParams';

Expand All @@ -32,6 +31,7 @@ export interface GridCellProps<V = any, F = V> {
hasFocus?: boolean;
height: number | 'auto';
isEditable?: boolean;
isOutlined?: boolean;
isSelected?: boolean;
showRightBorder?: boolean;
value?: V;
Expand Down Expand Up @@ -65,17 +65,21 @@ function doesSupportPreventScroll(): boolean {
return cachedSupportsPreventScroll;
}

type OwnerState = Pick<GridCellProps, 'align' | 'showRightBorder' | 'isEditable' | 'isSelected'> & {
type OwnerState = Pick<
GridCellProps,
'align' | 'showRightBorder' | 'isEditable' | 'isOutlined' | 'isSelected'
> & {
classes?: DataGridProcessedProps['classes'];
};

const useUtilityClasses = (ownerState: OwnerState) => {
const { align, showRightBorder, isEditable, isSelected, classes } = ownerState;
const { align, isOutlined, showRightBorder, isEditable, isSelected, classes } = ownerState;

const slots = {
root: [
'cell',
`cell--text${capitalize(align)}`,
isOutlined && `cell--outlined`,
isEditable && 'cell--editable',
isSelected && 'selected',
showRightBorder && 'cell--withRightBorder',
Expand All @@ -87,8 +91,6 @@ const useUtilityClasses = (ownerState: OwnerState) => {
return composeClasses(slots, getDataGridUtilityClass, classes);
};

let warnedOnce = false;

function GridCell(props: GridCellProps) {
const {
align,
Expand All @@ -101,6 +103,7 @@ function GridCell(props: GridCellProps) {
hasFocus,
height,
isEditable,
isOutlined,
isSelected,
rowId,
tabIndex,
Expand All @@ -118,6 +121,8 @@ function GridCell(props: GridCellProps) {
onMouseUp,
onMouseOver,
onKeyDown,
onFocus,
onBlur,
onKeyUp,
onDragEnter,
onDragOver,
Expand All @@ -130,7 +135,14 @@ function GridCell(props: GridCellProps) {
const apiRef = useGridApiContext();

const rootProps = useGridRootProps();
const ownerState = { align, showRightBorder, isEditable, classes: rootProps.classes, isSelected };
const ownerState = {
align,
showRightBorder,
isEditable,
classes: rootProps.classes,
isSelected,
isOutlined,
};
const classes = useUtilityClasses(ownerState);

const publishMouseUp = React.useCallback(
Expand Down Expand Up @@ -203,36 +215,6 @@ function GridCell(props: GridCellProps) {
}
}, [hasFocus, cellMode, apiRef]);

let handleFocus: any = other.onFocus;

if (
process.env.NODE_ENV === 'test' &&
rootProps.experimentalFeatures?.warnIfFocusStateIsNotSynced
) {
handleFocus = (event: React.FocusEvent) => {
const focusedCell = gridFocusCellSelector(apiRef);
if (focusedCell?.id === rowId && focusedCell.field === field) {
if (typeof other.onFocus === 'function') {
other.onFocus(event);
}
return;
}

if (!warnedOnce) {
console.warn(
[
`MUI: The cell with id=${rowId} and field=${field} received focus.`,
`According to the state, the focus should be at id=${focusedCell?.id}, field=${focusedCell?.field}.`,
"Not syncing the state may cause unwanted behaviors since the `cellFocusIn` event won't be fired.",
'Call `fireEvent.mouseUp` before the `fireEvent.click` to sync the focus with the state.',
].join('\n'),
);

warnedOnce = true;
}
};
}

const column = apiRef.current.getColumn(field);
const managesOwnFocus = column.type === 'actions';

Expand Down Expand Up @@ -272,10 +254,11 @@ function GridCell(props: GridCellProps) {
onMouseDown={publishMouseDown('cellMouseDown')}
onMouseUp={publishMouseUp('cellMouseUp')}
onKeyDown={publish('cellKeyDown', onKeyDown)}
onBlur={publish('cellBlur', onBlur)}
onFocus={publish('cellFocus', onFocus)}
onKeyUp={publish('cellKeyUp', onKeyUp)}
{...draggableEventHandlers}
{...other}
onFocus={handleFocus}
>
{renderChildren()}
</div>
Expand All @@ -299,6 +282,7 @@ GridCell.propTypes = {
hasFocus: PropTypes.bool,
height: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]).isRequired,
isEditable: PropTypes.bool,
isOutlined: PropTypes.bool,
isSelected: PropTypes.bool,
onClick: PropTypes.func,
onDoubleClick: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const GridRootStyles = styled('div', {
padding: '0 10px',
boxSizing: 'border-box',
},
[`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses.cell}:focus-within`]: {
[`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses['cell--outlined']}`]: {
outline: `solid ${
theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / 0.5)`
Expand All @@ -133,7 +133,7 @@ export const GridRootStyles = styled('div', {
outlineWidth: 1,
outlineOffset: -1,
},
[`& .${gridClasses.columnHeader}:focus, & .${gridClasses.cell}:focus`]: {
[`& .${gridClasses.columnHeader}:focus, & .${gridClasses['cell--outlined']}`]: {
outline: `solid ${theme.palette.primary.main} 1px`,
},
[`& .${gridClasses.columnHeaderCheckbox}, & .${gridClasses.cellCheckbox}`]: {
Expand Down Expand Up @@ -353,10 +353,6 @@ export const GridRootStyles = styled('div', {
display: 'flex',
boxShadow: theme.shadows[2],
backgroundColor: (theme.vars || theme).palette.background.paper,
'&:focus-within': {
outline: `solid ${(theme.vars || theme).palette.primary.main} 1px`,
outlineOffset: '-1px',
},
},
[`& .${gridClasses['row--editing']}`]: {
boxShadow: theme.shadows[2],
Expand Down
5 changes: 5 additions & 0 deletions packages/grid/x-data-grid/src/constants/gridClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export interface GridClasses {
* Styles applied to the cell element if the cell has a custom renderer.
*/
'cell--withRenderer': string;
/**
* Styles applied to the cell element if the cell is outlined.
*/
'cell--outlined': string;
/**
* Styles applied to the cell element if it is at the top edge of a cell selection range.
*/
Expand Down Expand Up @@ -548,6 +552,7 @@ export const gridClasses = generateUtilityClasses<GridClassKey>('MuiDataGrid', [
'cell--textLeft',
'cell--textRight',
'cell--withRenderer',
'cell--outlined',
'cell--rangeTop',
'cell--rangeBottom',
'cell--rangeLeft',
Expand Down
Loading

0 comments on commit 0203346

Please sign in to comment.