Skip to content

fix(calendar): keep the quick-create popover inside the window - #971

Merged
h4yfans merged 2 commits into
mainfrom
e2e-calendar-marquee-save-viewport
Aug 5, 2026
Merged

fix(calendar): keep the quick-create popover inside the window#971
h4yfans merged 2 commits into
mainfrom
e2e-calendar-marquee-save-viewport

Conversation

@h4yfans

@h4yfans h4yfans commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Desktop CI "Electron E2E full (3/16)" has been red on main with one failing test:

apps/desktop/tests/e2e/calendar-marquee.e2e.ts:100drag + Save-button click creates event (week view) — regression

TimeoutError: locator.click: Timeout 30000ms exceeded.
  - waiting for getByTestId('quick-create-popover').getByTestId('quick-create-save')
    - locator resolved to <button data-testid="quick-create-save" ...>Save</button>
    - element is visible, enabled and stable
    - scrolling into view if needed
    - element is outside of the viewport
    - retrying click action  (60+ times, always the same)

Not the old "submit button disables itself mid-click" defect — that fix (onPointerDown) is still in place and still works. This one is placement: the popover is rendered outside the window, so Save can never be clicked. That is a product bug, not a test bug: any user in that state cannot reach Save either.

Root cause

The week grid is an infinitely virtualized day strip. useWeekInfiniteScroll renders 36,525 day columns, so gridRef's element is roughly 4.5M px wide, and scrollLeft is set to todayIndex * columnWidth (~2.6M px). The strip's own getBoundingClientRect().x is therefore about -2,600,000 — far outside the window — while its .top is a normal viewport coordinate.

Two places trusted that rect:

  1. apps/desktop/src/renderer/src/components/calendar/use-time-grid-marquee.ts:117buildSelection fell back to gridRect.x whenever the day column element could not be measured, collapsing the marquee anchor onto the strip's off-screen left edge. anchorRect.y is derived from gridRect.top and stays correct, so only x goes wrong.
  2. apps/desktop/src/renderer/src/components/calendar/popover-position.ts:17computePopoverPosition clamped only the near edge (Math.max(8, ...)) and, in the "fits to the right" branch, nothing at all. It trusted the anchor to be on-screen, so an off-screen anchor produced an off-screen popover.

The CI failure screenshot confirms the split: the marquee overlay ("New Event 8:00 AM - 9:00") renders in the correct column at the correct y, and the popover is nowhere in the 1550x900 viewport. Correct y + wrong x is exactly the signature of the colRect?.x ?? gridRect?.x fallback.

Fix

  • use-time-grid-marquee.ts — offset the fallback anchor by the column: gridRect.x + columnIndex * columnWidth. Day view has a single column at index 0, so its anchor is identical to before.
  • popover-position.ts — clamp the popover into the viewport on the far edge as well as the near one, so no anchor can push its action row out of reach. This covers every caller: task, note, event, inbox-snooze and quick-create popovers.

Deliberately not done: no test.skip, no fixme, no extra retries, no larger timeout, no force: true. All of those would hide a real unreachable-Save bug from users.

Reproduction and evidence

The E2E does not reproduce on macOS — the failing geometry needs the column element to be momentarily unmeasurable, which only happened on the Linux runner. Verified 10/10 green locally at the exact CI viewport (1550x900, calendar view at x=256 w=974, popover left ~563), so the E2E alone could not prove or disprove anything. The defect is pure geometry, so it is reproduced deterministically at that level instead, using the real CI numbers.

Mutation check — fix reverted, tests go red:

× computePopoverPosition > keeps the popover inside the window when the anchor is far off-screen left
  → expected -2591581 to be greater than or equal to 8
× computePopoverPosition > keeps the popover inside the window when the anchor is far off-screen right
  → expected 2591706 to be less than or equal to 1550
× useTimeGridMarquee > anchors on the column offset when the column element cannot be measured
  → expected { x: -2583446, ... } to deeply equal ObjectContaining{...}
Tests  3 failed | 23 passed (26)

Fix restored:

Test Files  2 passed (2)
     Tests  26 passed (26)

Whole calendar unit suite: Test Files 38 passed (38) Tests 282 passed (282)

Marquee E2E, 3 consecutive runs of the full file (18/18):

✓  3 calendar-marquee.e2e.ts:100:7 › drag + Save-button click creates event (week view) — regression (5.3s)
✓  9 calendar-marquee.e2e.ts:100:7 › drag + Save-button click creates event (week view) — regression (5.4s)
✓ 15 calendar-marquee.e2e.ts:100:7 › drag + Save-button click creates event (week view) — regression (5.3s)
18 passed (1.6m)

pnpm typecheck 16/16 successful · pnpm lint 0 errors (10 pre-existing warnings, none in touched files) · pnpm docs:impact --strict covered · pnpm docs:build complete.

Notes

  • No IPC, schema, sync-protocol or file-format change — renderer geometry only, so nothing to migrate.
  • No RTL concern: the change is inline top/left on a portaled fixed element, no physical Tailwind classes added.

h4yfans added 2 commits August 5, 2026 21:45
The week grid is an infinitely virtualized day strip: its own element is
~4.5M px wide and, once scrolled to today, its left edge sits ~2.6M px
outside the window. Two places trusted that rect.

buildSelection fell back to the strip's left edge whenever the day column
element could not be measured, so the marquee anchor landed millions of
pixels off-viewport while its y stayed correct. computePopoverPosition
then trusted that anchor with no clamp on the far edge, parking the
quick-create popover outside the window — visible, enabled, and
permanently unclickable.

Offset the fallback anchor by the column index (day view has a single
column at index 0, so it is unchanged) and clamp the popover into the
viewport so no anchor can push its Save row out of reach. The clamp
covers every caller: task, note, event, inbox-snooze and quick-create.
Copilot AI lite review requested due to automatic review settings August 5, 2026 18:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation test labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 8c0f8d7.

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@h4yfans
h4yfans marked this pull request as ready for review August 5, 2026 19:08
@h4yfans
h4yfans merged commit d33f7ce into main Aug 5, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants