For the complete documentation index, see llms.txt. This page is also available as Markdown.

Phase 2: Split MainViewer Components

← Back to Index

Effort: 3-4 hours Risk: Low (isolated change, easy to test) Priority: Required before context refactoring Previous: Phase 1: Feature Flag Setup Next: Phase 3: Canvas Data

Goal

Split MainViewer into separate components for different viewing modes before refactoring the context. This simplifies the context work by allowing each viewer mode to consume only the values it needs.

Why Do This First?

Component splitting should happen before the main context refactoring because:

  1. Simpler context design: Each viewer type can consume exactly the context values it needs

  2. Clearer testing: Test each viewer mode independently with focused test suites

  3. Easier refactoring: Norming context values is simpler when components aren't juggling multiple modes

  4. Better architecture: Fixes architectural debt before building on top of it

Problem: MainViewer Has Two Faces

MainViewer.tsx has two fundamentally different implementations:

if (hasOnlyRenderableImages) {
  // Mode A: Virtualized image scrolling with FixedSizeList
  return <FixedSizeList>{ItemRenderer}</FixedSizeList>;
}

// Mode B: Direct item rendering with IIIFItem components
return <>{displayItems.map(item => <IIIFItem />)}</>;

These are two different components with:

  • Different data requirements

  • Different performance characteristics

  • Different rendering strategies

  • Different testing needs

This violates the Single Responsibility Principle and makes the code harder to maintain and test.

Solution: Split into Separate Components

Before:

After:

Benefits:

  • Clarity: Each component has one clear purpose

  • Testability: Test each mode independently

  • Performance: Separate bundle chunks, load only what's needed

  • Maintainability: Changes to one mode don't affect the other

  • Type safety: Each component has its own specific props

  • Context refactoring: Each viewer can consume only what it needs

Implementation Steps

2.1 Create VirtualizedImageViewer.tsx

Extract the image-only virtualized scrolling logic into its own component.

New file: content/webapp/views/pages/works/work/IIIFViewer/VirtualizedImageViewer.tsx

2.2 Create PaginatedItemViewer.tsx

Extract the non-image-only rendering logic into its own component. This component handles all non-image-only content: video, audio, PDF, and archives.

New file: content/webapp/views/pages/works/work/IIIFViewer/PaginatedItemViewer.tsx

2.3 Update MainViewer to Router Component

Convert MainViewer.tsx into a simple router that chooses which implementation to render.

File: content/webapp/views/pages/works/work/IIIFViewer/MainViewer.tsx

2.4 Write Tests for Each Component

Create separate test files for each viewer type:

Tests:

  • VirtualizedImageViewer.test.tsx - Test virtualized scrolling behavior

  • PaginatedItemViewer.test.tsx - Test paginated rendering behavior

  • MainViewer.test.tsx - Test routing logic

2.5 Verify No Regressions

Run existing E2E tests to ensure behavior hasn't changed:

  • Image-only works still render correctly

  • Archive works still render correctly

  • Mixed content works still render correctly

Testing Checklist

Other Candidates for Splitting (Future Work)

After this phase, consider splitting other components with multiple modes:

Look for these patterns:

  • Large if/else blocks returning different JSX

  • Components with multiple "modes" or "types"

  • Boolean props that drastically change behaviour

  • Different event handlers based on conditionals

Potential candidates:

  • IIIFViewer.tsx - different rendering for image-only vs mixed content

  • Any component checking hasOnlyRenderableImages extensively

When NOT to split:

  • Just conditional rendering of small UI elements (buttons, labels)

  • The modes share significant logic

  • Conditional is for progressive enhancement (browser support)

  • Splitting would create more complexity than it solves

Success Criteria

Time Estimate

  • Extract VirtualizedImageViewer: 1 hour

  • Extract PaginatedItemViewer: 1 hour

  • Convert MainViewer to router: 30 minutes

  • Write/update tests: 1 hour

  • Test and verify: 30 minutes

Total: 3-4 hours


Next: Phase 3: Canvas Data Context

Last updated