React/TypeScript frontend for Red Hat's Image Builder service, running on console.redhat.com as a federated micro-frontend. Also available as a Cockpit plugin for on-premises use.
- React 18 with TypeScript
- Redux Toolkit / RTK Query for state management and API calls
- PatternFly 6 component library
- Webpack with module federation
- Vitest + React Testing Library for unit tests
- Playwright for E2E tests
| Directory | Purpose |
|---|---|
src/Components/ |
React components organized by feature |
src/store/slices |
Redux store, slices, and RTK Query API definitions |
src/Utilities/ |
Shared utility functions |
src/Hooks/ |
Custom React hooks |
src/test/ |
Vitest utilities and legacy integration tests |
api/config/ |
RTK Query codegen configuration files |
playwright/ |
Playwright E2E tests |
cockpit/ |
Cockpit plugin build configuration |
npm ci # Install dependencies (prefer over npm install)
npm run start:prod # Run against production
npm run start:stage # Run against staging
npm run test # Run all Vitest tests
npm run test:unit # Run unit tests only (co-located with components)
npm run test:integration # Run integration tests only (src/test/) - legacy
npm run lint # Auto-fix lint errors
npm run format # Auto-format code with Prettier
npm run format:check # Check formatting (CI)
npm run api # Regenerate RTK Query API code from OpenAPI specs
npm run build # Production build
npm run build:cockpit # Build Cockpit pluginAPI endpoints are auto-generated from OpenAPI schemas using @rtk-query/codegen-openapi.
Configuration files: api/config/*.ts
Each config specifies:
- Remote OpenAPI schema URL
- Empty API slice to extend
- Output file location
- Endpoints to generate
To add a new endpoint: Update the filterEndpoints array in the relevant config file, then run npm run api.
To add a new API: Follow the pattern in README.md - create an empty API slice in src/store/, add the config in api/config/, and update eslint.config.js to ignore the generated file.
Formatting is handled by Prettier (npm run format). ESLint handles linting only — it does not enforce formatting rules.
- Avoid JSDoc comments - TypeScript types document function signatures sufficiently
- Comments should explain why, not what - Code should be self-explanatory; use comments only to clarify intent or non-obvious decisions
- Focused changes - PRs should address a single concern; avoid unrelated refactors or improvements outside the task at hand
- Atomic commits - Each commit should represent a logical change
- Use
typedeclarations, notinterface- Always usetypefor TypeScript type definitions - Use PatternFly components; follow their usage patterns
- Follow existing file naming conventions (PascalCase for components)
- Keep components focused and reasonably sized
- Use RTK Query hooks for data fetching, not manual fetch calls
Imports are enforced alphabetically by ESLint. Group order:
- External packages
- Internal modules (absolute paths using
@/alias) - Relative imports (
./and../only)
Import paths: Prefer the @/ alias for imports outside the current or parent directory. Relative imports should be limited to ./ (current directory) and ../ (parent directory). Avoid deep relative paths like ../../ or deeper.
- All UI contributions must include tests
- Unit tests are co-located with components in a
tests/subdirectory (e.g.,src/Components/Feature/tests/Feature.test.tsx) - Unit test directories can include:
mocks/- Component-specific mock data and vitest mocks for API responseshelpers.tsx- Shared test utilities and render wrappers
- Use React Testing Library patterns (query by role, text, etc.)
- Mock API responses using vitest mocks; MSW is legacy and should not be used for new tests
- Integration tests in
src/test/are legacy; prefer co-located unit tests for new work - Playwright tests for E2E coverage of critical flows
- The
TZ=UTCprefix is applied automatically by npm scripts
Uses Unleash for feature toggles in the hosted service. Import useFlag from src/Utilities/useGetEnvironment.ts to check flag status:
import { useFlag } from '../../Utilities/useGetEnvironment';
const isEnabled = useFlag('image-builder.my-feature');On-premises (Cockpit): Unleash is not available, so useFlag uses a local onPremFlag function instead. To enable a flag for on-premises, add a case to the switch statement in onPremFlag.
Ephemeral environments: Use useFlagWithEphemDefault to provide a default value for flags in QA/ephemeral environments.
Mock flags in tests when testing both code paths.
- Hosted service - Federated module for console.redhat.com
- Cockpit plugin - Standalone plugin for on-premises use (
npm run build:cockpit)