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 a8a5b55c81
commit a3ce1d484d
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"
}
}
}