Repository navigation
fix(ui): stop setting cursor: 'pointer' on sidebar rows - #956
Merged
dannyhw merged 2 commits intoNov 8, 2026
Merged
Conversation
On iPadOS with a mouse or trackpad, collapsing a sidebar group in a debug build aborts the app with "Attempt to unmount a view which has a different index". React Native maps `cursor: 'pointer'` on iOS 17+ to a UIHoverStyle with UIHoverAutomaticEffect. When the pointer hovers such a view, UIKit inserts a `_UIPointerEffectPlatterView` into the same container. Fabric unmounts children by index into the container's subviews, so once that extra view is there the indices are off and RCTAssert fails (react/react-native#55489). Release builds compile the assertion out, but Storybook runs in debug builds, so every iPad user with a pointer attached hits it. The tree rows, the collapse buttons, the search clear button and the search result rows of both @storybook/react-native-ui and @storybook/react-native-ui-lite set `cursor: 'pointer'`. All of them are TouchableOpacity, which react-native-web already renders with a pointer cursor, so the style did nothing on web and only opted the rows into the iPad hover effect. Remove it. Add a `no-restricted-syntax` lint rule for the packages' sources so the style can't be reintroduced while the React Native bug is unfixed. Fixes storybookjs#936
🦋 Changeset detectedLatest commit: 70d9752 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
# Conflicts: # packages/react-native-ui-lite/src/TreeNode.tsx # packages/react-native-ui/src/TreeNode.tsx
dannyhw
approved these changes
Nov 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue: #936
What I did
On iPadOS with a mouse or trackpad, collapsing a sidebar group in a debug build terminates the app with
Attempt to unmount a view which has a different index.React Native turns
cursor: 'pointer'into aUIHoverStylewithUIHoverAutomaticEffecton iOS 17+. When the pointer hovers that view, UIKit adds a_UIPointerEffectPlatterViewto the same container, and Fabric, which unmounts children by index intosubviews, then hitsRCTAssert(react/react-native#55489, fix still open in react/react-native#58686). Release builds skip the assertion, but Storybook runs in debug builds, so anyone using an iPad with a pointer hits it.Changes:
cursor: 'pointer'from the tree rows (BranchNode,LeafNode), the rootCollapseButton, the searchClearIconand the searchResultRowin both@storybook/react-native-uiand@storybook/react-native-ui-lite. All of them areTouchableOpacity, which react-native-web already renders withcursor: pointer(itsactionablestyle), so web looks the same. On Android the style was ignored. The only effect on iOS is that the rows no longer get the iPad hover effect that triggers the crash.no-restricted-syntaxrule forpackages/*/srcthat flagscursor: 'pointer'and explains why, so it doesn't creep back in while the React Native bug is open. Thecol-resize/row-resizecursors in the liteResizeHandlearen't affected, because iOS only mapsautoandpointer.@storybook/react-native-uiand@storybook/react-native-ui-lite.I went with removing the style rather than gating it with
Platform.OSbecause on web it does nothing beyond whatTouchableOpacityalready does.How to test
pnpm lintpasses. If you put any of the removedcursor: 'pointer'lines back, it fails with the new rule.Manually, with the reproduction from the issue (https://github.com/ekrapfl/sb-rn-ipad-collapse-crash) or
examples/expo-example, use a debug build on an iPad wide enough for the desktop sidebar (e.g. iPad Pro 13" simulator), hover a group row with the pointer for a second, then click it to collapse. It used to crash and now collapses normally. On web (pnpm --filter expo-example web), sidebar rows and search results still show the pointer cursor.Fixes #936