Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,12 @@
"name": "Custom Navigation",
"url": "./keyboard-navigation/#custom-navigation"
}
},
"tabToNextGridContainer": {
"more": {
"name": "Custom Navigation",
"url": "./keyboard-navigation/#custom-navigation"
}
}
},
"pagination": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<div class="form-container">
<label>
Input Above
<input />
<input type="text" />
</label>
</div>
</div>
<div id="myGrid"></div>
<div class="form-container">
<label>
Input Below
<input />
<input type="text" />
</label>
</div>
</div>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { clickAllButtons, ensureGridReady, test, waitForGridContent } from '@utils/grid/test-utils';

test.agExample(import.meta, () => {
test.eachFramework('Example', async ({ page }) => {
// PLACEHOLDER - MINIMAL TEST TO ENSURE GRID LOADS WITHOUT ERRORS
await ensureGridReady(page);
await waitForGridContent(page);
await clickAllButtons(page);
// END PLACEHOLDER
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="test-container">
<div>
<div class="form-container">
<label>
Input Above
<input type="text" />
</label>
</div>
</div>
<div id="myGrid"></div>
<div class="form-container">
<label>
Input Below
<input type="text" />
</label>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type {
CellPosition,
ColDef,
GridApi,
GridOptions,
TabToNextGridContainer,
TabToNextGridContainerParams,
} from 'ag-grid-community';
import {
ClientSideRowModelModule,
ModuleRegistry,
PaginationModule,
TextFilterModule,
ValidationModule,
createGrid,
} from 'ag-grid-community';

ModuleRegistry.registerModules([
PaginationModule,
TextFilterModule,
ClientSideRowModelModule,
...(process.env.NODE_ENV !== 'production' ? [ValidationModule] : []),
]);

const columnDefs: ColDef[] = [
{ headerName: '#', colId: 'rowNum', valueGetter: 'node.id', maxWidth: 90 },
{ field: 'athlete', minWidth: 170 },
{ field: 'age' },
{ field: 'country' },
{ field: 'year' },
{ field: 'date' },
{ field: 'sport' },
{ field: 'gold' },
{ field: 'silver' },
{ field: 'bronze' },
{ field: 'total' },
];

let gridApi: GridApi<IOlympicData>;
let lastFocusedCell: CellPosition | null = null;

const tabToNextGridContainer: TabToNextGridContainer<IOlympicData> = (
params: TabToNextGridContainerParams<IOlympicData>
) => {
const { backwards, fromContainer, toContainer, defaultTarget } = params;

// route tabbing out of the last grid cell into pagination controls first.
if (!backwards && fromContainer === 'gridBody' && toContainer === 'external') {
return 'pagination';
}

// restore last focused cell when shift-tabbing from pagination back into the grid.
if (backwards && fromContainer === 'pagination' && toContainer === 'gridBody') {
const target = lastFocusedCell ?? defaultTarget;
return target == null ? undefined : target;
}

// from pagination forwards, allow browser default focus flow to leave the grid.
if (!backwards && fromContainer === 'pagination' && toContainer === 'external') {
return false;
}

// For everything else, keep grid defaults.
return undefined;
};

const gridOptions: GridOptions<IOlympicData> = {
columnDefs,
defaultColDef: {
flex: 1,
minWidth: 100,
filter: true,
},
pagination: true,
tabToNextGridContainer,
onCellFocused: (params) => {
const { rowIndex, rowPinned, column } = params;
if (rowIndex == null || !column || typeof column === 'string') {
return;
}

lastFocusedCell = {
rowIndex,
rowPinned: rowPinned ?? null,
column,
};
},
};

// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function () {
const gridDiv = document.querySelector<HTMLElement>('#myGrid')!;
gridApi = createGrid(gridDiv, gridOptions);

fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
.then((response) => response.json())
.then((data: IOlympicData[]) => gridApi!.setGridOption('rowData', data));
});
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ The example below has grouped headers, headers and floating filters to demonstra

## Custom Navigation

Most people will be happy with the default navigation the grid does when you use the arrow keys and the {% kbd "⇥ Tab" /%} key. Some people will want to override this (e.g. you may want the {% kbd "⇥ Tab" /%} key to navigate to the cell below, not the cell to the right). To facilitate this, the grid offers four methods: `navigateToNextCell`, `tabToNextCell`, `navigateToNextHeader` and `tabToNextHeader`.
Most people will be happy with the default navigation the grid does when you use the arrow keys and the {% kbd "⇥ Tab" /%} key. Some people will want to override this (e.g. you may want the {% kbd "⇥ Tab" /%} key to navigate to the cell below, not the cell to the right). To facilitate this, the grid offers five methods: `navigateToNextCell`, `tabToNextCell`, `navigateToNextHeader`, `tabToNextHeader` and `tabToNextGridContainer`.

{% apiDocumentation source="grid-options/properties.json" section="nav" names=["focusGridInnerElement", "navigateToNextCell", "tabToNextCell", "navigateToNextHeader", "tabToNextHeader"] /%}
{% apiDocumentation source="grid-options/properties.json" section="nav" names=["focusGridInnerElement", "navigateToNextCell", "tabToNextCell", "navigateToNextHeader", "tabToNextHeader", "tabToNextGridContainer"] /%}

{% note %}
The `navigateToNextCell` and `tabToNextCell` are only called while navigating across grid cells, while
`navigateToNextHeader` and `tabToNextHeader` are only called while navigating across grid headers.
`navigateToNextHeader` and `tabToNextHeader` are only called while navigating across grid headers. The
`tabToNextGridContainer` callback is called when tab navigation moves between core grid containers.
If you need to navigate from one container to another, pass `rowIndex: -1` in `CellPosition`
or `headerRowIndex: -1` in `HeaderPosition`.
{% /note %}
Expand Down Expand Up @@ -166,6 +167,19 @@ In the following example there are two input box provided to test tabbing into t

{% gridExampleRunner title="Tabbing into the Grid" name="tabbing-into-grid" /%}

### Custom Tabbing Between Grid Containers

Use `tabToNextGridContainer` to override focus behaviour when tabbing between grid containers such as the grid body, pagination toolbar and external elements.

In the following example:

- Tabbing out of the last grid cell is redirected to the pagination toolbar by returning `'pagination'`.
- Shift tabbing from the pagination toolbar back into the grid restores the last focused cell by returning a `CellPosition`.
- Tabbing forward from pagination returns `false`, allowing browser default behaviour to move focus outside the grid.
- All other transitions return `undefined`, which keeps the grid's default behaviour.

{% gridExampleRunner title="Custom Tab to Next Grid Container" name="tab-to-next-grid-container" /%}

### Custom Tabbing into the Grid

The `focusGridInnerElement` callback can be used to change the element focused by the grid when receiving focus from outside . Notice the following:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ import type {
StatusBar,
StoreRefreshedEvent,
TabToNextCell,
TabToNextGridContainer,
TabToNextHeader,
Theme,
ToolPanelSizeChangedEvent,
Expand Down Expand Up @@ -1938,6 +1939,12 @@ export class AgGridAngular<TData = any, TColDef extends ColDef<TData> = ColDef<a
* or `false` to let the browser handle the tab behaviour.
*/
@Input() public tabToNextCell: TabToNextCell<TData> | undefined = undefined;
/** Allows overriding the default behaviour when tabbing between core grid containers.
* Return a container name, a cell position, or a header position to focus that target,
* `true` to stay on the current focus, `false` to let the browser handle tab behaviour,
* or `undefined` to use the grid's default behaviour.
*/
@Input() public tabToNextGridContainer: TabToNextGridContainer<TData> | undefined = undefined;
/** A callback for localising text within the grid.
* @initial
* @agModule `LocaleModule`
Expand Down
8 changes: 8 additions & 0 deletions packages/ag-grid-community/src/entities/gridOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ import type {
ProcessUnpinnedColumns,
SendToClipboard,
TabToNextCell,
TabToNextGridContainer,
TabToNextHeader,
} from '../interfaces/iCallbackParams';
import type {
Expand Down Expand Up @@ -2194,6 +2195,13 @@ export interface GridOptions<TData = any> {
* or `false` to let the browser handle the tab behaviour.
*/
tabToNextCell?: TabToNextCell<TData>;
/**
* Allows overriding the default behaviour when tabbing between core grid containers.
* Return a container name, a cell position, or a header position to focus that target,
* `true` to stay on the current focus, `false` to let the browser handle tab behaviour,
* or `undefined` to use the grid's default behaviour.
*/
tabToNextGridContainer?: TabToNextGridContainer<TData>;

// *** Localisation *** //
/**
Expand Down
Loading
Loading