User Interface medium complexity mobile
2
Dependencies
1
Dependents
0
Entities
1
Integrations

Description

Inline bottom sheet presented within the expense wizard that offers camera capture and file-picker upload options without navigating away from the form. Handles modal presentation, option selection, and dismissal with full VoiceOver/TalkBack accessibility labels describing the attachment state.

Feature: Receipt Capture and Attachment

receipt-camera-sheet

Summaries

Attaching receipt evidence to expense claims is a core compliance requirement, and this component makes that process as frictionless as possible by presenting attachment options as a contextual bottom sheet directly within the claim wizard — eliminating the disorienting navigation away from the form that typically causes users to abandon multi-step workflows. Full accessibility support via VoiceOver and TalkBack ensures that peer mentors with visual impairments can participate equally in the claims process, which is both a user inclusion imperative and a legal compliance consideration for organisations subject to accessibility standards such as WCAG and EN 301 549.

This medium-complexity UI component depends on both the `receipt-image-picker-integration` and `receipt-thumbnail-preview` components, meaning those must be completed or at minimum interface-stable before this component can be fully integrated and tested end-to-end. The accessibility annotation requirement (VoiceOver/TalkBack semantic labels) adds a testing overhead that should be accounted for in QA planning — ideally with device testing on both iOS and Android with screen readers enabled. Modal presentation behaviour, loading state transitions, and cancellation handling each represent distinct test scenarios. Plan for an accessibility review pass before sign-off.

Implemented as a Flutter `ModalBottomSheet` presented via `showModalBottomSheet()` from within the expense wizard scaffold. The `show()` method accepts a `BuildContext` and an `onImageSelected` callback, keeping the sheet decoupled from form state management. The `setLoadingState(bool)` method drives a `CircularProgressIndicator` overlay while the image picker or camera is resolving. Accessibility labels are set via Flutter's `Semantics` widget wrapping each action button, with `announceAccessibilityState()` triggering `SemanticsService.announce()` for dynamic state changes.

Delegates image acquisition to `receipt-image-picker-integration` and delegates preview rendering to `receipt-thumbnail-preview`, maintaining single-responsibility boundaries.

Responsibilities

  • Present camera and file-picker action options
  • Open inline within wizard without full navigation
  • Announce attachment state via VoiceOver/TalkBack semantic labels
  • Display loading indicator while image is being captured or selected
  • Handle dismissal and cancellation gracefully

Interfaces

show(context, onImageSelected)
dismiss()
setLoadingState(bool)
announceAccessibilityState(String label)
onCameraSelected()
onFilePickerSelected()
onCancelled()

Relationships

Dependencies (2)

Components this component depends on

Dependents (1)

Components that depend on this component

Used Integrations (1)

External integrations and APIs this component relies on