Status: COMPLETED
Goal: Build the Function Edit Page with a TreeView file browser and CodeEditor for editing and deploying function code to GitHub.
Architecture: The page follows the project's layered architecture. A useFunctionEditPage hook (co-located in the view file, unexported) handles data loading, state management, and save logic. A FileTreeView component with a co-located useFileTreeView hook renders the file tree. An EditToolbar component with useEditToolbar hook manages save flow and navigation. The SourceControlService interface is extended with fetch() and updateRepo().
Tech Stack: React, PatternFly 6 (TreeView, Toolbar, Modal, Button, Alert, EmptyState), OCP Dynamic Plugin SDK (CodeEditor, DocumentTitle, ListPageHeader), Vitest + React Testing Library + MSW
Testing approach: MSW for all GitHub API interactions. vi.mock only for react-i18next and OCP SDK components.
- Added
fetch(repo)usinggit.getTree({ recursive: '1' })+ per-filegit.getBlob - Migrated all GithubService tests from
vi.mock('@octokit/rest')to MSW handlers - Renamed test file to
GithubService.test.ts - Merged
RepoInfo+SourceRepointo singleRepoMetadatatype - Added test for
fetchFileContenterror path
push()for initial commits (createRef, no parents)updateRepo()for subsequent commits with local commit SHA cache- Cache handles GitHub's eventually consistent ref storage (stale getRef on rapid saves)
- Cache clears only on "not a fast forward" errors (external push), not on network errors
- 5 updateRepo tests: first push, second from cache, third from cache, stale cache recovery, network error preserves cache
- Maps file extensions and special filenames (Dockerfile, Makefile) to Monaco Language enum
- Extracted to shared
src/utils/utils.tsalong withparseNamespaceAndRuntimeandhandlerMap
- Builds
TreeViewDataItem[]directly from flatFileEntry[]paths (no intermediate type) - Separate handling for root files and nested files
- Directories render before files, sorted alphabetically
- Loading state with spinner and "Loading source..." text
- Empty state with "No files" placeholder (not clickable)
- Dirty indicator after filename (
func.yaml ●) React.memowithuseMemo(justified: parent re-renders on every CodeEditor keystroke)- Fixed width (16rem) with horizontal scroll, vertical scroll for long file lists
- 11 test cases using realistic Node function file structure from knative/func templates
- Page component: toolbar + flex layout (FileTreeView left, SDK CodeEditor right)
Flexwithdirection: row,flexWrap: nowrap,alignItems: stretch(fixes baseline alignment stacking issue)- SDK CodeEditor with
height="70vh", empty state (code icon + "Start editing"), language label visible - Handler file auto-selected based on runtime from func.yaml (function.go for Go, index.js for Node, function/func.py for Python)
- Each page loads its own data from URL params (no navigation state passing between pages)
resolveRepoContentextracts API calls from state management- Repo metadata stored in state after load for save without re-fetching
- Back link (left), success/danger Alert (center), Save & Deploy button (right)
useEditToolbarhook: save flow with isSaving/error/success state- Success alert "Pushed to GitHub. Deployment running..." with 2-second auto-dismiss (tested with
vi.useFakeTimers) - Unsaved changes confirmation modal (LeaveModal) on back navigation when dirty
- Save & Deploy disabled when no changes
- Migrated from Jest to Vitest with MSW 2.x
- Custom jsdom environment removed (Vitest handles globals natively)
- CSP connect-src for GitHub API added to start-console.sh for dev mode
- Added CSS, co-location, React performance rules to style guide and architecture docs
- Added communication rules and draft PR step to workflow docs
- Used SDK CodeEditor instead of PatternFly CodeEditor (CSP blob URL issues with Monaco workers in OCP Console)
- Removed navigation state passing between list and edit page (each page is self-contained)
- Split
push()intopush()+updateRepo()(clearer separation of initial vs subsequent commits) - Added local commit SHA cache (GitHub eventual consistency workaround)
- Added success alert after save (not in original plan)
- Editor empty state with code icon (not in original plan)
autoSelectHandlerrenamed todetermineHandler(pure function, returns path instead of calling setState)