Skip to content

fix(app): close search dialog before navigating to prevent ghost overlay#2875

Merged
davydkov merged 1 commit into
mainfrom
fix/search-pointer-events
Apr 15, 2026
Merged

fix(app): close search dialog before navigating to prevent ghost overlay#2875
davydkov merged 1 commit into
mainfrom
fix/search-pointer-events

Conversation

@ckeller42

Copy link
Copy Markdown
Collaborator

Summary

Fix for the search overlay persisting after navigating to a view via Ctrl+K search, trapping focus and blocking interaction with the destination diagram.

Closes #2353

Root Cause

When clicking a view in the search panel, navigateTo called onClose() (React state update) and navigate() (route change) simultaneously. The route change could unmount the OverviewSearch component before the <dialog> element was properly closed via the DOM API. Since showModal() places the dialog in the browser's top layer, unmounting without calling .close() first left the dialog's backdrop and focus trap active.

Fix

Explicitly close all open search <dialog> elements via the DOM before triggering the React state change and route navigation. This ensures the browser removes the dialog from the top layer before the component unmounts.

Verified with Playwright

  • Open overview page
  • Ctrl+K to open search
  • Click a view result → navigates to view
  • After navigation: 0 open dialogs, 0 dialog elements, search overlay gone
  • View is fully interactive

Files changed (1)

  • packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx — 6 lines added

@changeset-bot

changeset-bot Bot commented Apr 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 438c40d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@likec4/diagram Patch
@likec4/playground Patch
likec4 Patch
@likec4/react Patch
@likec4/vscode-preview Patch
likec4-vscode Patch
@likec4/docs-astro Patch
@likec4/style-preset Patch
@likec4/styles Patch
@likec4/config Patch
@likec4/core Patch
@likec4/generators Patch
@likec4/language-server Patch
@likec4/language-services Patch
@likec4/layouts Patch
@likec4/leanix-bridge Patch
@likec4/log Patch
@likec4/lsp Patch
@likec4/mcp Patch
@likec4/tsconfig Patch
@likec4/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Close any open search-related native dialogs before route navigation and ensure overlay dialogs are explicitly closed on component unmount to avoid lingering open dialog state that can trap focus across route changes.

Changes

Cohort / File(s) Summary
Search dialog dismissal on navigation
packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx
navigateTo now queries dialog[data-likec4-search], filters to open HTMLDialogElement instances, and calls close() on each before invoking onClose() and performing route navigation.
Overlay unmount cleanup
packages/diagram/src/overlays/overlay/Overlay.tsx
useLayoutEffect that calls showModal() now returns a cleanup that sets a closing flag and calls dialog.close() on unmount if still open, preventing lingering top-layer dialogs.
End-to-end test
e2e/tests/overview-page.spec.ts
Added Playwright test that opens the Search overlay, navigates to a view, and asserts the search dialog is fully dismissed (dialog[open] count 0 and no [data-likec4-search]) while the diagram pane remains visible.
Changeset
.changeset/fix-search-overlay-close.md
Added a patch changeset note for the @likec4/diagram package describing the overlay close behavior fix.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant SearchAdapter as OverviewSearchAdapter
    participant DOM as Browser DOM
    participant Router as Router

    User->>SearchAdapter: trigger navigateTo(target)
    SearchAdapter->>DOM: querySelectorAll('dialog[data-likec4-search]')
    DOM-->>SearchAdapter: NodeList
    SearchAdapter->>SearchAdapter: filter HTMLDialogElement && open===true
    loop for each open dialog
        SearchAdapter->>DOM: dialog.close()
    end
    SearchAdapter->>SearchAdapter: onClose()
    SearchAdapter->>Router: navigate(target)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: closing the search dialog before navigation to prevent a ghost overlay persisting.
Description check ✅ Passed The description comprehensively covers the root cause, fix implementation, verification steps, and properly references the closed issue #2353.
Linked Issues check ✅ Passed The PR directly addresses the core requirement from #2353: ensuring the search overlay is dismissed after navigating to a view so the destination is fully interactive.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the search dialog persistence issue; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/search-pointer-events

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ckeller42 ckeller42 force-pushed the fix/search-pointer-events branch 2 times, most recently from 3304ff8 to 5fb58d3 Compare April 11, 2026 11:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/diagram/src/overlays/overlay/Overlay.tsx`:
- Around line 73-76: The cleanup currently calls dialogRef.current.close() which
can re-enter onClose via the dialog 'close' handler; before calling
dialog.close() set isClosingRef.current = true to prevent the
onCloseRef.current() path from running during teardown, then call
dialog.close(), and finally ensure the cleanup only clears the browser top-layer
entry (leave existing onClose handler intact). Update the effect teardown that
references dialogRef to set isClosingRef.current before invoking dialog.close()
and keep references to dialogRef and onCloseRef unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5e4baa8-97da-4eac-971b-38b523a67f15

📥 Commits

Reviewing files that changed from the base of the PR and between 07b90d7 and 3304ff8.

📒 Files selected for processing (2)
  • packages/diagram/src/overlays/overlay/Overlay.tsx
  • packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx

Comment thread packages/diagram/src/overlays/overlay/Overlay.tsx
@ckeller42 ckeller42 force-pushed the fix/search-pointer-events branch from 5fb58d3 to 954a5a7 Compare April 11, 2026 11:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/tests/overview-page.spec.ts (1)

79-86: Align the regression setup with the reported repro path (Ctrl+K).

This test is solid, but on Line 81 it opens search via button click instead of the issue’s documented Ctrl+K path. Using the shortcut here would make the regression guard tighter for #2353.

Proposed tweak
-    // Open search
-    await page.getByRole('button', { name: /Search/ }).click()
+    // Open search via the reported repro path
+    await page.keyboard.press('Control+k')
     await expect(page.getByRole('textbox', { name: /Search by title/ })).toBeVisible()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/overview-page.spec.ts` around lines 79 - 86, The test "search
overlay closes after navigating to a view (`#2353`)" opens search via a button
click but should use the Ctrl+K shortcut to match the reported repro; replace
the call that clicks the Search button in the test with a keyboard shortcut
invocation (e.g., page.keyboard.press('Control+K')) before asserting the search
textbox visibility, keeping the subsequent navigation step (clicking the
Landscape result and asserting URL) unchanged so the regression is guarded by
the actual repro path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/tests/overview-page.spec.ts`:
- Around line 79-86: The test "search overlay closes after navigating to a view
(`#2353`)" opens search via a button click but should use the Ctrl+K shortcut to
match the reported repro; replace the call that clicks the Search button in the
test with a keyboard shortcut invocation (e.g.,
page.keyboard.press('Control+K')) before asserting the search textbox
visibility, keeping the subsequent navigation step (clicking the Landscape
result and asserting URL) unchanged so the regression is guarded by the actual
repro path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d97544d7-d0b6-4f90-b41d-3906f3989b5f

📥 Commits

Reviewing files that changed from the base of the PR and between 3304ff8 and 5fb58d3.

📒 Files selected for processing (3)
  • e2e/tests/overview-page.spec.ts
  • packages/diagram/src/overlays/overlay/Overlay.tsx
  • packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/diagram/src/overlays/overlay/Overlay.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/likec4/app/src/components/search/OverviewSearchAdapter.tsx

@ckeller42

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ckeller42 ckeller42 requested a review from davydkov April 11, 2026 12:04
@ckeller42 ckeller42 force-pushed the fix/search-pointer-events branch from 954a5a7 to bfb9945 Compare April 11, 2026 14:39
@davydkov

Copy link
Copy Markdown
Member

Direct DOM operations from React might lead to unexpected results.
I think, better if we check Motion docs for callbacks, maybe onExitComplete will be more sustainable.

Lets discuss on our call

@github-actions github-actions Bot temporarily deployed to docs-preview April 13, 2026 07:39 Destroyed
@github-actions

Copy link
Copy Markdown
Contributor

Replace direct DOM query (document.querySelectorAll) with Motion's
onExitComplete callback to properly close the search dialog after
exit animation. Uses a captured ref pattern to retain the dialog
element beyond React unmount.

Addresses review feedback from @davydkov.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ckeller42 ckeller42 force-pushed the fix/search-pointer-events branch from bfb9945 to 438c40d Compare April 14, 2026 09:54
@ckeller42

Copy link
Copy Markdown
Collaborator Author

Replaced querySelectorAll with onExitComplete as you suggested. Captured ref pattern keeps the dialog reference past unmount. All e2e tests pass.

@davydkov davydkov merged commit 36d5627 into main Apr 15, 2026
16 checks passed
@davydkov davydkov deleted the fix/search-pointer-events branch April 15, 2026 18:32
@likec4-ci likec4-ci Bot mentioned this pull request Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reload required after a search

2 participants