I have a React implementation, however, since Rich's angular example helped me, I thought I could expand Rich's answer with moving the focus handling to the cell renderer rather than inside the suppressKeyboardEvent handler. This will allow us to give focus to the element immediately when the cell receives focus, rather than having to hit TAB a second time to give focus to the first element inside the cell.
In order to give focus to the element as soon as the cell receives focus, we can add a onCellFocused handler to the grid. When the our desired column receives focus we use an imperativeHandle on our custom cell renderer to hand over control to the renderer.
const cellFocusedHandler = (params) => {
if (params.column.userProvidedColDef.colId === 3) {
const rendererParams = {
rowNodes: [params.api.getDisplayedRowAtIndex(params.rowIndex)],
columns: [params.column],
};
const cellRenderer = params.api.getCellRendererInstances(rendererParams);
// since our rendererParams only provided for one cell, we've hardcode 0
cellRenderer[0].setFirstElementFocus();
}
};
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
onGridReady={onGridReady}
onCellFocused={cellFocusedHandler}
/>
Our custom cell renderer looks like this...
const ActionRenderer = (props, ref) => {
const anchor1Ref = useRef();
const anchor2Ref = useRef();
/* Component Editor Lifecycle methods */
useImperativeHandle(ref, () => {
return {
setFirstElementFocus() {
anchor1Ref.current.focus();
},
};
});
// This is just a start... there will be various key navigation that MAY need
// to be added. But for now, these are the main keys addressed
// ActionCellKeyboardHandler.js controls which key strokes make it through to
// the renderer.
const handleKeyDown = (e) => {
if (e.key === KeyCode.SPACE) {
// space key does nothing.
// prevent default so that the page doesn't scroll
e.preventDefault();
} else if (e.key === KeyCode.ENTER) {
e.stopPropagation();
// since we are controlling all keystrokes, an enter
// key triggers the click() event.
e.target.click();
} else if (e.key === KeyCode.TAB) {
e.stopPropagation();
// if we are tabbing away from the last element in our
// action cell, then we want to give focus to the
// next cell.
// In this example, the action cell is the last column, so
// we have hard coded the next row first cell
if (document.activeElement === anchor2Ref.current) {
// prevent default so that we control the next move
e.preventDefault();
// this is a primitive example with no extra logic,
// but demonstrates the concept. Edge case handling
// would need to be added.
props.api.setFocusedCell(
props.rowIndex + 1,
props.columnApi.columnModel.getAllDisplayedColumns()[0],
);
}
}
};
const handleLikeActionClick = (pEvnt) => {
alert("Like " + props.data.athlete);
};
const handleDislikeActionClick = (pEvnt) => {
alert("Dislike " + props.data.athlete);
};
return (
<div className="d-flex flex-row justify-content-center h-100">
<a ref={anchor1Ref}
onKeyDown={handleKeyDown}
onClick={handleLikeActionClick}
tabIndex={0}
>
<FontAwesomeIcon icon={faThumbsUp} />
</a>
<a ref={anchor2Ref}
onKeyDown={handleKeyDown}
onClick={handleDislikeActionClick}
className="action-spacer"
tabIndex={0}
>
<FontAwesomeIcon icon={faThumbsDown} />
</a>
</div>
);
};
export default forwardRef(ActionRenderer);
Here is a codesandbox example... Custom Cell Renderer with Tab Navigation