Compose calculation preview panel with total row
epic-expense-type-selection-user-interface-task-008 — Compose the full ExpenseCalculationPreview panel from ReimbursementLineItem rows plus a bold total row. The panel must be stateless, accept a CalculationResult model, and render the zero-state placeholder when the model is null. Layout must be vertically scrollable if many line items are present.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Define CalculationResult as an immutable Dart class (or use freezed if already adopted in the codebase) with a const constructor. Keep the total amount calculation out of the widget — CalculationResult should carry a pre-computed totalAmount to keep the widget pure. Use a Column inside SingleChildScrollView rather than a shrinkwrap ListView to keep physics consistent with the parent scroll context. Apply a visual weight distinction to the total row using AppTypography.titleSmall or equivalent bold token.
The Divider before the total row should use the design system's divider color token to respect dark/light theming.
Testing Requirements
Widget tests with flutter_test. Cover: (1) null CalculationResult renders placeholder and no line items, (2) CalculationResult with 3 items renders exactly 3 ReimbursementLineItem widgets, (3) total row displays sum matching CalculationResult.totalAmount formatted as NOK, (4) empty line items list (non-null result) renders total row with 'kr 0,00' and no line item rows, (5) panel is scrollable — verify ScrollController or SingleChildScrollView presence. Golden snapshot test for the 3-item state and the zero-state placeholder.
If the expense calculation preview subscribes to the full BLoC state stream, every unrelated state property change (e.g. a loading flag toggle) triggers a widget rebuild. With complex card animations for the disabled-state transition, this could cause frame drops on low-end Android devices used by some peer mentors.
Mitigation & Contingency
Mitigation: Use select() on the Riverpod provider to subscribe only to the specific state slice each widget needs; write a performance test asserting rebuild count on a rapid sequence of toggle events.
Contingency: If jank is detected in device testing, replace animated disabled-state transitions with instant opacity changes and defer animation polish to a follow-up sprint.
The disabled card state requires a specific contrast-safe colour combination that communicates unavailability without relying solely on colour (WCAG 1.4.1). If the current design token palette does not include a disabled-state token with sufficient contrast for text on the disabled background, the widget will either fail WCAG AA or require a last-minute design token addition that could break other components.
Mitigation & Contingency
Mitigation: Audit the existing design token manifest for disabled-state tokens at the start of the epic; if missing, raise with the design lead and add a contrast-validated token before widget implementation begins.
Contingency: If no design review is available, use the established --color-text-disabled and --color-surface-disabled tokens with an added strikethrough or lock icon to satisfy WCAG 1.4.1 non-colour requirement, and document the deviation for design review.