Skip to content

Commit 9c852c8

Browse files
authored
Support text field insets (#10951)
1 parent a3fd1f5 commit 9c852c8

File tree

10 files changed

+161
-10
lines changed

10 files changed

+161
-10
lines changed

financial-connections/src/main/java/com/stripe/android/financialconnections/features/networkinglinksignup/NetworkingLinkSignupScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ private fun PhoneNumberSection(
268268
enabled = true,
269269
showChevron = false,
270270
modifier = Modifier
271-
.padding(horizontal = 6.dp)
271+
.padding(start = 2.dp, end = 6.dp)
272272
.clip(RoundedCornerShape(8.dp))
273273
.background(colors.backgroundSecondary)
274274
.padding(vertical = 12.dp, horizontal = 8.dp)
Loading

paymentsheet/api/paymentsheet.api

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,8 +1402,8 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Appearance : and
14021402
public final fun component3 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;
14031403
public final fun component4 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;
14041404
public final fun component5 ()Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;
1405-
public final fun copy (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Embedded;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;Lcom/stripe/android/paymentsheet/PaymentSheet$Spacing;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;
1406-
public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Embedded;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;Lcom/stripe/android/paymentsheet/PaymentSheet$Spacing;ILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;
1405+
public final fun copy (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Embedded;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;Lcom/stripe/android/paymentsheet/PaymentSheet$Spacing;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;
1406+
public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Embedded;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;Lcom/stripe/android/paymentsheet/PaymentSheet$Spacing;Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;ILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;
14071407
public final fun describeContents ()I
14081408
public fun equals (Ljava/lang/Object;)Z
14091409
public final fun getColors (Z)Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;
@@ -1428,6 +1428,7 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Appearance$Build
14281428
public final fun primaryButton (Lcom/stripe/android/paymentsheet/PaymentSheet$PrimaryButton;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder;
14291429
public final fun sectionSpacing (Lcom/stripe/android/paymentsheet/PaymentSheet$Spacing;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder;
14301430
public final fun shapes (Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder;
1431+
public final fun textFieldInsets (Lcom/stripe/android/paymentsheet/PaymentSheet$Insets;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder;
14311432
public final fun typography (Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder;
14321433
}
14331434

paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,11 @@ class PaymentSheet internal constructor(
11111111
* be applied.
11121112
*/
11131113
internal val sectionSpacing: Spacing = Spacing.defaultSectionSpacing,
1114+
1115+
/**
1116+
* Defines spacing inside the input fields of a form.
1117+
*/
1118+
internal val textFieldInsets: Insets = Insets.defaultTextFieldInsets
11141119
) : Parcelable {
11151120
constructor() : this(
11161121
colorsLight = Colors.defaultLight,
@@ -1475,6 +1480,8 @@ class PaymentSheet internal constructor(
14751480
@OptIn(AppearanceAPIAdditionsPreview::class)
14761481
private var sectionSpacing: Spacing = Spacing.defaultSectionSpacing
14771482

1483+
private var textFieldInsets: Insets = Insets.defaultTextFieldInsets
1484+
14781485
@ExperimentalEmbeddedPaymentElementApi
14791486
private var embeddedAppearance: Embedded =
14801487
Embedded.default
@@ -1513,6 +1520,11 @@ class PaymentSheet internal constructor(
15131520
this.sectionSpacing = sectionSpacing
15141521
}
15151522

1523+
@AppearanceAPIAdditionsPreview
1524+
fun textFieldInsets(textFieldInsets: Insets) = apply {
1525+
this.textFieldInsets = textFieldInsets
1526+
}
1527+
15161528
@OptIn(ExperimentalEmbeddedPaymentElementApi::class, AppearanceAPIAdditionsPreview::class)
15171529
fun build(): Appearance {
15181530
return Appearance(
@@ -1524,6 +1536,7 @@ class PaymentSheet internal constructor(
15241536
embeddedAppearance = embeddedAppearance,
15251537
formInsetValues = formInsetValues,
15261538
sectionSpacing = sectionSpacing,
1539+
textFieldInsets = textFieldInsets,
15271540
)
15281541
}
15291542
}
@@ -2026,6 +2039,13 @@ class PaymentSheet internal constructor(
20262039
endDp = 20f,
20272040
bottomDp = 40f,
20282041
)
2042+
2043+
internal val defaultTextFieldInsets = Insets(
2044+
startDp = StripeThemeDefaults.textFieldInsets.start,
2045+
topDp = StripeThemeDefaults.textFieldInsets.top,
2046+
endDp = StripeThemeDefaults.textFieldInsets.end,
2047+
bottomDp = StripeThemeDefaults.textFieldInsets.bottom,
2048+
)
20292049
}
20302050
}
20312051

paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetConfigurationKtx.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.ui.unit.TextUnit
1111
import androidx.compose.ui.unit.sp
1212
import com.stripe.android.lpmfoundations.paymentmethod.WalletType
1313
import com.stripe.android.paymentelement.AppearanceAPIAdditionsPreview
14+
import com.stripe.android.uicore.FormInsets
1415
import com.stripe.android.uicore.PrimaryButtonColors
1516
import com.stripe.android.uicore.PrimaryButtonShape
1617
import com.stripe.android.uicore.PrimaryButtonTypography
@@ -103,6 +104,13 @@ internal fun PaymentSheet.Appearance.parseAppearance() {
103104
} else {
104105
null
105106
}
107+
108+
StripeTheme.textFieldInsets = FormInsets(
109+
start = textFieldInsets.startDp,
110+
end = textFieldInsets.endDp,
111+
top = textFieldInsets.topDp,
112+
bottom = textFieldInsets.bottomDp,
113+
)
106114
}
107115

108116
internal val PaymentSheet.WalletButtonsConfiguration.allowedWalletTypes: List<WalletType>

paymentsheet/src/test/java/com/stripe/android/lpmfoundations/paymentmethod/definitions/CardUiDefinitionFactoryTest.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class CardUiDefinitionFactoryTest {
3535
listOf(CustomSpacingAppearance)
3636
)
3737

38+
@get:Rule
39+
val customTextFieldsPaparazziRule = PaparazziRule(
40+
listOf(CustomTextInsetsAppearance)
41+
)
42+
3843
private val metadata = PaymentMethodMetadataFactory.create(
3944
stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.copy(
4045
paymentMethodTypes = listOf("card"),
@@ -200,6 +205,23 @@ class CardUiDefinitionFactoryTest {
200205
}
201206
}
202207

208+
@Test
209+
fun testCardWithCustomTextFieldInsets() {
210+
customTextFieldsPaparazziRule.snapshot {
211+
val setupIntent = SetupIntentFixtures.SI_REQUIRES_PAYMENT_METHOD.copy(
212+
paymentMethodTypes = listOf("card", "link"),
213+
)
214+
215+
CardDefinition.CreateFormUi(
216+
metadata = metadata.copy(
217+
stripeIntent = setupIntent,
218+
paymentMethodSaveConsentBehavior = PaymentMethodSaveConsentBehavior.Enabled,
219+
customerMetadata = getDefaultCustomerMetadata(),
220+
),
221+
)
222+
}
223+
}
224+
203225
@Test
204226
fun testCardRightToLeft() {
205227
rightToLeftPaparazziRule.snapshot {
@@ -256,4 +278,26 @@ class CardUiDefinitionFactoryTest {
256278
DefaultAppearance.appearance.parseAppearance()
257279
}
258280
}
281+
282+
@OptIn(AppearanceAPIAdditionsPreview::class)
283+
private data object CustomTextInsetsAppearance : PaparazziConfigOption {
284+
private val appearance = PaymentSheet.Appearance.Builder()
285+
.textFieldInsets(
286+
PaymentSheet.Insets(
287+
startDp = 24f,
288+
endDp = 20f,
289+
topDp = 28f,
290+
bottomDp = 20f,
291+
)
292+
)
293+
.build()
294+
295+
override fun initialize() {
296+
appearance.parseAppearance()
297+
}
298+
299+
override fun reset() {
300+
DefaultAppearance.appearance.parseAppearance()
301+
}
302+
}
259303
}

stripe-ui-core/src/main/java/com/stripe/android/uicore/StripeTheme.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ object StripeThemeDefaults {
325325
val textFieldInsets = FormInsets(
326326
start = 16f,
327327
top = 20f,
328-
end = 16f,
328+
end = 12f,
329329
bottom = 10f
330330
)
331331

stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/PhoneNumberElementUI.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import androidx.compose.ui.tooling.preview.Preview
3939
import androidx.compose.ui.unit.LayoutDirection
4040
import androidx.compose.ui.unit.dp
4141
import com.stripe.android.core.strings.resolvableString
42+
import com.stripe.android.uicore.LocalTextFieldInsets
4243
import com.stripe.android.uicore.R
4344
import com.stripe.android.uicore.elements.compat.CompatTextField
4445
import com.stripe.android.uicore.moveFocusSafely
@@ -131,6 +132,7 @@ fun PhoneNumberElementUI(
131132
val visualTransformation by controller.visualTransformation.collectAsState()
132133
val colors = TextFieldColors(shouldShowError != null)
133134
var hasFocus by rememberSaveable { mutableStateOf(false) }
135+
val textFieldInsets = LocalTextFieldInsets.current
134136

135137
if (moveToNextFieldOnceComplete) {
136138
LaunchedEffect(isComplete) {
@@ -198,6 +200,7 @@ fun PhoneNumberElementUI(
198200
singleLine = true,
199201
colors = colors,
200202
errorMessage = null,
203+
contentPadding = textFieldInsets.asPaddingValues(),
201204
)
202205
}
203206

@@ -213,12 +216,12 @@ fun PhoneNumberElementUI(
213216
@Composable
214217
private fun CountryDropdown(
215218
phoneNumberController: PhoneNumberController,
216-
enabled: Boolean
219+
enabled: Boolean,
217220
) {
218221
DropDown(
219222
controller = phoneNumberController.countryDropdownController,
220223
enabled = enabled,
221224
modifier = Modifier
222-
.padding(start = 16.dp, end = 8.dp)
225+
.padding(start = 11.7.dp, end = 8.dp)
223226
)
224227
}

stripe-ui-core/src/main/java/com/stripe/android/uicore/elements/compat/CompatTextField.kt

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
3232
import androidx.compose.foundation.interaction.collectIsFocusedAsState
3333
import androidx.compose.foundation.layout.Box
3434
import androidx.compose.foundation.layout.PaddingValues
35+
import androidx.compose.foundation.layout.calculateEndPadding
36+
import androidx.compose.foundation.layout.calculateStartPadding
37+
import androidx.compose.foundation.layout.fillMaxWidth
38+
import androidx.compose.foundation.layout.padding
3539
import androidx.compose.foundation.text.BasicTextField
3640
import androidx.compose.foundation.text.KeyboardActions
3741
import androidx.compose.foundation.text.KeyboardOptions
@@ -59,6 +63,7 @@ import androidx.compose.ui.graphics.Color
5963
import androidx.compose.ui.graphics.Shape
6064
import androidx.compose.ui.graphics.SolidColor
6165
import androidx.compose.ui.graphics.takeOrElse
66+
import androidx.compose.ui.platform.LocalLayoutDirection
6267
import androidx.compose.ui.res.stringResource
6368
import androidx.compose.ui.semantics.error
6469
import androidx.compose.ui.semantics.semantics
@@ -69,6 +74,8 @@ import androidx.compose.ui.text.input.KeyboardType
6974
import androidx.compose.ui.text.input.TextFieldValue
7075
import androidx.compose.ui.text.input.VisualTransformation
7176
import androidx.compose.ui.text.lerp
77+
import androidx.compose.ui.unit.coerceAtLeast
78+
import androidx.compose.ui.unit.dp
7279
import androidx.compose.ui.R as ComposeUiR
7380

7481
/**
@@ -181,8 +188,7 @@ fun CompatTextField(
181188
maxLines = maxLines,
182189
minLines = minLines,
183190
decorationBox = @Composable { innerTextField ->
184-
// places leading icon, text field with label and placeholder, trailing icon
185-
CommonDecorationBox(
191+
InsetDecorationBox(
186192
value = value,
187193
visualTransformation = visualTransformation,
188194
innerTextField = innerTextField,
@@ -312,8 +318,7 @@ fun CompatTextField(
312318
maxLines = maxLines,
313319
minLines = minLines,
314320
decorationBox = @Composable { innerTextField ->
315-
// places leading icon, text field with label and placeholder, trailing icon
316-
CommonDecorationBox(
321+
InsetDecorationBox(
317322
value = value.text,
318323
visualTransformation = visualTransformation,
319324
innerTextField = innerTextField,
@@ -333,6 +338,76 @@ fun CompatTextField(
333338
)
334339
}
335340

341+
@OptIn(ExperimentalMaterialApi::class)
342+
@Composable
343+
private fun InsetDecorationBox(
344+
value: String,
345+
innerTextField: @Composable () -> Unit,
346+
enabled: Boolean = true,
347+
label: @Composable (() -> Unit)? = null,
348+
placeholder: @Composable (() -> Unit)? = null,
349+
leadingIcon: @Composable (() -> Unit)? = null,
350+
trailingIcon: @Composable (() -> Unit)? = null,
351+
isError: Boolean = false,
352+
visualTransformation: VisualTransformation = VisualTransformation.None,
353+
singleLine: Boolean = false,
354+
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
355+
shape: Shape = TextFieldDefaults.TextFieldShape,
356+
colors: TextFieldColors = TextFieldDefaults.textFieldColors(),
357+
contentPadding: PaddingValues = if (label != null) {
358+
TextFieldDefaults.textFieldWithLabelPadding()
359+
} else {
360+
TextFieldDefaults.textFieldWithoutLabelPadding()
361+
},
362+
) {
363+
val layoutDirection = LocalLayoutDirection.current
364+
365+
val startPadding = contentPadding.calculateStartPadding(layoutDirection)
366+
val endPadding = contentPadding.calculateEndPadding(layoutDirection)
367+
368+
Box(
369+
modifier = Modifier.padding(
370+
start = leadingIcon?.let {
371+
(startPadding - HorizontalIconPadding).coerceAtLeast(0.dp)
372+
} ?: 0.dp,
373+
end = trailingIcon?.let {
374+
(endPadding - HorizontalIconPadding).coerceAtLeast(0.dp)
375+
} ?: 0.dp,
376+
)
377+
) {
378+
// places leading icon, text field with label and placeholder, trailing icon
379+
CommonDecorationBox(
380+
value = value,
381+
visualTransformation = visualTransformation,
382+
innerTextField = {
383+
Box(Modifier.fillMaxWidth(), propagateMinConstraints = true) {
384+
innerTextField()
385+
}
386+
},
387+
placeholder = placeholder,
388+
label = label,
389+
leadingIcon = leadingIcon,
390+
trailingIcon = trailingIcon,
391+
singleLine = singleLine,
392+
enabled = enabled,
393+
isError = isError,
394+
interactionSource = interactionSource,
395+
colors = colors,
396+
shape = shape,
397+
contentPadding = PaddingValues(
398+
top = contentPadding.calculateTopPadding(),
399+
bottom = contentPadding.calculateBottomPadding(),
400+
start = leadingIcon?.let {
401+
TextFieldPadding
402+
} ?: startPadding,
403+
end = trailingIcon?.let {
404+
TextFieldPadding
405+
} ?: endPadding,
406+
),
407+
)
408+
}
409+
}
410+
336411
/**
337412
* Implementation of the [CompatTextField]
338413
*/

0 commit comments

Comments
 (0)