Skip to content

fix: replace outer <button> with <div> in TimelineTrackContent to res…#705

Open
kehuaWangfff wants to merge 1 commit intoOpenCut-app:mainfrom
kehuaWangfff:bugfix/nested-button-hydration-error
Open

fix: replace outer <button> with <div> in TimelineTrackContent to res…#705
kehuaWangfff wants to merge 1 commit intoOpenCut-app:mainfrom
kehuaWangfff:bugfix/nested-button-hydration-error

Conversation

@kehuaWangfff
Copy link

@kehuaWangfff kehuaWangfff commented Feb 13, 2026

…olve nested button hydration error

TimelineTrackContent wrapped its entire content in a , while each timeline element (ElementInner) also rendered its own for click handling. This created an invalid HTML structure where was nested inside , triggering a React hydration error:

"In HTML,

cannot be a descendant of ."

The outer

in TimelineTrackContent only handled click-to-deselect and mousedown events — it did not need button semantics. Replacing it with a
preserves the exact same event handling behavior while eliminating the invalid nesting.

⚠️ READ BEFORE SUBMITTING ⚠️

We are not currently accepting PRs except for critical bugs.

If this is a bug fix:

  • I've opened an issue first
  • This was approved by a maintainer

If this is a feature:

This PR will be closed. Please open an issue to discuss first.

Summary by CodeRabbit

  • Refactor
    • Updated internal component structure for improved maintainability.

Note: This release contains primarily internal improvements with no user-facing changes to functionality or interface.

…olve nested button hydration error

TimelineTrackContent wrapped its entire content in a <button>, while each
timeline element (ElementInner) also rendered its own <button> for click
handling. This created an invalid HTML structure where <button> was nested
inside <button>, triggering a React hydration error:

  "In HTML, <button> cannot be a descendant of <button>."

The outer <button> in TimelineTrackContent only handled click-to-deselect
and mousedown events — it did not need button semantics. Replacing it with
a <div> preserves the exact same event handling behavior while eliminating
the invalid nesting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 13, 2026

Someone is attempting to deploy a commit to the OpenCut OSS Team on Vercel.

A member of the Team first needs to authorize it.

@netlify
Copy link

netlify bot commented Feb 13, 2026

👷 Deploy request for appcut pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 704b418

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

The root element of TimelineTrackContent in the timeline track component was changed from a <button> to a <div> element, with the type="button" attribute removed. Event handlers remain attached to the root element without functional changes.

Changes

Cohort / File(s) Summary
Timeline Track Element Update
apps/web/src/components/editor/panels/timeline/timeline-track.tsx
Root element changed from <button> to <div> with type="button" attribute removed. Event handlers (onClick, onMouseDown) preserved; no control flow alterations.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Poem

A button becomes a canvas so wide, 🎨
More flexible now, with nowhere to hide!
Events still flow like water so pure, 🐇
The structure's evolved, of that we're sure. ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: replacing an outer button element with a div to resolve a nested button hydration error.
Description check ✅ Passed The description explains the bug (nested button HTML structure causing hydration error), the fix (replacing button with div), and preserves required template sections, though the checklist items are unchecked.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
apps/web/src/components/editor/panels/timeline/timeline-track.tsx (1)

66-77: Add keyboard event handling and a role to the interactive <div>.

The fix for the nested <button> hydration error is correct, but the replacement <div> with onClick/onMouseDown handlers creates two accessibility violations flagged by Biome and the project's coding guidelines:

  1. A non-interactive element (<div>) has mouse event handlers without a corresponding keyboard event (onKeyUp, onKeyDown, or onKeyPress).
  2. A static element with a click handler lacks an appropriate role attribute.

Since this element's purpose is to deselect elements when clicking the empty track area, adding role="toolbar" (semantically fitting for a track container) with tabIndex={0} and an onKeyDown handler for keyboard-driven deselection would address both issues.

♻️ Suggested fix
 		<div
+			role="toolbar"
+			tabIndex={0}
 			className="size-full"
 			onClick={(event) => {
 				if (shouldIgnoreClick?.()) return;
 				clearElementSelection();
 				onTrackClick?.(event);
 			}}
 			onMouseDown={(event) => {
 				event.preventDefault();
 				onTrackMouseDown?.(event);
 			}}
+			onKeyDown={(event) => {
+				if (event.key === "Escape") {
+					clearElementSelection();
+				}
+			}}
 		>

As per coding guidelines: "Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress" and "Make static elements with click handlers use a valid role attribute".

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

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.

1 participant