修复弹窗内删除快捷键误删节点

排除 role=dialog 和 aria-modal 弹窗内的 Delete/Backspace 快捷键

补充弹窗焦点下节点不应被删除的回归测试

同步更新 UI 编辑器撤销重做规范
This commit is contained in:
2026-09-04 18:04:16 +08:00
parent fe813802a5
commit f029992083
3 changed files with 30 additions and 1 deletions
@@ -101,7 +101,7 @@ export default function UiEditorPage({
return Boolean(
(target instanceof HTMLElement && target.isContentEditable) ||
element?.closest(
'button, a, input, textarea, select, [contenteditable="true"], [role="button"]',
'button, a, input, textarea, select, [contenteditable="true"], [role="button"], [role="dialog"], [aria-modal="true"]',
),
);
};
@@ -514,6 +514,34 @@ describe('UiEditorPage', () => {
await waitFor(() => expect(screen.queryByText('page-child')).toBeNull());
});
it('does not delete a selected node when Delete originates inside a dialog', async () => {
const state = stateWithPages(['page']);
state.ui_trees[0]!.root.children = [node('page-child')];
const stateStore: IUiDesignStateStore = {
load: vi.fn().mockResolvedValue({ revision: 0, state }),
save: vi.fn(),
generateCode: vi.fn().mockRejectedValue(new Error('测试未配置代码生成')),
};
render(
createElement(UiEditorPage, {
projectPath: '/tmp/ui-editor-delete-dialog',
resourceId: 'ui-resource',
stateStore,
}),
);
const child = await screen.findByText('page-child');
fireEvent.click(child);
const dialog = document.createElement('div');
dialog.setAttribute('role', 'dialog');
document.body.appendChild(dialog);
fireEvent.keyDown(dialog, { key: 'Delete' });
expect(screen.queryAllByText('page-child').length).toBeGreaterThan(0);
dialog.remove();
});
it('keeps Inspector status highlighting separate from node navigation', async () => {
const { result } = await renderLoadedSession(stateWithPages(['page']));
const rootId = 'page-root';
@@ -51,6 +51,7 @@ UI 编辑器支持撤销最近一次或多次作品编辑,并支持重做被
- `input``textarea``select``contenteditable` 等文本编辑控件交给浏览器原生行为,不拦截文本撤销;按钮/链接等非文本控件仍允许编辑器撤销/重做快捷键生效。
- UI Editor 页面内选中节点后,支持不带修饰键的 `Delete` 删除节点及其子节点;快捷键复用 Inspector、树面板和右键菜单共用的 `deleteNode` 命令,因此沿用根节点/锁定禁删、单条历史记录、dirty 标记和后续保存语义。
- `Delete` / `Backspace``input``textarea``select``contenteditable`、按钮和链接等交互控件聚焦时交给浏览器原生行为;无选中、根节点、锁定或目标不存在时不执行删除。长按重复事件不重复删除,成功处理后阻止默认行为和事件冒泡。
- `Delete` / `Backspace` 在打开的对话框(`role="dialog"``aria-modal="true"`)内聚焦时交给对话框处理,不删除对话框背后的节点。
- 无可撤销或重做记录时按钮禁用,并提供可访问名称。
## 脏状态与选择