Skip to content

Commit

Permalink
Adds more complex focus control for DataGrid
Browse files Browse the repository at this point in the history
  • Loading branch information
Michail Yasonik committed Aug 13, 2019
1 parent 17ec36a commit 20b56fc
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 55 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"react-is": "~16.3.0",
"react-virtualized": "^9.18.5",
"resize-observer-polyfill": "^1.5.0",
"tabbable": "^1.1.0",
"tabbable": "^4.0.0",
"uuid": "^3.1.0"
},
"devDependencies": {
Expand All @@ -85,6 +85,7 @@
"@types/react-is": "~16.3.0",
"@types/react-virtualized": "^9.18.6",
"@types/resize-observer-browser": "^0.1.1",
"@types/tabbable": "^3.1.0",
"@types/uuid": "^3.4.4",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"@typescript-eslint/parser": "^1.9.0",
Expand Down
4 changes: 3 additions & 1 deletion src-docs/src/components/guide_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ $guideZLevelHighest: $euiZLevel9 + 1000;
top: 0;
bottom: 0;

display: none;

.guideSideNav__identity {
border-bottom: $euiBorderThin;
padding: $euiSize;
Expand Down Expand Up @@ -50,7 +52,7 @@ $guideZLevelHighest: $euiZLevel9 + 1000;
background-color: $euiColorEmptyShade;
border-left: $euiBorderThin;
max-width: 1000px;
margin-left: 240px;
// margin-left: 240px;
}

.guideDemo__highlightLayout {
Expand Down
47 changes: 46 additions & 1 deletion src-docs/src/views/datagrid/datagrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
EuiFormRow,
EuiPopover,
EuiButton,
EuiButtonIcon,
EuiLink,
} from '../../../../src/components/';
import { iconTypes } from '../../../../src-docs/src/views/icon/icons';

const columns = [
{
Expand All @@ -22,6 +25,12 @@ const columns = [
{
id: 'contributions',
},
{
id: 'actions',
},
{
id: 'a bug',
},
];

const data = [
Expand Down Expand Up @@ -304,6 +313,13 @@ export default class DataGrid extends Component {
pagination: { ...pagination, pageSize },
}));

dummyIcon = () => (
<EuiButtonIcon
aria-label="dummy icon"
iconType={iconTypes[Math.floor(Math.random() * iconTypes.length)]}
/>
);

render() {
const { pagination } = this.state;

Expand Down Expand Up @@ -396,7 +412,36 @@ export default class DataGrid extends Component {
rowHover: this.state.rowHoverSelected,
header: this.state.headerSelected,
}}
renderCellValue={({ rowIndex, columnId }) => data[rowIndex][columnId]}
renderCellValue={({ rowIndex, columnId }) => {
const value = data[rowIndex][columnId];

if (columnId === 'actions') {
return (
<>
{this.dummyIcon()}
{this.dummyIcon()}
</>
);
}

if (columnId === 'url') {
return <EuiLink href={value}>{value}</EuiLink>;
}

if (columnId === 'avatar_url') {
return (
<>
Avatar: <EuiLink href={value}>{value}</EuiLink>
</>
);
}

if (columnId === 'a bug') {
return <p>check it: {this.dummyIcon()}</p>;
}

return value;
}}
pagination={{
...pagination,
pageSizeOptions: [5, 10, 25],
Expand Down
2 changes: 1 addition & 1 deletion src-docs/src/views/icon/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
EuiCopy,
} from '../../../../src/components';

const iconTypes = [
export const iconTypes = [
'alert',
'apmTrace',
'apps',
Expand Down
104 changes: 65 additions & 39 deletions src/components/datagrid/data_grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { EuiDataGridBody } from './data_grid_body';
import { useColumnSelector } from './column_selector';
// @ts-ignore-next-line
import { EuiTablePagination } from '../table/table_pagination';
import { getTabbables, CELL_CONTENTS_ATTR } from './utils';

// Types for styling options, passed down through the `gridStyle` prop
type EuiDataGridStyleFontSizes = 's' | 'm' | 'l';
Expand Down Expand Up @@ -138,13 +139,11 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = props => {

useEffect(() => {
if (gridRef.current != null) {
const gridWidth = Math.max(
gridRef.current!.clientWidth / props.columns.length,
100
);
const gridWidth = gridRef.current.clientWidth;
const columnWidth = Math.max(gridWidth / props.columns.length, 100);
const columnWidths = props.columns.reduce(
(columnWidths: EuiDataGridColumnWidths, column) => {
columnWidths[column.id] = gridWidth;
columnWidths[column.id] = columnWidth;
return columnWidths;
},
{}
Expand All @@ -154,44 +153,71 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = props => {
}, []);

const [focusedCell, setFocusedCell] = useState<[number, number]>(ORIGIN);
const onCellFocus = useCallback(
(x: number, y: number) => {
setFocusedCell([x, y]);
},
[setFocusedCell]
);
const [isGridNavigationEnabled, setIsGridNavigationEnabled] = useState<
boolean
>(true);

const isInteractiveCell = (element: HTMLElement) => {
if (element.getAttribute('role') !== 'gridcell') {
return false;
}

const cellContents = element.querySelector(`[${CELL_CONTENTS_ATTR}]`)!;
const tabbables = getTabbables(cellContents);
const nodeCount = cellContents.childNodes.length;

// TODO fix the bug column (should check if when removing all tabbables from cell if anything is left)
return tabbables.length > 1 || (tabbables.length === 1 && nodeCount > 1);
};

const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
const colCount = props.columns.length - 1;
const [x, y] = focusedCell;
const rowCount = computeVisibleRows(props);
const key = e.keyCode;

if (
// @ts-ignore // TODO why do I need this ignore?
isInteractiveCell(e.target) &&
(key === keyCodes.ENTER || key === keyCodes.F2)
) {
e.preventDefault();
setIsGridNavigationEnabled(false);
}

if (key === keyCodes.ESCAPE || key === keyCodes.F2) {
e.preventDefault();
setIsGridNavigationEnabled(true);
}

switch (e.keyCode) {
case keyCodes.DOWN:
e.preventDefault();
if (y < rowCount) {
setFocusedCell([x, y + 1]);
}
break;
case keyCodes.LEFT:
e.preventDefault();
if (x > 0) {
setFocusedCell([x - 1, y]);
}
break;
case keyCodes.UP:
e.preventDefault();
// TODO sort out when a user can arrow up into the column headers
if (y > 0) {
setFocusedCell([x, y - 1]);
}
break;
case keyCodes.RIGHT:
e.preventDefault();
if (x < colCount) {
setFocusedCell([x + 1, y]);
}
break;
if (isGridNavigationEnabled) {
switch (e.keyCode) {
case keyCodes.DOWN:
e.preventDefault();
if (y < rowCount) {
setFocusedCell([x, y + 1]);
}
break;
case keyCodes.LEFT:
e.preventDefault();
if (x > 0) {
setFocusedCell([x - 1, y]);
}
break;
case keyCodes.UP:
e.preventDefault();
// TODO sort out when a user can arrow up into the column headers
if (y > 0) {
setFocusedCell([x, y - 1]);
}
break;
case keyCodes.RIGHT:
e.preventDefault();
if (x < colCount) {
setFocusedCell([x + 1, y]);
}
break;
}
}
};

Expand Down Expand Up @@ -238,7 +264,6 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = props => {
role="grid"
onKeyDown={handleKeyDown}
ref={gridRef}
// {...label}
{...rest}
className={classes}>
<div className="euiDataGrid__content">
Expand All @@ -251,10 +276,11 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = props => {
columnWidths={columnWidths}
columns={visibleColumns}
focusedCell={focusedCell}
onCellFocus={onCellFocus}
onCellFocus={useCallback(setFocusedCell, [setFocusedCell])}
pagination={pagination}
renderCellValue={renderCellValue}
rowCount={rowCount}
isGridNavigationEnabled={isGridNavigationEnabled}
/>
</div>
<EuiSpacer size="s" />
Expand Down
4 changes: 4 additions & 0 deletions src/components/datagrid/data_grid_body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface EuiDataGridBodyProps {
rowCount: number;
renderCellValue: EuiDataGridCellProps['renderCellValue'];
pagination?: EuiDataGridPaginationProps;
isGridNavigationEnabled: EuiDataGridCellProps['isGridNavigationEnabled'];
}

export const EuiDataGridBody: FunctionComponent<
Expand All @@ -31,6 +32,7 @@ export const EuiDataGridBody: FunctionComponent<
rowCount,
renderCellValue,
pagination,
isGridNavigationEnabled,
} = props;

const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0;
Expand All @@ -51,6 +53,7 @@ export const EuiDataGridBody: FunctionComponent<
onCellFocus={onCellFocus}
renderCellValue={renderCellValue}
rowIndex={i}
isGridNavigationEnabled={isGridNavigationEnabled}
/>
);
}
Expand All @@ -64,6 +67,7 @@ export const EuiDataGridBody: FunctionComponent<
onCellFocus,
renderCellValue,
startRow,
isGridNavigationEnabled,
]);

return <Fragment>{rows}</Fragment>;
Expand Down
Loading

0 comments on commit 20b56fc

Please sign in to comment.