fix: keep project dialog close controls clickable

Render project creation and settings dialogs in a body portal so the Electron title-bar drag region does not intercept their close controls. Cover dialog placement, scrolling, and focus restoration, and document the layout rule.

Release note: Project creation and settings dialogs keep their close buttons clickable after scrolling and restore focus to their trigger when closed.
This commit is contained in:
mesalogo
2026-09-13 13:31:42 +08:00
parent 3b560c9a5e
commit e9679fecf1
3 changed files with 22 additions and 2 deletions
+17
View File
@@ -354,6 +354,23 @@ describe('ProjectSwitcher project activity integration', () => {
})
describe('ProjectSwitcher runtime fields', () => {
it.each(['新建项目', '项目设置'])('keeps %s outside the sidebar and restores focus after closing', (name) => {
renderSwitcher()
const trigger = screen.getByLabelText(name)
fireEvent.click(trigger)
const dialog = screen.getByRole('dialog', { name })
expect(dialog.parentElement?.parentElement).toBe(document.body)
const body = dialog.querySelector('.project-create-card__body')!
const close = within(dialog).getByRole('button', { name: `关闭${name}` })
expect(body).toContainElement(within(dialog).getByLabelText('名称'))
expect(body).not.toContainElement(close)
expect(body).not.toContainElement(within(dialog).getByRole('button', { name: '取消' }))
fireEvent.scroll(body, { target: { scrollTop: 500 } })
fireEvent.click(close)
expect(screen.queryByRole('dialog', { name })).not.toBeInTheDocument()
expect(trigger).toHaveFocus()
})
it('creates an ordinary project with DeepSeek Harness', async () => {
const { onCreate } = renderSwitcher()
+4 -2
View File
@@ -17,6 +17,7 @@ import {
} from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import type {
AssistantProject,
@@ -1413,7 +1414,7 @@ export function ProjectSwitcher({
<Settings size={15} />
</button>
</div>
{dialogMode && (
{dialogMode && createPortal(
<div
className="project-create-backdrop"
onMouseDown={(event) => {
@@ -2139,7 +2140,8 @@ export function ProjectSwitcher({
)}
</div>
</div>
</div>
</div>,
document.body
)}
</div>
)