Skip to content

fix: editing and saving share dialog triggers hover state#8708

Merged
jaycnz merged 17 commits into
mainfrom
jaychen/nes-209-editing-and-saving-share-button-triggers-hover-state
Feb 12, 2026
Merged

fix: editing and saving share dialog triggers hover state#8708
jaycnz merged 17 commits into
mainfrom
jaychen/nes-209-editing-and-saving-share-button-triggers-hover-state

Conversation

@jaycnz
Copy link
Copy Markdown
Contributor

@jaycnz jaycnz commented Feb 10, 2026

Summary by CodeRabbit

  • New Features
    • Components can now optionally notify parents when share/copy/other dialogs open or close to coordinate UI state.
  • Bug Fixes
    • Hover overlay no longer flickers or changes while a dialog is open.
    • Menu and dialog interactions consistently respect hover/overlay state when dialogs are active.
  • Tests
    • Added tests verifying dialog open/close callbacks and hover behavior while dialogs are open.

@linear
Copy link
Copy Markdown

linear Bot commented Feb 10, 2026

@jaycnz jaycnz requested review from edmonday and jianwei1 February 10, 2026 03:08
@jaycnz jaycnz self-assigned this Feb 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

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

Threads an optional setHasOpenDialog?: (hasOpenDialog: boolean) => void callback through JourneyCard menu and menu-item components (ShareItem, CopyToTeamMenuItem, TrashMenu, DefaultMenu) and adds local dialog state in JourneyCard to prevent hover overlay updates while dialogs are open; tests added/updated to assert signaling and hover gating.

Changes

Cohort / File(s) Summary
Share & toolbar plumbing
apps/journeys-admin/src/components/Editor/Toolbar/Items/ShareItem/ShareItem.tsx, apps/journeys-admin/src/components/Editor/Toolbar/Items/ShareItem/ShareItem.spec.tsx
Added setHasOpenDialog?: (hasOpenDialog: boolean) => void to ShareItem props; call with true when opening and false on dialog close. Added test asserting both calls.
Menu plumbing: JourneyCardMenu → DefaultMenu → items
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.tsx, apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DefaultMenu/DefaultMenu.tsx, apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.spec.tsx
Propagated setHasOpenDialog through JourneyCardMenu and DefaultMenu, invoked setHasOpenDialog?.(false) on various dialog closes, and added tests asserting setHasOpenDialog(true/false) for open/close flows.
JourneyCard hover gating & tests
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.tsx, apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.spec.tsx
Added local hasOpenDialog state and gated hover handlers via updateHoverState so overlay changes are suppressed while a dialog is open; added data-testid="JourneyCardOverlayBox" and test to verify hover does not change when dialog reported open.
Trash menu plumbing & tests
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TrashMenu/TrashMenu.tsx, apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TrashMenu/TrashMenu.spec.tsx
Added optional setHasOpenDialog prop and call setHasOpenDialog?.(true) when opening Restore/Delete dialogs; tests updated to assert the callback is invoked.
Copy-to-team plumbing & tests
apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx, apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.spec.tsx
Added optional setHasOpenDialog prop; call setHasOpenDialog(true) when opening Copy dialog and setHasOpenDialog(false) on close; tests added to assert behavior (duplicate test insertion noted).
Minor rename in watch app
apps/watch/src/components/ContentHeader/ContentHeader.tsx
Renamed local flag from isDialogOpen to hasOpenDialog and updated related handlers and prop rename usage; no API changes.

Sequence Diagram(s)

sequenceDiagram
    participant JC as JourneyCard
    participant JCM as JourneyCardMenu
    participant DM as DefaultMenu
    participant SI as ShareItem
    participant UI as OverlayHover

    JC->>JCM: render (pass setHasOpenDialog)
    JCM->>DM: render (propagate setHasOpenDialog)
    DM->>SI: render (propagate setHasOpenDialog)
    SI->>SI: user clicks Share -> open dialog
    SI->>DM: setHasOpenDialog?.(true)
    DM->>JCM: setHasOpenDialog?.(true)
    JCM->>JC: setHasOpenDialog?.(true)
    JC->>JC: hasOpenDialog = true
    UI->>JC: mouse enter
    JC-->>UI: ignore overlay change (hasOpenDialog prevents hover)
    SI->>SI: user closes share dialog
    SI->>DM: setHasOpenDialog?.(false)
    DM->>JCM: setHasOpenDialog?.(false)
    JCM->>JC: setHasOpenDialog?.(false)
    JC->>JC: hasOpenDialog = false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jianwei1
  • edmonday
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: fixing an issue where the share dialog interaction leaves the card in a hover state.
Linked Issues check ✅ Passed The code changes directly address NES-209 by introducing dialog state tracking to prevent persistent hover state after share dialog interactions.
Out of Scope Changes check ✅ Passed All changes are focused on dialog state tracking and hover state management, directly related to fixing the NES-209 issue. The ContentHeader.tsx refactoring is a related state naming consistency improvement.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jaychen/nes-209-editing-and-saving-share-button-triggers-hover-state

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.

@jaycnz jaycnz added type: fix Iterations on existing features or infrastructure. Bug Interns labels Feb 10, 2026
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Feb 10, 2026

View your CI Pipeline Execution ↗ for commit f29ac12

Command Status Duration Result
nx run watch-e2e:e2e ✅ Succeeded 25s View ↗
nx run journeys-admin-e2e:e2e ✅ Succeeded 31s View ↗
nx run-many --target=vercel-alias --projects=jo... ✅ Succeeded 2s View ↗
nx run-many --target=vercel-alias --projects=watch ✅ Succeeded 2s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 12s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 7s View ↗
nx run-many --target=deploy --projects=watch ✅ Succeeded 52s View ↗
nx run-many --target=deploy --projects=journeys... ✅ Succeeded 45s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-12 22:53:38 UTC

@github-actions github-actions Bot requested a deployment to Preview - journeys-admin February 10, 2026 03:11 Pending
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 10, 2026 03:15 Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/journeys-admin/src/components/Editor/Toolbar/Items/ShareItem/ShareItem.tsx`:
- Around line 104-106: The effect that sets setIsDialogOpen currently only
considers anchorEl, causing parent to be told dialogs are closed when
sub-dialogs (showSlugDialog, showEmbedDialog, showQrCodeDialog) remain open;
update the useEffect that calls setIsDialogOpen to compute open state as
Boolean(anchorEl) || showSlugDialog || showEmbedDialog || showQrCodeDialog and
include all those symbols in the dependency array so the parent correctly tracks
any visible dialog.
🧹 Nitpick comments (2)
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.spec.tsx (2)

255-257: waitFor wrapping fireEvent is a misuse of the API.

waitFor is designed for polling assertions until they pass, not for firing events. Since fireEvent.click (line 253) synchronously flushes React state in RTL with React 18's act wrapper, setIsDialogOpen(true) is already applied by the time you reach line 255. Just call fireEvent.mouseLeave directly.

♻️ Suggested fix
     fireEvent.click(screen.getByTestId('open-dialog'))
 
-    await waitFor(() => {
-      fireEvent.mouseLeave(card)
-    })
+    fireEvent.mouseLeave(card)
 
     fireEvent.mouseEnter(card)
 
     expect(overlay).toHaveStyle({ opacity: '0' })

With this change the test no longer needs to be async (unless you add async assertions elsewhere).


231-262: Consider adding coverage for the dialog-close path.

The test verifies that hover is suppressed while the dialog is open, but doesn't verify that hover behavior resumes after setIsDialogOpen(false). Without this, a bug that permanently locks hover suppression would go undetected. Adding a second mock button (or toggling the existing one) to call setIsDialogOpen(false) and asserting that mouseEnter restores opacity: 1 would complete the cycle.

Comment thread apps/journeys-admin/src/components/Editor/Toolbar/Items/ShareItem/ShareItem.tsx Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 10, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys-admin ✅ Ready journeys-admin preview Fri Feb 13 11:50:12 NZDT 2026

@github-actions github-actions Bot requested a deployment to Preview - journeys-admin February 11, 2026 02:34 Pending
@github-actions github-actions Bot requested a deployment to Preview - journeys-admin February 11, 2026 02:39 Pending
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 11, 2026 02:42 Inactive
@github-actions github-actions Bot requested a deployment to Preview - journeys-admin February 12, 2026 01:33 Pending
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 12, 2026 01:36 Inactive
@jaycnz jaycnz requested a review from csiyang February 12, 2026 01:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx (1)

119-119: ⚠️ Potential issue | 🔴 Critical

setIsDialogOpen?.(false) is missing on all non-cancel close paths — hover state will stay stuck.

The onClose handler (Cancel button) correctly calls setIsDialogOpen?.(false), but every other path that closes the dialog (successful copy, successful translation, translation error, duplication error) only calls setDuplicateTeamDialogOpen(false) without notifying the parent. This means the card's hover overlay will remain visible after a successful copy or any error — the exact bug this PR aims to fix.

Add setIsDialogOpen?.(false) before each setDuplicateTeamDialogOpen(false):

Proposed fix
@@ line 118-119
       handleCloseMenu() // Close menu when translation completes
+      setIsDialogOpen?.(false)
       setDuplicateTeamDialogOpen(false) // Close dialog when translation completes

@@ line 129-130
       handleCloseMenu() // Close menu on translation error
+      setIsDialogOpen?.(false)
       setDuplicateTeamDialogOpen(false) // Close dialog on translation error

@@ line 165-166
          handleCloseMenu()
+          setIsDialogOpen?.(false)
           setDuplicateTeamDialogOpen(false)

@@ line 196-197
       handleCloseMenu()
+      setIsDialogOpen?.(false)
       setDuplicateTeamDialogOpen(false)

Also applies to: 130-130, 166-166, 197-197

🤖 Fix all issues with AI agents
In
`@apps/journeys-admin/src/components/Editor/Toolbar/Items/ShareItem/ShareItem.spec.tsx`:
- Around line 440-474: The test opens the ShareItem dialog but doesn't set up
mockedUseRouter like the sibling tests, so router.push and router.events.on may
be undefined; before rendering in the 'should call setIsDialogOpen when opening
and closing dialog' test, call mockedUseRouter.mockReturnValue(...) to return an
object containing query: { tab: 'active' }, a push jest.fn(), and events: { on:
jest.fn() } so the ShareItem component's router usage (router.push,
router.events.on) is present during dialog open/close; update the test setup
near the existing setIsDialogOpen mock and before render to mirror other tests
that configure mockedUseRouter.

In
`@apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.spec.tsx`:
- Around line 889-922: The test is clicking the menu before the teams query
resolves and misses verifying success/error close behavior; update the test to
await getLastActiveTeamIdAndTeamsMock.result (use waitFor or await that promise)
before firing the menu click, and add two new assertions/tests that simulate a
successful duplication and a failing duplication to assert
setIsDialogOpen(false) is called in both cases. Also update the
CopyToTeamMenuItem component's duplication mutation handlers (the
mutation/method that performs the duplicate action) to call
setIsDialogOpen?.(false) in both the onCompleted and onError paths so the dialog
closes on success or failure. Ensure references:
getLastActiveTeamIdAndTeamsMock, setIsDialogOpen, and CopyToTeamMenuItem (and
the duplicate mutation handler) are updated accordingly.
🧹 Nitpick comments (3)
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.tsx (2)

43-50: Pre-existing: Webpack chunk name mismatch.

Line 46's chunk name comment says "RestoreJourneyDialog" but the import resolves TrashJourneyDialog. This won't cause a runtime bug but may confuse bundle analysis.

 const TrashJourneyDialog = dynamic(
   async () =>
     await import(
-      /* webpackChunkName: "RestoreJourneyDialog" */
+      /* webpackChunkName: "TrashJourneyDialog" */
       './TrashJourneyDialog'
     ).then((mod) => mod.TrashJourneyDialog),
   { ssr: false }
 )

83-99: JSDoc is missing the new setIsDialogOpen prop.

The docblock documents all other props but omits the newly added setIsDialogOpen. Consider adding a line for completeness.

apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.spec.tsx (1)

315-457: Refactor query method for consistency with existing patterns.

The new tests properly verify both the true call on open and false call on close for Edit Details, Access, Translate, and Trash dialogs. Two observations:

  1. Inconsistent query usage: Lines 380 and 452 use screen.getByTestId(...) while the existing access/trash dialog tests at lines 167–227 destructure getByTestId from render(). For consistency, add getByTestId to the destructured queries in the new test cases.

  2. Share dialog coverage: The Share dialog is tested separately in ShareItem.spec.tsx (line 440), which includes setIsDialogOpen verification. This is acceptable and provides isolated coverage of the Share flow.

Comment thread apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.tsx Outdated
Comment thread apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.tsx Outdated
Comment thread apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.spec.tsx Outdated
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 12, 2026 03:31 Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 12, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
watch ✅ Ready watch preview Fri Feb 13 11:50:11 NZDT 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.tsx`:
- Around line 92-96: The hover state isn't reset when a dialog closes because
updateHoverState blocks changes while hasOpenDialog is true; add logic to reset
isCardHovered when hasOpenDialog transitions to false — e.g., add a useEffect
that watches hasOpenDialog and calls setIsCardHovered(false) when hasOpenDialog
becomes false so the overlay clears immediately; locate updateHoverState,
isCardHovered, setIsCardHovered and hasOpenDialog in JourneyCard and implement
the effect to clear hover on dialog close.

In
`@apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.spec.tsx`:
- Around line 315-457: Add a new test in JourneyCardMenu.spec.tsx that mirrors
the other dialog tests to assert Share dialog signals via setHasOpenDialog:
render JourneyCardMenu with a jest.fn() setHasOpenDialog, open the menu
(fireEvent.click(getByRole('button'))), find and click the 'Share' menu item
(await findByRole('menuitem', { name: 'Share' }) or the label used by
ShareItem), await expect(setHasOpenDialog).toHaveBeenCalledWith(true) and then
close the dialog (use screen.getByTestId('dialog-close-button') or the dialog's
Cancel button) and await expect(setHasOpenDialog).toHaveBeenCalledWith(false)
and total call count of 2; reference JourneyCardMenu, ShareItem, and
setHasOpenDialog to locate where to add the test.
🧹 Nitpick comments (2)
apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.spec.tsx (1)

892-910: Inconsistent provider nesting order.

Every other test in this file wraps providers as MockedProvider > SnackbarProvider > TeamProvider, but this test uses SnackbarProvider > MockedProvider > TeamProvider. Keep the order consistent to avoid subtle behavioral differences.

Proposed fix
     const { getByRole } = render(
-      <SnackbarProvider>
-        <MockedProvider
-          mocks={[
-            updateLastActiveTeamIdMock,
-            mockLanguage,
-            getLastActiveTeamIdAndTeamsMock
-          ]}
-        >
+      <MockedProvider
+        mocks={[
+          updateLastActiveTeamIdMock,
+          mockLanguage,
+          getLastActiveTeamIdAndTeamsMock
+        ]}
+      >
+        <SnackbarProvider>
           <TeamProvider>
             <CopyToTeamMenuItem
               id="journeyId"
               handleCloseMenu={handleCloseMenu}
               setHasOpenDialog={setHasOpenDialog}
             />
           </TeamProvider>
-        </MockedProvider>
-      </SnackbarProvider>
+        </SnackbarProvider>
+      </MockedProvider>
     )
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/JourneyCardMenu.spec.tsx (1)

380-380: Inconsistent query source: screen vs destructured render result.

These two lines use screen.getByTestId(...) while the rest of each test uses destructured queries (getByRole, findByRole) from render(). Pick one approach per test for consistency.

Suggested fix
-    const closeButton = screen.getByTestId('dialog-close-button')
+    const closeButton = getByTestId('dialog-close-button')

You'd need to add getByTestId to the destructured render result for the affected tests (lines 355 and 427).

Also applies to: 452-452

Comment thread apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCard.spec.tsx Outdated
@JesusFilm JesusFilm deleted a comment from coderabbitai Bot Feb 12, 2026
@JesusFilm JesusFilm deleted a comment from coderabbitai Bot Feb 12, 2026
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 12, 2026 22:29 Inactive
@jaycnz jaycnz requested a review from csiyang February 12, 2026 22:34
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin February 12, 2026 22:47 Inactive
@jaycnz jaycnz added this pull request to the merge queue Feb 12, 2026
Merged via the queue into main with commit 7b2f335 Feb 12, 2026
22 checks passed
@jaycnz jaycnz deleted the jaychen/nes-209-editing-and-saving-share-button-triggers-hover-state branch February 12, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Interns type: fix Iterations on existing features or infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants