Skip to content

Commit

Permalink
Add screenshot tests for TextFieldUI. (#7532)
Browse files Browse the repository at this point in the history
  • Loading branch information
samer-stripe authored Oct 30, 2023
1 parent 9bc7198 commit d3831c7
Show file tree
Hide file tree
Showing 47 changed files with 383 additions and 80 deletions.
17 changes: 17 additions & 0 deletions stripe-ui-core/res/drawable/stripe_ic_card_visa.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56dp"
android:height="40dp"
android:viewportWidth="56"
android:viewportHeight="40">
<path
android:pathData="M39.5067,13.748C39.5891,13.2735 39.3216,12.8078 38.8703,12.6398C38.0406,12.331 36.7536,12 35.1712,12C33.1712,12 31.383,12.499 30.0634,13.4375C28.727,14.3879 27.8809,15.7915 27.8692,17.473C27.8497,18.8144 28.5054,19.8241 29.2866,20.5439C30.0521,21.2492 30.9968,21.7346 31.7256,22.0691C32.4376,22.3959 32.8078,22.6206 32.995,22.7841C33.0263,22.8114 33.048,22.8333 33.0626,22.8495C33.0384,22.8742 32.989,22.9151 32.8983,22.9617C32.654,23.0872 32.2651,23.1739 31.8359,23.1739C30.5026,23.1739 29.8342,22.9946 28.7247,22.5336L28.2424,22.3213C27.9606,22.1972 27.6374,22.2099 27.3662,22.3556C27.095,22.5013 26.906,22.7638 26.8539,23.0672L26.3312,26.1094C26.2528,26.5658 26.498,27.0163 26.9238,27.1983C27.9517,27.6376 29.7036,27.9846 31.4739,28H31.4826C33.5616,28 35.3854,27.5247 36.73,26.5841C36.6735,26.8423 36.7211,27.1157 36.8679,27.3421C37.0523,27.6263 37.3681,27.7978 37.7069,27.7978H41.6261C42.0396,27.7978 42.4103,27.5434 42.5591,27.1577L43.0963,25.7649H46.4026L46.6743,27.0102C46.7742,27.4699 47.1811,27.7978 47.6515,27.7978H51.11C51.4133,27.7978 51.7002,27.6601 51.89,27.4235C52.0798,27.1869 52.152,26.8769 52.0861,26.5808L49.0727,13.0286C48.971,12.5711 48.5652,12.2457 48.0966,12.2457H45.2074C44.683,12.2457 44.1089,12.3153 43.5793,12.5911C43.0106,12.8873 42.5939,13.3873 42.3388,13.9696L38.9235,21.6196C38.8003,20.9048 38.4887,20.2496 37.9912,19.6631C37.3442,18.9004 36.4138,18.2885 35.2677,17.773C34.5999,17.4559 34.1637,17.2357 33.8891,17.0381L33.8647,17.0203C34.0335,16.9361 34.3548,16.8406 34.8932,16.8406L34.9171,16.8403C35.95,16.8157 36.7009,17.0441 37.292,17.2831L37.6216,17.4235C37.9026,17.5432 38.2229,17.5279 38.4912,17.3819C38.7596,17.236 38.9465,16.9754 38.9987,16.6745L39.5067,13.748Z"
android:fillColor="#ffffff"
android:fillAlpha="0.87"/>
<path
android:pathData="M13.7161,14.1795L14.7345,19.0349L17.1718,12.8776C17.3227,12.4961 17.6914,12.2456 18.1016,12.2456H22.0513C22.3421,12.2456 22.616,12.372 22.8045,12.5878C22.9892,12.3765 23.2602,12.2456 23.5577,12.2456H27.2927C27.5876,12.2456 27.8675,12.3758 28.0575,12.6013C28.2475,12.8269 28.3283,13.1248 28.2782,13.4155L25.9421,26.9678C25.8594,27.4475 25.4434,27.798 24.9566,27.798H21.2214C20.9265,27.798 20.6466,27.6678 20.4566,27.4422C20.2666,27.2167 20.1858,26.9187 20.2359,26.6281L22.1481,15.5356L17.0976,27.1815C16.939,27.547 16.5786,27.7836 16.1801,27.7836H12.2302C11.7827,27.7836 11.3897,27.4863 11.268,27.0557L8.0552,15.6919C7.0772,15.1951 5.9722,14.7869 4.7424,14.4918C4.2109,14.3643 3.8802,13.8337 4,13.3004L4.0614,13.0265C4.1639,12.57 4.5693,12.2456 5.0371,12.2456H11.0468C11.0585,12.2456 11.0701,12.2458 11.0818,12.2462C11.5599,12.263 12.1223,12.3441 12.6242,12.6396C13.1682,12.9598 13.5409,13.4718 13.7079,14.1437C13.7108,14.1556 13.7136,14.1675 13.7161,14.1795Z"
android:fillColor="#ffffff"
android:fillAlpha="0.87"/>
<path
android:pathData="M8.9254,15.0078C7.7767,14.3778 6.4657,13.8711 5,13.5195L5.0615,13.2457H11.0711C11.8857,13.2742 12.5466,13.5193 12.7617,14.3849L14.0678,20.6116L14.4679,22.4871L18.1259,13.2457H22.0756L16.2045,26.7837H12.2546L8.9254,15.0078ZM24.9809,26.798H21.2457L23.582,13.2457H27.317L24.9809,26.798ZM38.5215,13.577L38.0135,16.5034L37.6757,16.3595C36.9998,16.0855 36.1076,15.8117 34.8933,15.8406C33.4186,15.8406 32.7572,16.4314 32.7419,17.0082C32.7419,17.6426 33.5419,18.0608 34.8483,18.6809C37.0004,19.6468 37.9986,20.829 37.9837,22.3717C37.9535,25.1832 35.4019,27 31.4826,27C29.8069,26.9854 28.1929,26.6532 27.3168,26.2787L27.8395,23.2365L28.3314,23.4531C29.5457,23.9582 30.3447,24.1739 31.836,24.1739C32.9114,24.1739 34.0644,23.7554 34.0791,22.8475C34.0791,22.2564 33.5879,21.8235 32.1428,21.1603C30.7289,20.5113 28.8385,19.4301 28.8692,17.4836C28.8851,14.8453 31.4826,13 35.1713,13C36.6162,13 37.7847,13.3028 38.5215,13.577ZM43.4859,21.9969H46.5906C46.437,21.3193 45.7297,18.0753 45.7297,18.0753L45.4686,16.9076C45.2842,17.4121 44.9617,18.234 44.9772,18.2051C44.9772,18.2051 43.7935,21.2183 43.4859,21.9969ZM48.0966,13.2457L51.11,26.7978H47.6515C47.6515,26.7978 47.3131,25.2407 47.2058,24.7649H42.4101C42.2714,25.1252 41.6262,26.7978 41.6262,26.7978H37.7069L43.2551,14.3701C43.6395,13.4906 44.3164,13.2457 45.2074,13.2457H48.0966Z"
android:fillColor="#1434CB"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import androidx.compose.ui.semantics.editableText
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import com.stripe.android.core.Logger
import com.stripe.android.uicore.BuildConfig
Expand Down Expand Up @@ -138,7 +139,7 @@ fun TextField(
val placeHolder by textFieldController.placeHolder.collectAsState(null)

var hasFocus by rememberSaveable { mutableStateOf(false) }
val colors = TextFieldColors(shouldShowError)

val fieldState by textFieldController.fieldState.collectAsState(
TextFieldStateConstants.Error.Blank
)
Expand Down Expand Up @@ -171,8 +172,9 @@ fun TextField(
autofillTree += autofillNode
}

TextField(
TextFieldUi(
value = value,
loading = loading,
onValueChange = { newValue ->
val acceptInput = fieldState.canAcceptInput(value, newValue)

Expand All @@ -184,8 +186,8 @@ fun TextField(
}
}
},
onDropdownItemClicked = textFieldController::onDropdownItemClicked,
modifier = modifier
.fillMaxWidth()
.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyDown &&
event.nativeKeyEvent.keyCode == KeyEvent.KEYCODE_DEL &&
Expand Down Expand Up @@ -219,21 +221,69 @@ fun TextField(
this.editableText = AnnotatedString("")
},
enabled = enabled && textFieldController.enabled,
label = label?.let {
stringResource(it)
},
showOptionalLabel = textFieldController.showOptionalLabel,
placeholder = placeHolder,
trailingIcon = trailingIcon,
shouldShowError = shouldShowError,
visualTransformation = textFieldController.visualTransformation,
keyboardOptions = KeyboardOptions(
keyboardType = textFieldController.keyboardType,
capitalization = textFieldController.capitalization,
imeAction = imeAction
),
keyboardActions = KeyboardActions(
onNext = {
focusManager.moveFocus(nextFocusDirection)
},
onDone = {
focusManager.clearFocus(true)
}
)
)
}

@Composable
internal fun TextFieldUi(
value: String,
enabled: Boolean,
loading: Boolean,
label: String?,
placeholder: String?,
trailingIcon: TextFieldIcon?,
showOptionalLabel: Boolean,
shouldShowError: Boolean,
modifier: Modifier = Modifier,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
onValueChange: (value: String) -> Unit = {},
onDropdownItemClicked: (item: TextFieldIcon.Dropdown.Item) -> Unit = {}
) {
val colors = TextFieldColors(shouldShowError)

TextField(
value = value,
onValueChange = onValueChange,
modifier = modifier.fillMaxWidth(),
enabled = enabled,
label = label?.let {
{
FormLabel(
text = if (textFieldController.showOptionalLabel) {
text = if (showOptionalLabel) {
stringResource(
R.string.stripe_form_label_optional,
stringResource(it)
it
)
} else {
stringResource(it)
it
}
)
}
},
placeholder = placeHolder?.let {
placeholder = placeholder?.let {
{ Placeholder(text = it) }
},
trailingIcon = trailingIcon?.let {
Expand All @@ -252,85 +302,20 @@ fun TextField(
}
}
is TextFieldIcon.Dropdown -> {
var expanded by remember {
mutableStateOf(false)
}

val show = !loading && !it.hide

Box {
Row(
modifier = Modifier
.padding(10.dp)
.testTag(DROPDOWN_MENU_CLICKABLE_TEST_TAG)
.clickable(enabled = show) {
expanded = true
},
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
TrailingIcon(
TextFieldIcon.Trailing(
it.currentItem.icon,
isTintable = false
),
loading
)

if (show) {
CompositionLocalProvider(
LocalContentColor
provides
MaterialTheme.stripeColors.placeholderText
) {
TrailingIcon(
trailingIcon = TextFieldIcon.Trailing(
R.drawable.stripe_ic_chevron_down,
isTintable = true
),
loading = false
)
}
}
}

SingleChoiceDropdown(
expanded = expanded,
title = it.title,
currentChoice = it.currentItem,
choices = it.items,
headerTextColor = MaterialTheme.stripeColors.subtitle,
optionTextColor = MaterialTheme.stripeColors.onComponent,
onChoiceSelected = { item ->
textFieldController.onDropdownItemClicked(item)

expanded = false
},
onDismiss = {
expanded = false
}
)
}
TrailingDropdown(
icon = it,
loading = loading,
onDropdownItemClicked = onDropdownItemClicked
)
}
}
}
}
},
isError = shouldShowError,
visualTransformation = textFieldController.visualTransformation,
keyboardOptions = KeyboardOptions(
keyboardType = textFieldController.keyboardType,
capitalization = textFieldController.capitalization,
imeAction = imeAction
),
keyboardActions = KeyboardActions(
onNext = {
focusManager.moveFocus(nextFocusDirection)
},
onDone = {
focusManager.clearFocus(true)
}
),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = true,
colors = colors
)
Expand Down Expand Up @@ -407,6 +392,73 @@ internal fun TrailingIcon(
}
}

@Composable
private fun TrailingDropdown(
icon: TextFieldIcon.Dropdown,
loading: Boolean,
onDropdownItemClicked: (item: TextFieldIcon.Dropdown.Item) -> Unit
) {
var expanded by remember {
mutableStateOf(false)
}

val show = !loading && !icon.hide

Box {
Row(
modifier = Modifier
.padding(10.dp)
.testTag(DROPDOWN_MENU_CLICKABLE_TEST_TAG)
.clickable(enabled = show) {
expanded = true
},
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
TrailingIcon(
TextFieldIcon.Trailing(
icon.currentItem.icon,
isTintable = false
),
loading
)

if (show) {
CompositionLocalProvider(
LocalContentColor
provides
MaterialTheme.stripeColors.placeholderText
) {
TrailingIcon(
trailingIcon = TextFieldIcon.Trailing(
R.drawable.stripe_ic_chevron_down,
isTintable = true
),
loading = false
)
}
}
}

SingleChoiceDropdown(
expanded = expanded,
title = icon.title,
currentChoice = icon.currentItem,
choices = icon.items,
headerTextColor = MaterialTheme.stripeColors.subtitle,
optionTextColor = MaterialTheme.stripeColors.onComponent,
onChoiceSelected = { item ->
onDropdownItemClicked(item)

expanded = false
},
onDismiss = {
expanded = false
}
)
}
}

private fun Modifier.conditionallyClickable(onClick: (() -> Unit)?): Modifier {
return if (onClick != null) {
clickable { onClick() }
Expand Down
Loading

0 comments on commit d3831c7

Please sign in to comment.