Replace OutlinedTextField by our TextField (#4521)

* Let TextFieldListItem take the entire width.

* Add unit test to detect usage of OutlinedTextField.

* Use TextField instead of OutlinedTextField

* Remove unnecessary opt in to ExperimentalFoundationApi

* Use TextField instead of OutlinedTextField

* Fix compilation issue.

* Update screenshots

* ListDialog: add space between items.

* Update screenshots

* Set applyPaddingToContents to true by default.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2025-04-02 16:04:07 +02:00 committed by GitHub
parent fcaf6a53f8
commit 670af86e8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 86 additions and 167 deletions

View file

@ -7,7 +7,6 @@
package io.element.android.features.licenses.impl.list
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
@ -15,7 +14,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -33,10 +31,11 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DependencyLicensesListView(
state: DependencyLicensesListState,
@ -60,7 +59,7 @@ fun DependencyLicensesListView(
) {
if (state.licenses.isSuccess()) {
// Search field
OutlinedTextField(
TextField(
value = state.filter,
onValueChange = { state.eventSink(DependencyLicensesListEvent.SetFilter(it)) },
leadingIcon = {

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components.customreaction
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -41,7 +40,7 @@ import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EmojiPicker(
onSelectEmoji: (Emoji) -> Unit,

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components.reactionsummary
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -94,7 +93,6 @@ fun ReactionSummaryView(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ReactionSummaryViewContent(
summary: ReactionSummaryState.Summary,

View file

@ -98,7 +98,6 @@ fun RoomMembersModerationView(
onDismissRequest = { state.eventSink(RoomMembersModerationEvents.Reset) },
placeholder = stringResource(id = CommonStrings.common_reason),
label = stringResource(id = CommonStrings.common_reason),
withBorder = true,
content = stringResource(R.string.screen_room_member_list_kick_member_confirmation_description),
value = "",
)
@ -138,7 +137,6 @@ fun RoomMembersModerationView(
onDismissRequest = { state.eventSink(RoomMembersModerationEvents.Reset) },
placeholder = stringResource(id = CommonStrings.common_reason),
label = stringResource(id = CommonStrings.common_reason),
withBorder = true,
content = stringResource(R.string.screen_room_member_list_ban_member_confirmation_description),
value = "",
)

View file

@ -10,7 +10,6 @@ package io.element.android.features.roomlist.impl.filters
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.animateScrollBy
@ -48,7 +47,6 @@ import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun RoomListFiltersView(
state: RoomListFiltersState,

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
@ -38,7 +39,7 @@ fun ListDialog(
cancelText: String = stringResource(CommonStrings.action_cancel),
submitText: String = stringResource(CommonStrings.action_ok),
enabled: Boolean = true,
applyPaddingToContents: Boolean = false,
applyPaddingToContents: Boolean = true,
listItems: LazyListScope.() -> Unit,
) {
val decoratedSubtitle: @Composable (() -> Unit)? = subtitle?.let {
@ -92,7 +93,8 @@ private fun ListDialogContent(
// No start padding if padding is already applied to the content
val horizontalPadding = if (applyPaddingToContents) 0.dp else 8.dp
LazyColumn(
modifier = Modifier.padding(horizontal = horizontalPadding)
modifier = Modifier.padding(horizontal = horizontalPadding),
verticalArrangement = Arrangement.spacedBy(16.dp),
) { listItems() }
}
}
@ -117,7 +119,7 @@ internal fun ListDialogContentPreview() {
cancelText = "Cancel",
submitText = "Save",
enabled = true,
applyPaddingToContents = false,
applyPaddingToContents = true,
)
}
}

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable
@ -44,7 +45,6 @@ fun TextFieldDialog(
maxLines: Int = 1,
content: String? = null,
label: String? = null,
withBorder: Boolean = false,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
submitText: String = stringResource(CommonStrings.action_ok),
) {
@ -65,7 +65,6 @@ fun TextFieldDialog(
onSubmit = { onSubmit(textFieldContents.text) },
onDismissRequest = onDismissRequest,
enabled = canSubmit,
applyPaddingToContents = content.isNullOrEmpty().not(),
submitText = submitText,
modifier = modifier,
) {
@ -81,7 +80,6 @@ fun TextFieldDialog(
TextFieldListItem(
placeholder = placeholder.orEmpty(),
label = label,
withBorder = withBorder,
text = textFieldContents,
onTextChange = {
error = if (!validation(it.text)) onValidationErrorMessage else null
@ -95,7 +93,9 @@ fun TextFieldDialog(
}
}),
maxLines = maxLines,
modifier = Modifier.focusRequester(focusRequester),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
)
canRequestFocus = true
}
@ -120,22 +120,6 @@ internal fun TextFieldDialogPreview() = ElementPreview {
)
}
@PreviewsDayNight
@Composable
internal fun TextFieldDialogWithBorderPreview() = ElementPreview {
TextFieldDialog(
title = "Title",
content = "Some content",
onSubmit = {},
onDismissRequest = {},
value = "Value",
placeholder = "Placeholder",
label = "Label",
withBorder = true,
onValidationErrorMessage = "Error message",
)
}
@PreviewsDayNight
@Composable
internal fun TextFieldDialogWithErrorPreview() = ElementPreview {
@ -148,7 +132,6 @@ internal fun TextFieldDialogWithErrorPreview() = ElementPreview {
value = "Value",
placeholder = "Placeholder",
label = "Label",
withBorder = true,
onValidationErrorMessage = "Error message",
)
}

View file

@ -9,17 +9,14 @@ package io.element.android.libraries.designsystem.components.list
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TextFieldValidity
@Composable
fun TextFieldListItem(
@ -29,33 +26,19 @@ fun TextFieldListItem(
modifier: Modifier = Modifier,
error: String? = null,
maxLines: Int = 1,
withBorder: Boolean = false,
label: String? = null,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
val textFieldStyle = ElementTheme.materialTypography.bodyLarge
OutlinedTextField(
TextField(
value = text,
onValueChange = { onTextChange(it) },
placeholder = placeholder?.let { @Composable { Text(it) } },
label = label?.let { @Composable { Text(it) } },
colors = if (withBorder) {
OutlinedTextFieldDefaults.colors()
} else {
OutlinedTextFieldDefaults.colors(
disabledBorderColor = Color.Transparent,
errorBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent,
unfocusedBorderColor = Color.Transparent,
)
},
isError = error != null,
supportingText = error?.let { @Composable { Text(it) } },
placeholder = placeholder,
label = label,
validity = if (error != null) TextFieldValidity.Invalid else TextFieldValidity.None,
supportingText = error,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
textStyle = textFieldStyle,
maxLines = maxLines,
singleLine = maxLines == 1,
modifier = modifier,
@ -70,33 +53,19 @@ fun TextFieldListItem(
modifier: Modifier = Modifier,
error: String? = null,
maxLines: Int = 1,
withBorder: Boolean = false,
label: String? = null,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
val textFieldStyle = ElementTheme.materialTypography.bodyLarge
OutlinedTextField(
TextField(
value = text,
onValueChange = { onTextChange(it) },
placeholder = placeholder?.let { @Composable { Text(it) } },
label = label?.let { @Composable { Text(it) } },
colors = if (withBorder) {
OutlinedTextFieldDefaults.colors()
} else {
OutlinedTextFieldDefaults.colors(
disabledBorderColor = Color.Transparent,
errorBorderColor = Color.Transparent,
focusedBorderColor = Color.Transparent,
unfocusedBorderColor = Color.Transparent,
)
},
isError = error != null,
supportingText = error?.let { @Composable { Text(it) } },
placeholder = placeholder,
label = label,
validity = if (error != null) TextFieldValidity.Invalid else TextFieldValidity.None,
supportingText = error,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
textStyle = textFieldStyle,
maxLines = maxLines,
singleLine = maxLines == 1,
modifier = modifier,
@ -138,31 +107,3 @@ internal fun TextFieldListItemTextFieldValuePreview() {
)
}
}
@Preview("Text field List item with border - empty", group = PreviewGroup.ListItems)
@Composable
internal fun TextFieldListItemWithBorderEmptyPreview() {
ElementThemedPreview {
TextFieldListItem(
placeholder = "Placeholder",
label = "Label",
text = "",
withBorder = true,
onTextChange = {},
)
}
}
@Preview("Text field List item with border - text", group = PreviewGroup.ListItems)
@Composable
internal fun TextFieldListItemWithBorderPreview() {
ElementThemedPreview {
TextFieldListItem(
placeholder = "Placeholder",
label = "Label",
text = "Text",
withBorder = true,
onTextChange = {},
)
}
}

View file

@ -24,4 +24,17 @@ class KonsistImportTest {
it.name == "org.jetbrains.annotations.VisibleForTesting"
}
}
@Test
fun `OutlinedTextField should not be used`() {
Konsist
.scopeFromProject()
.imports
.assertFalse(
additionalMessage = "Please use 'io.element.android.libraries.designsystem.theme.components.TextField' instead of " +
"'androidx.compose.material3.OutlinedTextField.",
) {
it.name == "androidx.compose.material3.OutlinedTextField"
}
}
}

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5248793987bd2f6f98e571366e6fcc87085fdba5372a4a31314a52a13efcc78a
size 30160
oid sha256:72a0143f03e008d0961066cd600d7567c5a64b36d8d4ed610324ae68b15660ef
size 30061

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d835fa6c041ca10d362e7c0dd77fe784e2f3b903523b9a813e1cd47a04f53df
size 31365
oid sha256:d6c2cbf04f228dc6d63660d4a0d5bd7a56e2dfa7aba0778e01860d99017d25e4
size 31325

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7363ab9655be9350ff4f10eda2278962489b8fd983a7b3f5a91b40f281bbb37f
size 29379
oid sha256:fd5044ab9be16487d6637d80aa81d2d70f48596f9bd9db2049cc152145ab5b06
size 29268

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a4290ae65632bee52d5c7ea6cb34de07e8fb585398edccb1a411d669410d8299
size 30547
oid sha256:8ecfa7ad0239499dfb3611b0049244d46d2dc795906bf5e5afdefd0bf9ede43f
size 30464

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b9a5256bf2af8a8e9ee87bbab2ebdc7cd5823bc6b2cfde6267a68bd797e6eb61
size 29650
oid sha256:6a3da0d5bba7824bc5a76b87bb2be17d17d7d2f0492f4332416929366efb7a61
size 30927

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:39b34e0940f38ab0d66ba68b61c07ba70a51acfb54f995eedce800231ea6a2c5
size 28257
oid sha256:cce848d6c16adfcbd2ef9eef5e65a035ccd4d0a3092953e51433fc134c286518
size 29471

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e07ab4816ff009eaa26e1f3cf69b613a9a34baea0dd55e0eface745ee7328758
size 27486
oid sha256:6723e18a9ec3f01a2909f97c5ad80b7ce682653ec16229e1129ac509c2decd0a
size 28912

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19483b4fae4026098273e6e4c941592074f08b3e7f9fa44e759bdab9daf2b27a
size 26233
oid sha256:2f7b32aed7f97cd97719c22400cbe7a4d14e9a4cf10d42b26b853cd81cb42f1e
size 27620

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:114c59fd334979528721e0409e22096a058f965b0cfb5b7860816590d7c84652
size 29924
oid sha256:4a35c5690e291b30e85491485dca6203d3541bae8aa6e680f53d5480b312b4fa
size 30859

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c5f9152fbdce0823fb728586486e8625f96caceb23b69685680313b62a81c1a8
size 18073
oid sha256:6bf99a794b62bcac4356d40c621e541cc8b4819851be94ee4fb5f990821dad43
size 18696

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1d324a3cf4090c18459f06753d28bee13102457da07ef17358a7d65797df965b
size 16234
oid sha256:9ac05090a322d3b3eb9bc6a8975a3f832e15099553186b9054036807b6d16124
size 16719

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1be0efd7f33ee57088b04aedf6c63cb8092abc0356a31636fe028e31d542e28d
size 15671

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e7609a1d51c1be29b0d117dd968bd44b250d02e82e1630fa062da4cd280bb950
size 13934

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4697eda8d2fb160b2cade0be583148ec0b2e55f35fe44929f98feb4c8535377
size 17761
oid sha256:869c9e0b2dd0ea776b99ab4e223c2e8c31f62a2ea9f7c2aa7c06423eb9bfa531
size 18638

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2774d56a1e788567c19df9a714f1399968e75d2df6c3a4f404e3c03a30286567
size 15951
oid sha256:5ca9146ee4758956ef5487e198352e836ed354a0c427f854780db2d3977621d9
size 16738

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3257be6487ff2f412564bb8f188860e39b0325156533ad519c9d6852000a173a
size 13119
oid sha256:a7874f4a2365bed4de566c8db579d8a456d298da8c913bbdabfd7b66ba2d99d9
size 13342

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:baf396f36cdc0ca5e01842f70fe0b93166fa82c11ad981c54a529f59c90b5111
size 11469
oid sha256:271f172f1c5cc924426afa2ae51f295c10cf51602600b46cac812a2973b6e68d
size 11694

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:73978f15c708b9bb39f20aaaf2e1510caa796b8875bd1418d0ce1e6a99f60ad6
size 8907
oid sha256:798d2a617d7d5cdad84a3e39a81e83c9de1d5b84bfd35f54fd3a1e2271bdc411
size 9277

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b02a6c58c1a54f4e86b7b5db36c925ccc9a2012dc30372736216d489d2a8e33e
size 9819
oid sha256:ddbf6a82c11d7dd55140c65677a5c96d53e09ad08c6a65313575a1d9b73744fa
size 10207

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7dc297457b140b0bcf72fb3de3e2fd8061983ab70ad2d5299cc5ba85d5a7db55
size 7270

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a3648b24b1640410c19eb5ee5c9808c48be5cb2dc0862d84f8419289988fd39
size 8708

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f25810294b831c73b678d93a0352c4d2e84e946ee197680be40074145a4ba5ab
size 6104
oid sha256:3427ed512414c9e6bdfb90db3caf29dacb8373878e56992eff260506d128c4bf
size 6564

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f4393abc83861f1ab67e0637d7192887c31d94ecad9642691bcfe87e68df3a42
size 13422
oid sha256:12f0ca8fdeddd40c744cb0ece103a98bfa2f38450d46458fcc6fc06751360ed5
size 13606

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57c8c7fa3b993fc52c3fbc2014252e8fff1395f855d6f4acd1681d843eedf2c0
size 11825
oid sha256:e7c5e43207805ef6241140c17405258d2d6615ca80180cc6880457326b6b8446
size 12011

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57d2c9529a6cef0b9a009710645f92d50f93563f4f765994f6f006ef4db1d5c4
size 14684
oid sha256:2928dcfc8005ecf9b4666ab6e4c53f79641eb6946271db8a994a40edb40c963d
size 15180

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a43a5ebccf177014e2d7726e9a0f0cf4970252b7301843b98c5ae7e72dc34aee
size 12955
oid sha256:69d4d152932d0ff43f179f739810a63afad8124dd5ea706d73ab399cc00871cb
size 13472

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0602f269f3d3879db0a6b18ff56de7dd3fcecf79ccee1c25e2aa763dab9e1e3d
size 16958
oid sha256:ac58ade49e587fe1be47055a6c0147dba40ee4e3ded1a3ae67ae9ccecf4c54b0
size 17424

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0ef3f08243ed2dde9dbe4eb7422d68c33a4ae1443b7b75dd3661d245f58b539a
size 15088
oid sha256:e9d00a99b3bfad8572a86562f1e40b4ac06f241efcd778398d1f7593ff3f5c43
size 15620