# 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](./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`) with `data-*` 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? 1. **Blocks TASK-004**: Adding migration status badges and filters to a jQuery template creates a Frankenstein component mixing React dialogs into jQuery views 2. **Philosophy alignment**: "When we touch a component, we clean it properly" 3. **Pattern establishment**: This migration creates a template for other legacy panels 4. **Maintainability**: React components are easier to test, extend, and debug ### Prior Art Several patterns already exist in the codebase: - `ReactView` wrapper class for hybrid components - `SidePanel.tsx` - the container that hosts sidebar panels (already React) - `SidebarModel` registration pattern supports both legacy Views and React components - `UndoQueuePanel` example in `docs/sidebar.md` shows the migration pattern --- ## Goals 1. **Full React rewrite** of ComponentsPanel with zero jQuery dependencies 2. **Feature parity** with existing functionality (drag-drop, folders, context menus, rename-in-place) 3. **Clean integration** with existing SidebarModel registration 4. **Prepare for badges/filters** - structure component to easily add TASK-004 features 5. **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: - `useModernModel` hook 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 component - `ComponentsPanel.module.scss` - Base styles (port from existing CSS) - `types.ts` - TypeScript interfaces - `hooks/useComponentsPanel.ts` - State hook skeleton **Tasks:** 1. Create directory structure 2. Define TypeScript interfaces for component/folder items 3. Create basic ComponentsPanel that renders static tree 4. Register with SidebarModel (replacing legacy panel) 5. 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.tsx` - `components/ComponentItem.tsx` - `components/FolderItem.tsx` **Tasks:** 1. Subscribe to ProjectModel with useModernModel 2. Build folder/component tree structure (port logic from `addComponentToFolderStructure`) 3. Implement recursive tree rendering 4. Add expand/collapse for folders 5. Implement component selection 6. 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:** 1. Implement header "+" button menu using existing PopupMenu 2. Implement component right-click context menu 3. Implement folder right-click context menu 4. 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:** 1. Create drag-drop hook using PopupLayer.startDragging pattern 2. Implement drag initiation on component/folder rows 3. Implement drop zones on folders and between items 4. Port drop validation logic (`getAcceptableDropType`) 5. Port drop execution logic (`dropOn`) 6. 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:** 1. Create rename mode state management 2. Implement inline input field rendering 3. Handle Enter to confirm, Escape to cancel 4. Validate name uniqueness 5. 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:** 1. Render sheet tabs 2. Handle sheet switching 3. Respect `hideSheets` option **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:** 1. Remove old ComponentsPanel.ts and template 2. Update any imports/references 3. Add data attributes for testing 4. Prepare component structure for badges/filters (TASK-004) 5. 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`: ```typescript 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 created - `componentRemoved` - Component deleted - `componentRenamed` - Component name changed - `rootComponentChanged` - Home component changed Use `useModernModel(ProjectModel.instance, [...events])` pattern. ### Existing Patterns to Follow Look at these files for patterns: - `SearchPanel.tsx` - Modern React sidebar panel - `VersionControlPanel.tsx` - Another React sidebar panel - `useModernModel` hook - Model subscription pattern - `PopupMenu` component - 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 1. **Feature parity**: All existing functionality works identically 2. **No regressions**: Existing projects work correctly 3. **Clean code**: No jQuery, no TSFixme, proper TypeScript 4. **Ready for TASK-004**: Easy to add migration badges/filters 5. **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`