Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Drop autoFocus #2239

Merged
merged 16 commits into from
Aug 9, 2021
Merged
12 changes: 6 additions & 6 deletions packages/grid/_modules_/grid/components/cell/GridCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ export const GridCell = React.memo(function GridCell(props: GridCellProps) {
};

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

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

if (
hasFocus &&
cellRef.current &&
(!doc.activeElement || !cellRef.current!.contains(doc.activeElement))
) {
if (cellRef.current && !cellRef.current.contains(doc.activeElement!)) {
const focusableElement = cellRef.current!.querySelector('[tabindex="0"]') as HTMLElement;
if (focusableElement) {
focusableElement!.focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import clsx from 'clsx';
import Checkbox from '@material-ui/core/Checkbox';
// @ts-expect-error fixed in Material-UI v5, types definitions were added.
import { unstable_useId as useId } from '@material-ui/core/utils';
import { useEnhancedEffect } from '../../utils/material-ui-utils';
import { GridCellParams } from '../../models/params/gridCellParams';

export function GridEditBooleanCell(
Expand All @@ -19,12 +20,14 @@ export function GridEditBooleanCell(
colDef,
cellMode,
isEditable,
tabIndex,
className,
getValue,
hasFocus,
...other
} = props;

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

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

useEnhancedEffect(() => {
if (hasFocus) {
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
@@ -1,6 +1,7 @@
import * as React from 'react';
import InputBase, { InputBaseProps } from '@material-ui/core/InputBase';
import { GridCellParams } from '../../models/params/gridCellParams';
import { useEnhancedEffect } from '../../utils/material-ui-utils';

export function GridEditDateCell(props: GridCellParams & InputBaseProps) {
const {
Expand All @@ -13,11 +14,13 @@ export function GridEditDateCell(props: GridCellParams & InputBaseProps) {
colDef,
cellMode,
isEditable,
tabIndex,
hasFocus,
getValue,
...other
} = props;

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

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

useEnhancedEffect(() => {
if (hasFocus) {
inputRef.current!.focus();
}
}, [hasFocus]);

let valueToDisplay = valueState || '';
if (valueState instanceof Date) {
const offset = valueState.getTimezoneOffset();
Expand All @@ -63,7 +72,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
@@ -1,6 +1,7 @@
import * as React from 'react';
import InputBase, { InputBaseProps } from '@material-ui/core/InputBase';
import { GridCellParams } from '../../models/params/gridCellParams';
import { useEnhancedEffect } from '../../utils/material-ui-utils';

export function GridEditInputCell(props: GridCellParams & InputBaseProps) {
const {
Expand All @@ -13,11 +14,13 @@ export function GridEditInputCell(props: GridCellParams & InputBaseProps) {
colDef,
cellMode,
isEditable,
tabIndex,
hasFocus,
getValue,
...other
} = props;

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

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

useEnhancedEffect(() => {
if (hasFocus) {
inputRef.current!.focus();
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
}
}, [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 @@ -3,6 +3,7 @@ import Select, { SelectProps } from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import { GridCellParams } from '../../models/params/gridCellParams';
import { isEscapeKey } from '../../utils/keyboardUtils';
import { useEnhancedEffect } from '../../utils/material-ui-utils';

const renderSingleSelectOptions = (option) =>
typeof option === 'object' ? (
Expand All @@ -26,12 +27,15 @@ export function GridEditSingleSelectCell(props: GridCellParams & SelectProps) {
colDef,
cellMode,
isEditable,
tabIndex,
className,
getValue,
hasFocus,
...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 +50,22 @@ export function GridEditSingleSelectCell(props: GridCellParams & SelectProps) {
}
};

useEnhancedEffect(() => {
if (hasFocus) {
// 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
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ export const GridRowCells = React.memo(function GridRowCells(props: RowCellsProp
isSelected,
hasFocus: cellFocus !== null && cellFocus.id === id && cellFocus.field === column.field,
tabIndex:
cellTabIndex !== null && cellTabIndex.id === id && cellTabIndex.field === column.field
cellTabIndex !== null &&
cellTabIndex.id === id &&
cellTabIndex.field === column.field &&
cellParams.cellMode === 'view'
? 0
: -1,
className: clsx(classNames),
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';
83 changes: 50 additions & 33 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 @@ -18,11 +18,12 @@ import { useGridState } from '../core/useGridState';
import { useLogger } from '../../utils/useLogger';
import { useGridApiEventHandler } from '../../root/useGridApiEventHandler';
import { GridComponentProps } from '../../../GridComponentProps';
import { isNavigationKey } from '../../../utils/keyboardUtils';

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 +66,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) => {
// GRID_CELL_NAVIGATION_KEY_DOWN handles the focus on Enter, Tab and navigation keys
if (event.key === 'Enter' || event.key === 'Tab' || isNavigationKey(event.key)) {
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 +102,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((state) => ({
...state,
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((state) => ({
...state,
focus: { cell: null, columnHeader: null },
}));
forceUpdate();
}
},
[apiRef, setGridState],
[apiRef, forceUpdate, setGridState],
);

const handleCellModeChange = React.useCallback(
Expand Down Expand Up @@ -172,17 +189,17 @@ export const useGridFocus = (apiRef: GridApiRef, props: Pick<GridComponentProps,

React.useEffect(() => {
const doc = ownerDocument(apiRef.current.rootElementRef!.current as HTMLElement);
doc.addEventListener('click', handleDocumentClick, true);
doc.addEventListener('click', handleDocumentClick);

return () => {
doc.removeEventListener('click', handleDocumentClick, true);
doc.removeEventListener('click', handleDocumentClick);
};
}, [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 @@ -113,6 +113,7 @@ export function useGridEditRows(
[apiRef],
);

// TODO it's returning undefined when colDef.editable is undefined
const isCellEditable = React.useCallback(
(params: GridCellParams) =>
params.colDef.editable &&
Expand Down
Loading