mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-24 13:58:50 +08:00
a8c18cd631
https://github.com/user-attachments/assets/6bce5fa3-fb25-4906-bef1-50d4da4a13f6 This PR addresses #12769 and improves table editing UX by making Enter commit changes and move focus down, and Tab/Shift+Tab move focus horizontally—matching spreadsheet-like behavior. Typing now immediately enters edit mode for selected cells without double-clicking. These updates apply to both the standard and virtual table views. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - You can now start editing a table cell by simply typing any character while the cell is selected. - **Improvements** - Pressing Enter while editing a cell will exit editing and move focus down. - Pressing Tab or Shift-Tab while editing a cell will exit editing and move focus right or left, respectively. - **Tests** - Added unit tests for table cell hotkey behaviors to ensure reliable editing and navigation. - **Chores** - Introduced Vitest configuration for streamlined testing and coverage reporting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: L-Sun <zover.v@gmail.com>
121 lines
3.9 KiB
TypeScript
121 lines
3.9 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
import { TableHotkeysController } from '../view-presets/table/pc/controller/hotkeys.js';
|
|
import { TableHotkeysController as VirtualHotkeysController } from '../view-presets/table/pc-virtual/controller/hotkeys.js';
|
|
import {
|
|
TableViewAreaSelection,
|
|
TableViewRowSelection,
|
|
} from '../view-presets/table/selection';
|
|
|
|
function createLogic() {
|
|
const view = {
|
|
rowsDelete: vi.fn(),
|
|
rows$: { value: [] },
|
|
groupTrait: { groupsDataList$: { value: [] } },
|
|
};
|
|
const ui = { disposables: { add: vi.fn() }, requestUpdate: vi.fn() };
|
|
const selectionController = {
|
|
selection: undefined as any,
|
|
getCellContainer: vi.fn(),
|
|
insertRowAfter: vi.fn(),
|
|
focusToCell: vi.fn(),
|
|
rowSelectionChange: vi.fn(),
|
|
areaToRows: vi.fn().mockReturnValue([]),
|
|
rowsToArea: vi.fn(),
|
|
navigateRowSelection: vi.fn(),
|
|
selectionAreaUp: vi.fn(),
|
|
selectionAreaDown: vi.fn(),
|
|
selectionAreaLeft: vi.fn(),
|
|
selectionAreaRight: vi.fn(),
|
|
isRowSelection: vi.fn().mockReturnValue(false),
|
|
};
|
|
const logic: any = {
|
|
view,
|
|
ui$: { value: ui },
|
|
selectionController,
|
|
bindHotkey: vi.fn((hotkeys: any) => {
|
|
logic.hotkeys = hotkeys;
|
|
return { dispose: vi.fn() };
|
|
}),
|
|
handleEvent: vi.fn((name: string, handler: any) => {
|
|
if (name === 'keyDown') logic.keyDown = handler;
|
|
return { dispose: vi.fn() };
|
|
}),
|
|
};
|
|
return { logic, view, ui, selectionController };
|
|
}
|
|
|
|
describe('TableHotkeysController', () => {
|
|
it('deletes rows on Backspace', () => {
|
|
const { logic, view, ui, selectionController } = createLogic();
|
|
const ctrl = new TableHotkeysController(logic as any);
|
|
ctrl.hostConnected();
|
|
selectionController.selection = TableViewRowSelection.create({
|
|
rows: [{ id: 'r1' }],
|
|
});
|
|
logic.hotkeys.Backspace();
|
|
expect(selectionController.selection).toBeUndefined();
|
|
expect(view.rowsDelete).toHaveBeenCalledWith(['r1']);
|
|
expect(ui.requestUpdate).toHaveBeenCalled();
|
|
});
|
|
|
|
it('starts editing on character key', () => {
|
|
const { logic, selectionController } = createLogic();
|
|
const ctrl = new TableHotkeysController(logic as any);
|
|
ctrl.hostConnected();
|
|
const cell = {
|
|
rowId: 'r1',
|
|
dataset: { rowId: 'r1', columnId: 'c1' },
|
|
column: { valueSetFromString: vi.fn() },
|
|
};
|
|
selectionController.getCellContainer.mockReturnValue(cell);
|
|
selectionController.selection = TableViewAreaSelection.create({
|
|
focus: { rowIndex: 0, columnIndex: 0 },
|
|
isEditing: false,
|
|
});
|
|
const evt = {
|
|
key: 'A',
|
|
metaKey: false,
|
|
ctrlKey: false,
|
|
altKey: false,
|
|
preventDefault: vi.fn(),
|
|
};
|
|
logic.keyDown({ get: () => ({ raw: evt }) });
|
|
expect(cell.column.valueSetFromString).toHaveBeenCalledWith('r1', 'A');
|
|
expect(selectionController.selection.isEditing).toBe(true);
|
|
expect(evt.preventDefault).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('Virtual TableHotkeysController', () => {
|
|
it('writes character to cell', () => {
|
|
const { logic, selectionController } = createLogic();
|
|
const ctrl = new VirtualHotkeysController(logic as any);
|
|
ctrl.hostConnected();
|
|
const cell = {
|
|
rowId: 'r1',
|
|
dataset: { rowId: 'r1', columnId: 'c1' },
|
|
column$: { value: { valueSetFromString: vi.fn() } },
|
|
};
|
|
selectionController.getCellContainer.mockReturnValue(cell);
|
|
selectionController.selection = TableViewAreaSelection.create({
|
|
focus: { rowIndex: 1, columnIndex: 0 },
|
|
isEditing: false,
|
|
});
|
|
const evt = {
|
|
key: 'b',
|
|
metaKey: false,
|
|
ctrlKey: false,
|
|
altKey: false,
|
|
preventDefault: vi.fn(),
|
|
};
|
|
logic.keyDown({ get: () => ({ raw: evt }) });
|
|
expect(cell.column$.value.valueSetFromString).toHaveBeenCalledWith(
|
|
'r1',
|
|
'b'
|
|
);
|
|
expect(selectionController.selection.isEditing).toBe(true);
|
|
expect(evt.preventDefault).toHaveBeenCalled();
|
|
});
|
|
});
|