This repository contains a React Native session-booking example for iOS and Android. The feature demonstrates a maintainable booking flow with date navigation, filtering, pagination, accessible selection state, and booking confirmation.
The original screen mixed data loading, layout, selection state, and controls in one implementation. The highest-priority issues were:
- Interactive controls did not consistently expose meaningful accessibility names or selected state.
- Date navigation and session cards were difficult to reuse because their state and presentation were coupled.
- The layout had no tablet strategy and relied on a single narrow list presentation.
- Filtering and date selection were not represented as reusable, testable interactions.
- Loading, empty, and booking-confirmation states were not clearly communicated to users.
The refactor addresses the functional and structural issues in the source code. Device-level accessibility and layout verification still requires the QA plan below.
The booking flow is organized around small components with explicit props:
HomePageControllerowns date, filter, pagination, selection, and confirmation state.SessionListowns list rendering, loading, empty, pagination, and responsive column layout.SessionCardis a reusable accessible selection target.SessionDetailControlleris a dedicated detail surface opened by the card's separate “View details” action; navigation passes explicit selection and booking-confirmation callbacks throughpassProps, avoiding global controller state.WeekDateSelectorprovides week navigation and date accessibility state.FilterActionSheetandDatePickerActionSheetisolate draft state from applied state.SelectedSessionBarsummarizes the current selection and exposes Continue/Clear actions.SessionConfirmationModalowns confirmation, simulated submission loading, success animation, and dismissal.sessionBookingServiceprovides a replaceable paginated data boundary instead of coupling the UI to mock data.
This keeps screen orchestration in one place while making the controls independently testable and reusable for future booking surfaces.
- Buttons expose
accessibilityRole="button"and meaningful labels. - Dates, quick filters, filter options, and session cards expose
accessibilityState.selected. - Disabled past dates and disabled submit state are represented semantically, not only through color.
- Touch targets use a minimum height of approximately 40 to 48 points.
- Selected sessions have both a visual check indicator and a semantic selected state.
- Loading, empty results, filter changes, selection changes, and confirmation success are represented as explicit UI states and announced with
AccessibilityInfo. - Bottom sheets expose modal context, and profile/confirmation surfaces move accessibility focus to their heading or success message when opened.
- Icons are paired with surrounding text or accessible labels so they do not carry meaning alone.
Further device validation should verify VoiceOver, TalkBack, keyboard navigation, focus order, and announcement timing on real devices.
The layout uses a 768 point breakpoint:
- Phones use a single-column session list with compact horizontal padding.
- Tablets use a centered content region capped at 960 points and a two-column session grid.
- The selected-session summary uses the same maximum content width on tablets.
- Header collapse, list scrolling, and bottom sheets remain shared across screen sizes.
- Date controls preserve minimum touch targets and use flex wrapping for filter and session metadata.
The native navigation bar is configured through React Native Navigation, while the booking UI remains platform-neutral React Native components.
The booking surface uses two shared styling layers: bookingTheme owns colors, spacing, radii, touch targets, layout, and motion values, while bookingStyles owns the reusable NativeWind class recipes and React Native styles for buttons, chips, cards, icon controls, and selected indicators. Stateful variants are generated through a shared helper rather than repeated inline class strings.
The example includes category, availability, and time-of-day filters, quick filters, date-specific schedules, pagination, selection toggling, independent session details, and a confirmation modal. Changing a date or filter clears the previous selection immediately. Confirming a session shows a simulated request loading state, then an animated success state before dismissing.
Screenshots and a recorded walkthrough of the iOS booking flow are available in the iOS demo.
Screenshots of the Android booking flow are available in the Android demo.
Run the test suite with:
npm test -- --runInBand
npx tsc --noEmitThe test suite covers session service pagination and date data, component selection and accessibility state, filter and date-sheet interactions, responsive list configuration, confirmation wiring, and booking loading behavior.
Before release, validate the following on iOS and Android:
- VoiceOver and TalkBack can identify date controls, filters, session cards, selected state, loading state, and confirmation actions.
- Keyboard or hardware navigation follows a logical order and does not focus hidden sheet content.
- Previous-day and date-picker minimum-date behavior cannot select a past date.
- Small phones, large phones, and tablets render without clipping or overlapping; verify both portrait and landscape.
- Tablet two-column cards maintain readable text, stable touch targets, and a centered content width.
- Date changes, quick filters, filter-sheet changes, pagination, selection toggling, cancellation, and confirmation produce consistent results.
- Loading indicators, empty states, disabled buttons, and success animation are understandable without relying on color alone.
Automated checks completed on 2026-08-19:
- Jest: 10 suites, 48 tests passed.
- TypeScript:
npx tsc --noEmitpassed. - ESLint: no errors; one existing
react-native/no-inline-styleswarning remains inindex.js. - Regression coverage includes native profile-button handling, selection announcements, modal states, date/filter announcements, responsive layout, and minimum-date behavior.
Manual device checks remain release-gate work: VoiceOver/TalkBack focus order, hardware keyboard navigation, iOS/Android announcement timing, portrait/landscape rotation, and small-phone/tablet visual inspection must be run on physical or simulator devices.
- The service is a deterministic local mock; no authentication, persistence, conflict handling, or real booking API is included.
- The profile navigation button opens a lightweight local profile modal; profile persistence and account navigation are outside this example.
- The success animation simulates a request delay and is not a server-confirmed reservation.
- Visual regression and real-device accessibility tests are left to the QA pass rather than automated in Jest.
Install dependencies, start Metro, then run one platform:
npm install
npm start
npm run ios
# or
npm run android