-
-
Notifications
You must be signed in to change notification settings - Fork 184
feat: add React 19 Admin UI with runtime switching #2267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Have you tried the signalk-shelly2 or bt-sensors plugins with this? |
Hi Scott,
|
12df93b to
60e2032
Compare
|
@tkurki |
2c23171 to
4c1a5e4
Compare
|
I just sat down to work on this today and was very happy to see someone already did. Pulling down now to test and will do a review, but anything else I can do to help move this along? |
Thanks, looking forward to it. Indeed you could, if you want. As you can see here are some related PRs attached. By design embedded Webapps (and just the embedded) fail installing in R19. The related PRs are hybrid installers for them, so after merging, their plugin installs and works on R16 and R19. This is quite some lifting where AI can help perfectly. |
|
I did a bunch of click testing and everything is looking good. It's a huge diff, so I'm not sure how much value there is in manually reviewing, but I'll start looking through it and see how far I get. |
|
Naive question because I have not used Federated Modules: is there a reason that all plugins have to use the same version of React? I know running multiple is gross, but breaking the dependency between framework versions on the server and plugins would be a huge win in terms of keeping the server on a modern version and allowing plugins to update at their convenience. |
|
This is by design and JUST for embedded Webapps, there are only a few. See discord discussion in channel Server about this. The breaking change would have been remove this feature. |
I'm not able to find a link to this, so if you could share one that would be helpful. I usually try to link to any Discord discussions on pull requests if there's relevant context.
Was there discussion about allowing multple versions? If If there's not a technical reason to lock server and plugin versions, I think it would be very advantagous to decouple them. |
https://discord.com/channels/1170433917761892493/1461248922201886772
No - Teppo was willing to kill the support for embedded Webapps in favor to R19 - which I fully understand. Nevertheless I found a way how embedded Webapp developers can make their plugin hybrid to happily exist in both worlds. |
Migrate server-admin-ui from React 16 to React 19, including: - React Router v4 to v6 (Switch→Routes, component→element) - Redux 3 to Redux 5 with react-redux 9 hooks - Bootstrap 4 to Bootstrap 5 class names (ml-*→ms-*, badge-*→text-bg-*) - ReactDOM.render to createRoot API - String refs to React.createRef() Module Federation configured with singleton:true for React to ensure hooks and context work correctly across host and plugin boundaries. Plugins built against React 16 will need to be rebuilt. Add improved error handling in dynamicutilities.js to detect React version incompatibility and display user-friendly messages. Add test plugin (examples/test-custom-renderer-plugin) demonstrating React 19 compatible embedded webapp and custom Data Browser renderer. BREAKING CHANGE: Embedded webapps and custom renderers built with React 16 must be updated and rebuilt to work with React 19.
Migrate Admin UI components from legacy Redux connect() HOC pattern to modern useSelector/useDispatch hooks. Convert affected files from JavaScript to TypeScript for improved type safety. Changes: - Convert 12 component files from .js to .tsx with full TypeScript types - Replace connect() mapStateToProps with useSelector hooks - Replace connect() mapDispatchToProps with useDispatch hooks - Add proper TypeScript interfaces for component props and state - Remove unused eslint-disable comments for unconfigured rules - Fix lint warnings for unused imports Components converted to TypeScript with hooks: - DataBrowser, Meta, Playground - ProvidersConfiguration, ServerUpdate, Settings - VesselConfiguration, Logging, BackupRestore, SourcePriorities - Apps, OIDCSettings Also includes fixes to existing hook-based components: - Login.js, ServerLog.js: Remove obsolete eslint-disable comments - Various security components: Modernize Redux usage
The component was importing appStore via connect() but never using it.
Remove old JavaScript files that were superseded by TypeScript conversions. Delete unused routerCompat.tsx shim that was never imported. Remove echo comments that simply describe what code does. Update documentation to follow maintainer guidelines: - Remove implementation details that may drift from code - Replace inline code snippets with links to source files - Update file references from .js to .tsx
- Remove @signalk/vesselpositions from optionalDependencies and categories.ts - Remove vesselpositions.png image and references from webapps.md - Fix sidebar submenu alignment for dropdown items without icons - Remove echo comments and stale code cruft: - SidebarFooter/Form/Header: remove commented boilerplate - Sidebar.tsx: remove redundant function name comments - bootstrap.tsx: remove section header comments - appSlice.ts: remove echo comments - Dashboard.tsx: remove implementation history comment - _custom.scss: simplify comments, remove variable name echoes - Fix SCSS deprecation warnings (PR 2237): - darken()/lighten() → color.adjust() - / division → math.div() - nth()/map-get()/map-keys() → list.nth()/map.get()/map.keys() - lightness() → color.channel() - Silence only @import deprecations (Bootstrap 5 still uses @import)
Generated build artifact was accidentally committed, adding 44K lines. Added to .gitignore to prevent future commits.
Enable strict type checking in server-admin-ui tsconfig.json and fix all resulting type errors. Add graceful error handling for plugins incompatible with React 19. Key changes: - Enable strict mode in tsconfig.json, update lib to ES2021 - Add missing properties and index signatures to store types - Fix null/undefined handling with nullish coalescing operators - Add type assertions for RJSF template compatibility - Create type declaration for jsonlint-mod module - Add PluginErrorBoundary to EmbeddedPluginConfigurationForm - Enhance error detection in dynamicutilities.ts for React 19 incompatibility patterns Incompatible plugins now show a friendly warning instead of crashing the entire configuration page.
Enable stricter TypeScript linting rules to catch unused code at compile time and fix all resulting errors. Changes: - Enable noUnusedLocals and noUnusedParameters in tsconfig.json - Remove unused React import from bootstrap.tsx - Remove unused dispatch parameter from enableSecurity function - Remove unused onClose prop from PluginConfigCard component - Remove unused FormText import from Meta.tsx - Remove unused Col and Row imports from ProvidersConfiguration.tsx - Remove dead changelog fetching code from ServerUpdate.tsx
Fix missing status feedback when installing or removing apps in the app store. The progress animation and status text (Installing, Removing, Removed, etc.) were not displayed because the installing app properties were not being merged into the derived app list. Changes: - Add missing properties to InstallingApp type (isRemoving, isRemove, installFailed) - Spread all installing app properties in Apps.tsx deriveAppList function so ActionCellRenderer receives the status flags
Add WebappErrorBoundary to Embedded.tsx to gracefully handle React 19 incompatibility errors when loading webapps built for older React versions. Previously, clicking on an incompatible webapp (e.g., @canboat/visual-analyzer) crashed the entire admin UI with "Minified React error SignalK#306". Now users see a friendly warning message with a "Try again" button, and can navigate away without reloading the page. The error boundary: - Catches render-phase errors from dynamically loaded webapps - Shows React 19 compatibility hint for known error patterns - Provides retry functionality for transient errors - Resets automatically when switching between webapps (via key prop)
Add container="body" to DropdownMenu in ActionCellRenderer to render the dropdown in a portal, preventing it from being clipped by the InfiniteScroll container's overflow.
Enable React Compiler 1.0 for automatic memoization and adopt the new useEffectEvent hook for cleaner callback patterns. React Compiler (babel-plugin-react-compiler): - Automatically optimizes component rendering without manual memo() - Removed memo() wrappers from 5 components: TimestampCell, DataRow, VirtualizedDataTable, CopyToClipboardWithFade, and ServerLog's LogRow useEffectEvent in DataBrowser.tsx: - Replaced ref-based pattern (updatePath$SourceKeysRef) with useEffectEvent - The hook always sees latest state without causing effect re-runs - Eliminates need for synchronization effects and reduces dependency arrays Dependencies upgraded: - react, react-dom: ^19.0.0 → ^19.2.0 - @types/react, @types/react-dom: ^19.0.0 → ^19.2.0 - Added babel-plugin-react-compiler: ^1.0.0
Resolves accessibility warnings about labels not being associated with form fields in the DataBrowser component.
Remove redundant htmlFor from switch wrapper Labels that already contain nested inputs. The text labels still retain htmlFor for proper association.
- Update @vitejs/plugin-react from ^4.3.4 to ^5.1.2 Fixes npm ERESOLVE error where 4.7.0 caused peer dependency conflicts - Update @module-federation/vite from ^1.0.10 to ^1.9.4 Version 1.0.10 no longer exists in npm registry Both plugins remain necessary: - @vitejs/plugin-react: Required for React Compiler (Babel-based) - @module-federation/vite: Required for module federation support
Wrap source selection checkboxes in <label> elements to satisfy browser accessibility checkers. The inputs already had aria-label attributes for screen readers, but the DOM validator requires a proper label association.
…ode virtualization Add React 19.2 optimizations for better performance with many AIS targets: - Add useDeferredValue for non-blocking search filtering - Add useTransition for localStorage writes - Memoize context dropdown options to prevent O(n) recalculation - Increase structure debounce from 50ms to 200ms Enable virtualization in RAW mode to reduce DOM size: - Use 80px row height estimate for RAW rows (vs 40px normal) - Apply content-visibility CSS for variable height handling - Remove special case that rendered all rows in RAW mode Add VirtualizedMetaTable component for Meta view virtualization. Replace FontAwesome copy icons with CSS-based icons to reduce DOM nodes. Make Sources card collapsible to reduce initial render.
Add eslint-plugin-react-hooks, eslint-plugin-react-compiler, and @eslint-react/eslint-plugin for stricter React 19 compliance. Update tsconfig.json with stricter compiler options: noImplicitReturns, noImplicitOverride, allowUnusedLabels, allowUnreachableCode. Fix lint violations properly throughout the codebase: - Refactor prop mutation patterns to immutable state updates - Replace array index keys with stable unique identifiers - Fix missing useEffect dependencies - Add proper button types and ref cleanup patterns - Remove echo comments that just restate the code
The CSS grid layout with minmax() minimum widths summed to more than narrow screen widths, causing columns to overlap on mobile devices. Switch to a stacked card layout on screens narrower than 768px where each row displays fields vertically with labels. Disable virtualization on narrow screens since the stacked layout has variable row heights. addresses SignalK#2281
The CSS grid layout with minmax() minimum widths summed to more than narrow screen widths, causing columns to overlap on mobile devices. Switch to a stacked card layout on screens narrower than 768px where each row displays fields vertically with labels. Use larger row height estimate (120px) for virtualization on narrow screens to account for the taller stacked layout.
|
Thanks for the context. Sorry to distract with discussions of a feature that probably should be deprecated. Either way, I'm 100% in support of moving this PR forward and will help with any issues or bugs that come up. |
No, this is incorrect. Either i have been ambiguous or you have not quite heard me. I am willing to break the backwards compatibility to move to up to date React. Made peace with that decision already when I added the feature… While supporting multiple reacts might be technically somehow possible it would be brittle and forfeit the advantage of NOT needing to load multiple Reacts. |
Replace Redux with Zustand for unified state management across the admin UI. This simplifies the codebase by using a single lightweight (~3KB) library with better TypeScript support and React 19 compatibility. Key changes: - Add Zustand store with slices for app state, WebSocket, data, and priorities - Route WebSocket delta messages directly to Zustand (remove ValueEmittingStore) - Use useSyncExternalStore for throttled per-path subscriptions in DataBrowser - Convert all components from useSelector/dispatch to useStore hooks - Remove Redux dependencies (@reduxjs/toolkit, react-redux) The WebSocket connection lifecycle and real-time data flow remain unchanged. All existing functionality is preserved with improved performance characteristics
Add a new React 19 Admin UI (server-admin-ui-react19) alongside the legacy React 16 Admin UI. Users can switch between them at runtime using the SIGNALK_ADMIN_UI environment variable. Changes: - Add @signalk/server-admin-ui-react19 package with Zustand state management replacing Redux - Restore legacy server-admin-ui from master as fallback - Add runtime UI selection via SIGNALK_ADMIN_UI=react19 env var - Add Vitest test suite for React 19 UI (86 tests for Zustand store) - Update eslint.config.js to handle both UI packages - Update typedoc.json to exclude both admin UI packages - Add test:admin-ui script to root package.json The legacy UI remains the default. Set SIGNALK_ADMIN_UI=react19 to use the new React 19 UI.



Description:
This PR adds a new React 19 Admin UI alongside the existing legacy UI, allowing runtime switching via environment variable.
What Changed
@signalk/server-admin-ui-react19with React 19, Zustand, and Vite@signalk/server-admin-uiSIGNALK_ADMIN_UI=react19to use the new UITechnical Details
React 19 UI Features
Usage
No Impact