Files
OpenNoodl/dev-docs/tasks/phase-2/TASK-004B-componentsPanel-react-migration/SESSION-PLAN.md
2025-12-23 09:39:33 +01:00

7.4 KiB

TASK-005 Session Plan for Cline

Context

You are migrating ComponentsPanel.ts from a legacy jQuery/underscore.js View to a modern React component. This is a prerequisite for TASK-004's migration badges feature.

Philosophy: "When we touch a component, we clean it properly" - full React rewrite, no jQuery, proper TypeScript.


Session 1: Foundation (Start Here)

Goal

Create the component structure and get it rendering in the sidebar.

Steps

  1. Create directory structure:
packages/noodl-editor/src/editor/src/views/panels/ComponentsPanel/
├── ComponentsPanel.tsx
├── ComponentsPanel.module.scss
├── components/
├── hooks/
├── types.ts
└── index.ts
  1. Define types in types.ts:
import { ComponentModel } from '@noodl-models/componentmodel';

export interface ComponentItemData {
  id: string;
  name: string;
  localName: string;
  component: ComponentModel;
  isRoot: boolean;
  isPage: boolean;
  isCloudFunction: boolean;
  isVisual: boolean;
  hasWarnings: boolean;
}

export interface FolderItemData {
  name: string;
  path: string;
  isOpen: boolean;
  isComponentFolder: boolean;
  component?: ComponentModel;
  children: TreeNode[];
}

export type TreeNode = 
  | { type: 'component'; data: ComponentItemData }
  | { type: 'folder'; data: FolderItemData };

export interface ComponentsPanelProps {
  options?: {
    showSheetList?: boolean;
    hideSheets?: string[];
  };
}
  1. Create basic ComponentsPanel.tsx:
import React from 'react';
import css from './ComponentsPanel.module.scss';

export function ComponentsPanel({ options }: ComponentsPanelProps) {
  return (
    <div className={css['ComponentsPanel']}>
      <div className={css['Header']}>
        <span className={css['Title']}>Components</span>
        <button className={css['AddButton']}>+</button>
      </div>
      <div className={css['Tree']}>
        {/* Tree will go here */}
        <div style={{ padding: 16, color: '#888' }}>
          ComponentsPanel React migration in progress...
        </div>
      </div>
    </div>
  );
}
  1. Update router.setup.ts:
// Change import
import { ComponentsPanel } from './views/panels/ComponentsPanel';

// In register call, panel should now be the React component
SidebarModel.instance.register({
  id: 'components',
  name: 'Components',
  order: 1,
  icon: IconName.Components,
  onOpen: () => { /* ... */ },
  panelProps: {
    options: {
      showSheetList: true,
      hideSheets: ['__cloud__']
    }
  },
  panel: ComponentsPanel  // React component
});
  1. Port base styles to ComponentsPanel.module.scss from styles/componentspanel.css

Verify

  • Panel appears when clicking Components icon in sidebar
  • Placeholder text visible
  • No console errors

Session 2: Tree Rendering

Goal

Render the actual component tree from ProjectModel.

Steps

  1. Create hooks/useComponentsPanel.ts:
  • Subscribe to ProjectModel using useModernModel
  • Build tree structure from components
  • Track expanded folders in useState
  • Track selected item in useState
  1. Port tree building logic from ComponentsPanel.ts:
  • addComponentToFolderStructure()
  • getFolderForComponentWithName()
  • Handle sheet filtering
  1. Create components/ComponentTree.tsx:
  • Recursive renderer
  • Pass tree data and handlers
  1. Create components/ComponentItem.tsx:
  • Single row for component
  • Icon based on type (use getComponentIconType)
  • Selection state
  • Warning indicator
  1. Create components/FolderItem.tsx:
  • Folder row with caret
  • Expand/collapse on click
  • Render children when expanded

Verify

  • Tree structure matches original
  • Folders expand/collapse
  • Selection works
  • Icons correct

Session 3: Context Menus

Goal

Implement all context menu functionality.

Steps

  1. Create components/AddComponentMenu.tsx:
  • Uses ComponentTemplates.instance.getTemplates()
  • Renders PopupMenu with template options + Folder
  1. Wire header "+" button to show AddComponentMenu

  2. Add context menu to ComponentItem:

  • Right-click handler
  • Menu: Add submenu, Make home, Rename, Duplicate, Delete
  1. Add context menu to FolderItem:
  • Right-click handler
  • Menu: Add submenu, Make home (if folder component), Rename, Duplicate, Delete
  1. Port action handlers:
  • performAdd() - create component/folder
  • onDeleteClicked() - with confirmation
  • onDuplicateClicked() / onDuplicateFolderClicked()

Verify

  • All menu items appear
  • Actions work correctly
  • Undo works

Session 4: Drag-Drop

Goal

Implement drag-drop for reorganizing components.

Steps

  1. Create hooks/useDragDrop.ts:
  • Track drag state
  • Integrate with PopupLayer.instance
  1. Add drag handlers to items:
  • mousedown/mousemove pattern from original
  • Call PopupLayer.startDragging()
  1. Add drop zone handlers:
  • Folders are drop targets
  • Top-level area is drop target
  • Show visual feedback
  1. Port drop logic:
  • getAcceptableDropType() - validation
  • dropOn() - execution with undo

Verify

  • Dragging shows label
  • Valid targets highlight
  • Invalid targets show feedback
  • Drops work correctly
  • Undo works

Session 5: Inline Rename + Sheets

Goal

Complete rename functionality and sheet selector.

Steps

  1. Create hooks/useRenameMode.ts:
  • Track which item is being renamed
  • Handle Enter/Escape/blur
  1. Add rename input UI:
  • Replaces label when in rename mode
  • Auto-select text
  • Validation
  1. Create components/SheetSelector.tsx:
  • Tab list from ProjectModel sheets
  • Handle hideSheets option
  • Switch current sheet on click
  1. Integrate SheetSelector:
  • Only show if options.showSheetList
  • Filter tree by current sheet

Verify

  • Rename via double-click works
  • Rename via menu works
  • Sheets display and switch correctly

Session 6: Polish + Cleanup

Goal

Final cleanup, remove old files, prepare for TASK-004.

Steps

  1. Style polish:
  • Match exact spacing/sizing
  • Hover and focus states
  1. Code cleanup:
  • Remove any any types
  • Add JSDoc comments
  • Consistent naming
  1. Remove old files:
  • Delete views/panels/componentspanel/ComponentsPanel.ts
  • Delete templates/componentspanel.html
  • Update remaining imports
  1. TASK-004 preparation:
  • Add migrationStatus to ComponentItemData
  • Add badge placeholder in ComponentItem
  • Document extension points
  1. Update CHANGELOG.md

Verify

  • All functionality works
  • No errors
  • Old files removed
  • Ready for badges feature

Key Files Reference

Read these first:

  • views/panels/componentspanel/ComponentsPanel.ts - Logic to port
  • templates/componentspanel.html - UI structure reference
  • views/panels/componentspanel/ComponentsPanelFolder.ts - Data model
  • views/panels/componentspanel/ComponentTemplates.ts - Template definitions

Pattern references:

  • views/panels/SearchPanel/SearchPanel.tsx - Modern panel example
  • views/SidePanel/SidePanel.tsx - Container that hosts panels
  • views/PopupLayer/PopupMenu.tsx - Context menu component
  • hooks/useModel.ts - useModernModel hook

Confidence Checkpoints

After each session, verify:

  1. No TypeScript errors: npx tsc --noEmit
  2. App launches: npm run dev
  3. Panel renders in sidebar
  4. Previous functionality still works

Before removing old files: Test EVERYTHING twice.