Skip to content

Commit

Permalink
Add Embedded PaymentMethodRowButton styles (#9787)
Browse files Browse the repository at this point in the history
* Add Embedded PaymentMethodRowButton styles
  • Loading branch information
tjclawson-stripe authored Dec 17, 2024
1 parent 5e9ea2e commit 238402f
Show file tree
Hide file tree
Showing 38 changed files with 598 additions and 10 deletions.
2 changes: 2 additions & 0 deletions paymentsheet/api/paymentsheet.api
Original file line number Diff line number Diff line change
Expand Up @@ -2310,7 +2310,9 @@ public final class com/stripe/android/paymentsheet/ui/SepaMandateResult$Canceled
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 static field lambda-2 Lkotlin/jvm/functions/Function3;
public fun <init> ()V
public final fun getLambda-1$paymentsheet_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-2$paymentsheet_release ()Lkotlin/jvm/functions/Function3;
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentsheet.ui.PaymentMethodIcon
import com.stripe.android.paymentsheet.verticalmode.UIConstants.iconHeight
import com.stripe.android.paymentsheet.verticalmode.UIConstants.iconWidth
Expand Down Expand Up @@ -47,6 +48,7 @@ internal fun NewPaymentMethodRowButton(
)
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
internal fun NewPaymentMethodRowButton(
isEnabled: Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android.paymentsheet.verticalmode

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
Expand All @@ -11,28 +12,40 @@ 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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.selectable
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
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.graphics.Color
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 androidx.compose.ui.unit.dp
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentsheet.PaymentSheet.Appearance.Embedded.RowStyle
import com.stripe.android.paymentsheet.ui.PaymentMethodIcon
import com.stripe.android.paymentsheet.ui.PromoBadge
import com.stripe.android.paymentsheet.verticalmode.UIConstants.iconWidth
import com.stripe.android.uicore.getBorderStroke
import com.stripe.android.uicore.image.StripeImageLoader
import com.stripe.android.uicore.stripeColors

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
internal fun PaymentMethodRowButton(
isEnabled: Boolean,
Expand All @@ -45,6 +58,7 @@ internal fun PaymentMethodRowButton(
onClick: () -> Unit,
contentDescription: String? = null,
modifier: Modifier = Modifier,
style: RowStyle = RowStyle.FloatingButton.default,
trailingContent: (@Composable RowScope.() -> Unit)? = null,
) {
val contentPaddingValues = if (subtitle != null) {
Expand All @@ -53,11 +67,11 @@ internal fun PaymentMethodRowButton(
12.dp
}

RowButtonFloatingOuterContent(
RowButtonOuterContent(
style = style,
isEnabled = isEnabled,
isSelected = isSelected,
contentPaddingValues = PaddingValues(horizontal = 12.dp, vertical = contentPaddingValues),
verticalArrangement = Arrangement.Center,
contentPaddingValues = contentPaddingValues,
modifier = modifier
.fillMaxWidth()
.heightIn(min = 52.dp)
Expand All @@ -66,26 +80,91 @@ internal fun PaymentMethodRowButton(
enabled = isClickable,
onClick = onClick
),
) {
trailingContent = trailingContent,
onClick = onClick
) { displayTrailingContent ->
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
RowButtonInnerContent(isEnabled, iconContent, title, subtitle, contentDescription)

Spacer(modifier = Modifier.weight(1f))
if (style !is RowStyle.FlatWithCheckmark) {
Spacer(modifier = Modifier.weight(1f))
}

if (promoText != null) {
PromoBadge(promoText)
}

if (trailingContent != null) {
if (trailingContent != null && displayTrailingContent) {
trailingContent()
}
}
}
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
private fun RowButtonOuterContent(
style: RowStyle,
isEnabled: Boolean,
isSelected: Boolean,
contentPaddingValues: Dp,
modifier: Modifier,
trailingContent: @Composable (RowScope.() -> Unit)?,
onClick: () -> Unit,
rowContent: @Composable (displayTrailingContent: Boolean) -> Unit
) {
when (style) {
is RowStyle.FloatingButton -> {
RowButtonFloatingOuterContent(
isEnabled = isEnabled,
isSelected = isSelected,
contentPaddingValues = PaddingValues(
horizontal = 12.dp,
vertical = contentPaddingValues + style.additionalInsetsDp.dp
),
verticalArrangement = Arrangement.Center,
modifier = modifier,
) {
rowContent(true)
}
}
is RowStyle.FlatWithCheckmark -> {
RowButtonCheckmarkOuterContent(
isSelected = isSelected,
contentPaddingValues = PaddingValues(
horizontal = 0.dp,
vertical = contentPaddingValues + style.additionalInsetsDp.dp
),
verticalArrangement = Arrangement.Center,
trailingContent = trailingContent,
style = style,
modifier = modifier
) {
rowContent(false)
}
}
is RowStyle.FlatWithRadio -> {
RowButtonRadioOuterContent(
isEnabled = isEnabled,
isSelected = isSelected,
contentPaddingValues = PaddingValues(
horizontal = 0.dp,
vertical = contentPaddingValues + style.additionalInsetsDp.dp
),
verticalArrangement = Arrangement.Center,
onClick = onClick,
style = style,
modifier = modifier
) {
rowContent(true)
}
}
}
}

@Composable
private fun RowButtonFloatingOuterContent(
isEnabled: Boolean,
Expand All @@ -112,6 +191,84 @@ private fun RowButtonFloatingOuterContent(
}
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
private fun RowButtonRadioOuterContent(
isEnabled: Boolean,
isSelected: Boolean,
contentPaddingValues: PaddingValues,
verticalArrangement: Arrangement.Vertical,
onClick: () -> Unit,
modifier: Modifier,
style: RowStyle.FlatWithRadio,
content: @Composable ColumnScope.() -> Unit,
) {
Row(
modifier = modifier
.background(MaterialTheme.stripeColors.component)
) {
RadioButton(
selected = isSelected,
onClick = onClick,
enabled = isEnabled,
modifier = Modifier.align(Alignment.CenterVertically).size(20.dp),
colors = RadioButtonDefaults.colors(
selectedColor = Color(style.selectedColor),
unselectedColor = Color(style.unselectedColor)
)
)
Spacer(Modifier.width(12.dp))
Column(
modifier = Modifier
.padding(contentPaddingValues)
.align(Alignment.CenterVertically),
verticalArrangement = verticalArrangement,
) {
content()
}
}
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
private fun RowButtonCheckmarkOuterContent(
isSelected: Boolean,
contentPaddingValues: PaddingValues,
verticalArrangement: Arrangement.Vertical,
trailingContent: (@Composable RowScope.() -> Unit)?,
modifier: Modifier,
style: RowStyle.FlatWithCheckmark,
content: @Composable ColumnScope.() -> Unit,
) {
Row(
modifier = modifier
.background(MaterialTheme.stripeColors.component),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.padding(contentPaddingValues),
verticalArrangement = verticalArrangement,
) {
content()
Row {
if (trailingContent != null) {
Spacer(Modifier.width(iconWidth + 16.dp))
trailingContent()
}
}
}
Spacer(Modifier.weight(1f))
if (isSelected) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.align(Alignment.CenterVertically).padding(end = style.checkmarkInsetDp.dp),
tint = Color(style.checkmarkColor)
)
}
}
}

/**
* Icon, title, and subtitle if provided. Common across all PaymentMethodRowButton configurations
*/
Expand Down Expand Up @@ -167,6 +324,7 @@ private fun TitleContent(title: String, subtitle: String?, isEnabled: Boolean, c
}
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
@Preview
private fun ButtonPreview() {
Expand All @@ -184,10 +342,14 @@ private fun ButtonPreview() {
contentAlignment = Alignment.Center,
)
},
title = "Card",
subtitle = "This is a card",
title = "•••• 4242",
subtitle = null,
promoText = null,
onClick = {}
onClick = {},
style = RowStyle.FloatingButton.default,
trailingContent = {
Text("Edit")
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.ui.unit.dp
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.model.CardBrand
import com.stripe.android.model.PaymentMethod
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentsheet.DisplayableSavedPaymentMethod
import com.stripe.android.paymentsheet.ui.PaymentMethodIconFromResource
import com.stripe.android.paymentsheet.ui.getLabel
Expand All @@ -23,6 +24,7 @@ import com.stripe.android.paymentsheet.verticalmode.UIConstants.iconHeight
import com.stripe.android.paymentsheet.verticalmode.UIConstants.iconWidth
import com.stripe.android.uicore.strings.resolve

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@Composable
internal fun SavedPaymentMethodRowButton(
displayableSavedPaymentMethod: DisplayableSavedPaymentMethod,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentsheet.PaymentSheet.Appearance.Embedded.RowStyle.FloatingButton
import com.stripe.android.screenshottesting.FontSize
import com.stripe.android.screenshottesting.PaparazziRule
import com.stripe.android.ui.core.R
import com.stripe.android.uicore.StripeThemeDefaults
import org.junit.Rule
import org.junit.Test

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
internal class PaymentMethodRowButtonScreenshotTest {

@get:Rule
Expand Down Expand Up @@ -186,4 +190,32 @@ internal class PaymentMethodRowButtonScreenshotTest {
)
}
}

@Test
fun testStyleAppearance() {
val style = FloatingButton(
spacingDp = StripeThemeDefaults.floating.spacing,
additionalInsetsDp = 40f
)
paparazziRule.snapshot {
PaymentMethodRowButton(
isEnabled = true,
isSelected = false,
iconContent = {
Image(
painter = painterResource(id = R.drawable.stripe_ic_paymentsheet_pm_card),
contentDescription = null
)
},
title = "**** 4242",
subtitle = null,
promoText = null,
onClick = {},
style = style,
trailingContent = {
Text(text = "View more")
}
)
}
}
}
Loading

0 comments on commit 238402f

Please sign in to comment.