Skip to content

Commit

Permalink
Merge pull request #348 from Orange-OpenSource/338-text-field-error-m…
Browse files Browse the repository at this point in the history
…essage-example

338 - text field - error message example
  • Loading branch information
florentmaitre authored Nov 28, 2022
2 parents b783dc5 + 421c20f commit 09a7c2e
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 250 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- \[Demo\] Add Snackbar component ([#114](https://github.com/Orange-OpenSource/ods-android/issues/114))
- \[Demo\] Display an error message below text fields if customization error switch is on ([#338](https://github.com/Orange-OpenSource/ods-android/issues/338))
- \[Lib\] Add `OdsSnackbar` and `OdsSnackbarHost` composable to manage snackbars display ([#114](https://github.com/Orange-OpenSource/ods-android/issues/114))
- \[Lib\] Add `errorMessage` parameter to `OdsTextField`, `OdsOutlinedTextField`, `OdsPasswordTextField` and `OdsPasswordOutlinedTextField` to allow the display of an error message below text fields ([#338](https://github.com/Orange-OpenSource/ods-android/issues/338))
- \[Lib\] Add `characterCounter` parameter to `OdsTextField`, `OdsOutlinedTextField`, `OdsPasswordTextField` and `OdsPasswordOutlinedTextField` to allow the display of a character counter below text fields ([#338](https://github.com/Orange-OpenSource/ods-android/issues/338))
- \[ThemeConfigurationContract\] Add `outlinedChips` boolean in the theme contract to allow to choose between outlined or filled chips in a custom theme ([#305](https://github.com/Orange-OpenSource/ods-android/issues/305))

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,16 @@
package com.orange.ods.demo.ui.components.textfields

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.orange.ods.compose.component.textfield.OdsTextField
import com.orange.ods.compose.component.textfield.OdsTextFieldCounter
import com.orange.ods.compose.component.textfield.OdsTextFieldCharacterCounter
import com.orange.ods.demo.R
import com.orange.ods.demo.ui.components.textfields.TextFieldCustomizationState.Companion.TextFieldMaxChars
import com.orange.ods.demo.ui.components.utilities.clickOnElement
Expand All @@ -34,17 +29,16 @@ import com.orange.ods.demo.ui.components.utilities.clickOnElement
fun TextFieldFilled(customizationState: TextFieldCustomizationState) {
val context = LocalContext.current
val trailingIconName = stringResource(id = R.string.component_element_trailing)
val focusRequester = remember { FocusRequester() }

Column {
OdsTextField(
modifier = Modifier
.fillMaxWidth()
.padding(top = dimensionResource(id = R.dimen.spacing_s))
.focusRequester(focusRequester),
.padding(top = dimensionResource(id = R.dimen.spacing_s)),
leadingIcon = if (customizationState.hasLeadingIcon) painterResource(id = R.drawable.ic_heart) else null,
enabled = customizationState.isEnabled,
isError = customizationState.isError,
errorMessage = if (customizationState.isError) stringResource(id = R.string.component_text_field_error_message) else null,
value = customizationState.displayedText,
onValueChange = { customizationState.updateText(it) },
label = stringResource(id = R.string.component_element_label),
Expand All @@ -55,21 +49,22 @@ fun TextFieldFilled(customizationState: TextFieldCustomizationState) {
} else null,
trailingText = if (customizationState.hasTrailingText) "units" else null,
singleLine = customizationState.isSingleLine,
keyboardOptions = customizationState.keyboardOptions
keyboardOptions = customizationState.keyboardOptions,
characterCounter = if (customizationState.hasCharacterCounter) {
{
TextFieldCharacterCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
} else null
)

if (customizationState.hasCharacterCounter) {
TextFieldCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
}
}

@Composable
fun ColumnScope.TextFieldCounter(valueLength: Int, enabled: Boolean) {
OdsTextFieldCounter(
modifier = Modifier.align(Alignment.End),
fun TextFieldCharacterCounter(valueLength: Int, enabled: Boolean) {
OdsTextFieldCharacterCounter(
valueLength = valueLength,
maxChars = TextFieldMaxChars,
enabled = enabled
)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@ fun TextFieldFilledPassword(customizationState: TextFieldCustomizationState) {
.padding(top = dimensionResource(id = R.dimen.spacing_s)),
enabled = customizationState.isEnabled,
isError = customizationState.isError,
errorMessage = if (customizationState.isError) stringResource(id = R.string.component_text_field_error_message) else null,
value = customizationState.displayedText,
onValueChange = { customizationState.updateText(it) },
label = stringResource(id = R.string.component_element_label),
placeholder = stringResource(id = R.string.component_text_field_placeholder),
visualisationIcon = customizationState.hasVisualisationIcon,
keyboardOptions = customizationState.keyboardOptions
keyboardOptions = customizationState.keyboardOptions,
characterCounter = if (customizationState.hasCharacterCounter) {
{
TextFieldCharacterCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
} else null
)

if (customizationState.hasCharacterCounter) {
TextFieldCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fun TextFieldOutlined(customizationState: TextFieldCustomizationState) {
leadingIcon = if (customizationState.leadingIcon.value) painterResource(id = R.drawable.ic_heart) else null,
enabled = customizationState.isEnabled,
isError = customizationState.isError,
errorMessage = if (customizationState.isError) stringResource(id = R.string.component_text_field_error_message) else null,
value = customizationState.displayedText,
onValueChange = { customizationState.updateText(it) },
label = stringResource(id = R.string.component_element_label),
Expand All @@ -46,11 +47,12 @@ fun TextFieldOutlined(customizationState: TextFieldCustomizationState) {
} else null,
trailingText = if (customizationState.hasTrailingText) "units" else null,
singleLine = customizationState.isSingleLine,
keyboardOptions = customizationState.keyboardOptions
keyboardOptions = customizationState.keyboardOptions,
characterCounter = if (customizationState.hasCharacterCounter) {
{
TextFieldCharacterCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
} else null
)

if (customizationState.hasCharacterCounter) {
TextFieldCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@ fun TextFieldOutlinedPassword(customizationState: TextFieldCustomizationState) {
.padding(top = dimensionResource(id = R.dimen.spacing_s)),
enabled = customizationState.isEnabled,
isError = customizationState.isError,
errorMessage = if (customizationState.isError) stringResource(id = R.string.component_text_field_error_message) else null,
value = customizationState.displayedText,
onValueChange = { customizationState.updateText(it) },
label = stringResource(id = R.string.component_element_label),
placeholder = stringResource(id = R.string.component_text_field_placeholder),
visualisationIcon = customizationState.hasVisualisationIcon,
keyboardOptions = customizationState.keyboardOptions
keyboardOptions = customizationState.keyboardOptions,
characterCounter = if (customizationState.hasCharacterCounter) {
{
TextFieldCharacterCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
} else null
)

if (customizationState.hasCharacterCounter) {
TextFieldCounter(valueLength = customizationState.displayedText.length, enabled = customizationState.isEnabled)
}
}
}
1 change: 1 addition & 0 deletions demo/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@
<string name="component_text_field_input_type_text_area">Text area</string>
<string name="component_text_field_placeholder">Hint text</string>
<string name="component_text_field_visualisation_icon">Visualisation icon</string>
<string name="component_text_field_error_message">Error message</string>
<string name="component_text_field_character_counter">Character counter</string>
<string name="component_text_field_keyboard_type">Keyboard type</string>
<string name="component_text_field_keyboard_action">Keyboard action</string>
Expand Down
49 changes: 43 additions & 6 deletions docs/components/TextFields.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ OdsTextField(
enabled = true, // true if not set
readOnly = false, // false if not set
isError = false, // false if not set
errorMessage = "Error message", // Optional
value = text,
onValueChange = { text = it },
label = "Label", // Optional
Expand All @@ -96,9 +97,18 @@ OdsTextField(
keyboardActions = KeyboardActions(), // `KeyboardActions()` if not set
singleLine = false, // false if not set
maxLines = Int.MAX_VALUE, // `Int.MAX_VALUE` if not set
characterCounter = {
OdsTextFieldCharacterCounter(
valueLength = valueLength,
maxChars = TextFieldMaxChars,
enabled = enabled
)
} // Optional
)
```

Note: You will find more information about the character counter in [Extras](#extras)

> **XML implementation**
_**Note:** The filled text field is the default style if the style is not set._
Expand Down Expand Up @@ -153,13 +163,21 @@ OdsPasswordTextField(
enabled = true, // true if not set
readOnly = false, // false if not set
isError = false, // false if not set
errorMessage = "Error message", // Optional
value = text,
onValueChange = { text = it },
label = "Label", // Optional
placeholder = "Placeholder", // Optional
visualisationIcon = true, // `true` if not set
keyboardOptions = KeyboardOptions.Default, // `KeyboardOptions.Default` if not set
keyboardActions = KeyboardActions() // `KeyboardActions()` if not set
keyboardActions = KeyboardActions(), // `KeyboardActions()` if not set
characterCounter = {
OdsTextFieldCharacterCounter(
valueLength = valueLength,
maxChars = TextFieldMaxChars,
enabled = enabled
)
} // Optional
)
```

Expand Down Expand Up @@ -190,6 +208,7 @@ OdsOutlinedTextField(
enabled = true, // true if not set
readOnly = false, // false if not set
isError = false, // false if not set
errorMessage = "Error message", // Optional
value = text,
onValueChange = { text = it },
label = "Label", // Optional
Expand All @@ -203,6 +222,13 @@ OdsOutlinedTextField(
keyboardActions = KeyboardActions(), // `KeyboardActions()` if not set
singleLine = false, // false if not set
maxLines = Int.MAX_VALUE, // `Int.MAX_VALUE` if not set
characterCounter = {
OdsTextFieldCharacterCounter(
valueLength = valueLength,
maxChars = TextFieldMaxChars,
enabled = enabled
)
} // Optional
)
```

Expand Down Expand Up @@ -259,13 +285,21 @@ OdsPasswordOutlinedTextField(
enabled = true, // true if not set
readOnly = false, // false if not set
isError = false, // false if not set
errorMessage = "Error message", // Optional
value = text,
onValueChange = { text = it },
label = "Label", // Optional
placeholder = "Placeholder", // Optional
visualisationIcon = true, // `true` if not set
keyboardOptions = KeyboardOptions.Default, // `KeyboardOptions.Default` if not set
keyboardActions = KeyboardActions() // `KeyboardActions()` if not set
keyboardActions = KeyboardActions(), // `KeyboardActions()` if not set
characterCounter = {
OdsTextFieldCharacterCounter(
valueLength = valueLength,
maxChars = TextFieldMaxChars,
enabled = enabled
)
} // Optional
)
```

Expand All @@ -277,23 +311,26 @@ _Not available_

### Character counter

You can add a character counter if there is a restriction on the number of characters in a field. It has to be placed below the text field, end aligned.

![TextField character counter light](images/textfield_character_counter_light.png)
![TextField character counter dark](images/textfield_character_counter_dark.png)

> **Jetpack Compose implementation**
In each TextField component, you can use the `characterCounter` parameter to add a character counter if there is a restriction on the number of characters in a field.
It will be placed properly below the text field, end aligned.

Please use the provided `OdsTextFieldCharacterCounter` composable for this behavior as shown below:

```kotlin
OdsTextFieldCounter(
OdsTextFieldCharacterCounter(
modifier = Modifier.align(Alignment.End),
valueLength = valueLength,
maxChars = 20,
enabled = true // `true` if not set. If `false` the counter is displayed with a disabled color
)
```

The limitation behavior should be managed by yourself in the `onValueChange` method of the text field.
Be careful, the limitation behavior should be managed by yourself in the `onValueChange` method of the text field:

```kotlin
if (text.length <= TextFieldMaxChars) {
Expand Down
Loading

0 comments on commit 09a7c2e

Please sign in to comment.