Minor keyboard navigation around fix

Summary: Fixed minor keyboard navigation annoyance: pressing arrow down on the last entry would remove selection, then jump to first row. Pressing up on first row would deselect then select first again. After this change the first/last item is kept selected in those cases

Reviewed By: passy

Differential Revision: D28958705

fbshipit-source-id: 01dbce3971ed965eae3b74e6706fef96aa86df66
This commit is contained in:
Michel Weststrate
2021-06-08 06:43:47 -07:00
committed by Facebook GitHub Bot
parent 6224daf247
commit 23c0781127
3 changed files with 41 additions and 7 deletions

View File

@@ -188,9 +188,9 @@ export function DataTable<T extends object>(
if (e.ctrlKey || e.metaKey) { if (e.ctrlKey || e.metaKey) {
tableManager.addRangeToSelection(index, index, true); tableManager.addRangeToSelection(index, index, true);
} else if (e.shiftKey) { } else if (e.shiftKey) {
tableManager.selectItem(index, true); tableManager.selectItem(index, true, true);
} else { } else {
tableManager.selectItem(index); tableManager.selectItem(index, false, true);
} }
dragging.current = true; dragging.current = true;

View File

@@ -59,6 +59,7 @@ type DataManagerActions<T> =
{ {
nextIndex: number | ((currentIndex: number) => number); nextIndex: number | ((currentIndex: number) => number);
addToSelection?: boolean; addToSelection?: boolean;
allowUnselect?: boolean;
} }
> >
| Action< | Action<
@@ -161,9 +162,14 @@ export const dataTableManagerReducer = produce<
break; break;
} }
case 'selectItem': { case 'selectItem': {
const {nextIndex, addToSelection} = action; const {nextIndex, addToSelection, allowUnselect} = action;
draft.selection = castDraft( draft.selection = castDraft(
computeSetSelection(draft.selection, nextIndex, addToSelection), computeSetSelection(
draft.selection,
nextIndex,
addToSelection,
allowUnselect,
),
); );
break; break;
} }
@@ -252,6 +258,7 @@ export type DataTableManager<T> = {
selectItem( selectItem(
index: number | ((currentSelection: number) => number), index: number | ((currentSelection: number) => number),
addToSelection?: boolean, addToSelection?: boolean,
allowUnselect?: boolean,
): void; ): void;
addRangeToSelection( addRangeToSelection(
start: number, start: number,
@@ -276,8 +283,13 @@ export function createDataTableManager<T>(
reset() { reset() {
dispatch({type: 'reset'}); dispatch({type: 'reset'});
}, },
selectItem(index: number, addToSelection = false) { selectItem(index: number, addToSelection = false, allowUnselect = false) {
dispatch({type: 'selectItem', nextIndex: index, addToSelection}); dispatch({
type: 'selectItem',
nextIndex: index,
addToSelection,
allowUnselect,
});
}, },
selectItemById(id, addToSelection = false) { selectItemById(id, addToSelection = false) {
dispatch({type: 'selectItemById', id, addToSelection}); dispatch({type: 'selectItemById', id, addToSelection});
@@ -517,11 +529,17 @@ export function computeSetSelection(
base: Selection, base: Selection,
nextIndex: number | ((currentIndex: number) => number), nextIndex: number | ((currentIndex: number) => number),
addToSelection?: boolean, addToSelection?: boolean,
allowUnselect?: boolean,
): Selection { ): Selection {
const newIndex = const newIndex =
typeof nextIndex === 'number' ? nextIndex : nextIndex(base.current); typeof nextIndex === 'number' ? nextIndex : nextIndex(base.current);
// special case: toggle existing selection off // special case: toggle existing selection off
if (!addToSelection && base.items.size === 1 && base.current === newIndex) { if (
!addToSelection &&
allowUnselect &&
base.items.size === 1 &&
base.current === newIndex
) {
return emptySelection; return emptySelection;
} }
if (newIndex < 0) { if (newIndex < 0) {

View File

@@ -61,6 +61,20 @@ test('computeSetSelection', () => {
items: new Set([5]), items: new Set([5]),
}); });
// single item existing selection, no selection toggle
expect(
computeSetSelection(
{
current: 4,
items: new Set([4]),
},
4,
),
).toEqual({
current: 4,
items: new Set([4]),
});
// single item existing selection, toggle item off // single item existing selection, toggle item off
expect( expect(
computeSetSelection( computeSetSelection(
@@ -69,6 +83,8 @@ test('computeSetSelection', () => {
items: new Set([4]), items: new Set([4]),
}, },
4, 4,
false,
true,
), ),
).toEqual({ ).toEqual({
current: -1, current: -1,