diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index d0336cdacb2e10..44635cb1a6ab2f 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -5956,6 +5956,7 @@ public final class com/facebook/react/uimanager/style/ComputedBorderRadius { public final fun copy (FFFF)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; public static synthetic fun copy$default (Lcom/facebook/react/uimanager/style/ComputedBorderRadius;FFFFILjava/lang/Object;)Lcom/facebook/react/uimanager/style/ComputedBorderRadius; public fun equals (Ljava/lang/Object;)Z + public final fun get (Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp;)F public final fun getBottomLeft ()F public final fun getBottomRight ()F public final fun getTopLeft ()F @@ -5965,6 +5966,16 @@ public final class com/facebook/react/uimanager/style/ComputedBorderRadius { public fun toString ()Ljava/lang/String; } +public final class com/facebook/react/uimanager/style/ComputedBorderRadiusProp : java/lang/Enum { + public static final field COMPUTED_BORDER_BOTTOM_LEFT_RADIUS Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; + public static final field COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; + public static final field COMPUTED_BORDER_TOP_LEFT_RADIUS Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; + public static final field COMPUTED_BORDER_TOP_RIGHT_RADIUS Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; + public static fun values ()[Lcom/facebook/react/uimanager/style/ComputedBorderRadiusProp; +} + public class com/facebook/react/uimanager/util/ReactFindViewUtil { public fun ()V public static fun addViewListener (Lcom/facebook/react/uimanager/util/ReactFindViewUtil$OnViewFoundListener;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt index d337e80ba35143..292b66c5b4dfbc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ComputedBorderRadius.kt @@ -7,6 +7,14 @@ package com.facebook.react.uimanager.style +/** Represents the collection of possible computed border radius style properties. */ +public enum class ComputedBorderRadiusProp { + COMPUTED_BORDER_TOP_LEFT_RADIUS, + COMPUTED_BORDER_TOP_RIGHT_RADIUS, + COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS, + COMPUTED_BORDER_BOTTOM_LEFT_RADIUS, +} + /** Phsysical edge lengths (in DIPs) for a border-radius. */ public data class ComputedBorderRadius( val topLeft: Float, @@ -18,5 +26,14 @@ public data class ComputedBorderRadius( return topLeft > 0f || topRight > 0f || bottomLeft > 0f || bottomRight > 0f } + public fun get(property: ComputedBorderRadiusProp): Float { + return when (property) { + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_LEFT_RADIUS -> topLeft + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_RIGHT_RADIUS -> topRight + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_LEFT_RADIUS -> bottomLeft + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS -> bottomRight + } + } + public constructor() : this(0f, 0f, 0f, 0f) } diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/BorderRadiusStyleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/BorderRadiusStyleTest.kt new file mode 100644 index 00000000000000..81f48635b4b1ee --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/BorderRadiusStyleTest.kt @@ -0,0 +1,186 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager + +import android.content.Context +import com.facebook.react.uimanager.style.BorderRadiusProp +import com.facebook.react.uimanager.style.BorderRadiusStyle +import com.facebook.react.uimanager.style.ComputedBorderRadiusProp +import org.assertj.core.api.Assertions.* +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +/** Tests for [BorderRadiusStyle] */ +@RunWith(RobolectricTestRunner::class) +class BorderRadiusStyleTest { + + private val ctx: Context = RuntimeEnvironment.getApplication() + + @Test + fun testCorrectPriorityLTR() { + val propertyOrderMap = + mapOf( + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_LEFT_RADIUS, + BorderRadiusProp.BORDER_TOP_START_RADIUS, + BorderRadiusProp.BORDER_START_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS, + BorderRadiusProp.BORDER_TOP_END_RADIUS, + BorderRadiusProp.BORDER_END_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_START_RADIUS, + BorderRadiusProp.BORDER_START_END_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_END_RADIUS, + BorderRadiusProp.BORDER_END_END_RADIUS), + ) + + propertyOrderMap.forEach { order -> + val borderRadiusStyle = BorderRadiusStyle() + // Starting count on 3 to test 0 override + var count = 3f + for (prop in order.value) { + borderRadiusStyle.set(prop, LengthPercentage(count, LengthPercentageType.POINT)) + val resolved = borderRadiusStyle.resolve(0, context = ctx, width = 100f, height = 100f) + assertThat(resolved.get(order.key)).isEqualTo(count) + count -= 1f + } + } + } + + @Test + fun testCorrectPriorityRTL() { + setContextLeftAndRightSwap(ctx, true) + val propertyOrderMap = + mapOf( + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS, + BorderRadiusProp.BORDER_TOP_END_RADIUS, + BorderRadiusProp.BORDER_END_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_LEFT_RADIUS, + BorderRadiusProp.BORDER_TOP_START_RADIUS, + BorderRadiusProp.BORDER_START_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_START_RADIUS, + BorderRadiusProp.BORDER_END_END_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_END_RADIUS, + BorderRadiusProp.BORDER_START_END_RADIUS), + ) + + propertyOrderMap.forEach { order -> + val borderRadiusStyle = BorderRadiusStyle() + // Starting count on 3 to test 0 override + var count = 3f + for (prop in order.value) { + borderRadiusStyle.set(prop, LengthPercentage(count, LengthPercentageType.POINT)) + val resolved = borderRadiusStyle.resolve(1, context = ctx, width = 100f, height = 100f) + assertThat(resolved.get(order.key)).isEqualTo(count) + count -= 1f + } + } + } + + @Test + fun testCorrectPriorityRTLNoSwap() { + setContextLeftAndRightSwap(ctx, false) + val propertyOrderMap = + mapOf( + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_LEFT_RADIUS, + BorderRadiusProp.BORDER_TOP_END_RADIUS, + BorderRadiusProp.BORDER_END_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_TOP_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_TOP_RIGHT_RADIUS, + BorderRadiusProp.BORDER_TOP_START_RADIUS, + BorderRadiusProp.BORDER_START_START_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_LEFT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_START_RADIUS, + BorderRadiusProp.BORDER_END_END_RADIUS), + ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS to + arrayOf( + BorderRadiusProp.BORDER_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS, + BorderRadiusProp.BORDER_BOTTOM_END_RADIUS, + BorderRadiusProp.BORDER_START_END_RADIUS), + ) + + propertyOrderMap.forEach { order -> + val borderRadiusStyle = BorderRadiusStyle() + // Starting count on 3 to test 0 override + var count = 3f + for (prop in order.value) { + borderRadiusStyle.set(prop, LengthPercentage(count, LengthPercentageType.POINT)) + val resolved = borderRadiusStyle.resolve(1, context = ctx, width = 100f, height = 100f) + assertThat(resolved.get(order.key)).isEqualTo(count) + count -= 1f + } + } + } + + @Test + fun testBorderRadiusPercentages() { + val borderRadiusStyle = + BorderRadiusStyle( + topLeft = LengthPercentage(0f, LengthPercentageType.PERCENT), + topRight = LengthPercentage(10f, LengthPercentageType.PERCENT), + bottomLeft = LengthPercentage(20f, LengthPercentageType.PERCENT), + bottomRight = LengthPercentage(30f, LengthPercentageType.PERCENT), + ) + val resolved = borderRadiusStyle.resolve(0, context = ctx, width = 1000f, height = 1000f) + + assertThat(resolved.topLeft).isEqualTo(0f) + assertThat(resolved.topRight).isEqualTo(100f) + assertThat(resolved.bottomLeft).isEqualTo(200f) + assertThat(resolved.bottomRight).isEqualTo(300f) + } + + /* + * Make I18nUtil.instance.doLeftAndRightSwapInRTL(context) return false + * by setting context preference + */ + private fun setContextLeftAndRightSwap(context: Context, leftAndRightSwap: Boolean) { + val sharedPrefs = + context.getSharedPreferences( + "com.facebook.react.modules.i18nmanager.I18nUtil", Context.MODE_PRIVATE) + val editor = sharedPrefs.edit() + editor.putBoolean("RCTI18nUtil_makeRTLFlipLeftAndRightStyles", leftAndRightSwap) + editor.apply() + } +}