design(text field) : allow setting validity (instead of just isError bool)
This commit is contained in:
@@ -29,6 +29,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TextFieldValidity
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -98,7 +99,11 @@ private fun RoomAddressField(
|
||||
"e.g. #room-name:matrix.org"
|
||||
}
|
||||
},
|
||||
isError = addressState is RoomAddressState.Invalid,
|
||||
validity = when (addressState) {
|
||||
RoomAddressState.Unknown -> null
|
||||
RoomAddressState.Invalid -> TextFieldValidity.Invalid
|
||||
is RoomAddressState.Valid -> if (addressState.matchingRoomFound) TextFieldValidity.Valid else null
|
||||
},
|
||||
onValueChange = onAddressChange,
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TextFieldValidity
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
@@ -90,7 +91,7 @@ fun BugReportView(
|
||||
keyboardController?.hide()
|
||||
}),
|
||||
minLines = 3,
|
||||
isError = state.isDescriptionInError,
|
||||
validity = if(state.isDescriptionInError) TextFieldValidity.Invalid else null,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TextFieldValidity
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
@@ -82,8 +83,8 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool
|
||||
var showPassword by remember { mutableStateOf(false) }
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onTabOrEnterKeyFocusNext(LocalFocusManager.current),
|
||||
.fillMaxWidth()
|
||||
.onTabOrEnterKeyFocusNext(LocalFocusManager.current),
|
||||
value = text,
|
||||
onValueChange = onTextChange,
|
||||
placeholder = stringResource(CommonStrings.common_password),
|
||||
@@ -99,7 +100,7 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool
|
||||
Icon(imageVector = image, description)
|
||||
}
|
||||
},
|
||||
isError = hasError,
|
||||
validity = if (hasError) TextFieldValidity.Invalid else null,
|
||||
supportingText = if (hasError) {
|
||||
stringResource(R.string.screen_reset_encryption_password_error)
|
||||
} else {
|
||||
|
||||
@@ -58,7 +58,7 @@ fun TextField(
|
||||
placeholder: String? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
trailingIcon: @Composable (() -> Unit)? = null,
|
||||
isError: Boolean = false,
|
||||
validity: TextFieldValidity? = null,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = false,
|
||||
@@ -93,7 +93,7 @@ fun TextField(
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
isFocused = isFocused,
|
||||
isError = isError,
|
||||
validity = validity,
|
||||
leadingIcon = leadingIcon,
|
||||
placeholder = placeholder,
|
||||
isTextEmpty = value.isEmpty(),
|
||||
@@ -114,7 +114,7 @@ fun TextField(
|
||||
placeholder: String? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
trailingIcon: @Composable (() -> Unit)? = null,
|
||||
isError: Boolean = false,
|
||||
validity: TextFieldValidity? = null,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = false,
|
||||
@@ -149,7 +149,7 @@ fun TextField(
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
isFocused = isFocused,
|
||||
isError = isError,
|
||||
validity = validity,
|
||||
leadingIcon = leadingIcon,
|
||||
placeholder = placeholder,
|
||||
isTextEmpty = value.text.isEmpty(),
|
||||
@@ -166,7 +166,7 @@ private fun DecorationBox(
|
||||
enabled: Boolean,
|
||||
readOnly: Boolean,
|
||||
isFocused: Boolean,
|
||||
isError: Boolean,
|
||||
validity: TextFieldValidity?,
|
||||
placeholder: String?,
|
||||
isTextEmpty: Boolean,
|
||||
supportingText: String?,
|
||||
@@ -187,7 +187,7 @@ private fun DecorationBox(
|
||||
enabled = enabled,
|
||||
readOnly = readOnly,
|
||||
isFocused = isFocused,
|
||||
isError = isError
|
||||
isError = validity == TextFieldValidity.Invalid
|
||||
) {
|
||||
Row(modifier = Modifier.padding(16.dp)) {
|
||||
if (leadingIcon != null) {
|
||||
@@ -216,7 +216,7 @@ private fun DecorationBox(
|
||||
}
|
||||
if (supportingText != null) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
SupportingTextLayout(isError, supportingText)
|
||||
SupportingTextLayout(validity, supportingText)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,24 +254,44 @@ private fun TextFieldContainer(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SupportingTextLayout(isError: Boolean, supportingText: String) {
|
||||
private fun SupportingTextLayout(validity: TextFieldValidity?, supportingText: String) {
|
||||
Row(horizontalArrangement = spacedBy(4.dp)) {
|
||||
if (isError) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Error(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary
|
||||
)
|
||||
when (validity) {
|
||||
TextFieldValidity.Invalid -> {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Error(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = ElementTheme.colors.iconCriticalPrimary
|
||||
)
|
||||
}
|
||||
TextFieldValidity.Valid -> {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.CheckCircleSolid(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = ElementTheme.colors.iconSuccessPrimary
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
Text(
|
||||
text = supportingText,
|
||||
color = if (isError) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary,
|
||||
color = when (validity) {
|
||||
TextFieldValidity.Invalid -> ElementTheme.colors.textCriticalPrimary
|
||||
TextFieldValidity.Valid -> ElementTheme.colors.textSuccessPrimary
|
||||
else -> ElementTheme.colors.textSecondary
|
||||
},
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class TextFieldValidity {
|
||||
Invalid,
|
||||
Valid
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun textFieldStyle(enabled: Boolean): TextStyle {
|
||||
return ElementTheme.typography.fontBodyLgRegular.copy(
|
||||
@@ -283,11 +303,11 @@ private fun textFieldStyle(enabled: Boolean): TextStyle {
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.TextFields)
|
||||
@Preview(group = PreviewGroup.TextFields, heightDp = 1000)
|
||||
@Composable
|
||||
internal fun TextFieldsLightPreview() = ElementPreviewLight { ContentToPreview() }
|
||||
|
||||
@Preview(group = PreviewGroup.TextFields)
|
||||
@Preview(group = PreviewGroup.TextFields, heightDp = 1000)
|
||||
@Composable
|
||||
internal fun TextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() }
|
||||
|
||||
@@ -295,15 +315,15 @@ internal fun TextFieldsDarkPreview() = ElementPreviewDark { ContentToPreview() }
|
||||
@ExcludeFromCoverage
|
||||
private fun ContentToPreview() {
|
||||
Column(modifier = Modifier.padding(4.dp)) {
|
||||
allBooleans.forEach { isError ->
|
||||
TextFieldValidity.entries.forEach { validity ->
|
||||
allBooleans.forEach { enabled ->
|
||||
allBooleans.forEach { readonly ->
|
||||
TextField(
|
||||
onValueChange = {},
|
||||
label = "Label",
|
||||
value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}",
|
||||
value = "Hello val=${validity}, en=${enabled.asInt()}, ro=${readonly.asInt()}",
|
||||
supportingText = "Supporting text",
|
||||
isError = isError,
|
||||
validity = validity,
|
||||
enabled = enabled,
|
||||
readOnly = readonly,
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
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
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@@ -56,7 +57,10 @@ fun RoomAddressField(
|
||||
}
|
||||
else -> supportingText
|
||||
},
|
||||
isError = addressValidity.isError(),
|
||||
validity = when (addressValidity) {
|
||||
RoomAddressValidity.InvalidSymbols, RoomAddressValidity.NotAvailable -> TextFieldValidity.Invalid
|
||||
else -> null
|
||||
},
|
||||
onValueChange = onAddressChange,
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package io.element.android.libraries.matrix.ui.room.address
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.designsystem.theme.components.TextFieldValidity
|
||||
|
||||
/**
|
||||
* Represents the validity state of a room address.
|
||||
@@ -19,8 +20,4 @@ sealed interface RoomAddressValidity {
|
||||
data object InvalidSymbols : RoomAddressValidity
|
||||
data object NotAvailable : RoomAddressValidity
|
||||
data object Valid : RoomAddressValidity
|
||||
|
||||
fun isError(): Boolean {
|
||||
return this is InvalidSymbols || this is NotAvailable
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user