Skip to content

Fix radio component active underline positioning on resize#2805

Merged
Crabcyborg merged 2 commits into
masterfrom
optimize/replace-resizeobserver-with-window-resize
Jan 22, 2026
Merged

Fix radio component active underline positioning on resize#2805
Crabcyborg merged 2 commits into
masterfrom
optimize/replace-resizeobserver-with-window-resize

Conversation

@shervElmi
Copy link
Copy Markdown
Contributor

@shervElmi shervElmi commented Jan 15, 2026

Fixed radio component active underline tracker positioning when window is resized.

Before

CleanShot 2026-01-15 at 18 14 07

After

CleanShot 2026-01-15 at 18 19 23

Summary by CodeRabbit

  • New Features
    • Improved radio button UI responsiveness during window resize events.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

The changes add resize event handling with throttling to radio UI components. A new resizeTimeout property and throttledUpdateAllTrackers method are introduced, which uses requestAnimationFrame to batch expensive UI updates when the window resizes, ensuring radio trackers update only when wrappers are visible.

Changes

Cohort / File(s) Summary
Radio Component Resize Handling
js/formidable-settings-components.js, js/src/settings-components/components/radio-component.js
Adds defensive resize event handling with throttled updates. Introduces resizeTimeout property for tracking pending updates and throttledUpdateAllTrackers() method that debounces UI recalculations using requestAnimationFrame. Updates radio trackers when window resizes and wrapper elements are visible.

Sequence Diagram

sequenceDiagram
    participant Window
    participant Listener as Event Listener
    participant Component as Radio Component
    participant RAF as requestAnimationFrame
    participant DOM

    Window->>Listener: resize event
    Listener->>Component: throttledUpdateAllTrackers()
    
    alt Guard Check: resizeTimeout exists
        Component->>Component: return (skip)
    else Guard Check: resizeTimeout null
        Component->>RAF: schedule callback
        RAF->>Component: invoke on frame
        
        loop for each checked radio
            Component->>DOM: get wrapper element
            Component->>DOM: check if visible (width > 0)
            alt wrapper visible
                Component->>Component: call onRadioChange()
            end
        end
        
        Component->>Component: clear resizeTimeout
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

action: needs qa

Suggested reviewers

  • Crabcyborg

Poem

🐰 When windows resize, our radios dance,
With throttled timing and responsive stance,
requestAnimationFrame keeps updates at bay,
No excessive recalculations to slow down the day! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding throttled resize handling to fix radio component active underline positioning during window resizes.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@shervElmi shervElmi requested a review from Crabcyborg January 15, 2026 15:20
@shervElmi shervElmi marked this pull request as ready for review January 15, 2026 15:20
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 `@js/src/settings-components/components/radio-component.js`:
- Around line 155-173: The resize handler throttledUpdateAllTrackers() currently
queries '.frm-radio-component input[type="radio"]:checked' which is inconsistent
with initialization and can reach elements not initialized; update the selector
to use the same scope as initialization (e.g.
'.frm-style-component.frm-radio-component input[type="radio"]:checked') and
ensure the wrapper lookup in onRadioChange() uses or expects the
'.frm-style-component.frm-radio-component' wrapper so only initialized radio
components are processed.
🧹 Nitpick comments (1)
js/src/settings-components/components/radio-component.js (1)

42-45: Resize listener is not cleaned up on page unload.

The beforeunload handler at line 41 cleans up observers but not the resize event listener. While this is typically fine since the page is unloading anyway, for consistency and if this component is ever dynamically destroyed/recreated, consider storing and removing the resize listener.

Additionally, the resize listener closure captures this via arrow function, which is correct.

♻️ Optional: Store and cleanup resize listener
 		// Cleanup observers when page unloads to prevent memory leaks
-		window.addEventListener( 'beforeunload', () => this.cleanupObservers() );
+		window.addEventListener( 'beforeunload', () => this.cleanup() );

 		// Handle window resize with throttling
 		this.resizeTimeout = null;
-		window.addEventListener( 'resize', () => this.throttledUpdateAllTrackers() );
+		this.boundResizeHandler = () => this.throttledUpdateAllTrackers();
+		window.addEventListener( 'resize', this.boundResizeHandler );

Then update cleanup:

-	cleanupObservers() {
+	cleanup() {
+		if ( this.boundResizeHandler ) {
+			window.removeEventListener( 'resize', this.boundResizeHandler );
+		}
+		if ( this.resizeTimeout ) {
+			cancelAnimationFrame( this.resizeTimeout );
+			this.resizeTimeout = null;
+		}
 		this.observers.forEach( observer => {
 			observer.disconnect();
 		} );

 		this.observers.clear();
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4997cb7 and 55af94e.

📒 Files selected for processing (2)
  • js/formidable-settings-components.js
  • js/src/settings-components/components/radio-component.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

**/*.js: - Do not make suggestions when we use x.x for a version placeholder. This is used like "@SInCE x.x" in a JavaScript doc comment.

  • Treat these placeholders as acceptable in this codebase.

Files:

  • js/formidable-settings-components.js
  • js/src/settings-components/components/radio-component.js
🧬 Code graph analysis (1)
js/formidable-settings-components.js (4)
js/admin/style.js (5)
  • frmDom (23-23)
  • frmDom (24-24)
  • frmDom (25-25)
  • frmDom (26-26)
  • frmDom (1162-1162)
js/src/admin/addon-state.js (2)
  • frmDom (3-3)
  • a (198-198)
js/src/admin/upgrade-popup.js (1)
  • frmDom (3-3)
js/bootstrap.min.js (4)
  • h (6-6)
  • O (6-6)
  • V (6-6)
  • D (6-6)
🪛 Biome (2.1.2)
js/formidable-settings-components.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cypress
🔇 Additional comments (1)
js/formidable-settings-components.js (1)

1-1: Autogenerated minified file — static analysis warnings are false positives.

This is a bundled/minified output file. The Biome static analysis errors (variable used before declaration, function reassignment) are false positives caused by minification transformations that reorder and transform code in ways static analyzers cannot interpret correctly.

The minified code correctly includes the new resize handling logic (resizeTimeout, throttledUpdateAllTrackers, and the window resize listener) matching the source file changes.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread js/src/settings-components/components/radio-component.js
Copy link
Copy Markdown
Contributor

@Crabcyborg Crabcyborg left a comment

Choose a reason for hiding this comment

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

Thanks @shervElmi!

This looks good to me.

🚀

@Crabcyborg Crabcyborg added this to the 6.28 milestone Jan 22, 2026
@Crabcyborg Crabcyborg merged commit bc3f841 into master Jan 22, 2026
37 of 38 checks passed
@Crabcyborg Crabcyborg deleted the optimize/replace-resizeobserver-with-window-resize branch January 22, 2026 19:38
@Crabcyborg
Copy link
Copy Markdown
Contributor

@shervElmi Do we need a similar update in Visual Views? The tabs still seem off there.

@shervElmi
Copy link
Copy Markdown
Contributor Author

shervElmi commented Jan 22, 2026

@Crabcyborg, the main PR for fixing the tab issues is “Fix tab active underline positioning on initial load and resize #2798”.

@shervElmi
Copy link
Copy Markdown
Contributor Author

@Crabcyborg, just FYI, this PR actually fixes the radio component. The tab component is implemented differently, and radios and tabs are entirely separate components.

stephywells pushed a commit that referenced this pull request Apr 4, 2026
…ver-with-window-resize

Fix radio component active underline positioning on resize
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants