Skip to content

deps: react-migration-to-v19#6764

Open
Zaimwa9 wants to merge 10 commits intomainfrom
deps/upgrade-to-react-19
Open

deps: react-migration-to-v19#6764
Zaimwa9 wants to merge 10 commits intomainfrom
deps/upgrade-to-react-19

Conversation

@Zaimwa9
Copy link
Contributor

@Zaimwa9 Zaimwa9 commented Feb 24, 2026

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Upgrades the frontend from React 16 to React 19, along with all incompatible dependencies.

React 19 migration

  • Updated react and react-dom from 16.14.0 to 19.x
  • Updated @types/react and @types/react-dom to 19.x
  • Configured Babel with "runtime": "automatic" for the new JSX transform
  • Removed @babel/transform-react-inline-elements (incompatible with React 19)
  • Updated tsconfig.json and tsconfig.jest.json to use "jsx": "react-jsx"
  • Deleted legacy common/Component.js (used React.createClass, removed in React 19)
  • Removed string refs (ref='wrappedComponent') from ConfigProvider and withSegmentOverrides

Dependency replacements

  • @material-ui/core → native implementations: Material UI v4 is incompatible with React 19
    • AccordionCard: replaced and with a custom height-animated accordion using useRef/useEffect
    • ChipInput: replaced material-ui-chip-input with a custom chip input component
    • SegmentSelect: removed @material-ui/core/Select type import, replaced with plain types
  • react-sortable-hoc → @dnd-kit/core + @dnd-kit/sortable: react-sortable-hoc relies on deprecated findDOMNode, incompatible with
    React 19. Migrated SegmentOverrides to use @dnd-kit with DndContext, SortableContext, and useSortable
  • react-select 2.x → 5.x: Updated for React 19 compatibility
  • react-click-outside: Removed (unused/incompatible)
  • react-window, react-window-infinite-loader, react-virtualized-auto-sizer: Removed (unused)
  • reactstrap: Updated from 9.0.1 to 9.2.3

react-tooltip v4 → v5 (performance fix)

  • Upgraded react-tooltip from 4.5.1 to 5.30.0 to fix severe UI lag when horizontally resizing the browser on pages with many
    feature rows
  • v4 created a separate DOM element per tooltip instance, each registering global resize and scroll event listeners — hundreds of
    listeners firing synchronously per resize frame
  • v5 uses floating-ui: tooltips are only materialised on hover, eliminating idle listeners
  • Migrated Tooltip.tsx to v5 API (data-tooltip-id/data-tooltip-html replacing data-for/data-tip)
  • Removed the TooltipPortal helper (v5 handles portalling internally)
  • Replaced Utils.GUID() with React useId() for stable tooltip IDs
  • Updated _tooltips.scss selectors from .__react_component_tooltip to .react-tooltip
  • Updated tooltip usage in Modal.tsx, ModalConfirm.tsx, and SegmentOverrides.js

Other fixes

  • Fixed AccordionCard chevron showing oversized focus ring (replaced with )

How did you test this code?

Please describe.

@Zaimwa9 Zaimwa9 requested a review from a team as a code owner February 24, 2026 10:28
@Zaimwa9 Zaimwa9 requested review from talissoncosta and removed request for a team February 24, 2026 10:28
@vercel
Copy link

vercel bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview, Comment Mar 11, 2026 8:17am
flagsmith-frontend-staging Ready Ready Preview, Comment Mar 11, 2026 8:17am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Mar 11, 2026 8:17am

Request Review

@github-actions github-actions bot added the front-end Issue related to the React Front End Dashboard label Feb 24, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 24, 2026

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-6764 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-6764 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-6764 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-6764 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-6764 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-6764 Finished ✅ Results

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 5 potential issues.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Autofix Details

Bugbot Autofix prepared fixes for 4 of the 5 issues found in the latest run.

  • ✅ Fixed: Sortable IDs use object coercion instead of segment ID
    • Sortable IDs now use a helper that derives a stable primitive identifier from segment.id/segment.name (or primitive segment value) instead of coercing objects.
  • ✅ Fixed: Fragment keys use undefined value.segment.name on object
    • Fragment keys now derive the segment name safely from object or segment_name and fall back to the corrected sortable ID for stability.
  • ✅ Fixed: Variable useSortable shadows the imported hook
    • The local boolean was renamed to isSortable so it no longer shadows the imported useSortable hook name.
  • ✅ Fixed: Drag-and-drop drops outside container may crash
    • handleDragEnd now returns early when over is null and only proceeds when both source and target indices are valid.

Create PR

Or push these changes by commenting:

@cursor push 594a1e2dc6
Preview (594a1e2dc6)
diff --git a/frontend/web/components/SegmentOverrides.js b/frontend/web/components/SegmentOverrides.js
--- a/frontend/web/components/SegmentOverrides.js
+++ b/frontend/web/components/SegmentOverrides.js
@@ -370,6 +370,16 @@
   )
 }
 
+const getSegmentIdentifier = (segment, fallback) => {
+  if (segment && typeof segment === 'object') {
+    return segment.id || segment.name || fallback
+  }
+  return segment ?? fallback
+}
+
+const getSegmentSortId = (value, index) =>
+  `segment-${getSegmentIdentifier(value.segment, index)}-${index}`
+
 const SegmentOverrideListInner = ({
   confirmRemove,
   controlValue,
@@ -392,14 +402,17 @@
   showEditSegment,
   toggle,
 }) => {
-  const useSortable = !id && !disabled
+  const isSortable = !id && !disabled
   return (
     <div>
       {items.map((value, index) => {
-        const sortId = `segment-${value.segment}-${index}`
-        if (useSortable) {
+        const sortId = getSegmentSortId(value, index)
+        const segmentName =
+          (typeof value.segment === 'object' && value.segment?.name) ||
+          value.segment_name
+        if (isSortable) {
           return (
-            <Fragment key={value.segment.name || sortId}>
+            <Fragment key={segmentName || sortId}>
               <SortableSegmentOverride
                 sortId={sortId}
                 id={id}
@@ -447,7 +460,7 @@
         }
 
         return (
-          <Fragment key={value.segment.name || sortId}>
+          <Fragment key={segmentName || sortId}>
             <SegmentOverrideInner
               id={id}
               name={name}
@@ -511,17 +524,19 @@
     }),
   )
 
-  const sortableIds = items.map(
-    (value, index) => `segment-${value.segment}-${index}`,
+  const sortableIds = items.map((value, index) =>
+    getSegmentSortId(value, index),
   )
 
   const handleDragEnd = (event) => {
     const { active, over } = event
-    if (active.id !== over?.id) {
-      const oldIndex = sortableIds.indexOf(active.id)
-      const newIndex = sortableIds.indexOf(over.id)
-      handleSortEnd({ newIndex, oldIndex })
-    }
+    if (!over || active.id === over.id) return
+
+    const oldIndex = sortableIds.indexOf(active.id)
+    const newIndex = sortableIds.indexOf(over.id)
+
+    if (oldIndex === -1 || newIndex === -1) return
+    handleSortEnd({ newIndex, oldIndex })
   }
 
   return (

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  43.6 seconds
commit  ee031af
info  🔄 Run: #15232 (attempt 1)

@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute, 5 seconds
commit  ee031af
info  🔄 Run: #15232 (attempt 1)

@github-actions github-actions bot added dependencies Pull requests that update a dependency file and removed dependencies Pull requests that update a dependency file labels Mar 11, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  10 passed

Details

stats  10 tests across 7 suites
duration  11.3 seconds
commit  ee031af
info  🔄 Run: #15232 (attempt 1)

@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute, 2 seconds
commit  ee031af
info  🔄 Run: #15232 (attempt 1)

@github-actions github-actions bot added dependencies Pull requests that update a dependency file and removed dependencies Pull requests that update a dependency file labels Mar 11, 2026
@github-actions github-actions bot added dependencies Pull requests that update a dependency file and removed dependencies Pull requests that update a dependency file labels Mar 11, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

})
})
}
}, [collapsed])
Copy link

Choose a reason for hiding this comment

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

Missing requestAnimationFrame cleanup in CollapsibleText effect

Medium Severity

The CollapsibleText component's useEffect does not return a cleanup function for requestAnimationFrame calls in the collapsed === true branch. If collapsed toggles rapidly or the component unmounts before the RAF callbacks fire, setHeight(0) will execute on a stale or unmounted component. The AccordionCard component correctly captures and cancels both animation frame IDs in its cleanup, but this component omits that cleanup entirely.

Additional Locations (1)
Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant