Skip to content

Commit

Permalink
[DataGrid] Drop autoFocus
Browse files Browse the repository at this point in the history
  • Loading branch information
m4theushw committed Jul 30, 2021
1 parent 08dab61 commit cd9ed3e
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 51 deletions.
18 changes: 11 additions & 7 deletions packages/grid/_modules_/grid/components/cell/GridCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,17 @@ export const GridCell = React.memo(function GridCell(props: GridCellProps) {
};

React.useLayoutEffect(() => {
const doc = ownerDocument(apiRef!.current.rootElementRef!.current as HTMLElement);
if (!hasFocus) {
return;
}

if (cellMode === 'edit' && isEditable) {
return;
}

const doc = ownerDocument(apiRef!.current.rootElementRef!.current as HTMLElement)!;

if (
hasFocus &&
cellRef.current &&
(!doc.activeElement || !cellRef.current!.contains(doc.activeElement))
) {
if (cellRef.current && (doc.activeElement || !cellRef.current!.contains(doc.activeElement))) {
const focusableElement = cellRef.current!.querySelector('[tabindex="0"]') as HTMLElement;
if (focusableElement) {
focusableElement!.focus();
Expand All @@ -175,7 +179,7 @@ export const GridCell = React.memo(function GridCell(props: GridCellProps) {
data-mode={cellMode}
aria-colindex={colIndex + 1}
style={style}
tabIndex={tabIndex}
tabIndex={cellMode === 'view' || !isEditable ? tabIndex : -1}
{...eventsHandlers}
>
{children || valueToRender?.toString()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ export function GridEditBooleanCell(
cellMode,
isEditable,
className,
tabIndex,
getValue,
hasFocus,
...other
} = props;

const inputRef = React.useRef<HTMLInputElement>(null);
const id = useId();
const [valueState, setValueState] = React.useState(value);

Expand All @@ -41,11 +43,17 @@ export function GridEditBooleanCell(
setValueState(value);
}, [value]);

React.useLayoutEffect(() => {
if (hasFocus && inputRef.current) {
inputRef.current.focus();
}
}, [hasFocus]);

return (
<label htmlFor={id} className={clsx('MuiDataGrid-editBooleanCell', className)} {...other}>
<Checkbox
autoFocus
id={id}
inputRef={inputRef}
checked={Boolean(valueState)}
onChange={handleChange}
size="small"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function GridEditDateCell(props: GridCellParams & InputBaseProps) {
...other
} = props;

const inputRef = React.useRef<any>();
const [valueState, setValueState] = React.useState(value);

const handleChange = React.useCallback(
Expand Down Expand Up @@ -54,6 +55,12 @@ export function GridEditDateCell(props: GridCellParams & InputBaseProps) {
setValueState(value);
}, [value]);

React.useLayoutEffect(() => {
if (hasFocus && inputRef.current) {
inputRef.current.focus();
}
}, [hasFocus]);

let valueToDisplay = valueState || '';
if (valueState instanceof Date) {
const offset = valueState.getTimezoneOffset();
Expand All @@ -63,7 +70,7 @@ export function GridEditDateCell(props: GridCellParams & InputBaseProps) {

return (
<InputBase
autoFocus
inputRef={inputRef}
fullWidth
className="MuiDataGrid-editInputCell"
type={isDateTime ? 'datetime-local' : 'date'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function GridEditInputCell(props: GridCellParams & InputBaseProps) {
...other
} = props;

const inputRef = React.useRef<any>();
const [valueState, setValueState] = React.useState(value);

const handleChange = React.useCallback(
Expand All @@ -33,9 +34,15 @@ export function GridEditInputCell(props: GridCellParams & InputBaseProps) {
setValueState(value);
}, [value]);

React.useLayoutEffect(() => {
if (hasFocus && inputRef.current) {
inputRef.current.focus();
}
}, [hasFocus]);

return (
<InputBase
autoFocus
inputRef={inputRef}
className="MuiDataGrid-editInputCell"
fullWidth
type={colDef.type === 'number' ? colDef.type : 'text'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export function GridEditSingleSelectCell(props: GridCellParams & SelectProps) {
...other
} = props;

const ref = React.useRef<any>();

const handleChange = (event) => {
api.setEditCellValue({ id, field, value: event.target.value }, event);
if (!event.key) {
Expand All @@ -46,14 +48,22 @@ export function GridEditSingleSelectCell(props: GridCellParams & SelectProps) {
}
};

React.useLayoutEffect(() => {
if (hasFocus && ref.current) {
// TODO v5: replace with inputRef.current.focus()
// See https://github.com/mui-org/material-ui/issues/21441
ref.current.querySelector('[role="button"]').focus();
}
}, [hasFocus]);

return (
<Select
ref={ref}
value={value}
onChange={handleChange}
MenuProps={{
onClose: handleClose,
}}
autoFocus
fullWidth
open
{...other}
Expand Down
1 change: 1 addition & 0 deletions packages/grid/_modules_/grid/components/cell/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './GridCell';
export * from './GridEditInputCell';
export * from './GridEditSingleSelectCell';
export * from './GridEmptyCell';
export * from './GridRowCells';
78 changes: 47 additions & 31 deletions packages/grid/_modules_/grid/hooks/features/focus/useGridFocus.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as React from 'react';
import { ownerDocument } from '@material-ui/core/utils';
import {
GRID_CELL_CLICK,
GRID_CELL_DOUBLE_CLICK,
GRID_CELL_MOUSE_UP,
GRID_CELL_FOCUS_OUT,
GRID_CELL_KEY_DOWN,
GRID_COLUMN_HEADER_BLUR,
GRID_COLUMN_HEADER_FOCUS,
GRID_CELL_MODE_CHANGE,
Expand All @@ -22,7 +22,7 @@ import { GridComponentProps } from '../../../GridComponentProps';
export const useGridFocus = (apiRef: GridApiRef, props: Pick<GridComponentProps, 'rows'>): void => {
const logger = useLogger('useGridFocus');
const [, setGridState, forceUpdate] = useGridState(apiRef);
const insideFocusedCell = React.useRef(false);
const lastClickedCell = React.useRef<GridCellParams | null>(null);

const setCellFocus = React.useCallback(
(id: GridRowId, field: string) => {
Expand Down Expand Up @@ -65,13 +65,24 @@ export const useGridFocus = (apiRef: GridApiRef, props: Pick<GridComponentProps,
[apiRef, forceUpdate, logger, setGridState],
);

const updateFocus = React.useCallback(
const handleCellDoubleClick = React.useCallback(
({ id, field }: GridCellParams) => {
apiRef.current.setCellFocus(id, field);
},
[apiRef],
);

const handleCellKeyDown = React.useCallback(
(params: GridCellParams, event: React.KeyboardEvent) => {
// Ignore Enter and Tab because GRID_CELL_NAVIGATION_KEY_DOWN handles the focus
if (event.key === 'Enter' || event.key === 'Tab') {
return;
}
apiRef.current.setCellFocus(params.id, params.field);
},
[apiRef],
);

const handleColumnHeaderFocus = React.useCallback(
({ field }: GridCellParams, event: React.FocusEvent) => {
if (event.target !== event.currentTarget) {
Expand All @@ -90,47 +101,52 @@ export const useGridFocus = (apiRef: GridApiRef, props: Pick<GridComponentProps,
}));
}, [logger, setGridState]);

const handleCellMouseUp = React.useCallback(
(params: GridCellParams) => {
const { cell } = apiRef.current.getState().focus;
if (!cell) {
return;
}

if (params.id === cell.id && params.field === cell.field) {
insideFocusedCell.current = true;
}
},
[apiRef],
);
const handleCellMouseUp = React.useCallback((params: GridCellParams) => {
lastClickedCell.current = params;
}, []);

const handleDocumentClick = React.useCallback(
(event: DocumentEventMap['click']) => {
const isInsideFocusedCell = insideFocusedCell.current;
insideFocusedCell.current = false;
(event: MouseEvent) => {
const cellParams = lastClickedCell.current;
lastClickedCell.current = null;

const { cell } = apiRef.current.getState().focus;
if (!cell || isInsideFocusedCell) {
const { cell: focusedCell } = apiRef.current.getState().focus;

if (!focusedCell) {
if (cellParams) {
apiRef.current.setCellFocus(cellParams.id, cellParams.field);
}
return;
}

const cellElement = apiRef.current.getCellElement(cell.id, cell.field);
if (cellElement?.contains(event.target as HTMLElement)) {
if (cellParams?.id === focusedCell.id && cellParams?.field === focusedCell.field) {
return;
}

setGridState((previousState) => ({
...previousState,
focus: { cell: null, columnHeader: null },
}));
const cellElement = apiRef.current.getCellElement(focusedCell.id, focusedCell.field);
if (cellElement?.contains(event.target as HTMLElement)) {
return;
}

// There's a focused cell but another cell was clicked
// Publishes an event to notify that the focus was lost
apiRef.current.publishEvent(
GRID_CELL_FOCUS_OUT,
apiRef.current.getCellParams(cell.id, cell.field),
apiRef.current.getCellParams(focusedCell.id, focusedCell.field),
event,
);

if (cellParams) {
apiRef.current.setCellFocus(cellParams.id, cellParams.field);
} else {
setGridState((previousState) => ({
...previousState,
focus: { cell: null, columnHeader: null },
}));
forceUpdate();
}
},
[apiRef, setGridState],
[apiRef, forceUpdate, setGridState],
);

const handleCellModeChange = React.useCallback(
Expand Down Expand Up @@ -180,9 +196,9 @@ export const useGridFocus = (apiRef: GridApiRef, props: Pick<GridComponentProps,
}, [apiRef, handleDocumentClick]);

useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_BLUR, handleBlur);
useGridApiEventHandler(apiRef, GRID_CELL_CLICK, updateFocus);
useGridApiEventHandler(apiRef, GRID_CELL_DOUBLE_CLICK, updateFocus);
useGridApiEventHandler(apiRef, GRID_CELL_DOUBLE_CLICK, handleCellDoubleClick);
useGridApiEventHandler(apiRef, GRID_CELL_MOUSE_UP, handleCellMouseUp);
useGridApiEventHandler(apiRef, GRID_CELL_KEY_DOWN, handleCellKeyDown);
useGridApiEventHandler(apiRef, GRID_CELL_MODE_CHANGE, handleCellModeChange);
useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_FOCUS, handleColumnHeaderFocus);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
getGridRowElement,
} from '../../../utils/domUtils';
import { useGridApiMethod } from '../../root/useGridApiMethod';
import { useGridSelector } from '../core/useGridSelector';
import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFocusStateSelector';

let warnedOnce = false;
Expand All @@ -26,9 +25,6 @@ function warnMissingColumn(field) {
}

export function useGridParamsApi(apiRef: GridApiRef) {
const cellFocus = useGridSelector(apiRef, gridFocusCellSelector);
const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector);

const getColumnHeaderParams = React.useCallback(
(field: string): GridColumnHeaderParams => ({
field,
Expand Down Expand Up @@ -66,6 +62,9 @@ export function useGridParamsApi(apiRef: GridApiRef) {
throw new Error(`No row with id #${id} found`);
}

const cellFocus = gridFocusCellSelector(apiRef.current.getState());
const cellTabIndex = gridTabIndexCellSelector(apiRef.current.getState());

const params: GridValueGetterParams = {
id,
field,
Expand All @@ -81,7 +80,7 @@ export function useGridParamsApi(apiRef: GridApiRef) {

return params;
},
[apiRef, cellFocus, cellTabIndex],
[apiRef],
);

const getCellParams = React.useCallback(
Expand All @@ -94,6 +93,9 @@ export function useGridParamsApi(apiRef: GridApiRef) {
throw new Error(`No row with id #${id} found`);
}

const cellFocus = gridFocusCellSelector(apiRef.current.getState());
const cellTabIndex = gridTabIndexCellSelector(apiRef.current.getState());

const params: GridCellParams = {
id,
field,
Expand All @@ -114,7 +116,7 @@ export function useGridParamsApi(apiRef: GridApiRef) {

return params;
},
[apiRef, cellFocus, cellTabIndex],
[apiRef],
);

const getCellValue = React.useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ describe('<DataGrid /> - Keyboard', () => {
const input = screen.getByTestId('custom-input');
input.focus();
expect(getActiveCell()).to.equal('0-1');
input.focus(); // The focus moves back to the cell
fireEvent.keyDown(input, { key: 'ArrowLeft' });
expect(getActiveCell()).to.equal('0-1');
});
Expand Down
Loading

0 comments on commit cd9ed3e

Please sign in to comment.