-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: support keyboard navigation (arrow key up/down) in grid mode (#1231
- Loading branch information
Showing
6 changed files
with
332 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
240 changes: 240 additions & 0 deletions
240
...omponents/listbox/interactions/keyboard-navigation/__tests__/find-next-item-index.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
import KEYS from '../../../../../keys'; | ||
import findNextItemIndex from '../find-next-item-index'; | ||
|
||
describe('find next item index to focus on key up and key down', () => { | ||
const numCells = 12; | ||
let rowCount; | ||
let columnCount; | ||
let layoutOrder; | ||
let keyCode; | ||
|
||
describe('key down', () => { | ||
beforeEach(() => { | ||
keyCode = KEYS.ARROW_DOWN; | ||
}); | ||
describe('row layout', () => { | ||
beforeEach(() => { | ||
layoutOrder = 'row'; | ||
columnCount = 5; | ||
rowCount = 3; | ||
}); | ||
test('should return correct index for rowIndex 0', () => { | ||
const rowIndex = 0; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(5); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(6); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 2, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(7); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 3, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(8); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 4, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(9); | ||
}); | ||
|
||
test('should return correct index for rowIndex 1', () => { | ||
const rowIndex = 1; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(10); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(11); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 2, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(3); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 3, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(4); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 4, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(-1); | ||
}); | ||
|
||
test('should return correct index for rowIndex 2', () => { | ||
const rowIndex = 2; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(1); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(2); | ||
}); | ||
}); | ||
|
||
describe('column layout', () => { | ||
beforeEach(() => { | ||
layoutOrder = 'column'; | ||
columnCount = 3; | ||
rowCount = 5; | ||
}); | ||
test('should return correct index for columnIndex 0', () => { | ||
const columnIndex = 0; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(3); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(6); | ||
expect( | ||
findNextItemIndex({ rowIndex: 2, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(8); | ||
expect( | ||
findNextItemIndex({ rowIndex: 3, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(10); | ||
expect( | ||
findNextItemIndex({ rowIndex: 4, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(1); | ||
}); | ||
|
||
test('should return correct index for columnIndex 1', () => { | ||
const columnIndex = 1; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(4); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(7); | ||
expect( | ||
findNextItemIndex({ rowIndex: 2, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(9); | ||
expect( | ||
findNextItemIndex({ rowIndex: 3, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(11); | ||
expect( | ||
findNextItemIndex({ rowIndex: 4, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(2); | ||
}); | ||
|
||
test('should return correct index for columnIndex 2', () => { | ||
const columnIndex = 2; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(5); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(-1); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('key up', () => { | ||
beforeEach(() => { | ||
keyCode = KEYS.ARROW_UP; | ||
}); | ||
describe('row layout', () => { | ||
beforeEach(() => { | ||
layoutOrder = 'row'; | ||
columnCount = 5; | ||
rowCount = 3; | ||
}); | ||
test('should return correct index for rowIndex 0', () => { | ||
const rowIndex = 0; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(-1); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(10); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 2, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(11); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 3, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(7); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 4, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(8); | ||
}); | ||
|
||
test('should return correct index for rowIndex 1', () => { | ||
const rowIndex = 1; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(0); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(1); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 2, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(2); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 3, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(3); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 4, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(4); | ||
}); | ||
|
||
test('should return correct index for rowIndex 2', () => { | ||
const rowIndex = 2; | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 0, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(5); | ||
expect( | ||
findNextItemIndex({ rowIndex, columnIndex: 1, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(6); | ||
}); | ||
}); | ||
|
||
describe('column layout', () => { | ||
beforeEach(() => { | ||
layoutOrder = 'column'; | ||
columnCount = 3; | ||
rowCount = 5; | ||
}); | ||
test('should return correct index for columnIndex 0', () => { | ||
const columnIndex = 0; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(-1); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(0); | ||
expect( | ||
findNextItemIndex({ rowIndex: 2, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(3); | ||
expect( | ||
findNextItemIndex({ rowIndex: 3, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(6); | ||
expect( | ||
findNextItemIndex({ rowIndex: 4, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(8); | ||
}); | ||
|
||
test('should return correct index for columnIndex 1', () => { | ||
const columnIndex = 1; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(10); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(1); | ||
expect( | ||
findNextItemIndex({ rowIndex: 2, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(4); | ||
expect( | ||
findNextItemIndex({ rowIndex: 3, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(7); | ||
expect( | ||
findNextItemIndex({ rowIndex: 4, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(9); | ||
}); | ||
|
||
test('should return correct index for columnIndex 2', () => { | ||
const columnIndex = 2; | ||
expect( | ||
findNextItemIndex({ rowIndex: 0, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(11); | ||
expect( | ||
findNextItemIndex({ rowIndex: 1, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) | ||
).toEqual(2); | ||
}); | ||
}); | ||
}); | ||
}); |
2 changes: 1 addition & 1 deletion
2
...c/components/listbox/interactions/keyboard-navigation/__tests__/keyboard-nav-rows.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
apis/nucleus/src/components/listbox/interactions/keyboard-navigation/find-next-item-index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import KEYS from '../../../../keys'; | ||
|
||
// Find next item index to focus in the dom element list on key up and key down | ||
const findNextItemIndex = ({ rowIndex, columnIndex, rowCount, columnCount, layoutOrder, keyCode, numCells }) => { | ||
const getNumCellsInColumn = (colIdx) => { | ||
let remain; | ||
if (layoutOrder === 'row') { | ||
remain = numCells % columnCount; | ||
return colIdx < remain ? rowCount : rowCount - 1; | ||
} | ||
remain = numCells % rowCount; | ||
return colIdx < columnCount - 1 ? rowCount : remain; | ||
}; | ||
|
||
let nextRowIndex = rowIndex; | ||
let nextColumnIndex = columnIndex; | ||
if (keyCode === KEYS.ARROW_DOWN) { | ||
if (rowIndex >= getNumCellsInColumn(columnIndex) - 1) { | ||
if (columnIndex === columnCount - 1) return -1; | ||
nextRowIndex = 0; | ||
nextColumnIndex = columnIndex + 1; | ||
} else { | ||
nextRowIndex = rowIndex + 1; | ||
} | ||
} else if (rowIndex === 0) { | ||
if (columnIndex === 0) return -1; | ||
nextRowIndex = getNumCellsInColumn(columnIndex - 1) - 1; | ||
nextColumnIndex = columnIndex - 1; | ||
} else { | ||
nextRowIndex = rowIndex - 1; | ||
} | ||
|
||
// Convert from row, column indices to the element index in the dom element list | ||
if (layoutOrder === 'row') { | ||
return nextRowIndex * columnCount + nextColumnIndex; | ||
} | ||
|
||
// The dom element list is always row order. If the layout is column order then the conversion is not straight forward | ||
const remain = numCells % rowCount; | ||
if (remain === 0 || nextRowIndex < remain) return nextRowIndex * columnCount + nextColumnIndex; | ||
return (nextRowIndex - remain) * (columnCount - 1) + nextColumnIndex + remain * columnCount; | ||
}; | ||
|
||
export default findNextItemIndex; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters