Skip to content

[WOOMOB-3761] Add Store design system segment control and modal sheet - #16373

Merged
hichamboushaba merged 11 commits into
feature/store-design-system-migrationfrom
issue/woomob-3761-implementation
Aug 13, 2026
Merged

hichamboushaba merged 11 commits into
feature/store-design-system-migrationfrom
issue/woomob-3761-implementation

Conversation

@hichamboushaba

@hichamboushaba hichamboushaba commented Aug 5, 2026 •

Copy link
Copy Markdown
Member

Description

Fixes WOOMOB-3761

Promotes Segment Control and Modal Bottom Sheet from preview-only Store design-system examples into production Woo* components, then migrates both Dashboard-owned sheet flows to consume them.

WooSegmentControl keeps caller-owned selection and localization, exposes radio/selectable semantics, preserves the 36dp track and 32dp pill, and provides a 48dp interaction target without clipping large text. WooModalBottomSheet keeps experimental Material types internal while delegating gestures, scrim, back handling, focus, semantics, system insets, IME behavior, and width to Material. Its Store styling supplies the Surface Bright container, rounded top corners, and drag handle defined by the canonical Figma component.

The Performance card now uses these components for revenue segments and order-date selection. The Analytics updates sheet also moves from its legacy WCBottomSheetDialogFragment and navigation destination into the Dashboard's design-system composition, keeping the Dashboard visible behind the scrim while Material owns Back, scrim, and swipe dismissal.

The scheduled-import feature retains its existing copy, repository calls, Learn more behavior, success/failure handling, and Dashboard analytics. Sheet visibility and the last server-confirmed option survive recreation, while optimistic, loading, and error state remain transient. Manual dismissal cancels in-flight UI work; if the server has already applied a change, the Dashboard's existing repository observer converges to that stored value and reseeds the sheet the next time it opens.

Focused component and ViewModel tests, catalog previews, and design-system documentation are included.

Note

Known dark-theme limitation: The modal sheet currently has limited contrast against the surrounding surface. We will address this in a separate PR once Design provides the intended treatment. p1786013954706019/1785758642.339779-slack-C03L1NF1EA3

Test Steps

  1. Install this branch's Wasabi debug build and open My Store with the Performance card visible.
  2. Select Total, Gross, and Net; confirm the selected pill moves correctly, labels remain readable, and the displayed statistics update.
  3. Tap the Paid orders selector; confirm the order-date sheet opens at the bottom with a bright surface, rounded top corners, no outlined boundary, and a drag handle.
  4. Drag the sheet slightly and let it settle, then dismiss it with Back or the scrim; confirm only the sheet closes.
  5. Reopen the order-date sheet and select Placed orders or Completed orders; confirm it closes after the update succeeds and the Performance card reflects the selected type.
  6. Tap the Last update row or its Analytics update settings info icon; confirm the Analytics updates sheet opens over the still-visible Dashboard and shows Scheduled, Immediately, the current selection, and Learn more.
  7. Dismiss the Analytics updates sheet with Back, the scrim, and a downward swipe, reopening it between methods; confirm each action closes only the sheet.
  8. Reopen it and tap the already-selected update option; confirm the sheet closes without changing the store-wide setting.

Images/gif

Segment Control

Light Dark
Dashboard Segment Control in the light theme Dashboard Segment Control in the dark theme

Modal Bottom Sheet

Light Dark
Order date modal bottom sheet in the light theme Order date modal bottom sheet in the dark theme
  • I have considered if this change warrants release notes and have added them to RELEASE-NOTES.txt if necessary. Use the "[Internal]" label for non-user-facing changes.

@hichamboushaba hichamboushaba added type: enhancement A request for an enhancement. feature: stats Related to stats shown in the app. category: design Layout and style elements in the UI or user interface, including color and animations. category: accessibility Related to accessibility. category: unit tests Related to unit testing. compose Uses Jetpack Compose framework labels Aug 5, 2026
@hichamboushaba hichamboushaba added this to the 25.4 milestone Aug 5, 2026
@dangermattic

dangermattic commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator
2 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ Class WooModalBottomSheetState is missing tests, but unit-tests-exemption label was set to ignore this.

Generated by 🚫 Danger

@wpmobilebot

wpmobilebot commented Aug 5, 2026 •

Copy link
Copy Markdown
Collaborator

App Icon📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App NameWooCommerce Android
Platform📱 Mobile
FlavorJalapeno
Build TypeDebug
Build Number776
Version25.3-rc-1
Application IDcom.woocommerce.android.prealpha
Commit7fdf0db
Installation URL1vgeicsur6d78
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@hichamboushaba
hichamboushaba marked this pull request as ready for review August 6, 2026 12:26

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Code Review - Found 1 potential issue

This is a clean design-system migration: the scheduled-import bottom sheet moves off a WCBottomSheetDialogFragment onto the new WooModalBottomSheet, and WooSegmentControl / WooModalBottomSheet are promoted to production APIs. A few things I verified positively:

  • The removed ScheduledImportInfoBottomSheetFragment, its nav-graph <dialog> entry, and the action_dashboard_to_scheduledImportInfoBottomSheet action are fully cleaned up — no dangling references remain in the codebase.
  • The ViewModel's move from nav-args to a hosted show()/onDismissed() model correctly persists isVisible/confirmedIsEnabled in SavedStateHandle while keeping isUpdating/hasError transient, and ensureActive() after setEnabled() properly guards against writing state on a dismissed/cancelled update. Restoration and cancellation paths are well covered by the new tests.
  • WooSegmentControl exposes correct selectableGroup/Role.RadioButton semantics and a 48dp interaction target, and stays fully controlled (index in, index out).

One minor UX note is inline.

PR housekeeping
  • Applied repo AGENTS.md/CLAUDE.md guidance: verified store-app MVVM patterns (ScopedViewModel, triggerEvent, StateFlow for new state) and that no Android framework types leak into the ViewModel.

Automatic review · claude-opus-4-8 · Workflow run

How to reply to a finding

Reply on this review (or inline at the line the finding refers to) with one of:

  • @claude addressed - I made the change. Bot verifies against the next diff before marking resolved.
  • @claude rejected: <reason> - Will not fix; reason gets quoted on the next review.
  • @claude not-applicable - Finding does not apply (wrong file, already covered elsewhere, etc.).

The bot honours these on the next review pass.

onOptionSelected: (Boolean) -> Unit,
onLearnMoreClick: () -> Unit,
) {
if (!state.isVisible) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Code Review [nit]

Issue: Visibility is driven purely by removing the composable from composition (if (!state.isVisible) return, with the parent onDismissed() flipping isVisible to false). When the sheet auto-closes after a successful save (SettingUpdated -> onDismissed()), the ModalBottomSheet is dropped from composition immediately, so Material's slide-down exit animation is skipped. The old WCBottomSheetDialogFragment.dismiss() animated on close, so this is a subtle regression on the programmatic-dismiss path (user-initiated swipe/scrim dismissals still animate). Same pattern applies to OrderDateTypeBottomSheet.

Suggestion: If preserving the exit animation matters, drive dismissal through the state the design system already exposes — e.g. scope.launch { sheetState.hide() }.invokeOnCompletion { if (!sheetState.isVisible) onDismissed() } — instead of removing the composable outright. Otherwise it's fine to accept the tradeoff as the standard conditional-composition idiom.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 01c8737c59d. Both Dashboard sheets now animate through WooModalBottomSheetState.hide() and leave composition only after reaching Hidden; I also verified the programmatic dismissal path on a Pixel 8a running API 36.

@hichamboushaba
hichamboushaba force-pushed the issue/woomob-3761-implementation branch from 6edd7ea to 9316e6e Compare August 6, 2026 12:32
@hichamboushaba
hichamboushaba force-pushed the feature/store-design-system-migration branch from 217d5bd to 62e0338 Compare August 6, 2026 21:12
@hichamboushaba
hichamboushaba requested review from a team as code owners August 6, 2026 21:12
@AdamGrzybkowski

Copy link
Copy Markdown
Contributor

Hey @hichamboushaba, the diff here is massive - is there a problem with the branch not being updated, or is that expected?

@wpmobilebot wpmobilebot modified the milestones: 25.4, 25.5 Aug 7, 2026
@wpmobilebot

Copy link
Copy Markdown
Collaborator

Version 25.4 has now entered code-freeze, so the milestone of this PR has been updated to 25.5.

@hichamboushaba
hichamboushaba force-pushed the feature/store-design-system-migration branch from 62e0338 to e903c9f Compare August 9, 2026 12:43
@AdamGrzybkowski AdamGrzybkowski self-assigned this Aug 10, 2026

@AdamGrzybkowski AdamGrzybkowski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good in general, left two comments/suggestions.

One weird thing that needs to be looked at (or at least confirmed it's real) is this weird slow animation that expand the bottom sheet to full screen height in landscape mode.

I've tried on two emulators and got the same result, but a physical devices works fine.

Screen_recording_20260810_125719.mp4

Comment on lines +89 to +103
val dismissOrderDateTypeBottomSheetWithAnimation: () -> Unit = {
if (!isOrderDateTypeBottomSheetDismissing) {
isOrderDateTypeBottomSheetDismissing = true
coroutineScope.launch {
try {
orderDateTypeBottomSheetState.hide()
if (!orderDateTypeBottomSheetState.isVisible) {
showOrderDateTypeBottomSheet = false
}
} finally {
isOrderDateTypeBottomSheetDismissing = false
}
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ We will need this logic every time we use the bottom sheet (two places already) - would it make sense to extract that to a helper funciton?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same question, and thought about adding a helper, but then later I decided to defer that. This logic is also needed for the M3 bottom sheet (https://developer.android.com/develop/ui/compose/components/bottom-sheets#control-sheet-state), our implementation is just a thin wrapper.

I will still think more about it, and reply later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a helper in 7fdf0db, please let me know what you think.

@hichamboushaba

hichamboushaba commented Aug 10, 2026 •

Copy link
Copy Markdown
Member Author

Scrim update: WooTheme.colors.overlay.overlay50 now resolves to Gray 80 (#2C3338) at 50% in both light and dark themes, replacing the previous black 50% light / 75% dark values. This matches the current Figma variable export and the guidance we received. WooModalBottomSheet passes that semantic token to Material as scrimColor, while Material continues to own scrim rendering and behavior. We kept the existing overlay50 naming and limited the implementation to the token value and component usage. The light/dark treatment is still subject to final confirmation from Design, and we will adjust the token if the designer confirms a different mode-specific value.

Light Dark
Screenshot_1786385118 Screenshot_1786385175

@hichamboushaba

Copy link
Copy Markdown
Member Author

Ready for another round @AdamGrzybkowski

@hichamboushaba
hichamboushaba requested review from AdamGrzybkowski and removed request for a team August 10, 2026 18:49

@AdamGrzybkowski AdamGrzybkowski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! :shipit:

@hichamboushaba
hichamboushaba merged commit 1e46b55 into feature/store-design-system-migration Aug 13, 2026
14 of 17 checks passed
@hichamboushaba
hichamboushaba deleted the issue/woomob-3761-implementation branch August 13, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: accessibility Related to accessibility. category: design Layout and style elements in the UI or user interface, including color and animations. category: unit tests Related to unit testing. compose Uses Jetpack Compose framework feature: stats Related to stats shown in the app. type: enhancement A request for an enhancement. unit-tests-exemption

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants