Skip to content

[Web] Assign role for Native gesture in detector#4130

Open
m-bert wants to merge 6 commits intomainfrom
@mbert/native-types
Open

[Web] Assign role for Native gesture in detector#4130
m-bert wants to merge 6 commits intomainfrom
@mbert/native-types

Conversation

@m-bert
Copy link
Copy Markdown
Collaborator

@m-bert m-bert commented Apr 29, 2026

Description

In this PR I change how the role is assigned to Native gesture. Previously we relied on different checks, like hasAttribute and querySelector. In this PR I changed it so that Detector uses displayName to determine underlying component.

I've also removed double hitSlop assignment (289c922)

Test plan

Tested on the following example:
import React from 'react';
import { ScrollView, StyleSheet, Switch, Text, View } from 'react-native';
import {
  GestureDetector,
  GestureHandlerRootView,
  InterceptingGestureDetector,
  ScrollView as RNGHScrollView,
  Touchable,
  useNativeGesture,
  VirtualGestureDetector,
} from 'react-native-gesture-handler';

const BOXES = ['#ff6b6b', '#ffd93d', '#6bcb77', '#4d96ff', '#c77dff'];

function Section({
  label,
  color,
  children,
}: {
  label: string;
  color: string;
  children: React.ReactNode;
}) {
  return (
    <View style={styles.section}>
      <Text style={styles.label}>{label}</Text>
      <View style={[styles.border, { borderColor: color }]}>{children}</View>
    </View>
  );
}

function NativeGestureCallbacks() {
  return {
    onBegin: () => console.log(Date.now(), 'begin'),
    onActivate: () => console.log(Date.now(), 'activate'),
    onUpdate: () => console.log(Date.now(), 'update'),
    onDeactivate: () => console.log(Date.now(), 'deactivate'),
    onFinalize: () => console.log(Date.now(), 'finalize'),
  };
}

export default function EmptyExample() {
  const virtualScrollGesture = useNativeGesture(NativeGestureCallbacks());
  const virtualSwitchGesture = useNativeGesture(NativeGestureCallbacks());
  const switchGesture = useNativeGesture(NativeGestureCallbacks());
  const touchableGesture = useNativeGesture(NativeGestureCallbacks());
  const virtualTouchableGesture = useNativeGesture(NativeGestureCallbacks());

  return (
    <GestureHandlerRootView style={styles.root}>
      <Section label="RNGHScrollView (v3, NativeDetector)" color="blue">
        <RNGHScrollView style={styles.scrollView} {...NativeGestureCallbacks()}>
          {BOXES.map((c, i) => (
            <View key={i} style={[styles.box, { backgroundColor: c }]} />
          ))}
        </RNGHScrollView>
      </Section>

      <Section
        label="ScrollView inside InterceptingGestureDetector (VirtualDetector)"
        color="green">
        <InterceptingGestureDetector>
          <VirtualGestureDetector gesture={virtualScrollGesture}>
            <ScrollView style={styles.scrollView}>
              {BOXES.map((c, i) => (
                <View key={i} style={[styles.box, { backgroundColor: c }]} />
              ))}
            </ScrollView>
          </VirtualGestureDetector>
        </InterceptingGestureDetector>
      </Section>

      <Section label="Switch (v3, NativeDetector)" color="purple">
        <GestureDetector gesture={switchGesture}>
          <Switch />
        </GestureDetector>
      </Section>

      <Section
        label="Switch inside InterceptingGestureDetector (VirtualDetector)"
        color="orange">
        <InterceptingGestureDetector>
          <VirtualGestureDetector gesture={virtualSwitchGesture}>
            <Switch />
          </VirtualGestureDetector>
        </InterceptingGestureDetector>
      </Section>

      <Section label="Touchable" color="teal">
        <Touchable
          onPress={() => console.log(Date.now(), 'press')}
          style={styles.touchable}>
          <Text>Press me</Text>
        </Touchable>
      </Section>
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  root: {
    flex: 1,
    padding: 16,
    gap: 16,
  },
  section: {
    gap: 4,
  },
  label: {
    fontSize: 12,
    color: '#555',
    marginBottom: 4,
  },
  border: {
    borderWidth: 2,
    borderRadius: 4,
    overflow: 'hidden',
  },
  scrollView: {
    height: 150,
  },
  box: {
    height: 60,
    marginBottom: 4,
  },
  touchable: {
    padding: 16,
    alignItems: 'center',
    backgroundColor: '#e0e0e0',
    borderRadius: 4,
  },
});

Copilot AI review requested due to automatic review settings April 29, 2026 09:59
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 a web-specific mechanism to tag “Native” gesture targets with a semantic role so NativeViewGestureHandler can apply role-dependent behavior when used via v3 Native/Virtual detectors.

Changes:

  • Introduces NativeGestureRole plus a rngh-role DOM attribute constant for role tagging on web.
  • Updates HostGestureDetector.web and VirtualDetector to set the role attribute based on the wrapped child’s displayName.
  • Refactors NativeViewGestureHandler to use a single role field (instead of buttonRole/switchRole) and to read the role from the new attribute for detector-based paths.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/react-native-gesture-handler/src/web/interfaces.ts Adds NativeGestureRole enum.
packages/react-native-gesture-handler/src/web/constants.ts Adds NATIVE_GESTURE_ROLE_ATTRIBUTE (rngh-role).
packages/react-native-gesture-handler/src/web/handlers/GestureHandler.ts Makes actionType protected for subclass access.
packages/react-native-gesture-handler/src/web/handlers/NativeViewGestureHandler.ts Reads role from rngh-role for detector paths; consolidates role handling.
packages/react-native-gesture-handler/src/v3/detectors/HostGestureDetector.web.tsx Sets rngh-role on the native-detector child element based on displayName.
packages/react-native-gesture-handler/src/v3/detectors/VirtualDetector/VirtualDetector.tsx Attempts to set rngh-role for virtual-detector children on web.
packages/react-native-gesture-handler/src/components/GestureHandlerButton.web.tsx Sets ButtonComponent.displayName = 'Button' to enable role inference.

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

@m-bert m-bert marked this pull request as ready for review May 4, 2026 08:48
@m-bert m-bert requested a review from Copilot May 4, 2026 08:48
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

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


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

@m-bert m-bert requested a review from j-piasecki May 4, 2026 09:26
);
};

ButtonComponent.displayName = 'Button';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want to change that to GestureHandlerButton or do we want to keep it generic?

I'm not sure whether this will behave well with other components, given the integration between native gesture and button.

[props.children]
);

useEffect(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this be a custom hook in its own file, which will no-op on native and contain the logic for web (.ts and .web.ts)?

useRequiredInterceptingDetectorContext();

const viewRef = useRef(null);
const viewRef = useRef<HTMLElement | null>(null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can be part of the new hook as well

});
};

useEffect(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any chance to unify this with the one in virtual detector?

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.

3 participants