Skip to content

Commit

Permalink
Restore Wallet Payment Method Menu (#9866)
Browse files Browse the repository at this point in the history
  • Loading branch information
toluo-stripe authored Jan 7, 2025
1 parent 8c69d08 commit 8a503d4
Show file tree
Hide file tree
Showing 25 changed files with 257 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.stripe.android.link.ui.menu

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
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.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.stripe.android.link.theme.HorizontalPadding
import com.stripe.android.link.theme.MinimumTouchTargetSize
import com.stripe.android.link.theme.linkColors

/**
* Displays a generic bottom sheet with the provided [items].
*
* @param items The list of items that implement [LinkMenuItem]
* @param onItemPress Called when an item in the list is pressed
*/
@Composable
internal fun LinkMenu(
modifier: Modifier = Modifier,
items: List<LinkMenuItem>,
onItemPress: (LinkMenuItem) -> Unit
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
) {
for (item in items) {
LinkBottomSheetRow(
item = item,
modifier = Modifier.clickable {
onItemPress(item)
}
)
}
}
}

@Composable
private fun LinkBottomSheetRow(
item: LinkMenuItem,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.height(MinimumTouchTargetSize)
.fillMaxWidth()
) {
Text(
text = item.text.resolve(context),
color = if (item.isDestructive) {
MaterialTheme.linkColors.errorText
} else {
Color.Unspecified
},
modifier = Modifier.padding(horizontal = HorizontalPadding)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.stripe.android.link.ui.menu

import com.stripe.android.core.strings.ResolvableString

/**
* An item to be displayed in a [LinkMenu].
*
* @property text The text content of the item
* @property isDestructive Whether this item should be rendered with the error text color
*/
internal interface LinkMenuItem {
val text: ResolvableString
val isDestructive: Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.stripe.android.link.ui.wallet

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.link.ui.menu.LinkMenu
import com.stripe.android.model.ConsumerPaymentDetails
import com.stripe.android.paymentsheet.R

@Composable
internal fun WalletPaymentMethodMenu(
modifier: Modifier = Modifier,
paymentDetails: ConsumerPaymentDetails.PaymentDetails,
onEditClick: () -> Unit,
onSetDefaultClick: () -> Unit,
onRemoveClick: () -> Unit,
onCancelClick: () -> Unit
) {
val items = remember(paymentDetails) {
buildList {
if (paymentDetails is ConsumerPaymentDetails.Card) {
add(WalletPaymentMethodMenuItem.EditCard)
}

if (!paymentDetails.isDefault) {
add(WalletPaymentMethodMenuItem.SetAsDefault)
}

add(
element = WalletPaymentMethodMenuItem.RemoveItem(
text = paymentDetails.removeLabel.resolvableString
)
)
add(WalletPaymentMethodMenuItem.Cancel)
}
}

LinkMenu(
modifier = modifier,
items = items,
onItemPress = { item ->
when (item) {
is WalletPaymentMethodMenuItem.EditCard -> onEditClick()
is WalletPaymentMethodMenuItem.SetAsDefault -> onSetDefaultClick()
is WalletPaymentMethodMenuItem.RemoveItem -> onRemoveClick()
is WalletPaymentMethodMenuItem.Cancel -> onCancelClick()
}
}
)
}

private val ConsumerPaymentDetails.PaymentDetails.removeLabel
get() = when (this) {
is ConsumerPaymentDetails.Card,
is ConsumerPaymentDetails.Passthrough -> R.string.stripe_paymentsheet_remove_card
is ConsumerPaymentDetails.BankAccount -> R.string.stripe_wallet_remove_linked_account
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.stripe.android.link.ui.wallet

import com.stripe.android.core.strings.ResolvableString
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.link.ui.menu.LinkMenuItem
import com.stripe.android.paymentsheet.R
import com.stripe.android.R as StripeR

internal sealed class WalletPaymentMethodMenuItem(
override val text: ResolvableString,
override val isDestructive: Boolean = false
) : LinkMenuItem {
data class RemoveItem(
override val text: ResolvableString
) : WalletPaymentMethodMenuItem(text, true)

data object EditCard : WalletPaymentMethodMenuItem(
text = R.string.stripe_wallet_update_card.resolvableString
)

data object SetAsDefault : WalletPaymentMethodMenuItem(
text = R.string.stripe_wallet_set_as_default.resolvableString
)

data object Cancel : WalletPaymentMethodMenuItem(
text = StripeR.string.stripe_cancel.resolvableString
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.stripe.android.link.ui.wallet

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.stripe.android.link.TestFactory
import com.stripe.android.link.theme.DefaultLinkTheme
import com.stripe.android.model.ConsumerPaymentDetails
import com.stripe.android.screenshottesting.FontSize
import com.stripe.android.screenshottesting.PaparazziRule
import com.stripe.android.screenshottesting.SystemAppearance
import org.junit.Rule
import org.junit.Test

internal class WalletPaymentMethodMenuScreenshotTest {
@get:Rule
val paparazziRule = PaparazziRule(
SystemAppearance.entries,
FontSize.entries,
boxModifier = Modifier
.padding(0.dp)
.fillMaxWidth(),
)

@Test
fun testCard() {
snapshot(
paymentDetails = TestFactory.CONSUMER_PAYMENT_DETAILS_CARD
.copy(
isDefault = false
)
)
}

@Test
fun testCardAsDefault() {
snapshot(
paymentDetails = TestFactory.CONSUMER_PAYMENT_DETAILS_CARD
.copy(
isDefault = true
)
)
}

@Test
fun testBankAccount() {
snapshot(
paymentDetails = TestFactory.CONSUMER_PAYMENT_DETAILS_BANK_ACCOUNT
.copy(
isDefault = false
)
)
}

@Test
fun testBankAccountAsDefault() {
snapshot(
paymentDetails = TestFactory.CONSUMER_PAYMENT_DETAILS_BANK_ACCOUNT
.copy(
isDefault = true
)
)
}

@Test
fun testPassthrough() {
snapshot(TestFactory.CONSUMER_PAYMENT_DETAILS_PASSTHROUGH)
}

private fun snapshot(
paymentDetails: ConsumerPaymentDetails.PaymentDetails
) {
paparazziRule.snapshot {
DefaultLinkTheme {
WalletPaymentMethodMenu(
paymentDetails = paymentDetails,
onEditClick = {},
onCancelClick = {},
onRemoveClick = {},
onSetDefaultClick = {}
)
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8a503d4

Please sign in to comment.