-
-
Notifications
You must be signed in to change notification settings - Fork 576
fix: clicking on Pressable located in screen header #2466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Hi @coado I like this approach. However, I'm afraid in current form it may not respect the placement of the elements in regards to flex layout of the header. Have you tested it with smaller elements, headerLeft and/or headerRight for example? You should be able to use the Element Inspector from the dev menu to inspect the actual placement of the pressables laid out by yoga. I remember using it in #2292 |
Hey @alduzy, thanks for the reply! This is how it looks when I set Pressable on
Please let me know if this is desired behaviour. Also I've checked a placement using inspector as you proposed and it seems like Pressable boundary in Yoga is not perfectly aligned with what is displayed. This is something that I will have a closer look at! codeimport { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator, NativeStackNavigationProp } from "@react-navigation/native-stack";
import React, { ForwardedRef, forwardRef } from "react";
import { findNodeHandle, Pressable, PressableProps, StyleSheet, Text, View, Button } from "react-native";
type StackParamList = {
Home: undefined,
}
type RouteProps = {
navigation: NativeStackNavigationProp<StackParamList>;
}
type PressableState = 'pressed-in' | 'pressed' | 'pressed-out'
const Stack = createNativeStackNavigator<StackParamList>();
const PressableWithFeedback = forwardRef((props: PressableProps, ref: ForwardedRef<View>): React.JSX.Element => {
const [pressedState, setPressedState] = React.useState<PressableState>('pressed-out');
const onPressInCallback = React.useCallback((e) => {
console.log('Pressable onPressIn', {
locationX: e.nativeEvent.locationX,
locationY: e.nativeEvent.locationY,
pageX: e.nativeEvent.pageX,
pageY: e.nativeEvent.pageY,
});
setPressedState('pressed-in');
props.onPressIn?.();
}, []);
const onPressCallback = React.useCallback(() => {
console.log('Pressable onPress');
setPressedState('pressed');
}, []);
const onPressOutCallback = React.useCallback(() => {
console.log('Pressable onPressOut');
setPressedState('pressed-out');
}, []);
const onResponderMoveCallback = React.useCallback(() => {
console.log('Pressable onResponderMove');
}, []);
const contentsStyle = pressedState === 'pressed-out'
? styles.pressablePressedOut
: (pressedState === 'pressed'
? styles.pressablePressed
: styles.pressablePressedIn);
return (
<View ref={ref} style={[contentsStyle]}>
<Pressable
onPressIn={onPressInCallback}
onPress={onPressCallback}
onPressOut={onPressOutCallback}
onResponderMove={onResponderMoveCallback}
>
{props.children}
</Pressable>
</View>
);
})
function HeaderTitle(): React.JSX.Element {
return (
<PressableWithFeedback
onPressIn={() => {
console.log('Pressable onPressIn')
}}
onPress={() => console.log('Pressable onPress')}
onPressOut={() => console.log('Pressable onPressOut')}
onResponderMove={() => console.log('Pressable onResponderMove')}
ref={ref => {
console.log(findNodeHandle(ref));
ref?.measure((x, y, width, height, pageX, pageY) => {
console.log('header component measure', { x, y, width, height, pageX, pageY });
})
}}
>
<View style={{ height: 40, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ alignItems: 'center' }}>Regular Pressable</Text>
</View>
</PressableWithFeedback>
)
}
function Home(_: RouteProps): React.JSX.Element {
return (
<View style={{ flex: 1, backgroundColor: 'rgba(0, 0, 0, .8)' }}
>
<View style={{ flex: 1, alignItems: 'center', marginTop: 48 }}>
<PressableWithFeedback
onPressIn={() => console.log('Pressable onPressIn')}
onPress={() => console.log('Pressable onPress')}
onPressOut={() => console.log('Pressable onPressOut')}
>
<View style={{ height: 40, width: 200, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ alignItems: 'center' }}>Regular Pressable</Text>
</View>
</PressableWithFeedback>
</View>
</View>
);
}
function App(): React.JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{
// headerTitle: HeaderTitle,
headerRight: HeaderTitle,
// headerLeft: HeaderTitle
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
pressablePressedIn: {
backgroundColor: 'lightsalmon',
},
pressablePressed: {
backgroundColor: 'crimson',
},
pressablePressedOut: {
backgroundColor: 'lightseagreen',
}
});
export default App; |
Actually, when the Pressable is set as the |
Hello @coado Any news on this issue? I started upgrading my project to RN 0.76 and this issue is by far the most problematic |
Hey @thibaultcapelli |
I was just looking through the code and determined that the only reliable solution would be to update position of header elements in ShadowTree (ST) based on their position in HostTree (HT), i. e. you do send additional information on topinset now - maybe let's send whole frame instead and update headersubviews layout metrics in shadow node? I think this is only way to get this at least partially consistent. |
+1 for this issue 🙏 |
…g header subviews (#2623) ## Description In #2466 I've introduced `RCTMountingTransactionObserving` for `RNSScreenStackHeaderConfig` component. Now we can use this to reduce amount of calls to `updateViewControllerIfNeeded` (causing layout passes) when adding subviews. I'm not changing the `unmount` path of the code as we have some more additional logic there which requires some more careful consideration. This also aligns us with Paper implementation. Note that it has been only implemented this way, because at the implementation time there was no way to run this code on transaction completion. ## Changes `- [RNSScreenStackHeaderConfig updateViewControllerIfNeeded]` is now called only once per transaction when adding subviews to header config. ## Test code and steps to reproduce Test432, TestHeaderTitle, Test2552 - see that there are no regressions. > [!note] During testing I've found that there is a bug with subview layout when modifying subviews after first render. This is not a regression however. Notice the wrong position of header right on video below 👇🏻 (Test432) https://github.com/user-attachments/assets/7f8653e8-a7d9-4fb8-a875-182e7deb0495 ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
@kkafar Thank you very much for the effort! Seems like header right still does not work on iPhone XS. I am using TouchableOpacity and 4.6.0-beta.0 |
@JcbPrn thanks for the report, I'll look into this. If you had capacity to provide me with snack/repo/snippet where I can reproduce the issue directly, that would be awesome. |
Now, while the changes with 4.6.0. seem to have fixed the pressable issue, I am discovering a new issue, when setting the header right button dynamically from within a component: useFocusEffect(
React.useCallback(() => {
const stackNav = navigation.getParent();
stackNav?.setOptions({
title: ScreenName,
headerRight: () => (
<View
style={{
width: 20,
height: 20,
display: 'none',
}}></View>
... When using any kind of sized view, it works as expected on initial render, but after navigating (in my case to a different tab) and back, the header ~tripples in height, regardless of what the headerRight element is, or how its sized. What could help identify the issue is, when setting the headerRight element to a view with display: none (see snippet) the header increases in height slowly, without stopping as far as i can see. I assume this has to do with some of the size/position refactoring i saw mentioned for this issue. Using: Android, react-native 0.77.0, "react-native-screens": "^4.6.0", "@react-navigation/native": "^7.0.14", "@react-navigation/native-stack": "^7.2.0", |
Thanks @mikeswann for the detailed report. I'll look into it. |
I'm on v4.6.0 and found an issue on Android only with I was able to pin down the issue caused by this diff This is what it looks like with the current v4.6.0 version from the layout inspector: ![]() Clicking on the top half of the card doesn't register because of it. Happy to provide more details in a separate issue if needed, but at first glance the cause is clear. |
Hey @robertying, I've tried to reproduce your setup and tested on both architectures (I see you're using Paper in the screenshot, though) but I do not confirm the issue. Here's the snippet I've tested on: Snippetimport { NavigationContainer, RouteProp } from '@react-navigation/native';
import { createNativeStackNavigator, NativeStackNavigationProp } from '@react-navigation/native-stack';
import React from 'react';
import { findNodeHandle, Text, View } from 'react-native';
import PressableWithFeedback from '../shared/PressableWithFeedback';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
type StackParamList = {
Home: { marginTop?: number },
NestedTabsHost: undefined;
}
type RouteProps = {
navigation: NativeStackNavigationProp<StackParamList>;
route: RouteProp<StackParamList>;
}
const Stack = createNativeStackNavigator<StackParamList>();
const Tabs = createBottomTabNavigator();
const NestedStack = createNativeStackNavigator();
function NestedStackHost() {
return (
<NestedStack.Navigator>
<NestedStack.Screen name="NestedHome" component={Home} initialParams={{ marginTop: 0 }} />
</NestedStack.Navigator>
);
}
function NestedTabsHost() {
return (
<Tabs.Navigator>
<Tabs.Screen name="NestedStackHost" component={NestedStackHost} options={{ headerShown: false }} />
</Tabs.Navigator>
);
}
function HeaderTitle(): React.JSX.Element {
return (
<PressableWithFeedback
onLayout={event => {
const { x, y, width, height } = event.nativeEvent.layout;
console.log('Title onLayout', { x, y, width, height });
}}
onPressIn={() => {
console.log('Pressable onPressIn');
}}
onPress={() => console.log('Pressable onPress')}
onPressOut={() => console.log('Pressable onPressOut')}
onResponderMove={() => console.log('Pressable onResponderMove')}
ref={node => {
console.log(findNodeHandle(node));
node?.measure((x, y, width, height, pageX, pageY) => {
console.log('header component measure', { x, y, width, height, pageX, pageY });
});
}}
>
<View style={{ height: 40, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ alignItems: 'center' }}>Regular Pressable</Text>
</View>
</PressableWithFeedback>
);
}
function HeaderLeft(): React.JSX.Element {
return (
<HeaderTitle />
);
}
function Home({ navigation, route }: RouteProps): React.JSX.Element {
return (
<View style={{ flex: 1, backgroundColor: 'rgba(0, 0, 0, .8)' }}
>
<View style={{ flex: 1, alignItems: 'center', marginTop: route.params?.marginTop ?? 48 }}>
<PressableWithFeedback
onPressIn={() => console.log('Pressable onPressIn')}
onPress={() => console.log('Pressable onPress')}
onPressOut={() => console.log('Pressable onPressOut')}
>
<View style={{ height: 40, width: 200, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ alignItems: 'center' }}>Regular Pressable</Text>
</View>
</PressableWithFeedback>
<PressableWithFeedback onPress={() => navigation.navigate('NestedTabsHost')}>
<View style={{ height: 40, width: 200, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ alignItems: 'center' }}>Show tabs</Text>
</View>
</PressableWithFeedback>
</View>
</View>
);
}
function App(): React.JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{
headerTitle: HeaderTitle,
headerLeft: HeaderLeft,
headerRight: HeaderLeft,
}}
/>
<Stack.Screen name="NestedTabsHost" component={NestedTabsHost} options={{ headerShown: false }} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App; If you could modify it in such way that I can reproduce this issue & let me know that would be great. Preferably open a new ticket, but here is also fine. |
@kkafar sorry for the late response. Can you try using this App.tsximport {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {Text, View} from 'react-native';
const NoticeStackNavigator = createNativeStackNavigator();
const MainNavigator = createBottomTabNavigator();
const RootNavigator = createNativeStackNavigator();
const NoticeStack = () => (
<NoticeStackNavigator.Navigator>
<NoticeStackNavigator.Screen
name="Notices"
component={() => <View></View>}
options={{
headerTitle: () => <Text>Title</Text>,
}}
/>
</NoticeStackNavigator.Navigator>
);
const MainTab = () => (
<MainNavigator.Navigator>
<MainNavigator.Screen
name="NoticeStack"
component={NoticeStack}
options={{
title: 'Notices',
}}
/>
</MainNavigator.Navigator>
);
const App = () => {
return (
<NavigationContainer>
<RootNavigator.Navigator>
<RootNavigator.Screen name="MainTab" component={MainTab} />
</RootNavigator.Navigator>
</NavigationContainer>
);
};
export default App; ![]() |
I believe this PR might have introduced regressions. The ripple effect from my buttons inside of the header are now truncated ( Screen.Recording.2025-02-19.at.4.20.19.PM.mov |
Motivation: working on #2466 right now & need these changes + this is also required to fix the failing CI Bumped versions of reanimated, gesture handler and safe-area-context in both lib and the examples :fingers_crossed: CI? Okay, seems that Android part of the CI is fixed. iOS has some other, yet undetermined problems. I do not see a regression on iOS part, however. - [x] (kinda ☝🏻) Ensured that CI passes (cherry picked from commit 3555d23)
… the screen (#2781) ## Description Fixes #2758 #2466 removed old workaround for header config blocking gestures - we just set top: `-100%` to place the headerconfig in the top of the screen effectively preventing blocking. #2466 removed these styles & frame correction is now applied directly in shadow node. This is done fine on Fabric, however the solution was not replicated on Paper. ## Changes Beside padding information we now send native toolbar height to header config shadow node on Paper. This information is used there to offset the header config position by this value. Should solve the problem. ### Screenshots | before | after | | - | - | | <img src="https://github.com/user-attachments/assets/640cad09-584d-4983-af92-11ae68d49f9f" alt="before" /> | <img src="https://github.com/user-attachments/assets/de15190e-e999-495f-8a6d-abf7f375d468" alt="after" /> | ## Test code and steps to reproduce `Example` -> `Header options` -> click on the very top button (very top of it). Previously the button was not effectively clicked. Now it works. ## Checklist - [ ] Included code example that can be used to test this change - [ ] Updated TS types - [ ] Updated documentation: <!-- For adding new props to native-stack --> - [ ] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [ ] Ensured that CI passes
…et` presentation (#2788) ## Description This PR relates to new architecture only. Currently, the pressables on form sheet lose focus on `move` action. This is the same problem we had with `Pressables` in screen & header on new architecture. See: #2466 Basically the information about sheet position is different between `ShadowTree` (ST) and `HostTree` (HT), which leads to losing focus due to how pressables are now handled on new architecture. **Simplified description** of basically what happens when you click a pressable on new arch is as follows (Android): 1. The gesture is detected after the host platform dispatches touch event (touch events on Android are dispatched in top-down manner) [(link)](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java#L81-L105) 2. The JS responder is set & touch event dispatched - therefore pressable is always clickable, 3. Moreover, the JS responder is [measured **IN THE ST**](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/Libraries/Pressability/Pressability.js#L805-L810) 3. When you start moving your finger, the platform is still the source of motion events, and target [coordinates are **read from HOST TREE**](#2466) 4. Motion event (with platform measurement (HT)!!!) is then [compared in JS with responder measurements based on ST](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/Libraries/Pressability/Pressability.js#L831-L8750) Therefore, any inconsistency here leads to responder losing focus. Reference: 1. [`TouchesHelper.createPointersArray`](https://github.com/facebook/react-native/blob/ac97177651cf369783ca93fac50c2824b484abef/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/TouchesHelper.kt#L37) 2. [Responder measurement](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/Libraries/Pressability/Pressability.js#L805-L810) 3. [Gesture start detection (Android)](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java#L81-L105) 4. [Gesture move handling (Android)](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSTouchDispatcher.java#L137-L147) 5. [Checking whether native touch is in responder region](https://github.com/facebook/react-native/blob/d6ca25b0c1a0aeed3507ec3c2f65e453e2700dc2/packages/react-native/Libraries/Pressability/Pressability.js#L831-L875) What is bewildering is that it works on iOS, despite the fact, that e.g. when using React inspector the views are completely out of place in ShadowTree. I haven't debugged the iOS part to the very bottom, but my suspicion is as follows: 1. The form sheet on Android is mounted in subtree under react root view, while on iOS it is mounted under separate `UITransitionView` (different subtree of window), 2. additionally we use `RNSModalScreen` component on iOS, which has shadow node with `RootNodeKind` (measurements in subtree of its shadow node are done relative to it), 3. There is no root view / any react view above it -> native measurement (done by react-native) might also think that its positioned at (0, 0) (same as in shadow tree). This is something to verify in the future. Opened ticked on the board for it. ## Changes The sheet now sends updates to the shadow tree at following moments: 1. layout (including initial one), 2. sheet detent change to any stable state (could consider here not sending the update when the sheet is hidden, added to project board), I'm not sending updates on every sheet move (`View.top` change), to avoid clogging the JS thread (and later UI), whole advantage of our sheet is that it feels smooth (given sensible Android device). However, it could be considered in case of any further issues. Please note, that between the event syncs or in case the JS thread is blocked / clogged, this still will be a problem. However, any JS would lag, not only pressables and it's not down to us. Commits: - **Prevent modal content from disappearing** - **Add comment that createShadowNode is used only on old arch** - **Add comment on threading on NativeProxy mechanisms** - **Simplify code in RNSModalScreenShadowNode.cpp** - **Make modal screen component selection more clear in Screen.tsx** - **Add `headerHeight` to diff prop list when determining need for shadow state update** - **Add pressables to form sheet example!** - **Send updates to shadow tree on form sheet layout changes in appropriate moments** ### Recordings | before | after | | -- | -- | | <video src="https://github.com/user-attachments/assets/b5bbb149-61da-41d1-b6a4-73dd89eb1f92" alt="before" /> | <video src="https://github.com/user-attachments/assets/ce5c89c0-7215-4ebb-a347-fa50946308f4" alt="after" /> | ## Test code and steps to reproduce `TestFormSheet`, open the sheet, play with it & see that pressables work! ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes
…er subview (#2811) ## Description When setting header subviews from the rendered component via `setOptions` the native header enlarges (see the "before" video below :point_down:). ### Bug mechanism When `HeaderSubview` is set from `setOptions` its mounted after first render has happened. It means that `HeaderConfig` has already been laid out and possibly its shadow state got updated with its size. Now, when first layout for the `HeaderSubview` is computed Yoga will stretch-fit `HeaderSubview` height to fill available space - the `HeaderSubview` will receive height equal to the height of the `HeaderConfig`. Such frame will be then send to HostTree, triggering native header layout, which will expand to make enough space for such high `HeaderSubview` & additional padding. Thanks to #2696 the update cycle will be broken & the issue described in #2675 won't happen. Note that there is no such buggy behaviour in case the `HeaderSubviews` is passed directly as option when defining a `Screen`. This is because Yoga resolves the `childHeight` (`HeaderSubview` height) differently depending on whether `containingNode`'s height (`HeaderConfig`'s height) is defined upfront or not. When the `containingNode` height is not known (case of initial render with `HeaderSubview` present) the Yoga will use `FitContent` or `MaxContent` (not sure here) [`SizingMode`](https://github.com/facebook/yoga/blob/51e6095005fd713dbfcbaf2c6296009de782d966/yoga/algorithm/SizingMode.h#L21-L45). In cases, it is known (`HeaderConfig` has received state from HT, case of `HeaderSubview` rendered via `setOptions`) - `StretchFit` will be used for some reason (taking into consideration all layout options, including flex direction which is `row` for both `HeaderConfig` and `HeaderSubview`). I believe this regression has been introduced in #2466, where we added state updates for `HeaderConfig` and `HeaderSubviews`. We need these state updates though, however it seems that we do not need to inform Yoga with `HeaderConfig` height & therefore avoid this layout problem. ### Debugging trail It seems that the `SizingMode` for laying out `HeaderSubview` is determined [here.](https://github.com/facebook/yoga/blob/51e6095005fd713dbfcbaf2c6296009de782d966/yoga/algorithm/CalculateLayout.cpp#L208-L214), which is being called from [`computeFlexBasisForChild`](https://github.com/facebook/yoga/blob/main/yoga/algorithm/CalculateLayout.cpp#L66). The `resolveChildAlignment` method returns there `Align::Strech` and this leads to `SizingMode::StretchFit` being used later on when measuring/laying out `HeaderSubview`. ### Recordings | before | after | | -- | -- | | <video src="https://github.com/user-attachments/assets/f74039f5-919f-4e28-a56d-ebb360b6ce3a" alt="before" /> | <video src="https://github.com/user-attachments/assets/a9bab5f8-8176-4d6d-a93e-cdd5f8e5cca3" alt="after" /> | ## Changes Now, we do set only width & horizontal padding of the `HeaderConfig`. `YGUndefined` is passed as height argument to `setSize` call. ## Test code and steps to reproduce Added `Test2811` that allows to test these changes directly. We need to also check following test cases for regressions: * [x] #2675 Infinite state update loop * [x] #2466 Pressables in header * [x] `TestHeaderTitle` Header title truncation * [x] `TestHeaderTitle` Header spacing when changing orientation > [!warning] > There is a issue when header elements set in `setOptions` disappear / become invisible. [link to internal board](https://github.com/orgs/software-mansion/projects/3/views/1?pane=issue&itemId=103681490). It seems that it happens also on 4.9.2 and therefore is not a regression. However it is bad & must be fixed before stable release. ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes (db55977)
this issue has came back to me, on both |
…es (#2905) ## Description Regression most likely introduced in 4.5.0 by #2466. Fixes #2714 Fixes #2815 Supersedes #2845 This is a ugly hack to workaround issue with dynamic content change. When the size of this shadow node contents (children) change due to JS update, e.g. new icon is added, if the size is set for the yogaNode corresponding to this shadow node, the enforced size will be used and the size won't be updated by Yoga to reflect the contents size change -> host tree won't get layout metrics update -> we won't trigger native layout -> the views in header will be positioned incorrectly. > [!important] > This PR handles iOS only, however there is **similar** issue on Android. The issue can be reproduced on the same test example. Android will be handled in separate PR. ## Changes ## Test code and steps to reproduce In this approach I've settled with: 1. not calling set size on iOS for `RNSScreenStackHeaderSubviewShadowNode`, 2. updating header config padding & sending it as state to shadow tree. Added `Test2714` Most of the fragile header interactions must be tested: * [x] Header title truncation - `TestHeaderTitle` ~❌ This PR introduces regression here on iOS (Android not tested yet)~ ✅ Works * [x] Pressables in header - `Test2466` (iOS works, Android code is unmodified here) * [x] #2807 (this PR does not touch Android) * [x] #2811 (this PR does not touch Android) * [x] #2812 (this PR does not touch Android) * [x] Header behaviour on orientation changes - #2756 (this PR does not touch Android) * [x] New test `Test2714` handling header item resizing. ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes
…hanges (#2910) ## Description Fixes #2714 on Android Fixes #2815 on Android See #2905 for detailed description. ## Changes Removed call to `RNSScreenStaceaderSubviewShadowNode.setSize` in corresponding component descriptor. It seems that we do not need to enforce node size from HostTree. Setting appropriate content offset is enough for pressables to function correctly (assuming that native layout **does not change size of any subview**). I currently can't come up with any scenario where this would happen. ## Test code and steps to reproduce I've tested: * [x] `Test2714` introduced in PR with iOS fixes - #2905 * [x] Pressables in header - #2466, * [x] Header title truncation - #2325 (only few cases, as the list is long there) & noticed a regression (not related to this PR, described in comment below the PR description), * [x] Insets with orientation change (Android) - #2756 * [x] #2807 (on `TestHeaderTitle` with call to `setOptions` in `useLayoutEffect`) * [x] `Test2811` - #2811 * [x] #2812 (with snippet provided in that PR description) ## Checklist - [x] Included code example that can be used to test this change - [x] Ensured that CI passes
Description
This PR fixes clicking on Pressables that are added to the native header. Previously, Yoga had incorrect information about the location of the content in the header.
The first step was to remove
top: "-100%"
style from theScreenStackHeaderConfig
which made Yoga think, that the content is pushed up by the size of its parent (Screen size).The second step involved updating
layoutMetrics
of theRNSScreenStackHeaderConfigShadowNode
. The entire app content is pushed down by the size of the header, so it also has an impact on the header config in Yoga metrics. To mitigate this, the origin ofRNSScreenStackHeaderConfigShadowNode
is decreased by the size of the header which will zero out eventually leaving the header content in the desired position. On iOS this position is actually moved by the top inset size, so we also have to take it into account when setting header config layout metrics.Fixes #2219
Changes
Updated
ScreenShadowNode
to decreaseorigin.y
of theHeaderConfigShadowNode
by the size of the header. AddedpaddingTop
toHeaderConfigState
and set it as origin offset on iOS.Screenshots / GIFs
Before
ios-before.mov
android-before.mov
After
ios-after.mov
android-after.mov
Test code and steps to reproduce
Tested on this example:
code