flagsmith icon indicating copy to clipboard operation
flagsmith copied to clipboard

Refactor project-components.js: move from global window assignments to proper ES6 modules

Open talissoncosta opened this issue 3 months ago • 0 comments

Overview

Modernize project-components.js by converting global window/global assignments to proper ES6 module exports. This will improve component reusability, enable Storybook stories, and align with modern React patterns.

Current Problems

1. Global namespace pollution

Components and utilities are assigned to window and global, making them hard to:

  • Import into Storybook stories
  • Test in isolation
  • Track dependencies
  • Use with modern tooling (TypeScript, tree-shaking)
// Current approach
window.Button = Button
window.Loader = class extends PureComponent { ... }
global.Select = class extends PureComponent { ... }

2. Inline component definitions

Loader and Select are defined inline rather than in separate files:

  • Harder to maintain and test
  • Can't be easily imported by other modules
  • Mixed with setup/configuration code

3. E2E test logic mixed into components

The Select component has conditional rendering for E2E tests:

render() {
  return E2E ? (
    // E2E-specific DOM
  ) : (
    // Real component
  )
}

This makes it harder to render components in isolation (like in Storybook).

4. No clear module boundaries

File mixes concerns:

  • Component definitions
  • Provider setup
  • Store assignments
  • Utility configuration

Proposed Solution

Phase 1: Extract inline components

  • [ ] Move Loader to components/Loader.js (or .tsx)
  • [ ] Move Select to components/Select.js (or .tsx)
  • [ ] Export as ES6 modules

Phase 2: Convert window assignments to exports

// Before
window.Button = Button

// After (in project-components.js)
export { Button } from 'components/base/forms/Button'

// Or create index.js exports
export * from './components/base/forms/Button'

Phase 3: Separate E2E test concerns

  • [ ] Move E2E-specific rendering logic out of components
  • [ ] Use test-specific wrapper or mock if needed
  • [ ] Keep component logic pure and testable

Phase 4: Add proper types (optional but recommended)

  • [ ] Add TypeScript interfaces for Select props
  • [ ] Add types for Loader props
  • [ ] Convert to .tsx if doing TypeScript migration

Why This Blocks Storybook

To create a story, we need to:

// What we want to do:
import { Select } from 'components/Select'

export default {
  component: Select,
  // ...
}

But currently we can't because:

  • Components aren't exported as modules
  • We'd need to rely on global window.Select (doesn't work in Storybook)
  • E2E conditional logic would interfere with story rendering

Success Criteria

Must Have:

  • [ ] Components can be imported via ES6 imports (not window/global)
  • [ ] Existing functionality works exactly as before (zero regressions)
  • [ ] Select and Loader are in separate files with clean exports
  • [ ] E2E test logic is separated from component rendering logic
  • [ ] Can successfully create a Storybook story for at least one component

Nice to Have:

  • [ ] TypeScript types/interfaces
  • [ ] Functional components instead of class components
  • [ ] Clear documentation of what's exported

Migration Strategy

To avoid breaking existing code:

  1. Create new module exports alongside existing window assignments
  2. Test both paths work (old global way + new import way)
  3. Gradually migrate consuming code to use imports
  4. Remove globals once migration is complete (separate PR/issue)

Example transition:

// project-components.js (temporary dual support)
import Select from './components/Select'

// Keep old behavior for now
global.Select = Select

// But also support new way
export { Select }

Testing Plan

  • [ ] All existing E2E tests pass (nothing breaks)
  • [ ] Unit tests for extracted components
  • [ ] Can import components in a test file: import { Select } from 'components/project-components'
  • [ ] Create a basic Storybook story to verify isolation works
  • [ ] Visual QA: Components look and behave identically

Expected Impact

Immediate:

  • Enables Storybook story creation (unblocks epic)
  • Better component isolation and testability
  • Clearer component boundaries

Long-term:

  • Foundation for TypeScript migration
  • Better tree-shaking and bundle optimization
  • Easier onboarding (clear imports vs. magical globals)
  • Aligns with component audit work (clearer component inventory)

Related Work

  • UI component audit (will benefit from clear exports)
  • TypeScript migration of base components (60-day goals)
  • Storybook setup epic (blocked by this)

talissoncosta avatar Nov 20 '25 13:11 talissoncosta