Skip to content

Conversation

@haslinghuis
Copy link
Member

@haslinghuis haslinghuis commented Sep 9, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Dropdowns reliably place the option matching the provided text at the top while preserving selections and disabled states, improving consistency during page load and interactions.
  • Refactor
    • Internal restructuring of dropdown sorting logic to improve stability and predictable behavior across browsers.

@haslinghuis haslinghuis added this to the 2025.12 milestone Sep 9, 2025
@haslinghuis haslinghuis self-assigned this Sep 9, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The sortSelect jQuery plugin in src/js/utils/common.js was refactored from mutating a subset of DOM option elements to reconstructing each select element's options per-element: it now builds an optionData array (value, text, selected, disabled), sorts it (prioritizing a matching text), clears the select, and rebuilds options preserving states. No public API changes.

Changes

Cohort / File(s) Summary of changes
Refactor sortSelect implementation
src/js/utils/common.js
Rewrote the plugin to process each <select> element independently: collect option metadata into an optionData array, sort that array (matching text first, then locale-aware text order), clear existing options, and recreate option elements preserving selected and disabled states. Removed in-place partial DOM manipulation in favor of explicit per-element reconstruction. No signature changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as Ports Tab UI
  participant Old as sortSelect (Before)
  participant New as sortSelect (Now)

  rect rgba(200,230,255,0.3)
  UI->>Old: Invoke $(select).sortSelect()
  Old->>Old: Collect subset of option elements\nSort subset by text (match-first)
  Old->>Old: Reinsert sorted subset into DOM (partial mutation)
  Old-->>UI: Return jQuery collection
  end

  rect rgba(200,255,200,0.3)
  UI->>New: Invoke $(select).sortSelect()
  New->>New: For each select: build optionData[] with\n(value, text, selected, disabled)
  New->>New: Sort optionData (match-first, localeCompare)
  New->>New: Clear all options and recreate from optionData\n(preserve selected/disabled)
  New-->>UI: Return original jQuery collection
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks (2 passed, 3 warnings)

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The changes only refactor the sortSelect plugin implementation without applying the early-return workaround needed to bypass the problematic sorting logic in Chromium v140+, so the ports tab compatibility issue described in issue #4595 remains unaddressed. Please update the pull request to include the necessary early-return condition in $.fn.sortSelect to disable the sorting logic for Chromium v140+, thereby directly fixing the ports tab issue, and defer any internal refactor to a separate change.
Out of Scope Changes Check ⚠️ Warning The pull request includes a complete refactor of the sortSelect plugin implementation—even replacing the option mutation logic with a full per-element reconstruction—which goes beyond the targeted early-return workaround needed to restore ports tab functionality in Chromium v140 as described in issue #4595. Please remove the extensive refactoring and isolate only the early-return workaround for sortSelect in this pull request, deferring the broader plugin reconstruction to a separate change set or issue.
Description Check ⚠️ Warning The current description only contains a single bullet referencing the issue fixed and lacks nearly all required sections from the repository’s pull request template, including the important considerations header, detailed change summary, testing instructions, CI status, and removal of template text. Please expand the description to follow the repository’s pull request template by removing the template instructions, adding the “## Important considerations” section with relevant details, summarizing the change, listing impacted files, confirming CI/test status, and providing any necessary context for reviewers.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly describes the primary change addressing the Chromium v140 select issue by providing a quick fix for the broken select behavior in that environment, making it clear and specific for reviewers scanning the repository history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

This comment was marked as outdated.

@nerdCopter
Copy link
Member

works, just needs @coderabbitai full review

Copy link
Member

@nerdCopter nerdCopter left a comment

Choose a reason for hiding this comment

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

  • approving, pending copilot/coderabbitAI feedbacks

@coderabbitai

This comment was marked as outdated.

Copy link
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: 0

♻️ Duplicate comments (1)
src/js/utils/common.js (1)

96-101: Tighten the comment (grammar + reference)

Prefer a precise note and reference the issue. If you keep the comment, tweak grammar and add context.

-        Chrome 140 does not work when we use this function on select element directly
-
-        Temporary disable sorting for now
+        Chrome/Edge (Chromium 140+) misbehaves when sorting <option> nodes directly on a <select>.
+        Temporarily disable sorting. See #4595.
🧹 Nitpick comments (1)
src/js/utils/common.js (1)

94-120: Gate the workaround to Chromium ≥140 and avoid block-commented code; also keep behavior docs accurate

Blanket-disabling sorting affects all browsers. Scope it to affected Chromium builds and keep the original path active elsewhere. Also replace the block comment with code + a crisp inline note. Diff below keeps chainability and the “pin text first” behavior.

 $.fn.sortSelect = function (text = "") {
-
-    /* 
-    
-        Chrome 140 does not work when we use this function on select element directly
-
-        Temporary disable sorting for now
-
-    const op = this.children("option");
-
-    op.sort((a, b) => {
-        if (a.text === text) {
-            return -1;
-        }
-        if (b.text === text) {
-            return 1;
-        }
-        return a.text.localeCompare(b.text, window.navigator.language, { ignorePunctuation: true });
-    });
-
-    return this.empty().append(op);
-
-    */
-
-    return this;
+    // Workaround for Chromium 140+ (#4595): disable sorting only on affected browsers.
+    const isAffectedChromium = (() => {
+        const m = navigator.userAgent.match(/\b(?:Chrom(?:e|ium)|Edg)\/(\d+)/);
+        return !!m && parseInt(m[1], 10) >= 140;
+    })();
+    if (isAffectedChromium) return this; // preserve chainability when disabled
+
+    const op = this.children("option").get();
+    op.sort((a, b) => {
+        if (a.text === text) return -1;
+        if (b.text === text) return 1;
+        return a.text.localeCompare(b.text, window.navigator.language, { ignorePunctuation: true });
+    });
+    this.empty().append(op);
+    return this;
 };

Outside this hunk, please align the JSDoc above to reflect the temporary Chromium 140+ disable:

/**
 * Returns a jQuery collection of options sorted alphabetically, with an optional `text` pinned on top.
 * Note: Sorting is temporarily disabled on Chromium 140+ due to a browser issue (see #4595).
 */

Optionally, consider a feature-detection fallback instead of UA sniffing (try sorting options in a detached and verify the resulting order) if you want to future-proof the guard. 📜 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 4a5b646 and 95a72db. 📒 Files selected for processing (1) src/js/utils/common.js (2 hunks) 🔇 Additional comments (1) src/js/utils/common.js (1) 118-119: Good: chainability preserved Returning this avoids breaking jQuery chains when the behavior is disabled.

@haslinghuis haslinghuis linked an issue Sep 9, 2025 that may be closed by this pull request
@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 9, 2025

@github-actions
Copy link
Contributor

github-actions bot commented Sep 9, 2025

Copy link
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

🧹 Nitpick comments (2)
src/js/utils/common.js (2)

105-111: If we keep sorting later: pin by value as well as text.

Many call sites pass a value, not the rendered label. Matching only by text risks pinning the wrong option in localized UIs.

-        optionData.sort((a, b) => {
-            if (a.text === text) { return -1; }
-            if (b.text === text) { return 1; }
+        optionData.sort((a, b) => {
+            if (a.text === text || a.value === text) { return -1; }
+            if (b.text === text || b.value === text) { return 1; }
             return a.text.localeCompare(b.text, window.navigator.language, { ignorePunctuation: true });
         });

112-123: Avoid O(n²) removal and reduce reflow if/when sorting returns.

The while-remove(0) loop is quadratic and triggers repeated layout. Use a DocumentFragment plus replaceChildren for linear-time rebuild.

-        // Remove all options
-        while (select.options.length) { select.remove(0); }
-
-        // Add sorted options
-        optionData.forEach(opt => {
-            const option = document.createElement("option");
-            option.value = opt.value;
-            option.text = opt.text;
-            option.selected = opt.selected;
-            option.disabled = opt.disabled;
-            select.add(option);
-        });
+        // Rebuild efficiently
+        const frag = document.createDocumentFragment();
+        optionData.forEach(opt => {
+            const option = document.createElement("option");
+            option.value = opt.value;
+            option.text = opt.text;
+            option.selected = opt.selected;
+            option.disabled = opt.disabled;
+            frag.appendChild(option);
+        });
+        select.replaceChildren(frag);
📜 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 95a72db and b07ef40.

📒 Files selected for processing (1)
  • src/js/utils/common.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/js/utils/common.js (2)
src/js/tabs/osd.js (2)
  • text (2513-2513)
  • option (3462-3466)
src/tabs/presets/presets.js (1)
  • text (238-238)

@haslinghuis haslinghuis merged commit eed7343 into betaflight:master Sep 9, 2025
8 checks passed
@github-project-automation github-project-automation bot moved this from App to Done in 2025.12.0 Sep 9, 2025
@haslinghuis haslinghuis deleted the fix-chromium140 branch September 9, 2025 20:13
@haslinghuis haslinghuis changed the title Quick fix for Chromium v140 select issue Fix for Chromium v140 select issue Sep 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Latest configurator is messing ports tab in latest chrome/edge browser Прошивка

4 participants