Skip to content

feat(Android, Stack v5): expose showAsAction prop for toolbar menu items#4101

Open
kligarski wants to merge 9 commits into
mainfrom
@kligarski/stack-v5-android-toolbar-menu-show-as-action
Open

feat(Android, Stack v5): expose showAsAction prop for toolbar menu items#4101
kligarski wants to merge 9 commits into
mainfrom
@kligarski/stack-v5-android-toolbar-menu-show-as-action

Conversation

@kligarski
Copy link
Copy Markdown
Contributor

@kligarski kligarski commented May 26, 2026

Description

Adds showAsAction property to toolbar menu items in header on Android in Stack v5.

Closes https://github.com/software-mansion/react-native-screens-labs/issues/1202.

API shape

The initial API idea was:

showAsAction?: 'always' | 'ifRoom' | 'never' | undefined;
showAsActionWithText?: boolean | undefined;

But this turned out to be problematic on the native side. You can only set the
showAsAction flags, you can't read them from the MenuItem. If the user
changed only one of these 2 props, we wouldn't know all the flags that need to
be applied. We could add state to the ToolbarMenuCoordinator or HeaderConfig
but we would like to avoid it.

I decided to merge these 2 props into one enum. For never, we don't need the
withText modifier so there are only 5 options possible which isn't bad.

We could also use nested object but we've been trying to avoid complicated
nesting.

The final API therefore looks like this:

showAsAction?: 'always' | 'alwaysWithText' | 'ifRoom' | 'ifRoomWithText' | 'never' | undefined;

I'm open to suggestions though.

Codegen problem

When adding the
showAsAction?: CT.WithDefault<StackHeaderToolbarMenuItemShowAsActionAndroid, 'never'>;
prop to StackHeaderToolbarMenuItemAndroid interface in native spec on Android,
iOS build stopped working. In Props.h, all types are generated (also for
Android-only components). For some reason, if we use nested object as an array
( toolbarMenuItems?: StackHeaderToolbarMenuItemAndroid[] | undefined;), the
enum type isn't generated which causes the build to fail. After I added
DO_NOT_USE?: StackHeaderToolbarMenuItemAndroid | undefined; property, the enum
is generated correctly. I assumed that it's better to add this workaround
instead of losing the typing completely.

Limited support for ifRoom

ifRoom option doesn't react to orientation changes. The maximum width of
action items is determined on first layout and it remains that way even through
layout/orientation changes. This means that if you open the screen in portrait,
more items won't move from overflow menu to the toolbar when rotated to
landscape and if you open the screen in landscape, rotating to portrait won't
make the items move to the overflow menu, usually resulting in the title being
shrunk down.

portrait -> landscape landscape -> portrait
portrait_to_landscape.mp4
landscape_to_portrait.mp4

This happens due to native implementation. Typically on Android, every
orientation change causes the activity to restart. The maximum width of action
menu items is evaluated only on initialization of ActionMenuProesenter
(initForMenu). If activity restarts are disabled, as is the case in
react-native apps, the initially set width will still apply.

Note

By the way, this value turns out to be half of the width of the device:

public int getEmbeddedMenuWidthLimit() {
    return mContext.getResources().getDisplayMetrics().widthPixels / 2;
}

This value is then made smaller by subtracting the width of the collapse icon.

I tried looking for a way to force this value to reset. Unfortunately, I didn't
find any way that wouldn't involve heavy reflection usage or completely
resetting the menu, resulting in the loss of state (i.e. changes applied via
view commands would be completely lost). I think that keeping the state is more
important in this case but I'm open for discussion here.

While testing this I also noticed some problems with handling orientation
changes for the screen. I will investigate this separately
(https://github.com/software-mansion/react-native-screens-labs/issues/1499).

orientation_bug.mp4

Changes

  • add showAsAction prop and StackHeaderToolbarMenuItemShowAsAction(Android) enums
  • add DO_NOT_USE?: StackHeaderToolbarMenuItemAndroid | undefined; to native spec to ensure generation of enum in Props.h to fix build on iOS
  • minor fixes:
    • expose StackHeaderToolbarMenuItemClickedEvent
    • make clear, updateItem internal in ToolbarMenuCoordinator
    • adjust test-stack-toolbar-menu-commands to new convention

Visual documentation

showAsAction.mp4

Test plan

Run test-stack-toolbar-menu-show-as-action-android SFT.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

@kligarski kligarski requested a review from Copilot May 26, 2026 12:53
@kligarski kligarski marked this pull request as ready for review May 26, 2026 12:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an Android-only showAsAction configuration for Stack v5 (gamma) header toolbar menu items, including support for runtime updates via the existing setToolbarMenuItemOptions command, plus a codegen workaround to keep iOS builds passing.

Changes:

  • Introduces showAsAction for toolbarMenuItems (props + imperative updates) with a new enum mapping to Android MenuItem#setShowAsAction flags.
  • Adds a Fabric codegen workaround prop (DO_NOT_USE) to force enum generation in Props.h and avoid iOS build failures.
  • Adds/updates Single Feature Test scenarios and aligns scenario-description conventions.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/fabric/gamma/stack/StackHeaderConfigAndroidNativeComponent.ts Adds showAsAction to the codegen spec for toolbar menu items and introduces DO_NOT_USE workaround prop.
src/components/gamma/stack/index.ts Re-exports newly exposed Android types (StackHeaderToolbarMenuItemClickedEvent, StackHeaderToolbarMenuItemShowAsActionAndroid).
src/components/gamma/stack/header/StackHeaderConfig.android.types.ts Documents and exposes the showAsAction union type and prop in the public experimental API types.
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-show-as-action-android/scenario.md Adds manual test scenario coverage for all showAsAction variants and command updates.
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-show-as-action-android/scenario-descriptions.ts Adds scenario metadata for the new SFT.
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-show-as-action-android/index.tsx Implements the new SFT screen that drives prop + command updates for showAsAction.
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-commands-android/scenario.md Updates scenario formatting/details (OS version + prerequisites wording).
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-commands-android/scenario-descriptions.ts Extracts scenario metadata into the shared convention file.
apps/src/tests/single-feature-tests/stack-v5/test-stack-toolbar-menu-commands-android/index.tsx Uses extracted scenarioDescription instead of inline definition.
apps/src/tests/single-feature-tests/stack-v5/index.ts Registers the new SFT scenario in the Stack v5 group.
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/toolbar/StackHeaderToolbarMenuItemShowAsAction.kt Adds native enum and mapping to MenuItem show-as-action flags.
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/toolbar/StackHeaderToolbarMenuItemOptions.kt Extends command options with showAsAction (and currently also showAsActionWithText).
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/toolbar/StackHeaderToolbarMenuItemDefaults.kt Adds default SHOW_AS_ACTION = NEVER.
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/toolbar/StackHeaderToolbarMenuItemConfig.kt Persists showAsAction as part of the menu item config.
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/toolbar/StackHeaderToolbarMenuCoordinator.kt Applies showAsAction when creating/updating menu items; makes clear/updateItem internal.
android/src/main/java/com/swmansion/rnscreens/gamma/stack/header/config/StackHeaderConfigViewManager.kt Parses showAsAction from props/commands and adds a no-op setter for DO_NOT_USE.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

2 participants