Skip to content

Comments

Migrate UI from Entur design system to MUI#2060

Open
testower wants to merge 66 commits intomasterfrom
rewrite-mui
Open

Migrate UI from Entur design system to MUI#2060
testower wants to merge 66 commits intomasterfrom
rewrite-mui

Conversation

@testower
Copy link
Collaborator

@testower testower commented Feb 6, 2026

Migrate the entire UI component library from @entur/* (17 packages) to MUI (Material UI), replace SCSS stylesheets with MUI sx props, add an MSW-based mock layer for backend-free development, and establish comprehensive test coverage.

Issue

This is a large modernization effort addressing several longstanding needs:

Motivation:

  • Pre-existing motivation to replace the @entur/* component packages with MUI for better theming support
  • SCSS stylesheets were scattered across 57 files with no theming support
  • Test coverage was minimal, making refactoring risky
  • Local development required a running Uttu backend and authentication

Technical approach:

  1. MUI migration (Phases 0-4): Replaced all @entur/* components with MUI equivalents (TextField, Autocomplete, Button, Dialog, Table, Accordion, etc.). All styling moved to sx props using MUI theme tokens. The Entur theme is loaded dynamically via the existing extPath config, with
    bare MUI as the default fallback.
  2. SCSS removal: Deleted 57 SCSS files (~2800 lines). Only base.scss (global resets), App/styles.scss (app shell), FormMap (Leaflet map styles), and src/ext/ overrides are retained intentionally.
  3. Hardcoded colors replaced: All hex color literals converted to MUI theme tokens (theme.palette.*, text.disabled, grey[100], currentColor).
  4. MSW mock layer: Added MSW v2 browser mocking with realistic Norwegian transit data. Run VITE_ENABLE_MOCKS=true npm start for fully functional local development without backend or auth.
  5. Extension system: Moved the Entur theme and logo into src/ext/Entur/, following the existing extPath convention. Removed dead Fintraffic CustomStyle extension (218 lines of SCSS targeting non-existent class names). Other deployments are unaffected.
  6. Code deduplication: Extracted FlexibleStopPlaceSelector shared component, useEntityEditor hook, EntityEditorActions, PassingTimesEditorList, StopPointBookingArrangement, and DeleteStopPointDialog — reducing duplication across editors.

Design decisions:

  • Used MUI sx prop exclusively (no styled() or makeStyles) for consistency
  • Stack/Box/Container for all layout, matching the Inanna reference project
  • E2E tests run against MSW mocks (no real backend needed in CI)

Unit tests

  • Added test files covering component scenes, custom hooks, helper functions, and E2E flows
  • Redux slice and action creator tests (24 files, 135 tests) have been extracted and merged separately via PR Add unit tests for Redux slices and config #2066
  • E2E tests cover: app loading, provider selection, lines/networks/exports/stop places listings, flexible line editor full wizard flow, and network CRUD
  • Playwright E2E tests added to CI workflows
  • Vitest coverage configured with v8 provider, lcov/html/text reporters
  • All existing tests updated to work with MUI components (new label texts, role queries, etc.)
  • Fixed 37 SonarCloud issues flagged on this PR

Documentation

  • No new external documentation files added
  • Code is self-documenting: MUI components follow standard MUI patterns
  • MSW mock data in src/mocks/mockData.ts serves as living documentation of the data model
  • Extension system (extPath config) documented in AGENTS.md

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

Visit the preview URL for this PR (updated for commit e55f72a):

https://ent-enki-dev--fintraffic-preview-gw2u3kes.web.app

(expires Thu, 26 Feb 2026 10:59:06 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 50f71a028b1cba336bad43cc33ce2755ca3f1855

@testower testower marked this pull request as ready for review February 9, 2026 08:30
@testower testower closed this Feb 9, 2026
@testower testower reopened this Feb 9, 2026
testower added a commit that referenced this pull request Feb 9, 2026
- Extract shared DefaultLogo component (S6478)
- Extract ServiceJourneyAccordion to reduce nesting (S2004)
- Extract helpers in mock handlers to reduce nesting (S2004)
- Fix test missing assertion (S2699)
- Merge duplicate imports (S3863)
- Remove unused imports (S1128)
- Replace deprecated InputProps with slotProps.input (S1874)
- Replace parseInt with Number.parseInt (S7773)
- Replace window with globalThis (S7764)
- Use Set.has() instead of Array.includes() (S7776)
- Use .some() instead of .find() for boolean checks (S7754)
- Fix children-as-props pattern (S6748)
- Fix array index keys (S6479)
- Remove unused props and assignments (S6767, S1854)
- Fix negated ternary condition (S7735)
- Use export...from for re-exports (S7763)
- Simplify async return (S7746)
testower added a commit that referenced this pull request Feb 10, 2026
- Extract shared DefaultLogo component (S6478)
- Extract ServiceJourneyAccordion to reduce nesting (S2004)
- Extract helpers in mock handlers to reduce nesting (S2004)
- Fix test missing assertion (S2699)
- Merge duplicate imports (S3863)
- Remove unused imports (S1128)
- Replace deprecated InputProps with slotProps.input (S1874)
- Replace parseInt with Number.parseInt (S7773)
- Replace window with globalThis (S7764)
- Use Set.has() instead of Array.includes() (S7776)
- Use .some() instead of .find() for boolean checks (S7754)
- Fix children-as-props pattern (S6748)
- Fix array index keys (S6479)
- Remove unused props and assignments (S6767, S1854)
- Fix negated ternary condition (S7735)
- Use export...from for re-exports (S7763)
- Simplify async return (S7746)
…ents)

- Install MUI packages (@mui/material, @emotion/react, etc.)
- Create MUI theme (src/theme.ts) with Entur brand colors
- Wrap app with ThemeProvider, CssBaseline, LocalizationProvider
- Migrate shared helper types (dropdown, errorHandling)
- Add getMuiErrorProps helper
- Migrate App Shell & Navigation to MUI
- Migrate Listing Scenes & Tables to MUI
- Migrate CRUD Scenes (Networks, Brandings, StopPlaces, Providers, DayTypes) to MUI
- Migrate Shared UI Components (Page, Dialogs, Chips, Notification, etc.) to MUI
- Update SCSS files to remove @entur/tokens references
- Migrate Line Editor Infrastructure (GeneralLineEditor, LineEditorStepper, NavigationButtons)
- Migrate BookingArrangementEditor (Modal→Dialog, Contrast→Box, form controls)
- Migrate Notices, TimeUnitPicker, FormMap to MUI
- Migrate JourneyPatternEditor + CopyDialog (Modal→Dialog)
- Migrate JourneyPatterns (Accordion, Modal→Dialog)
- Migrate ServiceJourneyEditor + CopyDialog
- Migrate ServiceJourneys (BulkDeleteDialog, NewServiceJourneyDialog)
- Migrate StopPointsEditor (3 variants: Generic, MixedFlexible, FlexibleAreasOnly)
- Migrate PassingTimesEditor (TimePicker→MUI X TimePicker)
- Migrate DayTypesEditor (MultiSelect→Autocomplete, DatePicker→MUI X DatePicker, Pagination→MUI Pagination)
- Update all associated SCSS files to remove @entur/tokens references
- Migrate JourneyPatternStopPointMap ext components to MUI
- Migrate Fintraffic Navbar extension to MUI (SideNavigation→List)
- Migrate remaining datepicker usages (CopyDialog, BookingArrangement)
- Migrate Exports icons to MUI icons
- Remove all @entur CSS imports from src/styles/index.scss
- Update src/styles/base/base.scss to remove @entur/tokens
- Create Fintraffic MUI theme (src/ext/Fintraffic/theme/fintrafficTheme.ts)
- Reduce Fintraffic CustomStyle SCSS from 1,080 to ~238 lines
- Update all remaining SCSS files to remove @entur/tokens references
- Remove 17 Entur design system packages from dependencies
- Remove unused type packages (@types/react-modal, @types/react-intl-redux)
- Remove @reach/* peer dependency overrides from package.json
- Delete unused src/styles/base/dimensions.scss
- Clean up comment references to @entur/tokens
- Verify: 0 remaining @entur imports, 172 MUI imports, build passes, all 1061 tests pass
- Replace left sidebar with top AppBar + right-side navigation Drawer
- Create Header component (AppBar with logo, provider selector, user actions)
- Create MenuDrawer component (right-side Drawer with navigation links)
- Enhance MUI theme with AppBar, Drawer, TableRow, TableCell overrides
- Remove hardcoded heading colors from typography (inherit text.primary)
- Gut global SCSS (base.scss, index.scss) - remove styles redundant with MUI theme
- Migrate ~40 listing/editor pages from div+className+SCSS to MUI Stack/Box/Grid+sx
- Delete ~18 SCSS files replaced by MUI sx props
- Update Fintraffic extension theme and custom styles for new layout
…lector, delete buttons, tables, notifications
…tches expected structure

Apollo Client's InMemoryCache requires __typename fields to properly
normalize and return nested objects. Without them, the cache fails to
store journeyPatterns, causing the line editor to crash with
"Cannot read properties of undefined (reading 'every')" when
currentStepIsValid tries to validate journey patterns.

Add __typename annotations to the LineEditorQuery mock handler response
for all nested GraphQL types (Line, JourneyPattern, ServiceJourney, etc).
Migrated 4 MUI Button components from inline icon children to
startIcon prop (Lines, FlexibleLines, Exports list, Exports viewer).
Added Loading wrapper to Lines page which was missing a loading
state for its useQuery data fetch.
… gap

- Add fullWidth prop to Name, Description, Public code, Private code TextFields
- Add mt:2 spacing between heading and Grid container
- Replace bare divs with Box/Stack in LineEditorStepper
- Add mt:3 gap between stepper and content area
- Use Stack with spacing for NavigationButtons, push Delete to far right
- Add fullWidth to Name, Description, Private Code in JourneyPatternEditor/General
- Add fullWidth to Name, Description, Public Code, Private Code in ServiceJourneyEditor
- Replace fixed width (260px) with fullWidth on QuayRefField, FrontTextTextField, BoardingTypeSelect
- Replace inline style= with MUI Box/Stack sx in JourneyPatternEditor,
  GenericStopPointEditor, GenericStopPointsEditor, StopPointOrder
- Restructure passing times: group TimePicker + DayOffsetDropdown pairs
  side-by-side in flex rows with proper spacing
- Add divider lines between passing time stops
- Constrain DayOffsetDropdown maxWidth to prevent stretching
- Replace bare divs with Typography in FixedPassingTimeTitle
- Preserve all classNames for Fintraffic extension compatibility
- Replace ArrowBack with ArrowUpward/ArrowDownward for reorder buttons
- Stack order number and arrows vertically with centered alignment
- Change stop point fields from flex-wrap to column layout with gap spacing
- Add padding-bottom before divider for breathing room
Stack Delete/Locate buttons vertically in StopPointButtonGroup ext component
and remove hardcoded maxWidth on stop points container so fields flex naturally
alongside the map.
Convert div style= to Box sx= in DayTypesEditor, AddButton,
ServiceJourneys, CopyDialog, and flexible stop point editors.
Replace cramped Table-based layout with Stack/Box for cleaner spacing,
left-aligned add button, and proper flex row for notice text + delete icon.
Update tests to match new DOM structure.
Move provider selector from center-left to right side of AppBar,
grouped with action icons. Make it compact (size="small").
Simplify MenuDrawer: remove duplicated logo/user/language/logout
(already in AppBar), keep navigation links only with minimal header.
- Add fullWidth to all TextFields and Autocompletes in booking editor
- Use Grid layout for booking time dropdown instead of percentage widths
- Move Save/Cancel buttons to DialogActions (standard MUI dialog pattern)
- Replace outer <div> with <Box>, <i> tag with Typography fontStyle
- Add missing flexibleLineType_* and duration.hours translation keys
  for en, nb, sv, fi locales
- Add multiple journey patterns and service journeys to mock data
  for realistic testing of multi-JP/SJ layouts
- Add Divider between JP groups in Service Journeys step
- Reduce excessive mt:8 spacing to mt:4 in SJ editor sections
- Change JourneyPatterns wrapper from div to Box
- Remove orphaned classNames from GenericPassingTimesEditor
…iv to Box

- Fix Download button stretching full-width in Export Viewer (alignSelf)
- Remove orphaned CSS classNames from passing time editors (Mixed, FlexibleAreas, TimeWindow)
- Convert plain <div> wrappers to MUI <Box> across 11 component files
- Convert inline styles to MUI sx props in FlexibleLineTypeDrawer and DayTypeEditor
Install Playwright Chromium and run E2E tests after unit tests.
The E2E tests use MSW mocks via VITE_ENABLE_MOCKS=true, so no
real backend is needed.
- GeneralLineEditor.test: Use proper Organisation/Network/Branding types
- BulkDeleteDialog.test: Use DAY_OF_WEEK enum, add isAvailable field
- flexibleStopPlacesSlice.test: Use Coordinate type for GeoJSON coords
- Lines.test: Remove invalid addTypename option from MockLink (Apollo 4.x)
- handlers: Add extPath to mock bootstrap config
Extract dateUtils, formatDuration, and isActive as testable pure
functions. Coverage rises from ~43% to ~59% (statements/lines),
with 1589 total tests passing and zero TypeScript errors.
- Extract shared DefaultLogo component (S6478)
- Extract ServiceJourneyAccordion to reduce nesting (S2004)
- Extract helpers in mock handlers to reduce nesting (S2004)
- Fix test missing assertion (S2699)
- Merge duplicate imports (S3863)
- Remove unused imports (S1128)
- Replace deprecated InputProps with slotProps.input (S1874)
- Replace parseInt with Number.parseInt (S7773)
- Replace window with globalThis (S7764)
- Use Set.has() instead of Array.includes() (S7776)
- Use .some() instead of .find() for boolean checks (S7754)
- Fix children-as-props pattern (S6748)
- Fix array index keys (S6479)
- Remove unused props and assignments (S6767, S1854)
- Fix negated ternary condition (S7735)
- Use export...from for re-exports (S7763)
- Simplify async return (S7746)
Round 2 of test coverage improvements: 15 new test files and 5 expanded
existing ones covering DayTypesEditor, Notices, ConfirmNavigationDialog,
MixedFlexibleStopPoints, CoordinatesInputField, StopPlaceTypeDropdown,
LanguagePickerMenu, Providers, NoSelectedProvider, LineEditor, and
FlexibleLineEditor scenes plus delete flows for listing pages.
…tomTheme

The 218 lines of SCSS in CustomStyle targeted class names that no longer
exist after the MUI migration. Move the Public Sans font-face import into
fintrafficTheme.ts and delete the CustomStyle directory, its ComponentToggle
entry, and orphaned classNames in PassingTimePicker and FixedPassingTimeTitle.
…pPlaceSelector, LinesForExport, MenuDrawer, and ServiceJourneyEditor

New test files:
- WeekdayPicker component render tests (renamed .ts → .tsx, 6 new)
- PassingTimePicker component tests (8 new)
- FlexibleStopPlaceSelector tests (6 new)

Extended test files:
- LinesForExport: sorting and indeterminate checkbox (3 new)
- MenuDrawer: unsaved changes navigation guard (3 new)
- ServiceJourneyEditor: delete dialog and booking editor (5 new)
- Delete legacy notices.test.jsx (duplicate of Notices.test.tsx)
- Add .test.jsx to sonar.test.inclusions in CI workflows
- LinesForExport: sort by status/date columns, mapLine catch path
- CopyDialog: UI render tests (title, fields, multiple switch, save/cancel)
- NewServiceJourneyDialog: JP selection change, unnamed JP fallback
- DayTypeAssignmentsEditor: delete row, add button
- WeekdayPicker: filled/outlined chip variant branches
- PassingTimePicker: undefined/empty selectedTime branches
- ServiceJourneyEditor: operatorRef preselected, copy dialog
- StopPointBookingArrangement: new test file for adapter callbacks
… JourneyPatterns, ServiceJourneys, Header, and NetworkEditor

Add 33 new tests covering uncovered branches: booking limit radio switching,
flexible line type/booking sections, JP copy/add modal flows, SJ copy/bulk-delete
dialogs, header popover/drawer interactions, and network editor delete/save/loading.
…ourneyEditor, GeneralLineEditor, and small components

Extract compareExportableLines sort comparator from LinesForExport for
direct unit testing. Add tests for DayTypeAssignmentsEditor (toggle,
validation, delete, add), ServiceJourneyEditor (copy/delete dialogs,
notices, passing times), GeneralLineEditor (flexible line branches,
description/privateCode null handling, publicCode config). New test files
for Loading, OverlayLoader, Page, DayOffsetDropdown, and TimeUnitPicker.
Increase test timeout to 10s to prevent flaky timeouts under load. Fix
EnkiIntlProvider test async pattern.
…DayTypeAssignmentsEditor

- ServiceJourneyEditor: test operator Autocomplete (render options,
  select, clear, preselected value) and publicCode onChange
- PassingTimePicker: test onChange callback via keyboard interaction
- DayTypeAssignmentsEditor: test date change handlers and prop sync
@testower testower changed the title Rewrite mui Migrate UI from Entur design system to MUI Feb 10, 2026
…, GeneralLineEditor, DayTypes, CopyDialog

- Add LineMigration test file (13 tests): form rendering, error state,
  migration success/failure, API error handling, dry run toggle
- Add DayTypesModal test file (2 tests): open/closed rendering
- Extend BookingArrangementEditor tests: minimumBookingPeriod branch,
  STOP_POINT and SERVICE_JOURNEY attachment labels, contact field handlers
- Extend GeneralLineEditor tests: non-flexibleAreasOnly type change,
  lineSupportedVehicleModes config branch
- Extend DayTypesModalContent tests: add new day type flow
- Extend CopyDialog tests: validation error state, departure time picker
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant