14 KiB
TASK-005: ComponentsPanel React Migration
⚠️ CURRENT STATUS: BLOCKED
Last Updated: December 22, 2025
Status: 🚫 BLOCKED - Webpack/Electron caching preventing testing
Completion: ~85% (Backend works, UI update blocked)
📖 See STATUS-BLOCKED.md for complete details
Quick Summary
- ✅ Backend rename functionality works perfectly
- ✅ Code fixes implemented correctly in source files
- ❌ Webpack 5 persistent caching prevents new code from loading
- ❌ UI doesn't update after rename because useEventListener never subscribes
Next Action: Requires dedicated investigation into webpack caching issue or alternative approach. See STATUS-BLOCKED.md for detailed analysis and potential solutions.
Overview
Migrate the ComponentsPanel from the legacy jQuery/underscore.js View pattern to a modern React component. This eliminates tech debt, enables the migration badges/filters feature from TASK-004, and establishes a clean pattern for migrating remaining legacy panels.
Phase: 2 (Runtime Migration System)
Priority: HIGH (blocks TASK-004 parts 2 & 3)
Effort: 6-8 hours (Original estimate - actual time ~12 hours due to caching issues)
Risk: Medium → HIGH (Webpack caching complications)
Background
Current State
ComponentsPanel.ts is a ~800 line legacy View class that uses:
- jQuery for DOM manipulation and event handling
- Underscore.js HTML templates (
componentspanel.html) withdata-*attribute bindings - Manual DOM updates via
scheduleRender()pattern - Complex drag-and-drop via PopupLayer integration
- Deep integration with ProjectModel, NodeGraphEditor, and sheets system
Why Migrate Now?
- Blocks TASK-004: Adding migration status badges and filters to a jQuery template creates a Frankenstein component mixing React dialogs into jQuery views
- Philosophy alignment: "When we touch a component, we clean it properly"
- Pattern establishment: This migration creates a template for other legacy panels
- Maintainability: React components are easier to test, extend, and debug
Prior Art
Several patterns already exist in the codebase:
ReactViewwrapper class for hybrid componentsSidePanel.tsx- the container that hosts sidebar panels (already React)SidebarModelregistration pattern supports both legacy Views and React componentsUndoQueuePanelexample indocs/sidebar.mdshows the migration pattern
Goals
- Full React rewrite of ComponentsPanel with zero jQuery dependencies
- Feature parity with existing functionality (drag-drop, folders, context menus, rename-in-place)
- Clean integration with existing SidebarModel registration
- Prepare for badges/filters - structure component to easily add TASK-004 features
- TypeScript throughout - proper typing, no TSFixme
Architecture
Component Structure
ComponentsPanel/
├── ComponentsPanel.tsx # Main container, registered with SidebarModel
├── ComponentsPanel.module.scss # Scoped styles
├── components/
│ ├── ComponentTree.tsx # Recursive tree renderer
│ ├── ComponentItem.tsx # Single component row
│ ├── FolderItem.tsx # Folder row with expand/collapse
│ ├── SheetSelector.tsx # Sheet tabs (if showSheetList option)
│ └── AddComponentMenu.tsx # "+" button dropdown
├── hooks/
│ ├── useComponentsPanel.ts # Main state management hook
│ ├── useDragDrop.ts # Drag-drop logic
│ └── useRenameMode.ts # Inline rename handling
├── types.ts # TypeScript interfaces
└── index.ts # Exports
State Management
Use React hooks with ProjectModel as source of truth:
useModernModelhook to subscribe to ProjectModel events- Local state for UI concerns (expanded folders, selection, rename mode)
- Derive tree structure from ProjectModel on each render
Drag-Drop Strategy
Two options to evaluate:
Option A: Native HTML5 Drag-Drop
- Lighter weight, no dependencies
- Already used elsewhere in codebase via PopupLayer
- Requires manual drop zone management
Option B: @dnd-kit library
- Already planned as dependency for DASH-003 (Project Organisation)
- Better accessibility, smoother animations
- More code but cleaner abstractions
Recommendation: Start with Option A to maintain existing PopupLayer integration patterns. Can upgrade to dnd-kit later if needed.
Implementation Phases
Phase 1: Foundation (1-2 hours)
Create the component structure and basic rendering without interactivity.
Files to create:
ComponentsPanel.tsx- Shell componentComponentsPanel.module.scss- Base styles (port from existing CSS)types.ts- TypeScript interfaceshooks/useComponentsPanel.ts- State hook skeleton
Tasks:
- Create directory structure
- Define TypeScript interfaces for component/folder items
- Create basic ComponentsPanel that renders static tree
- Register with SidebarModel (replacing legacy panel)
- Verify it mounts without errors
Success criteria:
- Panel appears in sidebar
- Shows hardcoded component list
- No console errors
Phase 2: Tree Rendering (1-2 hours)
Implement proper tree structure from ProjectModel.
Files to create:
components/ComponentTree.tsxcomponents/ComponentItem.tsxcomponents/FolderItem.tsx
Tasks:
- Subscribe to ProjectModel with useModernModel
- Build folder/component tree structure (port logic from
addComponentToFolderStructure) - Implement recursive tree rendering
- Add expand/collapse for folders
- Implement component selection
- Add proper icons (home, page, cloud function, visual)
Success criteria:
- Tree matches current panel exactly
- Folders expand/collapse
- Selection highlights correctly
- Icons display correctly
Phase 3: Context Menus (1 hour)
Port context menu functionality.
Files to create:
components/AddComponentMenu.tsx
Tasks:
- Implement header "+" button menu using existing PopupMenu
- Implement component right-click context menu
- Implement folder right-click context menu
- Wire up all actions (rename, duplicate, delete, make home)
Success criteria:
- All context menu items work
- Actions perform correctly (components created, renamed, deleted)
- Undo/redo works for all actions
Phase 4: Drag-Drop (2 hours)
Port the drag-drop system.
Files to create:
hooks/useDragDrop.ts
Tasks:
- Create drag-drop hook using PopupLayer.startDragging pattern
- Implement drag initiation on component/folder rows
- Implement drop zones on folders and between items
- Port drop validation logic (
getAcceptableDropType) - Port drop execution logic (
dropOn) - Handle cross-sheet drops
Success criteria:
- Components can be dragged to folders
- Folders can be dragged to folders
- Invalid drops show appropriate feedback
- Drop creates undo action
Phase 5: Inline Rename (1 hour)
Port rename-in-place functionality.
Files to create:
hooks/useRenameMode.ts
Tasks:
- Create rename mode state management
- Implement inline input field rendering
- Handle Enter to confirm, Escape to cancel
- Validate name uniqueness
- Handle focus management
Success criteria:
- Double-click or menu triggers rename
- Input shows with current name selected
- Enter saves, Escape cancels
- Invalid names show error
Phase 6: Sheet Selector (30 min)
Port sheet/tab functionality (if showSheetList option is true).
Files to create:
components/SheetSelector.tsx
Tasks:
- Render sheet tabs
- Handle sheet switching
- Respect
hideSheetsoption
Success criteria:
- Sheets display correctly
- Switching sheets filters component list
- Hidden sheets don't appear
Phase 7: Polish & Integration (1 hour)
Final cleanup and TASK-004 preparation.
Tasks:
- Remove old ComponentsPanel.ts and template
- Update any imports/references
- Add data attributes for testing
- Prepare component structure for badges/filters (TASK-004)
- Write migration notes for other legacy panels
Success criteria:
- No references to old files
- All tests pass
- Ready for TASK-004 badge implementation
Files to Modify
Create (New)
packages/noodl-editor/src/editor/src/views/panels/ComponentsPanel/
├── ComponentsPanel.tsx
├── ComponentsPanel.module.scss
├── components/
│ ├── ComponentTree.tsx
│ ├── ComponentItem.tsx
│ ├── FolderItem.tsx
│ ├── SheetSelector.tsx
│ └── AddComponentMenu.tsx
├── hooks/
│ ├── useComponentsPanel.ts
│ ├── useDragDrop.ts
│ └── useRenameMode.ts
├── types.ts
└── index.ts
Modify
packages/noodl-editor/src/editor/src/router.setup.ts
- Update ComponentsPanel import to new location
- Verify SidebarModel.register call works with React component
packages/noodl-editor/src/editor/src/models/sidebar/sidebarmodel.tsx
- May need adjustment if React components need different handling
Delete (After Migration Complete)
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanel.ts
packages/noodl-editor/src/editor/src/templates/componentspanel.html
Keep (Reference/Integration)
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanelFolder.ts
- Data structure class, can be reused or ported to types.ts
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentTemplates.ts
- Template definitions, used by AddComponentMenu
Technical Notes
SidebarModel Registration
Current registration in router.setup.ts:
SidebarModel.instance.register({
id: 'components',
name: 'Components',
order: 1,
icon: IconName.Components,
onOpen: () => {
/* ... */
},
panelProps: {
options: {
showSheetList: true,
hideSheets: ['__cloud__']
}
},
panel: ComponentsPanel // Currently legacy View class
});
React components can be registered directly - see how SidePanel.tsx handles this with SidebarModel.instance.getPanelComponent().
ProjectModel Integration
Key events to subscribe to:
componentAdded- New component createdcomponentRemoved- Component deletedcomponentRenamed- Component name changedrootComponentChanged- Home component changed
Use useModernModel(ProjectModel.instance, [...events]) pattern.
Existing Patterns to Follow
Look at these files for patterns:
SearchPanel.tsx- Modern React sidebar panelVersionControlPanel.tsx- Another React sidebar paneluseModernModelhook - Model subscription patternPopupMenucomponent - For context menus
CSS Migration
Port styles from:
packages/noodl-editor/src/editor/src/styles/componentspanel.css
To CSS modules in ComponentsPanel.module.scss.
Testing Checklist
Basic Rendering
- Panel appears in sidebar when Components icon clicked
- Components display with correct names
- Folders display with correct names
- Nested structure renders correctly
- Icons display correctly (home, page, cloud, visual, folder)
Selection
- Clicking component selects it
- Clicking folder selects it
- Selection opens component in node graph editor
- Only one item selected at a time
Folders
- Clicking caret expands/collapses folder
- Folder state persists during session
- Empty folders display correctly
- "Folder components" (folders that are also components) work
Context Menus
- "+" button shows add menu
- Component context menu shows all options
- Folder context menu shows all options
- "Make home" option works
- "Rename" option works
- "Duplicate" option works
- "Delete" option works (with confirmation)
Drag-Drop
- Can drag component to folder
- Can drag folder to folder
- Cannot drag folder into its own children
- Drop indicator shows correctly
- Invalid drops show feedback
- Undo works after drop
Rename
- Double-click enables rename
- Context menu "Rename" enables rename
- Enter confirms rename
- Escape cancels rename
- Tab moves to next item (optional)
- Invalid names show error
Sheets
- Sheet tabs display (if enabled)
- Clicking sheet filters component list
- Hidden sheets don't appear
Integration
- Warnings icon appears for components with warnings
- Selection syncs with node graph editor
- New component appears immediately after creation
- Deleted component disappears immediately
Risks & Mitigations
Risk: Drag-drop edge cases
Mitigation: Port logic directly from existing implementation, test thoroughly
Risk: Performance with large component trees
Mitigation: Use React.memo for tree items, virtualize if needed (future)
Risk: Breaking existing functionality
Mitigation: Test all features before removing old code, keep old files until verified
Risk: Subtle event timing issues
Mitigation: Use same ProjectModel subscription pattern as other panels
Success Criteria
- Feature parity: All existing functionality works identically
- No regressions: Existing projects work correctly
- Clean code: No jQuery, no TSFixme, proper TypeScript
- Ready for TASK-004: Easy to add migration badges/filters
- Pattern established: Can be used as template for other panel migrations
Future Enhancements (Out of Scope)
- Virtualized rendering for huge component trees
- Keyboard navigation (arrow keys)
- Multi-select for bulk operations
- Search/filter within panel (separate from SearchPanel)
- Drag to reorder (not just move to folder)
Dependencies
Blocked by: None
Blocks:
- TASK-004 Parts 2 & 3 (Migration Status Badges & Filters)
References
- Current implementation:
views/panels/componentspanel/ComponentsPanel.ts - Template:
templates/componentspanel.html - Styles:
styles/componentspanel.css - Folder model:
views/panels/componentspanel/ComponentsPanelFolder.ts - Sidebar docs:
packages/noodl-editor/docs/sidebar.md - SidePanel container:
views/SidePanel/SidePanel.tsx