diff --git a/packages/@react-native/tester/js/examples/Touchable/TouchableExample.js b/packages/@react-native/tester/js/examples/Touchable/TouchableExample.js
index 8f6f0949cd8..17321394a45 100644
--- a/packages/@react-native/tester/js/examples/Touchable/TouchableExample.js
+++ b/packages/@react-native/tester/js/examples/Touchable/TouchableExample.js
@@ -312,6 +312,107 @@ class TouchableHitSlop extends React.Component<{...}, $FlowFixMeState> {
}
}
+class TouchableWithoutFeedbackHitSlop extends React.Component<
+ {...},
+ $FlowFixMeState,
+> {
+ state: any | {timesPressed: number} = {
+ timesPressed: 0,
+ };
+
+ onPress = () => {
+ this.setState({
+ timesPressed: this.state.timesPressed + 1,
+ });
+ };
+
+ render(): React.Node {
+ let log = '';
+ if (this.state.timesPressed > 1) {
+ log = this.state.timesPressed + 'x onPress';
+ } else if (this.state.timesPressed > 0) {
+ log = 'onPress';
+ }
+
+ return (
+
+
+
+
+
+ Press Outside This View
+
+
+
+
+
+
+ {log}
+
+
+
+ );
+ }
+}
+
+class TouchableWithoutFeedbackStyleUpdate extends React.Component<
+ {...},
+ $FlowFixMeState,
+> {
+ state: any | {dynamicColor: string, timesPressed: number} = {
+ dynamicColor: '#007AFF',
+ timesPressed: 0,
+ };
+
+ onPress = () => {
+ const colors = ['#007AFF', '#FF6B35', '#4ECDC4', '#45B7D1', '#96CEB4'];
+ const nextColor = colors[(this.state.timesPressed + 1) % colors.length];
+ this.setState({
+ dynamicColor: nextColor,
+ timesPressed: this.state.timesPressed + 1,
+ });
+ };
+
+ render(): React.Node {
+ const dynamicStyle = {
+ backgroundColor: this.state.dynamicColor,
+ padding: 10,
+ borderRadius: 8,
+ };
+
+ let log = '';
+ if (this.state.timesPressed > 1) {
+ log = this.state.timesPressed + 'x style updated';
+ } else if (this.state.timesPressed > 0) {
+ log = 'style updated';
+ }
+
+ return (
+
+
+
+
+
+ Press to Update Style!
+
+
+
+
+
+
+ {log}
+
+
+
+ );
+ }
+}
+
function TouchableNativeMethodChecker<
T: component(ref?: React.RefSetter, ...any),
>(props: {Component: T, name: string}): React.Node {
@@ -776,6 +877,23 @@ exports.examples = [
return ;
},
},
+ {
+ title: 'TouchableWithoutFeedback Hit Slop',
+ description:
+ ('TouchableWithoutFeedback accepts hitSlop prop which extends the touch area ' +
+ 'without changing the view bounds.': string),
+ render(): React.MixedElement {
+ return ;
+ },
+ },
+ {
+ title: 'TouchableWithoutFeedback Style Update',
+ description:
+ ('TouchableWithoutFeedback can update styles dynamically and should support fast refresh.': string),
+ render(): React.MixedElement {
+ return ;
+ },
+ },
{
title: 'Touchable Native Methods',
description:
diff --git a/packages/e2e-test-app-fabric/test/TouchableComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TouchableComponentTest.test.ts
index ba1f75fbcc4..a3e36c9c386 100644
--- a/packages/e2e-test-app-fabric/test/TouchableComponentTest.test.ts
+++ b/packages/e2e-test-app-fabric/test/TouchableComponentTest.test.ts
@@ -116,4 +116,42 @@ describe('Touchable Tests', () => {
expect(dump2).toMatchSnapshot();
await searchBox('');
});
+ test('TouchableWithoutFeedback should register press in clicked within hitSlop range', async () => {
+ await searchBox('TouchableWithoutFeedback Hit Slop');
+ const component = await app.findElementByTestID(
+ 'touchable_without_feedback_hit_slop_button',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree(
+ 'touchable_without_feedback_hit_slop_button',
+ );
+ expect(dump).toMatchSnapshot();
+ await component.click();
+ const dump2 = await dumpVisualTree(
+ 'touchable_without_feedback_hit_slop_console',
+ );
+ expect(dump2).toMatchSnapshot();
+ await searchBox('');
+ });
+ test('TouchableWithoutFeedback should update style upon fast refresh', async () => {
+ await searchBox('TouchableWithoutFeedback Style Update');
+ const component = await app.findElementByTestID(
+ 'touchable_without_feedback_style_update_button',
+ );
+ await component.waitForDisplayed({timeout: 5000});
+ const dump = await dumpVisualTree(
+ 'touchable_without_feedback_style_update_button',
+ );
+ expect(dump).toMatchSnapshot();
+ await component.click();
+ const dump2 = await dumpVisualTree(
+ 'touchable_without_feedback_style_update_button',
+ );
+ expect(dump2).toMatchSnapshot();
+ const dump3 = await dumpVisualTree(
+ 'touchable_without_feedback_style_update_console',
+ );
+ expect(dump3).toMatchSnapshot();
+ await searchBox('');
+ });
});