Skip to content

Commit 33475d8

Browse files
twasilczykfacebook-github-bot
authored andcommitted
RN: populate IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS
Summary: Use overflow state to set IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS Differential Revision: D112356198
1 parent bf691fa commit 33475d8

4 files changed

Lines changed: 108 additions & 4 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ImportantForInteractionHelper.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ package com.facebook.react.views.view
99

1010
import android.view.View
1111
import com.facebook.react.R
12+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
1213
import com.facebook.react.uimanager.PointerEvents
14+
import com.facebook.react.uimanager.style.Overflow
1315

1416
/**
1517
* Helper class for managing the important_for_interaction view tag. This tag determines how a view
@@ -28,6 +30,8 @@ internal object ImportantForInteractionHelper {
2830
/** CSS pointer-events: auto heuristics. */
2931
const val IMPORTANT_FOR_INTERACTION_AUTO_CSSPOINTEREVENTSAUTO: Int = 0x8
3032

33+
const val IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS: Int = 0x10
34+
3135
/**
3236
* Sets the important_for_interaction tag on a view based on the given [PointerEvents] value.
3337
*
@@ -37,9 +41,11 @@ internal object ImportantForInteractionHelper {
3741
*
3842
* @param view The view to set the tag on
3943
* @param pointerEvents The pointer events value to convert and set
44+
* @param overflow The overflow value; descendants are not clipped when it is [Overflow.VISIBLE]
4045
*/
4146
@JvmStatic
42-
fun setImportantForInteraction(view: View, pointerEvents: PointerEvents) {
47+
fun setImportantForInteraction(view: View, pointerEvents: PointerEvents, overflow: Overflow) {
48+
if (!ReactNativeFeatureFlags.syncAndroidClipBoundsWithOverflow()) return
4349
val value =
4450
when (pointerEvents) {
4551
PointerEvents.AUTO -> IMPORTANT_FOR_INTERACTION_AUTO_CSSPOINTEREVENTSAUTO
@@ -49,7 +55,9 @@ internal object ImportantForInteractionHelper {
4955
IMPORTANT_FOR_INTERACTION_AUTO_CSSPOINTEREVENTSAUTO or
5056
IMPORTANT_FOR_INTERACTION_EXCLUDE_DESCENDANTS
5157
PointerEvents.BOX_NONE -> IMPORTANT_FOR_INTERACTION_NO
52-
}
58+
} or
59+
(if (overflow == Overflow.VISIBLE) IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS
60+
else 0)
5361
view.setTag(R.id.important_for_interaction, value)
5462
}
5563
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ public open class ReactViewGroup public constructor(context: Context?) :
140140

141141
public override var hitSlopRect: Rect? = null
142142
public override var pointerEvents: PointerEvents = PointerEvents.AUTO
143+
set(value) {
144+
field = value
145+
ImportantForInteractionHelper.setImportantForInteraction(this, value, _overflow)
146+
}
143147

144148
public var axOrderList: MutableList<String>? = null
145149

@@ -175,9 +179,9 @@ public open class ReactViewGroup public constructor(context: Context?) :
175179
allChildrenCount = 0
176180
clippingRect = null
177181
hitSlopRect = null
182+
// pointerEvents setter reads _overflow, so _overflow must be assigned first.
178183
_overflow = Overflow.VISIBLE
179184
pointerEvents = PointerEvents.AUTO
180-
ImportantForInteractionHelper.setImportantForInteraction(this, pointerEvents)
181185
childrenLayoutChangeListener = null
182186
onInterceptTouchEventListener = null
183187
needsOffscreenAlphaCompositing = false
@@ -828,6 +832,7 @@ public open class ReactViewGroup public constructor(context: Context?) :
828832
}
829833
set(overflow) {
830834
_overflow = Overflow.fromString(overflow)
835+
ImportantForInteractionHelper.setImportantForInteraction(this, pointerEvents, _overflow)
831836
invalidate()
832837
}
833838

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ public open class ReactViewManager : ReactClippingViewManager<ReactViewGroup>()
307307
@ReactProp(name = ViewProps.POINTER_EVENTS)
308308
public open fun setPointerEvents(view: ReactViewGroup, pointerEventsStr: String?) {
309309
view.pointerEvents = PointerEvents.parsePointerEvents(pointerEventsStr)
310-
ImportantForInteractionHelper.setImportantForInteraction(view, view.pointerEvents)
311310
}
312311

313312
@ReactProp(name = "nativeBackgroundAndroid")
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.view
9+
10+
import android.app.Activity
11+
import android.content.Context
12+
import android.view.View
13+
import com.facebook.react.R
14+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
15+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults
16+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
17+
import com.facebook.react.uimanager.PointerEvents
18+
import com.facebook.react.uimanager.style.Overflow
19+
import org.assertj.core.api.Assertions.assertThat
20+
import org.junit.After
21+
import org.junit.Before
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import org.robolectric.Robolectric
25+
import org.robolectric.RobolectricTestRunner
26+
27+
/** Tests for [ImportantForInteractionHelper]. */
28+
@RunWith(RobolectricTestRunner::class)
29+
class ImportantForInteractionHelperTest {
30+
31+
private lateinit var context: Context
32+
33+
private val dontClip =
34+
ImportantForInteractionHelper.IMPORTANT_FOR_INTERACTION_DONT_CLIP_DESCENDANTS
35+
private val auto =
36+
ImportantForInteractionHelper.IMPORTANT_FOR_INTERACTION_AUTO_CSSPOINTEREVENTSAUTO
37+
private val no = ImportantForInteractionHelper.IMPORTANT_FOR_INTERACTION_NO
38+
private val exclude = ImportantForInteractionHelper.IMPORTANT_FOR_INTERACTION_EXCLUDE_DESCENDANTS
39+
40+
@Before
41+
fun setUp() {
42+
ReactNativeFeatureFlagsForTests.setUp()
43+
ReactNativeFeatureFlags.override(
44+
object : ReactNativeFeatureFlagsDefaults() {
45+
override fun syncAndroidClipBoundsWithOverflow(): Boolean = true
46+
}
47+
)
48+
context = Robolectric.buildActivity(Activity::class.java).create().get()
49+
}
50+
51+
@After
52+
fun tearDown() {
53+
ReactNativeFeatureFlags.dangerouslyReset()
54+
}
55+
56+
private fun tagFor(pointerEvents: PointerEvents, overflow: Overflow): Int {
57+
val view = View(context)
58+
ImportantForInteractionHelper.setImportantForInteraction(view, pointerEvents, overflow)
59+
return view.getTag(R.id.important_for_interaction) as Int
60+
}
61+
62+
@Test
63+
fun overflowMapping() {
64+
assertThat(tagFor(PointerEvents.AUTO, Overflow.VISIBLE) and dontClip).isEqualTo(dontClip)
65+
assertThat(tagFor(PointerEvents.AUTO, Overflow.HIDDEN) and dontClip).isEqualTo(0)
66+
assertThat(tagFor(PointerEvents.AUTO, Overflow.SCROLL) and dontClip).isEqualTo(0)
67+
}
68+
69+
@Test
70+
fun pointerEventsMapping() {
71+
assertThat(tagFor(PointerEvents.AUTO, Overflow.HIDDEN)).isEqualTo(auto)
72+
assertThat(tagFor(PointerEvents.NONE, Overflow.HIDDEN)).isEqualTo(no or exclude)
73+
assertThat(tagFor(PointerEvents.BOX_ONLY, Overflow.HIDDEN)).isEqualTo(auto or exclude)
74+
assertThat(tagFor(PointerEvents.BOX_NONE, Overflow.HIDDEN)).isEqualTo(no)
75+
}
76+
77+
@Test
78+
fun tagRecomputesOnProperties() {
79+
val rvg = ReactViewGroup(context)
80+
fun tag() = rvg.getTag(R.id.important_for_interaction) as Int
81+
82+
// overflow defaults to VISIBLE
83+
rvg.pointerEvents = PointerEvents.BOX_NONE
84+
assertThat(tag()).isEqualTo(no or dontClip)
85+
86+
rvg.overflow = "hidden"
87+
assertThat(tag()).isEqualTo(no)
88+
89+
rvg.pointerEvents = PointerEvents.AUTO
90+
assertThat(tag()).isEqualTo(auto)
91+
}
92+
}

0 commit comments

Comments
 (0)