ef4601c555
src/view/ui-editor/useUiEditorPage.ts 的 deleteNode 默认 treeId 改为 treeForSelectedNode?.src_ui_design ?? activeImageId:选中节点可以来自非激活界面图,Inspector 删除按钮不给 treeId 时会静默删不掉 src/view/ui-editor/uiEditorKeyboardShortcuts.ts 与 view/ui-editor/index.tsx 去掉键盘删除强制传 activeImageId 的接线,treeId 改为可选,同时删掉已无消费者的 activeImageId 入参 tests/uiEditorPage.test.ts 补「非激活树里的节点默认也能删除」用例,uiEditorKeyboardShortcuts.test.ts 断言改为只传节点 id
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { fireEvent } from '@testing-library/react';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { handleUiEditorKeyDown } from '../src/view/ui-editor/uiEditorKeyboardShortcuts';
|
|
|
|
describe('ui editor keyboard shortcuts', () => {
|
|
afterEach(() => {
|
|
document.body.replaceChildren();
|
|
});
|
|
|
|
it.each([
|
|
{ key: 'z', ctrlKey: true },
|
|
{ key: 'z', ctrlKey: true, shiftKey: true },
|
|
{ key: 'y', ctrlKey: true },
|
|
])('does not change history from inside a modal (%o)', (shortcut) => {
|
|
const dialog = document.createElement('div');
|
|
dialog.setAttribute('role', 'dialog');
|
|
const button = document.createElement('button');
|
|
dialog.append(button);
|
|
document.body.append(dialog);
|
|
const historyUndo = vi.fn(() => true);
|
|
const historyRedo = vi.fn(() => true);
|
|
const listener = (event: KeyboardEvent) =>
|
|
handleUiEditorKeyDown(event, {
|
|
selectedNodeId: null,
|
|
deleteNode: vi.fn(),
|
|
historyUndo,
|
|
historyRedo,
|
|
});
|
|
window.addEventListener('keydown', listener);
|
|
|
|
fireEvent.keyDown(button, shortcut);
|
|
|
|
expect(historyUndo).not.toHaveBeenCalled();
|
|
expect(historyRedo).not.toHaveBeenCalled();
|
|
window.removeEventListener('keydown', listener);
|
|
});
|
|
|
|
it('keeps undo available from a non-modal button', () => {
|
|
const button = document.createElement('button');
|
|
document.body.append(button);
|
|
const historyUndo = vi.fn(() => true);
|
|
const historyRedo = vi.fn(() => true);
|
|
const listener = (event: KeyboardEvent) =>
|
|
handleUiEditorKeyDown(event, {
|
|
selectedNodeId: null,
|
|
deleteNode: vi.fn(),
|
|
historyUndo,
|
|
historyRedo,
|
|
});
|
|
window.addEventListener('keydown', listener);
|
|
|
|
fireEvent.keyDown(button, { key: 'z', ctrlKey: true });
|
|
|
|
expect(historyUndo).toHaveBeenCalledTimes(1);
|
|
window.removeEventListener('keydown', listener);
|
|
});
|
|
|
|
it.each(['Delete', 'Backspace'])(
|
|
'does not delete a node when the zoom slider has focus (%s)',
|
|
(key) => {
|
|
const slider = document.createElement('input');
|
|
slider.type = 'range';
|
|
document.body.append(slider);
|
|
const deleteNode = vi.fn(() => ({ ok: true }));
|
|
const listener = (event: KeyboardEvent) =>
|
|
handleUiEditorKeyDown(event, {
|
|
selectedNodeId: 'node-1',
|
|
deleteNode,
|
|
historyUndo: vi.fn(() => true),
|
|
historyRedo: vi.fn(() => true),
|
|
});
|
|
window.addEventListener('keydown', listener);
|
|
|
|
fireEvent.keyDown(slider, { key });
|
|
|
|
expect(deleteNode).not.toHaveBeenCalled();
|
|
window.removeEventListener('keydown', listener);
|
|
},
|
|
);
|
|
|
|
it('keeps node deletion available after zoom button focus is released', () => {
|
|
const zoomControls = document.createElement('div');
|
|
const zoomIn = document.createElement('button');
|
|
zoomControls.append(zoomIn);
|
|
document.body.append(zoomControls);
|
|
const deleteNode = vi.fn(() => ({ ok: true }));
|
|
const listener = (event: KeyboardEvent) =>
|
|
handleUiEditorKeyDown(event, {
|
|
selectedNodeId: 'node-1',
|
|
deleteNode,
|
|
historyUndo: vi.fn(() => true),
|
|
historyRedo: vi.fn(() => true),
|
|
});
|
|
window.addEventListener('keydown', listener);
|
|
|
|
fireEvent.click(zoomIn);
|
|
fireEvent.keyDown(window, { key: 'Delete' });
|
|
|
|
expect(deleteNode).toHaveBeenCalledWith('node-1');
|
|
window.removeEventListener('keydown', listener);
|
|
});
|
|
});
|