flagsmith
flagsmith copied to clipboard
Refactor project-components.js: move from global window assignments to proper ES6 modules
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
Loadertocomponents/Loader.js(or.tsx) - [ ] Move
Selecttocomponents/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
.tsxif 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)
- [ ]
SelectandLoaderare 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:
- Create new module exports alongside existing window assignments
- Test both paths work (old global way + new import way)
- Gradually migrate consuming code to use imports
- 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)