From 0d5fafb8bae704cc05de1cf6b19f496187bb097a Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Thu, 21 Nov 2024 19:25:35 -0500 Subject: [PATCH 1/2] Refactor PaymentMethodRowButton to use RowButtonInnerContent --- .../verticalmode/PaymentMethodRowButton.kt | 111 ++++++++++++++++-- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt index 27c68956bbf..878b493ab2c 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt @@ -2,23 +2,34 @@ package com.stripe.android.paymentsheet.verticalmode import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.selectable +import androidx.compose.material.Card import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.stripe.android.paymentsheet.ui.RowButton +import com.stripe.android.paymentsheet.ui.PaymentMethodIcon +import com.stripe.android.uicore.getBorderStroke +import com.stripe.android.uicore.image.StripeImageLoader import com.stripe.android.uicore.stripeColors @Composable @@ -40,27 +51,25 @@ internal fun PaymentMethodRowButton( 12.dp } - RowButton( + RowButtonFloatingOuterContent( isEnabled = isEnabled, isSelected = isSelected, - isClickable = isClickable, - onClick = onClick, contentPaddingValues = PaddingValues(horizontal = 12.dp, vertical = contentPaddingValues), verticalArrangement = Arrangement.Center, - modifier = modifier.fillMaxWidth().heightIn(min = 52.dp), + modifier = modifier + .fillMaxWidth() + .heightIn(min = 52.dp) + .selectable( + selected = isSelected, + enabled = isClickable, + onClick = onClick + ), ) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, ) { - iconContent() - - TitleContent( - title = title, - subtitle = subtitle, - isEnabled = isEnabled, - contentDescription = contentDescription - ) + RowButtonInnerContent(isEnabled, iconContent, title, subtitle, contentDescription) if (trailingContent != null) { Spacer(modifier = Modifier.weight(1f)) @@ -70,6 +79,58 @@ internal fun PaymentMethodRowButton( } } +@Composable +private fun RowButtonFloatingOuterContent( + isEnabled: Boolean, + isSelected: Boolean, + contentPaddingValues: PaddingValues, + verticalArrangement: Arrangement.Vertical, + modifier: Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Card( + modifier = modifier + .alpha(alpha = if (isEnabled) 1.0F else 0.6F), + shape = MaterialTheme.shapes.medium, + backgroundColor = MaterialTheme.stripeColors.component, + border = MaterialTheme.getBorderStroke(isSelected), + elevation = if (isSelected) 1.5.dp else 0.dp + ) { + Column( + modifier = Modifier.padding(contentPaddingValues), + verticalArrangement = verticalArrangement, + ) { + content() + } + } +} + +/** + * Icon, title, and subtitle if provided. Common across all PaymentMethodRowButton configurations + */ +@Composable +private fun RowButtonInnerContent( + isEnabled: Boolean, + iconContent: @Composable RowScope.() -> Unit, + title: String, + subtitle: String?, + contentDescription: String? = null, +) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + iconContent() + + TitleContent( + title = title, + subtitle = subtitle, + isEnabled = isEnabled, + contentDescription = contentDescription + ) + } +} + @Composable private fun TitleContent(title: String, subtitle: String?, isEnabled: Boolean, contentDescription: String?,) { val textColor = MaterialTheme.stripeColors.onComponent @@ -98,3 +159,27 @@ private fun TitleContent(title: String, subtitle: String?, isEnabled: Boolean, c } } } + +@Composable +@Preview +internal fun ButtonPreview() { + Row { + PaymentMethodRowButton( + isEnabled = true, + isSelected = true, + iconContent = { + PaymentMethodIcon( + iconRes = com.stripe.android.ui.core.R.drawable.stripe_ic_paymentsheet_pm_card, + iconUrl = null, + imageLoader = StripeImageLoader(LocalContext.current.applicationContext), + iconRequiresTinting = true, + modifier = Modifier.height(22.dp).width(22.dp), + contentAlignment = Alignment.Center, + ) + }, + title = "Card", + subtitle = "This is a card", + onClick = {} + ) + } +} From d85f6902933de51ecc1805e800eea8f0549d330f Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Fri, 22 Nov 2024 14:01:35 -0500 Subject: [PATCH 2/2] Fix tests --- paymentsheet/api/paymentsheet.api | 7 +++++++ .../com/stripe/android/paymentsheet/PaymentSheetPage.kt | 3 +-- .../paymentsheet/verticalmode/PaymentMethodRowButton.kt | 2 +- .../com/stripe/android/paymentsheet/VerticalModePage.kt | 7 ++----- .../paymentsheet/verticalmode/ManageScreenUITest.kt | 6 ++---- .../verticalmode/PaymentMethodVerticalLayoutUITest.kt | 6 +++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/paymentsheet/api/paymentsheet.api b/paymentsheet/api/paymentsheet.api index 1a4a15f65f1..2a9e4014703 100644 --- a/paymentsheet/api/paymentsheet.api +++ b/paymentsheet/api/paymentsheet.api @@ -2249,3 +2249,10 @@ public final class com/stripe/android/paymentsheet/ui/SepaMandateResult$Canceled public synthetic fun newArray (I)[Ljava/lang/Object; } +public final class com/stripe/android/paymentsheet/verticalmode/ComposableSingletons$PaymentMethodRowButtonKt { + public static final field INSTANCE Lcom/stripe/android/paymentsheet/verticalmode/ComposableSingletons$PaymentMethodRowButtonKt; + public static field lambda-1 Lkotlin/jvm/functions/Function3; + public fun ()V + public final fun getLambda-1$paymentsheet_release ()Lkotlin/jvm/functions/Function3; +} + diff --git a/paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/PaymentSheetPage.kt b/paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/PaymentSheetPage.kt index 2c4a134fde1..7ab96870b6c 100644 --- a/paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/PaymentSheetPage.kt +++ b/paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/PaymentSheetPage.kt @@ -5,7 +5,6 @@ package com.stripe.android.paymentsheet import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.hasAnyDescendant import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isEnabled @@ -268,7 +267,7 @@ internal class PaymentSheetPage( fun assertLpmSelected(code: String) { composeTestRule.waitUntil { composeTestRule - .onAllNodes(hasTestTag("${TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON}_$code").and(hasAnyDescendant(isSelected()))) + .onAllNodes(hasTestTag("${TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON}_$code").and(isSelected())) .fetchSemanticsNodes() .isNotEmpty() } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt index 878b493ab2c..536d255b220 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodRowButton.kt @@ -162,7 +162,7 @@ private fun TitleContent(title: String, subtitle: String?, isEnabled: Boolean, c @Composable @Preview -internal fun ButtonPreview() { +private fun ButtonPreview() { Row { PaymentMethodRowButton( isEnabled = true, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/VerticalModePage.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/VerticalModePage.kt index 873dc733493..1b4c4855b1e 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/VerticalModePage.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/VerticalModePage.kt @@ -45,9 +45,7 @@ internal class VerticalModePage( fun assertLpmIsSelected(paymentMethodCode: PaymentMethodCode) { composeTestRule.onNode( - hasTestTag("${TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON}_$paymentMethodCode").and( - hasAnyDescendant(isSelected()) - ) + hasTestTag("${TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON}_$paymentMethodCode").and(isSelected()) ).assertExists() } @@ -79,8 +77,7 @@ internal class VerticalModePage( fun assertHasSelectedSavedPaymentMethod(paymentMethodId: String, cardBrand: String? = null) { composeTestRule.onNode( - hasTestTag("${TEST_TAG_SAVED_PAYMENT_METHOD_ROW_BUTTON}_$paymentMethodId") - .and(hasAnyDescendant(isSelected())) + hasTestTag("${TEST_TAG_SAVED_PAYMENT_METHOD_ROW_BUTTON}_$paymentMethodId").and(isSelected()) ).assertExists() if (cardBrand != null) { diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenUITest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenUITest.kt index 57415e13101..2c67244e125 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenUITest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenUITest.kt @@ -2,11 +2,10 @@ package com.stripe.android.paymentsheet.verticalmode import android.os.Build import androidx.compose.ui.test.SemanticsNodeInteraction -import androidx.compose.ui.test.assertAny +import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.hasContentDescriptionExactly import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onChild import androidx.compose.ui.test.onChildren import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick @@ -78,7 +77,7 @@ class ManageScreenUITest { ) { composeRule.onNodeWithTag( "${TEST_TAG_SAVED_PAYMENT_METHOD_ROW_BUTTON}_${savedCard.id}" - ).onChildren().assertAny( + ).assert( hasContentDescriptionExactly("Visa ending in 4 2 4 2 ") ) } @@ -186,7 +185,6 @@ class ManageScreenUITest { "${TEST_TAG_SAVED_PAYMENT_METHOD_ROW_BUTTON}_${displayableSavedPaymentMethods[1].paymentMethod.id}" ) // The selected node is the PaymentMethodRowButton which is a child of the SavedPaymentMethodRowButton - .onChild() .assertIsSelected() } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutUITest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutUITest.kt index 25299af1fb3..7b15bb44b5f 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutUITest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutUITest.kt @@ -1,8 +1,8 @@ package com.stripe.android.paymentsheet.verticalmode import android.os.Build +import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertAll -import androidx.compose.ui.test.assertAny import androidx.compose.ui.test.isSelected import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onChildren @@ -204,7 +204,7 @@ internal class PaymentMethodVerticalLayoutUITest { composeRule.onNodeWithTag( TEST_TAG_SAVED_PAYMENT_METHOD_ROW_BUTTON + "_${PaymentMethodFixtures.displayableCard().paymentMethod.id}" ).assertExists() - .onChildren().assertAny(isSelected()) + .assert(isSelected()) composeRule.onNodeWithTag(TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON + "_card") .assertExists() @@ -253,7 +253,7 @@ internal class PaymentMethodVerticalLayoutUITest { .onChildren().assertAll(isSelected().not()) composeRule.onNodeWithTag(TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON + "_cashapp") .assertExists() - .onChildren().assertAny(isSelected()) + .assert(isSelected()) composeRule.onNodeWithTag(TEST_TAG_NEW_PAYMENT_METHOD_ROW_BUTTON + "_klarna") .assertExists() .onChildren().assertAll(isSelected().not())