From 7708e197cb480a29919b7484af577c286620f617 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 17 Jun 2026 16:43:46 +0200 Subject: [PATCH 01/10] Add rich text to alert message demo # Conflicts: # core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt --- .../ouds/app/ui/components/ComponentCode.kt | 20 ++- .../alert/AlertMessageDemoScreen.kt | 132 +++++++++++++----- .../components/alert/AlertMessageDemoState.kt | 26 +++- .../bottomsheet/ModalBottomSheetDemoScreen.kt | 4 +- .../composable/CustomizationElements.kt | 10 +- app/src/main/res/values/strings.xml | 2 + .../ouds/core/component/OudsAlertMessage.kt | 4 +- 7 files changed, 153 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt index fafc6aac79..dd285fedd8 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt @@ -22,6 +22,8 @@ import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.FunctionCall import com.orange.ouds.core.component.OudsColoredBoxColor import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.OudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.OudsAnnotatedString fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.() -> Unit) { if (onColoredBox) { @@ -35,6 +37,16 @@ fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.() } } +internal inline fun FunctionCall.Builder.annotatedStringArgument(name: String?) where T : OudsAnnotatedString { + val functionName = "build${T::class.simpleName}" + functionCallArgument(name, functionName) { + trailingLambda = true + lambdaArgument("builder") { + comment("Build annotated string") + } + } +} + inline fun FunctionCall.Builder.iconArgument( name: String, @DrawableRes resId: Int, @@ -77,9 +89,13 @@ fun FunctionCall.Builder.enabledArgument(value: Boolean) = typedArgument(Argumen fun FunctionCall.Builder.tintedArgument(value: Boolean) = typedArgument(Argument.Tinted, value) -fun FunctionCall.Builder.errorArgument(message: String) { +fun FunctionCall.Builder.errorArgument(message: String, annotatedMessage: Boolean = false) { constructorCallArgument(Argument.Error) { - typedArgument("message", message) + if (annotatedMessage) { + annotatedStringArgument("message") + } else { + typedArgument("message", message) + } } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt index 29c9cc9608..241b7d185c 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.R -import com.orange.ouds.app.ui.components.alert.AlertMessageDemoState.Companion.MaxBulletCount +import com.orange.ouds.app.ui.components.annotatedStringArgument import com.orange.ouds.app.ui.components.iconArgument import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.components.onClickArgument @@ -44,6 +44,13 @@ import com.orange.ouds.core.component.OudsAlertMessage import com.orange.ouds.core.component.OudsAlertMessageActionLink import com.orange.ouds.core.component.OudsAlertMessageActionLinkPosition import com.orange.ouds.core.component.OudsAlertMessageStatus +import com.orange.ouds.core.component.common.text.OudsAnnotatedAlertMessageBulletListLabel +import com.orange.ouds.core.component.common.text.OudsAnnotatedAlertMessageDescription +import com.orange.ouds.core.component.common.text.OudsLinkAnnotation +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedAlertMessageBulletListLabel +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedAlertMessageDescription +import com.orange.ouds.core.component.common.text.withLink +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -123,7 +130,9 @@ private fun AlertMessageDemoBottomSheetContent(state: AlertMessageDemoState) { applyTopPadding = true, label = stringResource(R.string.app_components_common_description_tech), value = description.orEmpty(), - onValueChange = { value -> description = value } + onValueChange = { value -> description = value }, + enabled = descriptionTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -143,16 +152,23 @@ private fun AlertMessageDemoBottomSheetContent(state: AlertMessageDemoState) { selectedChipIndex = OudsAlertMessageActionLinkPosition.entries.indexOf(actionLinkPosition), onSelectionChange = { id -> actionLinkPosition = OudsAlertMessageActionLinkPosition.entries[id] } ) - for (id in 1..MaxBulletCount) { + for (index in 0.. - bulletList = bulletList.orEmpty().toMutableMap().apply { put(id, value) } - } + bulletList = bulletList.toMutableList().apply { set(index, value) }.toList() + }, + enabled = bulletListTextInputsEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) } + CustomizationSwitchItem( + label = stringResource(R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it }, + ) } } @@ -164,27 +180,67 @@ private fun AlertMessageDemoContent(state: AlertMessageDemoState) { AlertMessageDemoState.Icon.Tinted -> OudsAlertIcon(painter = painterResource(LocalThemeDrawableResources.current.tipsAndTricks), tinted = true) AlertMessageDemoState.Icon.Untinted -> OudsAlertIcon(painter = rememberUntintedIconPainter(), tinted = false) } - OudsAlertMessage( - label = label, - description = description, - status = when (status) { - is OudsAlertMessageStatus.Accent -> OudsAlertMessageStatus.Accent(alertIcon) - is OudsAlertMessageStatus.Neutral -> OudsAlertMessageStatus.Neutral(alertIcon) - is OudsAlertMessageStatus.Info -> OudsAlertMessageStatus.Info - is OudsAlertMessageStatus.Negative -> OudsAlertMessageStatus.Negative - is OudsAlertMessageStatus.Positive -> OudsAlertMessageStatus.Positive - is OudsAlertMessageStatus.Warning -> OudsAlertMessageStatus.Warning - }, - onClose = if (hasCloseButton) { - {} - } else { - null - }, - actionLink = actionLink?.let { actionLinkLabel -> - OudsAlertMessageActionLink(label = actionLinkLabel, onClick = {}, position = actionLinkPosition) - }, - bulletList = bulletList?.toSortedMap()?.values?.toList() - ) + val status = when (status) { + is OudsAlertMessageStatus.Accent -> OudsAlertMessageStatus.Accent(alertIcon) + is OudsAlertMessageStatus.Neutral -> OudsAlertMessageStatus.Neutral(alertIcon) + is OudsAlertMessageStatus.Info -> OudsAlertMessageStatus.Info + is OudsAlertMessageStatus.Negative -> OudsAlertMessageStatus.Negative + is OudsAlertMessageStatus.Positive -> OudsAlertMessageStatus.Positive + is OudsAlertMessageStatus.Warning -> OudsAlertMessageStatus.Warning + } + val onClose = if (hasCloseButton) { + {} + } else { + null + } + val actionLink = actionLink?.let { actionLinkLabel -> + OudsAlertMessageActionLink(label = actionLinkLabel, onClick = {}, position = actionLinkPosition) + } + if (annotatedText) { + val annotatedDescription = buildOudsAnnotatedAlertMessageDescription { + append("Your last payment attempt was ") + withStrong { append("declined") } + append(". Please check your payment details or ") + withStrong { append("available balance") } + append(" and try again, or ") + withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("update your payment details") } + append(".") + } + val annotatedBulletList = listOf( + buildOudsAnnotatedAlertMessageBulletListLabel { + append("Your payment was ") + withStrong { append("declined") } + append(".") + }, + buildOudsAnnotatedAlertMessageBulletListLabel { + append("Check your ") + withStrong { append("available balance") } + append(" before retrying.") + }, + buildOudsAnnotatedAlertMessageBulletListLabel { + append("Update your ") + withStrong { append("payment details") } + append(" if needed.") + } + ) + OudsAlertMessage( + label = label, + description = annotatedDescription, + status = status, + onClose = onClose, + actionLink = actionLink, + bulletList = annotatedBulletList + ) + } else { + OudsAlertMessage( + label = label, + description = description, + status = status, + onClose = onClose, + actionLink = actionLink, + bulletList = bulletList + ) + } } } @@ -209,7 +265,11 @@ private fun Code.Builder.alertMessageDemoCodeSnippet(state: AlertMessageDemoStat } } labelArgument(label) - description?.let { typedArgument("description", description) } + if (annotatedText) { + annotatedStringArgument("description") + } else { + description?.let { typedArgument("description", description) } + } if (hasCloseButton) { lambdaArgument("onClose") { comment("Close alert message") @@ -224,10 +284,18 @@ private fun Code.Builder.alertMessageDemoCodeSnippet(state: AlertMessageDemoStat typedArgument("position", actionLinkPosition) } } - bulletList?.let { bulletLabelById -> + if (bulletList.any { it.isNotBlank() } || annotatedText) { functionCallArgument("bulletList", "listOf") { - bulletLabelById.toSortedMap().values.forEach { label -> - typedArgument(null, label) + if (annotatedText) { + repeat(AlertMessageDemoState.MaxBulletCount) { + annotatedStringArgument(null) + } + } else { + bulletList.forEach { label -> + if (label.isNotBlank()) { + typedArgument(null, label) + } + } } } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoState.kt index b7fb98bfc9..6a924a6881 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoState.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import com.orange.ouds.app.R +import com.orange.ouds.app.ui.components.alert.AlertMessageDemoState.Companion.MaxBulletCount import com.orange.ouds.core.component.OudsAlertMessageActionLinkPosition import com.orange.ouds.core.component.OudsAlertMessageDefaults import com.orange.ouds.core.component.OudsAlertMessageStatus @@ -35,7 +36,8 @@ fun rememberAlertMessageDemoState( description: String? = null, actionLink: String? = null, actionLinkPosition: OudsAlertMessageActionLinkPosition = OudsAlertMessageDefaults.ActionLinkPosition, - bulletList: Map? = null, + bulletList: List = List(MaxBulletCount) { "" }, + annotatedText: Boolean = false ) = rememberSaveable( status, icon, @@ -45,9 +47,10 @@ fun rememberAlertMessageDemoState( actionLink, actionLinkPosition, bulletList, + annotatedText, saver = AlertMessageDemoState.Saver ) { - AlertMessageDemoState(status, icon, hasCloseButton, label, description, actionLink, actionLinkPosition, bulletList) + AlertMessageDemoState(status, icon, hasCloseButton, label, description, actionLink, actionLinkPosition, bulletList, annotatedText) } class AlertMessageDemoState( @@ -58,7 +61,8 @@ class AlertMessageDemoState( description: String?, actionLink: String?, actionLinkPosition: OudsAlertMessageActionLinkPosition, - bulletList: Map? + bulletList: List, + annotatedText: Boolean ) { @Suppress("UNCHECKED_CAST") @@ -83,7 +87,8 @@ class AlertMessageDemoState( description, actionLink, actionLinkPosition, - bulletList + bulletList, + annotatedText ) } }, @@ -99,7 +104,8 @@ class AlertMessageDemoState( list[4] as String?, list[5] as String?, list[6] as OudsAlertMessageActionLinkPosition, - list[7] as Map? + list[7] as List, + list[8] as Boolean ) } ) @@ -129,8 +135,16 @@ class AlertMessageDemoState( val actionLinkPositionChipsEnabled: Boolean get() = !actionLink.isNullOrEmpty() + + var bulletList: List by mutableStateOf(bulletList) - var bulletList: Map? by mutableStateOf(bulletList) + val descriptionTextInputEnabled: Boolean + get() = !annotatedText + + val bulletListTextInputsEnabled: Boolean + get() = !annotatedText + + var annotatedText: Boolean by mutableStateOf(annotatedText) val enabledIcons: List get() = if (status !in FunctionalStatuses) Icon.entries else listOf(Icon.Tinted) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bottomsheet/ModalBottomSheetDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bottomsheet/ModalBottomSheetDemoScreen.kt index bf0017fb97..966ae95086 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bottomsheet/ModalBottomSheetDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bottomsheet/ModalBottomSheetDemoScreen.kt @@ -117,10 +117,10 @@ private fun Code.Builder.modalBottomSheetDemoCodeSnippet(state: ModalBottomSheet typedArgument("sheetGesturesEnabled", state.sheetGesturesEnabled) functionCallArgument("sheetState", "rememberModalBottomSheetState") lambdaArgument("onDismissRequest") { - comment("do something on dismiss") + comment("Do something on dismiss") } lambdaArgument(null) { - comment("sheet content") + comment("Sheet content") } } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/utilities/composable/CustomizationElements.kt b/app/src/main/java/com/orange/ouds/app/ui/utilities/composable/CustomizationElements.kt index be2e0fbc8e..18e16d3248 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/utilities/composable/CustomizationElements.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/utilities/composable/CustomizationElements.kt @@ -128,7 +128,7 @@ fun CustomizationFilterChips( } else { true } - + Row( modifier = Modifier .fillMaxWidth() @@ -159,6 +159,9 @@ fun CustomizationTextInput( modifier: Modifier = Modifier, enabled: Boolean = true, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + suffix: String? = null, + helperText: String? = null, + resetValue: String = "" ) { var textFieldValue by remember { mutableStateOf(TextFieldValue(text = value, selection = TextRange(value.length))) } @@ -173,6 +176,9 @@ fun CustomizationTextInput( modifier = modifier, enabled = enabled, keyboardOptions = keyboardOptions, + suffix = suffix, + helperText = helperText, + resetValue = resetValue ) } @@ -186,6 +192,7 @@ fun CustomizationTextInput( enabled: Boolean = true, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, suffix: String? = null, + helperText: String? = null, resetValue: String = "" ) { @Suppress("NAME_SHADOWING") @@ -211,6 +218,7 @@ fun CustomizationTextInput( null }, suffix = suffix, + helperText = helperText, keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }) ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b1f7b472a7..7cf1b52ccf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -123,6 +123,8 @@ Line height: %s sp + Annotated text + This parameter supports annotated text. Appearance Color Constrained max width diff --git a/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt b/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt index 826c811474..111109a9f7 100644 --- a/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt +++ b/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt @@ -258,9 +258,9 @@ private fun OudsAlertMessage( val descriptionModifier = Modifier.widthIn(max = OudsTheme.sizes.maxWidth.label.medium) val descriptionColor = status.contentColor val descriptionStyle = OudsTheme.typography.label.medium.default - if (annotatedDescription != null) { + if (!annotatedDescription.isNullOrBlank()) { Text(modifier = descriptionModifier, text = annotatedDescription.annotatedString(), color = descriptionColor, style = descriptionStyle) - } else if (description != null) { + } else if (!description.isNullOrBlank()) { Text(modifier = descriptionModifier, text = description, color = descriptionColor, style = descriptionStyle) } annotatedBulletList.orElse { bulletList }?.let { list -> From dd94bb0d9a58abd8415394c9852d46e0d26822b6 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 17 Jun 2026 17:19:02 +0200 Subject: [PATCH 02/10] Add rich text to control item demos --- .../checkbox/CheckboxItemDemoScreen.kt | 52 ++++++++++------ .../checkbox/CheckboxItemDemoState.kt | 13 ++-- .../controlitem/ControlItemDemoScreen.kt | 59 ++++++++++++++----- .../controlitem/ControlItemDemoState.kt | 19 +++++- .../radiobutton/RadioButtonDemoScreen.kt | 4 +- .../radiobutton/RadioButtonDemoState.kt | 6 +- .../radiobutton/RadioButtonItemDemoScreen.kt | 34 +++++++---- .../radiobutton/RadioButtonItemDemoState.kt | 15 +++-- .../components/switch/SwitchItemDemoScreen.kt | 18 ++++-- .../components/switch/SwitchItemDemoState.kt | 36 ++++++++--- 10 files changed, 181 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt index f73cb6f657..173e80d2bd 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt @@ -20,7 +20,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.ui.components.controlitem.ControlItemCustomizations import com.orange.ouds.app.ui.components.controlitem.controlItemArguments -import com.orange.ouds.app.ui.components.controlitem.getControlItemIcon +import com.orange.ouds.app.ui.components.controlitem.controlItemIcon +import com.orange.ouds.app.ui.components.controlitem.controlItemError import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources @@ -30,6 +31,8 @@ import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsCheckboxItem import com.orange.ouds.core.component.OudsTriStateCheckboxItem import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -71,13 +74,13 @@ private fun CheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, enabled = enabled, readOnly = readOnly, - error = if (error) OudsError(if (isLastItem) errorMessage else "") else null, + error = checkboxItemError(state = this, isLastItem = isLastItem), constrainedMaxWidth = constrainedMaxWidth ) } @@ -106,13 +109,13 @@ private fun IndeterminateCheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, enabled = enabled, readOnly = readOnly, - error = if (error) OudsError(if (isLastItem) errorMessage else "") else null, + error = checkboxItemError(state = this, isLastItem = isLastItem,), constrainedMaxWidth = constrainedMaxWidth ) } @@ -127,24 +130,39 @@ private fun CheckboxItemDemoColumn(edgeToEdge: Boolean, content: @Composable () } } +@Composable +private fun checkboxItemError(state: CheckboxItemDemoState, isLastItem: Boolean): OudsError? { + return controlItemError( + state = state, + isLastItem = isLastItem, + annotatedMessage = buildOudsAnnotatedErrorMessage { + append("You must select at ") + withStrong { append("least one service") } + append(" to continue.") + }) +} + private fun Code.Builder.checkboxItemDemoCodeSnippet(state: CheckboxItemDemoState, indeterminate: Boolean, themeDrawableResources: ThemeDrawableResources) { val functionName = if (indeterminate) "OudsTriStateCheckboxItem" else "OudsCheckboxItem" val lambdaCommentText = "Change state" - comment("First checkbox item") with(state) { - functionCall(functionName) { - if (indeterminate) { - typedArgument("state", toggleableStateValues.first) - onClickArgument { - comment(lambdaCommentText) - } - } else { - typedArgument("checked", checkedValues.first) - lambdaArgument("onCheckedChange") { - comment(lambdaCommentText) + repeat(CheckboxIdentifier.entries.count()) { index -> + functionCall(functionName) { + if (indeterminate) { + val value = if (index == 0) toggleableStateValues.first else toggleableStateValues.second + typedArgument("state", value) + onClickArgument { + comment(lambdaCommentText) + } + } else { + val value = if (index == 0) checkedValues.first else checkedValues.second + typedArgument("checked", value) + lambdaArgument("onCheckedChange") { + comment(lambdaCommentText) + } } + controlItemArguments(state, themeDrawableResources, index == CheckboxIdentifier.entries.lastIndex) } - controlItemArguments(state, themeDrawableResources) } } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoState.kt index 125f6a0fa9..f8c6f257c3 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoState.kt @@ -41,6 +41,7 @@ fun rememberCheckboxItemDemoState( label: String = stringResource(id = R.string.app_components_common_label_label), description: String? = null, constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( checkedValues, toggleableStateValues, @@ -55,6 +56,7 @@ fun rememberCheckboxItemDemoState( label, description, constrainedMaxWidth, + annotatedText, saver = CheckboxItemDemoState.Saver ) { CheckboxItemDemoState( @@ -70,7 +72,8 @@ fun rememberCheckboxItemDemoState( errorMessage, label, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -87,8 +90,9 @@ class CheckboxItemDemoState( errorMessage: String, label: String, description: String?, - constrainedMaxWidth: Boolean -) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, label, description, constrainedMaxWidth) { + constrainedMaxWidth: Boolean, + annotatedText: Boolean +) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, label, description, constrainedMaxWidth, annotatedText) { companion object { val Saver = listSaver( @@ -116,7 +120,8 @@ class CheckboxItemDemoState( errorMessage, label, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt index b8daa59623..b7abab2771 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt @@ -30,6 +30,8 @@ import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput import com.orange.ouds.app.ui.utilities.rememberUntintedIconPainter import com.orange.ouds.core.component.OudsControlItemIcon +import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.OudsAnnotatedErrorMessage data class ControlItemCustomization(val index: Int, val content: @Composable () -> Unit) @@ -48,7 +50,8 @@ fun ControlItemCustomizations(state: ControlItemDemoState, extraCustomizations: { ControlItemErrorMessageCustomization(state = state) }, { ControlItemLabelCustomization(state = state) }, { ControlItemDescriptionCustomization(state = state) }, - { ControlItemConstrainedMaxWidthCustomization(state = state) } + { ControlItemConstrainedMaxWidthCustomization(state = state) }, + { ControlItemAnnotatedTextCustomization(state = state) } ) extraCustomizations.forEach { (index, content) -> customizations.add(minOf(index, customizations.count()), content) @@ -61,8 +64,8 @@ private fun ControlItemIconCustomization(state: ControlItemDemoState) { with(state) { CustomizationFilterChips( applyTopPadding = false, - label = stringResource(R.string.app_components_common_icon_tech), - chipLabels = ControlItemDemoState.Icon.entries.map { stringResource(it.labelRes) }, + label = stringResource(id = R.string.app_components_common_icon_tech), + chipLabels = ControlItemDemoState.Icon.entries.map { stringResource(id = it.labelRes) }, selectedChipIndex = ControlItemDemoState.Icon.entries.indexOf(icon), onSelectionChange = { index -> icon = ControlItemDemoState.Icon.entries[index] } ) @@ -73,7 +76,7 @@ private fun ControlItemIconCustomization(state: ControlItemDemoState) { private fun ControlItemEdgeToEdgeCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_controlItem_edgeToEdge_tech), + label = stringResource(id = R.string.app_components_controlItem_edgeToEdge_tech), checked = edgeToEdge, onCheckedChange = { edgeToEdge = it }, ) @@ -85,7 +88,7 @@ private fun ControlItemEdgeToEdgeCustomization(state: ControlItemDemoState) { private fun ControlItemDividerCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_controlItem_divider_tech), + label = stringResource(id = R.string.app_components_controlItem_divider_tech), checked = divider, onCheckedChange = { divider = it }, ) @@ -96,7 +99,7 @@ private fun ControlItemDividerCustomization(state: ControlItemDemoState) { private fun ControlItemReversedCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_controlItem_reversed_tech), + label = stringResource(id = R.string.app_components_controlItem_reversed_tech), checked = reversed, onCheckedChange = { reversed = it }, ) @@ -107,7 +110,7 @@ private fun ControlItemReversedCustomization(state: ControlItemDemoState) { private fun ControlItemEnabledCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_common_enabled_tech), + label = stringResource(id = R.string.app_common_enabled_tech), checked = enabled, onCheckedChange = { enabled = it }, enabled = enabledSwitchEnabled @@ -119,7 +122,7 @@ private fun ControlItemEnabledCustomization(state: ControlItemDemoState) { private fun ControlItemReadOnlyCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_common_readOnly_tech), + label = stringResource(id = R.string.app_components_common_readOnly_tech), checked = readOnly, onCheckedChange = { readOnly = it }, enabled = readOnlySwitchEnabled @@ -131,7 +134,7 @@ private fun ControlItemReadOnlyCustomization(state: ControlItemDemoState) { private fun ControlItemErrorCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_common_error_tech), + label = stringResource(id = R.string.app_components_common_error_tech), checked = error, onCheckedChange = { error = it }, enabled = errorSwitchEnabled @@ -144,10 +147,22 @@ private fun ControlItemErrorMessageCustomization(state: ControlItemDemoState) { with(state) { CustomizationTextInput( applyTopPadding = true, - label = stringResource(R.string.app_components_common_errorMessage_tech), + label = stringResource(id = R.string.app_components_common_errorMessage_tech), value = errorMessage, onValueChange = { value -> errorMessage = value }, - enabled = errorMessageTextInputEnabled + enabled = errorMessageTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) + ) + } +} + +@Composable +fun ControlItemAnnotatedTextCustomization(state: ControlItemDemoState) { + with(state) { + CustomizationSwitchItem( + label = stringResource(id = R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it } ) } } @@ -157,7 +172,7 @@ private fun ControlItemLabelCustomization(state: ControlItemDemoState) { with(state) { CustomizationTextInput( applyTopPadding = true, - label = stringResource(R.string.app_components_common_label_tech), + label = stringResource(id = R.string.app_components_common_label_tech), value = label, onValueChange = { value -> label = value } ) @@ -169,7 +184,7 @@ private fun ControlItemDescriptionCustomization(state: ControlItemDemoState) { with(state) { CustomizationTextInput( applyTopPadding = true, - label = stringResource(R.string.app_components_common_description_tech), + label = stringResource(id = R.string.app_components_common_description_tech), value = description.orEmpty(), onValueChange = { value -> description = value } ) @@ -180,7 +195,7 @@ private fun ControlItemDescriptionCustomization(state: ControlItemDemoState) { private fun ControlItemConstrainedMaxWidthCustomization(state: ControlItemDemoState) { with(state) { CustomizationSwitchItem( - label = stringResource(R.string.app_components_common_constrainedMaxWidth_tech), + label = stringResource(id = R.string.app_components_common_constrainedMaxWidth_tech), checked = constrainedMaxWidth, onCheckedChange = { constrainedMaxWidth = it }, ) @@ -188,7 +203,7 @@ private fun ControlItemConstrainedMaxWidthCustomization(state: ControlItemDemoSt } @Composable -fun getControlItemIcon(state: ControlItemDemoState): OudsControlItemIcon? { +fun controlItemIcon(state: ControlItemDemoState): OudsControlItemIcon? { return when (state.icon) { ControlItemDemoState.Icon.None -> null ControlItemDemoState.Icon.Tinted -> OudsControlItemIcon(painter = painterResource(id = LocalThemeDrawableResources.current.tipsAndTricks)) @@ -196,6 +211,18 @@ fun getControlItemIcon(state: ControlItemDemoState): OudsControlItemIcon? { } } +@Composable +fun controlItemError(state: ControlItemDemoState, isLastItem: Boolean, annotatedMessage: OudsAnnotatedErrorMessage): OudsError? { + return with(state) { + when { + error && !isLastItem -> OudsError("") + error && !annotatedText -> OudsError(errorMessage) + error && annotatedText -> OudsError(annotatedMessage) + else -> null + } + } +} + fun FunctionCall.Builder.controlItemArguments(state: ControlItemDemoState, themeDrawableResources: ThemeDrawableResources, hasErrorMessage: Boolean = false) = with(state) { labelArgument(label) @@ -208,6 +235,6 @@ fun FunctionCall.Builder.controlItemArguments(state: ControlItemDemoState, theme if (reversed) typedArgument("reversed", reversed) if (!enabled) enabledArgument(enabled) if (readOnly) readOnlyArgument(readOnly) - if (error) errorArgument(if (hasErrorMessage) errorMessage else "") + if (error) errorArgument(if (hasErrorMessage) errorMessage else "", hasErrorMessage && annotatedText) if (constrainedMaxWidth) constrainedMaxWidthArgument(constrainedMaxWidth) } \ No newline at end of file diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt index b08dadb247..04aa1c9fbf 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt @@ -30,7 +30,8 @@ open class ControlItemDemoState( errorMessage: String, label: String, description: String?, - constrainedMaxWidth: Boolean + constrainedMaxWidth: Boolean, + annotatedText: Boolean ) { companion object { @@ -50,6 +51,7 @@ open class ControlItemDemoState( label, description, constrainedMaxWidth, + annotatedText ) } }, @@ -65,7 +67,8 @@ open class ControlItemDemoState( list[7] as String, list[8] as String, list[9] as String?, - list[10] as Boolean + list[10] as Boolean, + list[11] as Boolean ) } ) @@ -83,6 +86,16 @@ open class ControlItemDemoState( var label: String by mutableStateOf(label) var description: String? by mutableStateOf(description) + private var _annotatedText: Boolean by mutableStateOf(annotatedText) + var annotatedText: Boolean + get() = _annotatedText + set(value) { + _annotatedText = value + if (value) { + error = true + } + } + val enabledSwitchEnabled: Boolean get() = !error @@ -93,7 +106,7 @@ open class ControlItemDemoState( get() = enabled && !readOnly val errorMessageTextInputEnabled: Boolean - get() = error + get() = error && !annotatedText enum class Icon(@StringRes val labelRes: Int) { None(R.string.app_components_common_none_tech), diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoScreen.kt index 439d640a0a..be27a6a360 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoScreen.kt @@ -76,7 +76,7 @@ private fun RadioButtonDemoContent(state: RadioButtonDemoState) { horizontalArrangement = Arrangement.Center ) { with(state) { - RadioButtonDemoState.values.forEach { value -> + RadioButtonDemoState.Values.forEach { value -> val contentDescription = stringResource(R.string.app_components_radioButton_radioButton_a11y, value.toString()) OudsRadioButton( modifier = Modifier.semantics { @@ -97,7 +97,7 @@ private fun Code.Builder.radioButtonDemoCodeSnippet(state: RadioButtonDemoState) with(state) { comment("First radio button") functionCall("OudsRadioButton") { - typedArgument("selected", selectedValue == RadioButtonDemoState.values.first()) + typedArgument("selected", selectedValue == RadioButtonDemoState.Values.first()) onClickArgument { comment("Change state") } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoState.kt index 664173d31c..42502a85d0 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonDemoState.kt @@ -19,11 +19,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.mapSaver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import com.orange.ouds.app.ui.components.radiobutton.RadioButtonDemoState.Companion.values +import com.orange.ouds.app.ui.components.radiobutton.RadioButtonDemoState.Companion.Values @Composable fun rememberRadioButtonDemoState( - selectedValue: Int = values.first(), + selectedValue: Int = Values.first(), enabled: Boolean = true, readOnly: Boolean = false, error: Boolean = false @@ -38,7 +38,7 @@ class RadioButtonDemoState( error: Boolean ) { companion object { - val values = listOf(1, 2) + val Values = listOf(1, 2) val Saver = run { val selectedValueKey = "selectedValue" diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt index 9483c8cd61..dcf4906276 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt @@ -24,7 +24,8 @@ import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.controlitem.ControlItemCustomizations import com.orange.ouds.app.ui.components.controlitem.controlItemArguments import com.orange.ouds.app.ui.components.controlitem.controlItemCustomization -import com.orange.ouds.app.ui.components.controlitem.getControlItemIcon +import com.orange.ouds.app.ui.components.controlitem.controlItemError +import com.orange.ouds.app.ui.components.controlitem.controlItemIcon import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources @@ -34,7 +35,8 @@ import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsRadioButtonItem -import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -93,14 +95,21 @@ private fun RadioButtonItemDemoContent(state: RadioButtonItemDemoState) { label = label, extraLabel = extraLabel, description = description, - icon = getControlItemIcon(this@with), + icon = controlItemIcon(this@with), edgeToEdge = edgeToEdge, divider = divider, outlined = outlined, reversed = reversed, enabled = enabled, readOnly = readOnly, - error = if (error) OudsError(if (isLastItem) errorMessage else "") else null, + error = controlItemError( + state = this@with, + isLastItem = isLastItem, + annotatedMessage = buildOudsAnnotatedErrorMessage { + append("Please select ") + withStrong { append("one contact method") } + append(" to proceed.") + }), constrainedMaxWidth = constrainedMaxWidth ) } @@ -109,16 +118,17 @@ private fun RadioButtonItemDemoContent(state: RadioButtonItemDemoState) { } private fun Code.Builder.radioButtonItemDemoCodeSnippet(state: RadioButtonItemDemoState, themeDrawableResources: ThemeDrawableResources) { - comment("First radio button item") with(state) { - functionCall("OudsRadioButtonItem") { - typedArgument("selected", selectedValue == RadioButtonItemDemoState.values.first()) - onClickArgument { - comment("Change selection") + RadioButtonItemDemoState.values.forEachIndexed { index, value -> + functionCall("OudsRadioButtonItem") { + typedArgument("selected", selectedValue == value) + onClickArgument { + comment("Change selection") + } + controlItemArguments(state, themeDrawableResources, index == RadioButtonItemDemoState.values.lastIndex) + if (!extraLabel.isNullOrBlank()) typedArgument("extraLabel", extraLabel) + if (outlined) typedArgument("outlined", outlined) } - controlItemArguments(state, themeDrawableResources) - if (!extraLabel.isNullOrBlank()) typedArgument("extraLabel", extraLabel) - if (outlined) typedArgument("outlined", outlined) } } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt index d03cc7200e..f9d02c5665 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt @@ -38,7 +38,8 @@ fun rememberRadioButtonItemDemoState( label: String = stringResource(id = R.string.app_components_common_label_label), extraLabel: String? = null, description: String? = null, - constrainedMaxWidth: Boolean = false + constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( selectedValue, icon, @@ -54,6 +55,7 @@ fun rememberRadioButtonItemDemoState( extraLabel, description, constrainedMaxWidth, + annotatedText, saver = RadioButtonItemDemoState.Saver ) { RadioButtonItemDemoState( @@ -70,7 +72,8 @@ fun rememberRadioButtonItemDemoState( label, extraLabel, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -88,8 +91,9 @@ class RadioButtonItemDemoState( label: String, extraLabel: String?, description: String?, - constrainedMaxWidth: Boolean -) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, label, description, constrainedMaxWidth) { + constrainedMaxWidth: Boolean, + annotatedText: Boolean +) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, label, description, constrainedMaxWidth, annotatedText) { companion object { val values = listOf(1, 2) @@ -121,7 +125,8 @@ class RadioButtonItemDemoState( label, list[2] as String?, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt index 6ca5b736bc..0d84e4c6d6 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt @@ -19,14 +19,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.ui.components.controlitem.ControlItemCustomizations import com.orange.ouds.app.ui.components.controlitem.controlItemArguments -import com.orange.ouds.app.ui.components.controlitem.getControlItemIcon +import com.orange.ouds.app.ui.components.controlitem.controlItemError +import com.orange.ouds.app.ui.components.controlitem.controlItemIcon import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources import com.orange.ouds.app.ui.utilities.ThemeDrawableResources import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsSwitchItem -import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -52,13 +54,21 @@ private fun SwitchItemDemoContent(state: SwitchItemDemoState) { label = label, onCheckedChange = { checked = it }, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, enabled = enabled, readOnly = readOnly, - error = if (error) OudsError(errorMessage) else null, + error = controlItemError( + state = this, + isLastItem = true, + annotatedMessage = buildOudsAnnotatedErrorMessage { + append("You must enable ") + withStrong { append("automatic payments") } + append(" to activate this offer.") + } + ), constrainedMaxWidth = constrainedMaxWidth ) } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoState.kt index 483aff07db..148935c544 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoState.kt @@ -33,9 +33,10 @@ fun rememberSwitchItemDemoState( readOnly: Boolean = false, error: Boolean = false, errorMessage: String = stringResource(id = R.string.app_components_common_errorMessage_label), - text: String = stringResource(id = R.string.app_components_common_label_label), + label: String = stringResource(id = R.string.app_components_common_label_label), description: String? = null, - constrainedMaxWidth: Boolean = false + constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( checked, icon, @@ -46,9 +47,10 @@ fun rememberSwitchItemDemoState( readOnly, error, errorMessage, - text, + label, description, constrainedMaxWidth, + annotatedText, saver = SwitchItemDemoState.Saver ) { SwitchItemDemoState( @@ -61,9 +63,10 @@ fun rememberSwitchItemDemoState( readOnly, error, errorMessage, - text, + label, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -77,10 +80,24 @@ class SwitchItemDemoState( readOnly: Boolean, error: Boolean, errorMessage: String, - text: String, + label: String, description: String?, - constrainedMaxWidth: Boolean -) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, text, description, constrainedMaxWidth) { + constrainedMaxWidth: Boolean, + annotatedText: Boolean +) : ControlItemDemoState( + icon, + edgeToEdge, + divider, + reversed, + enabled, + readOnly, + error, + errorMessage, + label, + description, + constrainedMaxWidth, + annotatedText +) { companion object { @@ -106,7 +123,8 @@ class SwitchItemDemoState( errorMessage, label, description, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } } From cab205a887fd1d06c0c42c3d0778e4963a9e078a Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 17 Jun 2026 18:49:35 +0200 Subject: [PATCH 03/10] Add rich text to bullet list demo --- .../bulletlist/BulletListDemoScreen.kt | 96 ++++++++++++++----- .../bulletlist/BulletListDemoState.kt | 27 +++--- 2 files changed, 85 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt index b5e130396f..81437a1604 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.Component +import com.orange.ouds.app.ui.components.annotatedStringArgument import com.orange.ouds.app.ui.components.bulletlist.BulletListDemoState.Companion.MaxLevelCount import com.orange.ouds.app.ui.components.bulletlist.BulletListDemoState.Companion.MinLevelCount import com.orange.ouds.app.ui.components.iconArgument @@ -44,6 +45,11 @@ import com.orange.ouds.core.component.OudsBulletListFontWeight import com.orange.ouds.core.component.OudsBulletListTextStyle import com.orange.ouds.core.component.OudsBulletListType import com.orange.ouds.core.component.OudsBulletListUnorderedAsset +import com.orange.ouds.core.component.common.text.OudsAnnotatedBulletListLabel +import com.orange.ouds.core.component.common.text.OudsLinkAnnotation +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedBulletListLabel +import com.orange.ouds.core.component.common.text.withLink +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -113,7 +119,14 @@ private fun BulletListDemoBottomSheetContent(state: BulletListDemoState) { applyTopPadding = true, label = stringResource(R.string.app_components_common_label_tech), value = label, - onValueChange = { value -> label = value } + onValueChange = { value -> label = value }, + enabled = labelTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) + ) + CustomizationSwitchItem( + label = stringResource(R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it }, ) } } @@ -121,24 +134,22 @@ private fun BulletListDemoBottomSheetContent(state: BulletListDemoState) { @Composable private fun BulletListDemoContent(state: BulletListDemoState) { with(state) { - val builder: OudsBulletListBuilder.() -> Unit = remember(levelCount, label) { + val builder: OudsBulletListBuilder.() -> Unit = remember(levelCount, label, annotatedText) { { when (levelCount) { - 1 -> { - item(label = label) - item(label = label) - item(label = label) + 1 -> repeat(3) { index -> + bulletListDemoItem(index, this@with) } 2 -> { - item(label = label) { - item(label = label) - item(label = label) + bulletListDemoItem(0, this@with) { + bulletListDemoItem(1, this@with) + bulletListDemoItem(2, this@with) } } else -> { - item(label = label) { - item(label = label) { - item(label = label) + bulletListDemoItem(0, this@with) { + bulletListDemoItem(1, this@with) { + bulletListDemoItem(2, this@with) } } } @@ -155,10 +166,9 @@ private fun BulletListDemoContent(state: BulletListDemoState) { } else { type }, - textStyle = OudsBulletListTextStyle(fontSize, fontWeight) - ) { - builder() - } + textStyle = OudsBulletListTextStyle(fontSize, fontWeight), + builder = builder + ) } } @@ -190,19 +200,19 @@ private fun Code.Builder.bulletListDemoCodeSnippet(state: BulletListDemoState, t when (levelCount) { 1 -> { repeat(3) { - itemFunctionCall(label) + itemFunctionCall(state) } } 2 -> { - itemFunctionCall(label) { - itemFunctionCall(label) - itemFunctionCall(label) + itemFunctionCall(state) { + itemFunctionCall(state) + itemFunctionCall(state) } } else -> { - itemFunctionCall(label) { - itemFunctionCall(label) { - itemFunctionCall(label) + itemFunctionCall(state) { + itemFunctionCall(state) { + itemFunctionCall(state) } } } @@ -212,10 +222,16 @@ private fun Code.Builder.bulletListDemoCodeSnippet(state: BulletListDemoState, t } } -private fun Code.Builder.itemFunctionCall(label: String, content: (Code.Builder.() -> Unit)? = null) = functionCall("item") { +private fun Code.Builder.itemFunctionCall(state: BulletListDemoState, content: (Code.Builder.() -> Unit)? = null) = functionCall("item") { trailingLambda = true isMultiline = false - labelArgument(label) + with(state) { + if (annotatedText) { + annotatedStringArgument("label") + } else { + labelArgument(label) + } + } content?.let { lambdaArgument("builder") { content() @@ -263,6 +279,36 @@ private fun getUnorderedAssetClasses() = if (LocalInspectionMode.current) { OudsBulletListUnorderedAsset::class.sealedSubclasses } +private fun OudsBulletListBuilder.bulletListDemoItem(index: Int, state: BulletListDemoState, builder: (OudsBulletListBuilder.() -> Unit)? = null) { + with(state) { + if (annotatedText) { + val annotatedLabel = buildOudsAnnotatedBulletListLabel { + when (index) { + 0 -> { + append("Your payment was ") + withStrong { append("declined") } + append(".") + } + 1 -> { + append("Check your ") + withStrong { append("available balance") } + append(" before retrying.") + } + 2 -> { + append("Update your ") + withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("declined") } + append(" if needed.") + } + else -> {} + } + } + item(label = annotatedLabel, builder = builder) + } else { + item(label = label, builder = builder) + } + } +} + @PreviewLightDark @Composable private fun PreviewBulletListDemoScreen() = AppPreview { diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoState.kt index 66578d27b2..698bb03800 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoState.kt @@ -36,7 +36,8 @@ fun rememberBulletListDemoState( fontSize: OudsBulletListFontSize = OudsBulletListDefaults.TextStyle.fontSize, fontWeight: OudsBulletListFontWeight = OudsBulletListDefaults.TextStyle.fontWeight, levelCount: Int = BulletListDemoState.MinLevelCount, - label: String = stringResource(R.string.app_components_common_label_label) + label: String = stringResource(R.string.app_components_common_label_label), + annotatedText: Boolean = false ): BulletListDemoState { return rememberSaveable( type, @@ -46,17 +47,10 @@ fun rememberBulletListDemoState( fontWeight, levelCount, label, + annotatedText, saver = BulletListDemoState.Saver ) { - BulletListDemoState( - type = type, - unorderedAssetClassName = unorderedAssetClassName, - unorderedAssetBrandColor = unorderedAssetBrandColor, - fontSize = fontSize, - fontWeight = fontWeight, - levelCount = levelCount, - label = label - ) + BulletListDemoState(type, unorderedAssetClassName, unorderedAssetBrandColor, fontSize, fontWeight, levelCount, label, annotatedText) } } @@ -67,7 +61,8 @@ class BulletListDemoState( fontSize: OudsBulletListFontSize, fontWeight: OudsBulletListFontWeight, levelCount: Int, - label: String + label: String, + annotatedText: Boolean ) { companion object { @@ -84,7 +79,8 @@ class BulletListDemoState( fontSize, fontWeight, levelCount, - label + label, + annotatedText ) } }, @@ -97,7 +93,8 @@ class BulletListDemoState( list[3] as OudsBulletListFontSize, list[4] as OudsBulletListFontWeight, list[5] as Int, - list[6] as String + list[6] as String, + list[7] as Boolean ) } ) @@ -117,10 +114,14 @@ class BulletListDemoState( var label: String by mutableStateOf(label) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val unorderedAssetChipsEnabled: Boolean get() = type is OudsBulletListType.Unordered val unorderedAssetBrandColorSwitchEnabled: Boolean get() = type is OudsBulletListType.Unordered + val labelTextInputEnabled: Boolean + get() = !annotatedText } \ No newline at end of file From 2b295d346258f78c117ff958720b934f17b7d662 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Thu, 18 Jun 2026 16:50:20 +0200 Subject: [PATCH 04/10] Add rich text to password input, pin code input, text area and text input demos --- .../ouds/app/ui/components/ComponentCode.kt | 15 ++- .../passwordinput/PasswordInputDemoScreen.kt | 89 +++++++++++++---- .../passwordinput/PasswordInputDemoState.kt | 23 +++-- .../pincodeinput/PinCodeInputDemoScreen.kt | 68 ++++++++++--- .../pincodeinput/PinCodeInputDemoState.kt | 23 +++-- .../components/textarea/TextAreaDemoScreen.kt | 88 +++++++++++++---- .../components/textarea/TextAreaDemoState.kt | 23 +++-- .../textinput/TextInputDemoScreen.kt | 98 ++++++++++++++----- .../textinput/TextInputDemoState.kt | 21 +++- 9 files changed, 345 insertions(+), 103 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt index dd285fedd8..38acbfb797 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt @@ -23,6 +23,7 @@ import com.orange.ouds.app.ui.utilities.FunctionCall import com.orange.ouds.core.component.OudsColoredBoxColor import com.orange.ouds.core.component.common.OudsError import com.orange.ouds.core.component.common.text.OudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.OudsAnnotatedHelperText import com.orange.ouds.core.component.common.text.OudsAnnotatedString fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.() -> Unit) { @@ -92,9 +93,9 @@ fun FunctionCall.Builder.tintedArgument(value: Boolean) = typedArgument(Argument fun FunctionCall.Builder.errorArgument(message: String, annotatedMessage: Boolean = false) { constructorCallArgument(Argument.Error) { if (annotatedMessage) { - annotatedStringArgument("message") + annotatedStringArgument(Argument.ErrorMessage) } else { - typedArgument("message", message) + typedArgument(Argument.ErrorMessage, message) } } } @@ -112,6 +113,14 @@ fun FunctionCall.Builder.onClickArgument(init: Code.Builder.() -> Unit = {}) = l fun FunctionCall.Builder.readOnlyArgument(value: Boolean) = typedArgument(Argument.ReadOnly, value) +fun FunctionCall.Builder.helperTextArgument(helperText: String, annotated: Boolean = false) { + if (annotated) { + annotatedStringArgument(Argument.HelperText) + } else if (helperText.isNotEmpty()) { + typedArgument(Argument.HelperText, helperText) + } +} + private object Argument { const val Color = "color" @@ -120,6 +129,8 @@ private object Argument { const val Content = "content" const val Enabled = "enabled" const val Error = "error" + const val ErrorMessage = "message" + const val HelperText = "helperText" const val Id = "id" const val Label = "label" const val OnClick = "onClick" diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt index 46e332ea2e..e0888f49f6 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt @@ -12,6 +12,7 @@ package com.orange.ouds.app.ui.components.passwordinput +import androidx.compose.foundation.text.input.KeyboardActionHandler import androidx.compose.foundation.text.input.TextObfuscationMode import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalFocusManager @@ -22,6 +23,7 @@ import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.enabledArgument import com.orange.ouds.app.ui.components.errorArgument +import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.components.readOnlyArgument import com.orange.ouds.app.ui.utilities.Code @@ -33,6 +35,9 @@ import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsPasswordInput import com.orange.ouds.core.component.OudsTextInputLoader import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.theme.OudsVersion @@ -90,7 +95,8 @@ private fun PasswordInputDemoBottomSheetContent(state: PasswordInputDemoState) { label = stringResource(R.string.app_components_common_errorMessage_tech), value = errorMessage, onValueChange = { value -> errorMessage = value }, - enabled = errorMessageTextInputEnabled + enabled = errorMessageTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -114,7 +120,9 @@ private fun PasswordInputDemoBottomSheetContent(state: PasswordInputDemoState) { applyTopPadding = true, label = stringResource(R.string.app_components_common_helperText_tech), value = helperText, - onValueChange = { value -> helperText = value } + onValueChange = { value -> helperText = value }, + enabled = helperTextTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationSwitchItem( label = stringResource(R.string.app_components_common_constrainedMaxWidth_tech), @@ -132,28 +140,69 @@ private fun PasswordInputDemoBottomSheetContent(state: PasswordInputDemoState) { passwordInputState.textObfuscationMode = textObfuscationModes[index] } ) + CustomizationSwitchItem( + label = stringResource(id = R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it } + ) } } @Composable private fun PasswordInputDemoContent(state: PasswordInputDemoState) { - val focusManager = LocalFocusManager.current with(state) { - OudsPasswordInput( - state = passwordInputState, - label = label, - placeholder = placeholder, - outlined = outlined, - lockIcon = lockIcon, - loader = if (hasLoader) OudsTextInputLoader(null) else null, - enabled = enabled, - readOnly = readOnly, - error = if (error) OudsError(errorMessage) else null, - prefix = prefix, - helperText = helperText, - constrainedMaxWidth = constrainedMaxWidth, - onKeyboardAction = { focusManager.clearFocus() } - ) + val focusManager = LocalFocusManager.current + val loader = if (hasLoader) OudsTextInputLoader(null) else null + val passwordInputError = when { + error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { + append("Your password can't be ") + withStrong { append("empty") } + append(".") + }) + error -> OudsError(errorMessage) + else -> null + } + val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } + if (annotatedText) { + val annotatedHelperText = buildOudsAnnotatedHelperText { + append("Your password must be between ") + withStrong { append("8") } + append(" and ") + withStrong { append("20") } + append(" characters long.") + } + OudsPasswordInput( + state = passwordInputState, + label = label, + placeholder = placeholder, + outlined = outlined, + lockIcon = lockIcon, + loader = loader, + enabled = enabled, + readOnly = readOnly, + error = passwordInputError, + prefix = prefix, + helperText = annotatedHelperText, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) + } else { + OudsPasswordInput( + state = passwordInputState, + label = label, + placeholder = placeholder, + outlined = outlined, + lockIcon = lockIcon, + loader = loader, + enabled = enabled, + readOnly = readOnly, + error = passwordInputError, + prefix = prefix, + helperText = helperText, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) + } } } @@ -172,9 +221,9 @@ private fun Code.Builder.passwordInputDemoCodeSnippet(state: PasswordInputDemoSt } if (!enabled) enabledArgument(false) if (readOnly) readOnlyArgument(true) - if (error) errorArgument(errorMessage) + if (error) errorArgument(errorMessage, annotatedText) if (prefix.isNotEmpty()) typedArgument("prefix", prefix) - if (helperText.isNotEmpty()) typedArgument("helperText", helperText) + helperTextArgument(helperText, annotatedText) if (constrainedMaxWidth) constrainedMaxWidthArgument(true) lambdaArgument("onKeyboardAction") { functionCall("focusManager.clearFocus") diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoState.kt index 6cfd8f2928..be4cbee421 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoState.kt @@ -37,7 +37,8 @@ fun rememberPasswordInputDemoState( errorMessage: String = stringResource(id = R.string.app_components_common_errorMessage_label), prefix: String = "", helperText: String = "", - constrainedMaxWidth: Boolean = false + constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( passwordInputState, label, @@ -52,6 +53,7 @@ fun rememberPasswordInputDemoState( prefix, helperText, constrainedMaxWidth, + annotatedText, saver = PasswordInputDemoState.Saver ) { PasswordInputDemoState( @@ -67,7 +69,8 @@ fun rememberPasswordInputDemoState( errorMessage, prefix, helperText, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -84,7 +87,8 @@ class PasswordInputDemoState( errorMessage: String, prefix: String, helperText: String, - constrainedMaxWidth: Boolean + constrainedMaxWidth: Boolean, + annotatedText: Boolean ) { companion object { @@ -104,7 +108,8 @@ class PasswordInputDemoState( errorMessage, prefix, helperText, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } }, @@ -123,7 +128,8 @@ class PasswordInputDemoState( list[9] as String, list[10] as String, list[11] as String, - list[12] as Boolean + list[12] as Boolean, + list[13] as Boolean ) } ) @@ -155,6 +161,8 @@ class PasswordInputDemoState( var constrainedMaxWidth: Boolean by mutableStateOf(constrainedMaxWidth) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val enabledSwitchEnabled: Boolean get() = !error && !hasLoader @@ -162,11 +170,14 @@ class PasswordInputDemoState( get() = !readOnly && !hasLoader && enabled val errorMessageTextInputEnabled: Boolean - get() = error + get() = error && !annotatedText val readOnlySwitchEnabled: Boolean get() = !error && !hasLoader val loaderSwitchEnabled: Boolean get() = enabled && !readOnly && !error && passwordInputState.text.isNotEmpty() + + val helperTextTextInputEnabled: Boolean + get() = !annotatedText } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt index bf62141a5b..59dd29f564 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt @@ -12,12 +12,14 @@ package com.orange.ouds.app.ui.components.pincodeinput +import androidx.compose.foundation.text.input.KeyboardActionHandler import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.errorArgument +import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem @@ -26,6 +28,9 @@ import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsPinCodeInput import com.orange.ouds.core.component.OudsPinCodeInputLength import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.theme.OudsVersion @@ -66,30 +71,65 @@ private fun PinCodeInputDemoBottomSheetContent(state: PinCodeInputDemoState) { label = stringResource(R.string.app_components_common_errorMessage_tech), value = errorMessage, onValueChange = { value -> errorMessage = value }, - enabled = errorMessageTextInputEnabled + enabled = errorMessageTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, label = stringResource(R.string.app_components_common_helperText_tech), value = helperText, - onValueChange = { value -> helperText = value } + onValueChange = { value -> helperText = value }, + enabled = helperTextTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) + ) + CustomizationSwitchItem( + label = stringResource(id = R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it } ) } } @Composable private fun PinCodeInputDemoContent(state: PinCodeInputDemoState) { - val focusManager = LocalFocusManager.current with(state) { - OudsPinCodeInput( - value = value, - onValueChange = { value = it }, - length = length, - outlined = outlined, - error = if (error) OudsError(errorMessage) else null, - helperText = helperText, - onKeyboardAction = { focusManager.clearFocus() } - ) + val focusManager = LocalFocusManager.current + val onValueChange: (String) -> Unit = { value = it } + val pinCodeInputError = when { + error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { + withStrong { append("Verification failed") } + append(". Check and enter the correct code.") + }) + error -> OudsError(errorMessage) + else -> null + } + val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } + if (annotatedText) { + val annotatedHelperText = buildOudsAnnotatedHelperText { + append("Enter the ") + withStrong { append("one-time code") } + append(" sent to your device.") + } + OudsPinCodeInput( + value = value, + onValueChange = onValueChange, + length = length, + outlined = outlined, + error = pinCodeInputError, + helperText = annotatedHelperText, + onKeyboardAction = onKeyboardAction + ) + } else { + OudsPinCodeInput( + value = value, + onValueChange = onValueChange, + length = length, + outlined = outlined, + error = pinCodeInputError, + helperText = helperText, + onKeyboardAction = onKeyboardAction + ) + } } } @@ -102,8 +142,8 @@ private fun Code.Builder.pinCodeInputDemoCodeSnippet(state: PinCodeInputDemoStat } typedArgument("length", length) if (outlined) typedArgument("outlined", outlined) - if (error) errorArgument(errorMessage) - if (helperText.isNotEmpty()) typedArgument("helperText", helperText) + if (error) errorArgument(errorMessage, annotatedText) + helperTextArgument(helperText, annotatedText) } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoState.kt index 6c814fa2b6..19fb0d2cc2 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoState.kt @@ -30,9 +30,10 @@ fun rememberPinCodeInputDemoState( outlined: Boolean = false, error: Boolean = false, errorMessage: String = stringResource(id = R.string.app_components_common_errorMessage_label), - helperText: String = "" -) = rememberSaveable(value, length, outlined, error, errorMessage, helperText, saver = PinCodeInputDemoState.Saver) { - PinCodeInputDemoState(value, length, outlined, error, errorMessage, helperText) + helperText: String = "", + annotatedText: Boolean = false +) = rememberSaveable(value, length, outlined, error, errorMessage, helperText, annotatedText, saver = PinCodeInputDemoState.Saver) { + PinCodeInputDemoState(value, length, outlined, error, errorMessage, helperText, annotatedText) } class PinCodeInputDemoState( @@ -41,7 +42,8 @@ class PinCodeInputDemoState( outlined: Boolean, error: Boolean, errorMessage: String, - helperText: String + helperText: String, + annotatedText: Boolean ) { companion object { @@ -54,7 +56,8 @@ class PinCodeInputDemoState( outlined, error, errorMessage, - helperText + helperText, + annotatedText ) } }, @@ -65,7 +68,8 @@ class PinCodeInputDemoState( list[2] as Boolean, list[3] as Boolean, list[4] as String, - list[5] as String + list[5] as String, + list[6] as Boolean ) } ) @@ -83,6 +87,11 @@ class PinCodeInputDemoState( var helperText: String by mutableStateOf(helperText) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val errorMessageTextInputEnabled: Boolean - get() = error + get() = error && !annotatedText + + val helperTextTextInputEnabled: Boolean + get() = !annotatedText } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt index e4452fe2b1..2b250fa45a 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt @@ -12,6 +12,7 @@ package com.orange.ouds.app.ui.components.textarea +import androidx.compose.foundation.text.input.KeyboardActionHandler import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -21,6 +22,7 @@ import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.enabledArgument import com.orange.ouds.app.ui.components.errorArgument +import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.components.readOnlyArgument @@ -33,6 +35,9 @@ import com.orange.ouds.core.component.OudsTextArea import com.orange.ouds.core.component.OudsTextInputHelperLink import com.orange.ouds.core.component.OudsTextInputLoader import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.theme.OudsVersion @Composable @@ -89,7 +94,8 @@ private fun TextAreaDemoBottomSheetContent(state: TextAreaDemoState) { label = stringResource(R.string.app_components_common_errorMessage_tech), value = errorMessage, onValueChange = { value -> errorMessage = value }, - enabled = errorMessageTextInputEnabled + enabled = errorMessageTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -107,7 +113,9 @@ private fun TextAreaDemoBottomSheetContent(state: TextAreaDemoState) { applyTopPadding = true, label = stringResource(R.string.app_components_common_helperText_tech), value = helperText, - onValueChange = { value -> helperText = value } + onValueChange = { value -> helperText = value }, + enabled = helperTextTextInputEnabled, + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -120,28 +128,68 @@ private fun TextAreaDemoBottomSheetContent(state: TextAreaDemoState) { checked = constrainedMaxWidth, onCheckedChange = { constrainedMaxWidth = it }, ) + CustomizationSwitchItem( + label = stringResource(id = R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it } + ) } } @Composable private fun TextAreaDemoContent(state: TextAreaDemoState) { - val focusManager = LocalFocusManager.current with(state) { - OudsTextArea( - textFieldState = textFieldState, - label = label, - placeholder = placeholder, - outlined = outlined, - loader = if (hasLoader) OudsTextInputLoader(null) else null, - enabled = enabled, - readOnly = readOnly, - autoResize = autoResize, - error = if (error) OudsError(errorMessage) else null, - helperText = helperText, - helperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = { }) else null, - constrainedMaxWidth = constrainedMaxWidth, - onKeyboardAction = { focusManager.clearFocus() } - ) + val focusManager = LocalFocusManager.current + val loader = if (hasLoader) OudsTextInputLoader(null) else null + val textAreaError = when { + error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { + append("You have ") + withStrong { append("20") } + append(" characters too many.") + }) + error -> OudsError(errorMessage) + else -> null + } + val textAreaHelperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = { }) else null + val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } + if (annotatedText) { + val annotatedHelperText = buildOudsAnnotatedHelperText { + append("Please provide a ") + withStrong { append("detailed description") } + append(" of your request.") + } + OudsTextArea( + textFieldState = textFieldState, + label = label, + placeholder = placeholder, + outlined = outlined, + loader = loader, + enabled = enabled, + readOnly = readOnly, + autoResize = autoResize, + error = textAreaError, + helperText = annotatedHelperText, + helperLink = textAreaHelperLink, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) + } else { + OudsTextArea( + textFieldState = textFieldState, + label = label, + placeholder = placeholder, + outlined = outlined, + loader = loader, + enabled = enabled, + readOnly = readOnly, + autoResize = autoResize, + error = textAreaError, + helperText = helperText, + helperLink = textAreaHelperLink, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) + } } } @@ -160,8 +208,8 @@ private fun Code.Builder.textAreaDemoCodeSnippet(state: TextAreaDemoState) { if (!enabled) enabledArgument(false) if (readOnly) readOnlyArgument(true) if (!autoResize) typedArgument("autoResize", autoResize) - if (error) errorArgument(errorMessage) - if (helperText.isNotEmpty()) typedArgument("helperText", helperText) + if (error) errorArgument(errorMessage, annotatedText) + helperTextArgument(helperText, annotatedText) if (helperLink.isNotEmpty()) { constructorCallArgument("helperLink") { typedArgument("text", helperLink) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoState.kt index d17e0dae75..9413c7b606 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoState.kt @@ -37,7 +37,8 @@ fun rememberTextAreaDemoState( errorMessage: String = stringResource(id = R.string.app_components_common_errorMessage_label), helperText: String = "", helperLink: String = "", - constrainedMaxWidth: Boolean = false + constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( textFieldState, label, @@ -52,6 +53,7 @@ fun rememberTextAreaDemoState( helperText, helperLink, constrainedMaxWidth, + annotatedText, saver = TextAreaDemoState.Saver ) { TextAreaDemoState( @@ -67,7 +69,8 @@ fun rememberTextAreaDemoState( errorMessage, helperText, helperLink, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -84,7 +87,8 @@ class TextAreaDemoState( errorMessage: String, helperText: String, helperLink: String, - constrainedMaxWidth: Boolean + constrainedMaxWidth: Boolean, + annotatedText: Boolean ) { companion object { @@ -105,7 +109,8 @@ class TextAreaDemoState( errorMessage, helperText, helperLink, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } }, @@ -124,7 +129,8 @@ class TextAreaDemoState( list[9] as String, list[10] as String, list[11] as String, - list[12] as Boolean + list[12] as Boolean, + list[13] as Boolean ) } ) @@ -156,6 +162,8 @@ class TextAreaDemoState( var constrainedMaxWidth: Boolean by mutableStateOf(constrainedMaxWidth) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val enabledSwitchEnabled: Boolean get() = !error && !hasLoader @@ -163,11 +171,14 @@ class TextAreaDemoState( get() = !readOnly && !hasLoader && enabled val errorMessageTextInputEnabled: Boolean - get() = error + get() = error && !annotatedText val readOnlySwitchEnabled: Boolean get() = !error && !hasLoader val loaderSwitchEnabled: Boolean get() = enabled && !readOnly && !error && textFieldState.text.isNotEmpty() + + val helperTextTextInputEnabled: Boolean + get() = !annotatedText } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt index f872a804f9..ccb6aeb733 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt @@ -12,6 +12,7 @@ package com.orange.ouds.app.ui.components.textinput +import androidx.compose.foundation.text.input.KeyboardActionHandler import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource @@ -23,6 +24,7 @@ import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.contentDescriptionArgument import com.orange.ouds.app.ui.components.enabledArgument import com.orange.ouds.app.ui.components.errorArgument +import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.components.iconArgument import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.components.onClickArgument @@ -43,6 +45,9 @@ import com.orange.ouds.core.component.OudsTextInputLeadingIcon import com.orange.ouds.core.component.OudsTextInputLoader import com.orange.ouds.core.component.OudsTextInputTrailingIconButton import com.orange.ouds.core.component.common.OudsError +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText +import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.theme.OudsVersion @Composable @@ -110,7 +115,8 @@ private fun TextInputDemoBottomSheetContent(state: TextInputDemoState) { label = stringResource(R.string.app_components_common_errorMessage_tech), value = errorMessage, onValueChange = { value -> errorMessage = value }, - enabled = errorMessageTextInputEnabled + enabled = errorMessageTextInputEnabled, + helperText = stringResource(R.string.app_components_common_errorMessage_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -140,7 +146,9 @@ private fun TextInputDemoBottomSheetContent(state: TextInputDemoState) { applyTopPadding = true, label = stringResource(R.string.app_components_common_helperText_tech), value = helperText, - onValueChange = { value -> helperText = value } + onValueChange = { value -> helperText = value }, + enabled = helperTextTextInputEnabled, + helperText = stringResource(R.string.app_components_common_errorMessage_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -153,6 +161,11 @@ private fun TextInputDemoBottomSheetContent(state: TextInputDemoState) { checked = constrainedMaxWidth, onCheckedChange = { constrainedMaxWidth = it }, ) + CustomizationSwitchItem( + label = stringResource(id = R.string.app_components_common_annotatedText_tech), + checked = annotatedText, + onCheckedChange = { annotatedText = it } + ) } } @@ -177,26 +190,65 @@ private fun TextInputDemoContent(state: TextInputDemoState) { TextInputDemoState.TrailingIcon.Tinted -> OudsTextInputTrailingIconButton( painter = painterResource(id = LocalThemeDrawableResources.current.tipsAndTricks), contentDescription = stringResource(id = R.string.app_components_textInput_trailingAction_a11y), - onClick = {}) + onClick = {} + ) + } + val loader = if (hasLoader) OudsTextInputLoader(null) else null + val textInputError = when { + error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { + append("This field can’t be ") + withStrong { append("empty") } + append(".") + }) + error -> OudsError(errorMessage) + else -> null + } + val textInputHelperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = {}) else null + val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } + if (annotatedText) { + val annotatedHelperText = buildOudsAnnotatedHelperText { + append("You can find your ") + withStrong { append("customer ID") } + append("on your latest invoice.") + } + OudsTextInput( + textFieldState = textFieldState, + label = label, + placeholder = placeholder, + outlined = outlined, + leadingIcon = textInputLeadingIcon, + trailingIconButton = textInputTrailingIcon, + loader = loader, + enabled = enabled, + readOnly = readOnly, + error = textInputError, + prefix = prefix, + suffix = suffix, + helperText = annotatedHelperText, + helperLink = textInputHelperLink, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) + } else { + OudsTextInput( + textFieldState = textFieldState, + label = label, + placeholder = placeholder, + outlined = outlined, + leadingIcon = textInputLeadingIcon, + trailingIconButton = textInputTrailingIcon, + loader = loader, + enabled = enabled, + readOnly = readOnly, + error = textInputError, + prefix = prefix, + suffix = suffix, + helperText = helperText, + helperLink = textInputHelperLink, + constrainedMaxWidth = constrainedMaxWidth, + onKeyboardAction = onKeyboardAction + ) } - OudsTextInput( - textFieldState = textFieldState, - label = label, - placeholder = placeholder, - outlined = outlined, - leadingIcon = textInputLeadingIcon, - trailingIconButton = textInputTrailingIcon, - loader = if (hasLoader) OudsTextInputLoader(null) else null, - enabled = enabled, - readOnly = readOnly, - error = if (error) OudsError(errorMessage) else null, - prefix = prefix, - suffix = suffix, - helperText = helperText, - helperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = { }) else null, - constrainedMaxWidth = constrainedMaxWidth, - onKeyboardAction = { focusManager.clearFocus() } - ) } } @@ -230,10 +282,10 @@ private fun Code.Builder.textInputDemoCodeSnippet(state: TextInputDemoState, the } if (!enabled) enabledArgument(false) if (readOnly) readOnlyArgument(true) - if (error) errorArgument(errorMessage) + if (error) errorArgument(errorMessage, annotatedText) if (prefix.isNotEmpty()) typedArgument("prefix", prefix) if (suffix.isNotEmpty()) typedArgument("suffix", suffix) - if (helperText.isNotEmpty()) typedArgument("helperText", helperText) + helperTextArgument(helperText, annotatedText) if (helperLink.isNotEmpty()) { constructorCallArgument("helperLink") { typedArgument("text", helperLink) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoState.kt index 083816520f..aab4f4476d 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoState.kt @@ -41,7 +41,8 @@ fun rememberTextInputDemoState( suffix: String = "", helperText: String = "", helperLink: String = "", - constrainedMaxWidth: Boolean = false + constrainedMaxWidth: Boolean = false, + annotatedText: Boolean = false ) = rememberSaveable( textFieldState, label, @@ -59,6 +60,7 @@ fun rememberTextInputDemoState( helperText, helperLink, constrainedMaxWidth, + annotatedText, saver = TextInputDemoState.Saver ) { TextInputDemoState( @@ -77,7 +79,8 @@ fun rememberTextInputDemoState( suffix, helperText, helperLink, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } @@ -98,6 +101,7 @@ class TextInputDemoState( helperText: String, helperLink: String, constrainedMaxWidth: Boolean, + annotatedText: Boolean ) { companion object { @@ -121,7 +125,8 @@ class TextInputDemoState( suffix, helperText, helperLink, - constrainedMaxWidth + constrainedMaxWidth, + annotatedText ) } }, @@ -143,7 +148,8 @@ class TextInputDemoState( list[12] as String, list[13] as String, list[14] as String, - list[15] as Boolean + list[15] as Boolean, + list[16] as Boolean ) } ) @@ -181,6 +187,8 @@ class TextInputDemoState( var constrainedMaxWidth: Boolean by mutableStateOf(constrainedMaxWidth) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val enabledSwitchEnabled: Boolean get() = !error && !hasLoader @@ -188,7 +196,7 @@ class TextInputDemoState( get() = !readOnly && !hasLoader && enabled val errorMessageTextInputEnabled: Boolean - get() = error + get() = error && !annotatedText val readOnlySwitchEnabled: Boolean get() = !error && !hasLoader @@ -196,6 +204,9 @@ class TextInputDemoState( val loaderSwitchEnabled: Boolean get() = enabled && !readOnly && !error && textFieldState.text.isNotEmpty() + val helperTextTextInputEnabled: Boolean + get() = !annotatedText + enum class LeadingIcon(@StringRes val labelRes: Int) { None(R.string.app_components_common_none_tech), Tinted(R.string.app_components_common_tintedIcon_tech), From c2285d25aa02f3ee2906473a0f1d3b2dfa88ddb1 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Thu, 18 Jun 2026 17:35:17 +0200 Subject: [PATCH 05/10] Annotated text switch is now disabled when error is disabled in control item demo screens --- .../components/controlitem/ControlItemDemoScreen.kt | 3 ++- .../components/controlitem/ControlItemDemoState.kt | 13 ++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt index b7abab2771..785f49c3dd 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt @@ -162,7 +162,8 @@ fun ControlItemAnnotatedTextCustomization(state: ControlItemDemoState) { CustomizationSwitchItem( label = stringResource(id = R.string.app_components_common_annotatedText_tech), checked = annotatedText, - onCheckedChange = { annotatedText = it } + onCheckedChange = { annotatedText = it }, + enabled = annotatedTextSwitchEnabled ) } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt index 04aa1c9fbf..83c98c6a84 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoState.kt @@ -86,15 +86,7 @@ open class ControlItemDemoState( var label: String by mutableStateOf(label) var description: String? by mutableStateOf(description) - private var _annotatedText: Boolean by mutableStateOf(annotatedText) - var annotatedText: Boolean - get() = _annotatedText - set(value) { - _annotatedText = value - if (value) { - error = true - } - } + var annotatedText: Boolean by mutableStateOf(annotatedText) val enabledSwitchEnabled: Boolean get() = !error @@ -108,6 +100,9 @@ open class ControlItemDemoState( val errorMessageTextInputEnabled: Boolean get() = error && !annotatedText + val annotatedTextSwitchEnabled: Boolean + get() = error + enum class Icon(@StringRes val labelRes: Int) { None(R.string.app_components_common_none_tech), Tinted(R.string.app_components_common_tintedIcon_tech), From 7b2143dc1c1ee959830e7a9c862dbc15ebc2f4a3 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Fri, 19 Jun 2026 11:28:41 +0200 Subject: [PATCH 06/10] Minor fixes --- .../java/com/orange/ouds/app/ui/components/ComponentCode.kt | 2 +- .../ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt index 38acbfb797..7c1fa06533 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt @@ -116,7 +116,7 @@ fun FunctionCall.Builder.readOnlyArgument(value: Boolean) = typedArgument(Argume fun FunctionCall.Builder.helperTextArgument(helperText: String, annotated: Boolean = false) { if (annotated) { annotatedStringArgument(Argument.HelperText) - } else if (helperText.isNotEmpty()) { + } else if (helperText.isNotBlank()) { typedArgument(Argument.HelperText, helperText) } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt index 81437a1604..e70d08c0bf 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt @@ -296,7 +296,7 @@ private fun OudsBulletListBuilder.bulletListDemoItem(index: Int, state: BulletLi } 2 -> { append("Update your ") - withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("declined") } + withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("payment details") } append(" if needed.") } else -> {} From 5961506a9f2d057e226f3d014644b7afb0612863 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Mon, 22 Jun 2026 11:12:48 +0200 Subject: [PATCH 07/10] Use string resources instead of hardcoded values to display rich text in demo app --- .../alert/AlertMessageDemoScreen.kt | 40 ++++++--------- .../bulletlist/BulletListDemoScreen.kt | 49 +++++++------------ .../checkbox/CheckboxItemDemoScreen.kt | 14 ++---- .../controlitem/ControlItemDemoScreen.kt | 13 +++-- .../passwordinput/PasswordInputDemoScreen.kt | 20 ++++---- .../pincodeinput/PinCodeInputDemoScreen.kt | 17 ++++--- .../radiobutton/RadioButtonItemDemoScreen.kt | 9 +--- .../components/switch/SwitchItemDemoScreen.kt | 9 +--- .../components/textarea/TextAreaDemoScreen.kt | 17 ++++--- .../textinput/TextInputDemoScreen.kt | 17 ++++--- .../ui/utilities/OudsAnnotatedStringExt.kt | 21 ++++++++ app/src/main/res/values-ar/strings.xml | 18 +++++++ app/src/main/res/values-fr/strings.xml | 18 +++++++ app/src/main/res/values/strings.xml | 18 +++++++ 14 files changed, 164 insertions(+), 116 deletions(-) create mode 100644 app/src/main/java/com/orange/ouds/app/ui/utilities/OudsAnnotatedStringExt.kt diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt index 241b7d185c..ea2c08139a 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt @@ -29,6 +29,7 @@ import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources import com.orange.ouds.app.ui.utilities.ThemeDrawableResources +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.CustomizationDropdownMenu import com.orange.ouds.app.ui.utilities.composable.CustomizationDropdownMenuItem @@ -46,11 +47,8 @@ import com.orange.ouds.core.component.OudsAlertMessageActionLinkPosition import com.orange.ouds.core.component.OudsAlertMessageStatus import com.orange.ouds.core.component.common.text.OudsAnnotatedAlertMessageBulletListLabel import com.orange.ouds.core.component.common.text.OudsAnnotatedAlertMessageDescription -import com.orange.ouds.core.component.common.text.OudsLinkAnnotation import com.orange.ouds.core.component.common.text.buildOudsAnnotatedAlertMessageBulletListLabel import com.orange.ouds.core.component.common.text.buildOudsAnnotatedAlertMessageDescription -import com.orange.ouds.core.component.common.text.withLink -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -180,7 +178,7 @@ private fun AlertMessageDemoContent(state: AlertMessageDemoState) { AlertMessageDemoState.Icon.Tinted -> OudsAlertIcon(painter = painterResource(LocalThemeDrawableResources.current.tipsAndTricks), tinted = true) AlertMessageDemoState.Icon.Untinted -> OudsAlertIcon(painter = rememberUntintedIconPainter(), tinted = false) } - val status = when (status) { + val alertMessageStatus = when (status) { is OudsAlertMessageStatus.Accent -> OudsAlertMessageStatus.Accent(alertIcon) is OudsAlertMessageStatus.Neutral -> OudsAlertMessageStatus.Neutral(alertIcon) is OudsAlertMessageStatus.Info -> OudsAlertMessageStatus.Info @@ -193,51 +191,43 @@ private fun AlertMessageDemoContent(state: AlertMessageDemoState) { } else { null } - val actionLink = actionLink?.let { actionLinkLabel -> + val alertMessageActionLink = actionLink?.let { actionLinkLabel -> OudsAlertMessageActionLink(label = actionLinkLabel, onClick = {}, position = actionLinkPosition) } if (annotatedText) { + val annotatedDescriptionHtml = stringResource(R.string.app_components_alert_alertMessage_annotatedDescription_text) val annotatedDescription = buildOudsAnnotatedAlertMessageDescription { - append("Your last payment attempt was ") - withStrong { append("declined") } - append(". Please check your payment details or ") - withStrong { append("available balance") } - append(" and try again, or ") - withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("update your payment details") } - append(".") + appendHtml(annotatedDescriptionHtml) } + val annotatedBullet1Html = stringResource(R.string.app_components_alert_alertMessage_annotatedBullet1_text) + val annotatedBullet2Html = stringResource(R.string.app_components_alert_alertMessage_annotatedBullet2_text) + val annotatedBullet3Html = stringResource(R.string.app_components_alert_alertMessage_annotatedBullet3_text) val annotatedBulletList = listOf( buildOudsAnnotatedAlertMessageBulletListLabel { - append("Your payment was ") - withStrong { append("declined") } - append(".") + appendHtml(annotatedBullet1Html) }, buildOudsAnnotatedAlertMessageBulletListLabel { - append("Check your ") - withStrong { append("available balance") } - append(" before retrying.") + appendHtml(annotatedBullet2Html) }, buildOudsAnnotatedAlertMessageBulletListLabel { - append("Update your ") - withStrong { append("payment details") } - append(" if needed.") + appendHtml(annotatedBullet3Html) } ) OudsAlertMessage( label = label, description = annotatedDescription, - status = status, + status = alertMessageStatus, onClose = onClose, - actionLink = actionLink, + actionLink = alertMessageActionLink, bulletList = annotatedBulletList ) } else { OudsAlertMessage( label = label, description = description, - status = status, + status = alertMessageStatus, onClose = onClose, - actionLink = actionLink, + actionLink = alertMessageActionLink, bulletList = bulletList ) } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt index e70d08c0bf..09b3186877 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt @@ -30,6 +30,7 @@ import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources import com.orange.ouds.app.ui.utilities.ThemeDrawableResources +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChip import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips @@ -46,10 +47,7 @@ import com.orange.ouds.core.component.OudsBulletListTextStyle import com.orange.ouds.core.component.OudsBulletListType import com.orange.ouds.core.component.OudsBulletListUnorderedAsset import com.orange.ouds.core.component.common.text.OudsAnnotatedBulletListLabel -import com.orange.ouds.core.component.common.text.OudsLinkAnnotation import com.orange.ouds.core.component.common.text.buildOudsAnnotatedBulletListLabel -import com.orange.ouds.core.component.common.text.withLink -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -134,22 +132,28 @@ private fun BulletListDemoBottomSheetContent(state: BulletListDemoState) { @Composable private fun BulletListDemoContent(state: BulletListDemoState) { with(state) { - val builder: OudsBulletListBuilder.() -> Unit = remember(levelCount, label, annotatedText) { + val label1Html = stringResource(R.string.app_components_bulletList_annotatedLabel1_text) + val label2Html = stringResource(R.string.app_components_bulletList_annotatedLabel2_text) + val label3Html = stringResource(R.string.app_components_bulletList_annotatedLabel3_text) + + val builder: OudsBulletListBuilder.() -> Unit = remember(levelCount, label, annotatedText, label1Html, label2Html, label3Html) { { when (levelCount) { - 1 -> repeat(3) { index -> - bulletListDemoItem(index, this@with) + 1 -> { + bulletListDemoItem(this@with, label1Html) + bulletListDemoItem(this@with, label2Html) + bulletListDemoItem(this@with, label3Html) } 2 -> { - bulletListDemoItem(0, this@with) { - bulletListDemoItem(1, this@with) - bulletListDemoItem(2, this@with) + bulletListDemoItem(this@with, label1Html) { + bulletListDemoItem(this@with, label2Html) + bulletListDemoItem(this@with, label3Html) } } else -> { - bulletListDemoItem(0, this@with) { - bulletListDemoItem(1, this@with) { - bulletListDemoItem(2, this@with) + bulletListDemoItem(this@with, label1Html) { + bulletListDemoItem(this@with, label2Html) { + bulletListDemoItem(this@with, label3Html) } } } @@ -279,27 +283,12 @@ private fun getUnorderedAssetClasses() = if (LocalInspectionMode.current) { OudsBulletListUnorderedAsset::class.sealedSubclasses } -private fun OudsBulletListBuilder.bulletListDemoItem(index: Int, state: BulletListDemoState, builder: (OudsBulletListBuilder.() -> Unit)? = null) { +private fun OudsBulletListBuilder.bulletListDemoItem(state: BulletListDemoState, labelHtml: String, builder: (OudsBulletListBuilder.() -> Unit)? = null) { with(state) { if (annotatedText) { val annotatedLabel = buildOudsAnnotatedBulletListLabel { - when (index) { - 0 -> { - append("Your payment was ") - withStrong { append("declined") } - append(".") - } - 1 -> { - append("Check your ") - withStrong { append("available balance") } - append(" before retrying.") - } - 2 -> { - append("Update your ") - withLink(OudsLinkAnnotation.Url("https://unified-design-system.orange.com")) { append("payment details") } - append(" if needed.") - } - else -> {} + if (labelHtml.isNotEmpty()) { + appendHtml(labelHtml) } } item(label = annotatedLabel, builder = builder) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt index 173e80d2bd..cfbd6ba4e0 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt @@ -18,10 +18,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.controlitem.ControlItemCustomizations import com.orange.ouds.app.ui.components.controlitem.controlItemArguments -import com.orange.ouds.app.ui.components.controlitem.controlItemIcon import com.orange.ouds.app.ui.components.controlitem.controlItemError +import com.orange.ouds.app.ui.components.controlitem.controlItemIcon import com.orange.ouds.app.ui.components.onClickArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources @@ -31,8 +32,6 @@ import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsCheckboxItem import com.orange.ouds.core.component.OudsTriStateCheckboxItem import com.orange.ouds.core.component.common.OudsError -import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -115,7 +114,7 @@ private fun IndeterminateCheckboxItemDemoContent(state: CheckboxItemDemoState) { reversed = reversed, enabled = enabled, readOnly = readOnly, - error = checkboxItemError(state = this, isLastItem = isLastItem,), + error = checkboxItemError(state = this, isLastItem = isLastItem), constrainedMaxWidth = constrainedMaxWidth ) } @@ -135,11 +134,8 @@ private fun checkboxItemError(state: CheckboxItemDemoState, isLastItem: Boolean) return controlItemError( state = state, isLastItem = isLastItem, - annotatedMessage = buildOudsAnnotatedErrorMessage { - append("You must select at ") - withStrong { append("least one service") } - append(" to continue.") - }) + errorMessageHtmlResId = R.string.app_components_checkbox_checkboxItem_annotatedErrorMessage_text + ) } private fun Code.Builder.checkboxItemDemoCodeSnippet(state: CheckboxItemDemoState, indeterminate: Boolean, themeDrawableResources: ThemeDrawableResources) { diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt index 785f49c3dd..52dd722ef9 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/controlitem/ControlItemDemoScreen.kt @@ -12,6 +12,7 @@ package com.orange.ouds.app.ui.components.controlitem +import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -25,13 +26,14 @@ import com.orange.ouds.app.ui.components.readOnlyArgument import com.orange.ouds.app.ui.utilities.FunctionCall import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources import com.orange.ouds.app.ui.utilities.ThemeDrawableResources +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput import com.orange.ouds.app.ui.utilities.rememberUntintedIconPainter import com.orange.ouds.core.component.OudsControlItemIcon import com.orange.ouds.core.component.common.OudsError -import com.orange.ouds.core.component.common.text.OudsAnnotatedErrorMessage +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage data class ControlItemCustomization(val index: Int, val content: @Composable () -> Unit) @@ -213,12 +215,17 @@ fun controlItemIcon(state: ControlItemDemoState): OudsControlItemIcon? { } @Composable -fun controlItemError(state: ControlItemDemoState, isLastItem: Boolean, annotatedMessage: OudsAnnotatedErrorMessage): OudsError? { +fun controlItemError(state: ControlItemDemoState, isLastItem: Boolean, @StringRes errorMessageHtmlResId: Int): OudsError? { return with(state) { when { error && !isLastItem -> OudsError("") error && !annotatedText -> OudsError(errorMessage) - error && annotatedText -> OudsError(annotatedMessage) + error && annotatedText -> { + val errorMessageHtml = stringResource(id = errorMessageHtmlResId) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorMessageHtml) + }) + } else -> null } } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt index e0888f49f6..1abb1f8323 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/passwordinput/PasswordInputDemoScreen.kt @@ -27,6 +27,7 @@ import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.components.labelArgument import com.orange.ouds.app.ui.components.readOnlyArgument import com.orange.ouds.app.ui.utilities.Code +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem @@ -37,7 +38,6 @@ import com.orange.ouds.core.component.OudsTextInputLoader import com.orange.ouds.core.component.common.OudsError import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.theme.OudsVersion @@ -154,22 +154,20 @@ private fun PasswordInputDemoContent(state: PasswordInputDemoState) { val focusManager = LocalFocusManager.current val loader = if (hasLoader) OudsTextInputLoader(null) else null val passwordInputError = when { - error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { - append("Your password can't be ") - withStrong { append("empty") } - append(".") - }) + error && annotatedText -> { + val errorMessageHtml = stringResource(R.string.app_components_passwordInput_annotatedErrorMessage_text) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorMessageHtml) + }) + } error -> OudsError(errorMessage) else -> null } val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } if (annotatedText) { + val helperTextHtml = stringResource(R.string.app_components_passwordInput_annotatedHelperText_text) val annotatedHelperText = buildOudsAnnotatedHelperText { - append("Your password must be between ") - withStrong { append("8") } - append(" and ") - withStrong { append("20") } - append(" characters long.") + appendHtml(helperTextHtml) } OudsPasswordInput( state = passwordInputState, diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt index 59dd29f564..44411268f6 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/pincodeinput/PinCodeInputDemoScreen.kt @@ -21,6 +21,7 @@ import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.errorArgument import com.orange.ouds.app.ui.components.helperTextArgument import com.orange.ouds.app.ui.utilities.Code +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput @@ -30,7 +31,6 @@ import com.orange.ouds.core.component.OudsPinCodeInputLength import com.orange.ouds.core.component.common.OudsError import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.theme.OudsVersion @@ -96,19 +96,20 @@ private fun PinCodeInputDemoContent(state: PinCodeInputDemoState) { val focusManager = LocalFocusManager.current val onValueChange: (String) -> Unit = { value = it } val pinCodeInputError = when { - error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { - withStrong { append("Verification failed") } - append(". Check and enter the correct code.") - }) + error && annotatedText -> { + val errorMessageHtml = stringResource(R.string.app_components_pinCodeInput_annotatedErrorMessage_text) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorMessageHtml) + }) + } error -> OudsError(errorMessage) else -> null } val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } if (annotatedText) { + val helperTextHtml = stringResource(R.string.app_components_pinCodeInput_annotatedHelperText_text) val annotatedHelperText = buildOudsAnnotatedHelperText { - append("Enter the ") - withStrong { append("one-time code") } - append(" sent to your device.") + appendHtml(helperTextHtml) } OudsPinCodeInput( value = value, diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt index dcf4906276..13e2fb1477 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt @@ -35,8 +35,6 @@ import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsRadioButtonItem -import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -105,11 +103,8 @@ private fun RadioButtonItemDemoContent(state: RadioButtonItemDemoState) { error = controlItemError( state = this@with, isLastItem = isLastItem, - annotatedMessage = buildOudsAnnotatedErrorMessage { - append("Please select ") - withStrong { append("one contact method") } - append(" to proceed.") - }), + errorMessageHtmlResId = R.string.app_components_radioButtonItem_annotatedErrorMessage_text + ), constrainedMaxWidth = constrainedMaxWidth ) } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt index 0d84e4c6d6..3d93acd60a 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark +import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.controlitem.ControlItemCustomizations import com.orange.ouds.app.ui.components.controlitem.controlItemArguments import com.orange.ouds.app.ui.components.controlitem.controlItemError @@ -27,8 +28,6 @@ import com.orange.ouds.app.ui.utilities.ThemeDrawableResources import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.DemoScreen import com.orange.ouds.core.component.OudsSwitchItem -import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -63,11 +62,7 @@ private fun SwitchItemDemoContent(state: SwitchItemDemoState) { error = controlItemError( state = this, isLastItem = true, - annotatedMessage = buildOudsAnnotatedErrorMessage { - append("You must enable ") - withStrong { append("automatic payments") } - append(" to activate this offer.") - } + errorMessageHtmlResId = R.string.app_components_switchItem_annotatedErrorMessage_text ), constrainedMaxWidth = constrainedMaxWidth ) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt index 2b250fa45a..29bded52a4 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textarea/TextAreaDemoScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.R +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.components.Component import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.enabledArgument @@ -142,21 +143,21 @@ private fun TextAreaDemoContent(state: TextAreaDemoState) { val focusManager = LocalFocusManager.current val loader = if (hasLoader) OudsTextInputLoader(null) else null val textAreaError = when { - error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { - append("You have ") - withStrong { append("20") } - append(" characters too many.") - }) + error && annotatedText -> { + val errorHtml = stringResource(R.string.app_components_textArea_annotatedErrorMessage_text) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorHtml) + }) + } error -> OudsError(errorMessage) else -> null } val textAreaHelperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = { }) else null val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } if (annotatedText) { + val helperTextHtml = stringResource(R.string.app_components_textArea_annotatedHelperText_text) val annotatedHelperText = buildOudsAnnotatedHelperText { - append("Please provide a ") - withStrong { append("detailed description") } - append(" of your request.") + appendHtml(helperTextHtml) } OudsTextArea( textFieldState = textFieldState, diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt index ccb6aeb733..984015aabe 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.Component +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.contentDescriptionArgument import com.orange.ouds.app.ui.components.enabledArgument @@ -195,21 +196,21 @@ private fun TextInputDemoContent(state: TextInputDemoState) { } val loader = if (hasLoader) OudsTextInputLoader(null) else null val textInputError = when { - error && annotatedText -> OudsError(buildOudsAnnotatedErrorMessage { - append("This field can’t be ") - withStrong { append("empty") } - append(".") - }) + error && annotatedText -> { + val errorHtml = stringResource(R.string.app_components_textInput_annotatedErrorMessage_text) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorHtml) + }) + } error -> OudsError(errorMessage) else -> null } val textInputHelperLink = if (helperLink.isNotEmpty()) OudsTextInputHelperLink(text = helperLink, onClick = {}) else null val onKeyboardAction: KeyboardActionHandler = { focusManager.clearFocus() } if (annotatedText) { + val helperTextHtml = stringResource(R.string.app_components_textInput_annotatedHelperText_text) val annotatedHelperText = buildOudsAnnotatedHelperText { - append("You can find your ") - withStrong { append("customer ID") } - append("on your latest invoice.") + appendHtml(helperTextHtml) } OudsTextInput( textFieldState = textFieldState, diff --git a/app/src/main/java/com/orange/ouds/app/ui/utilities/OudsAnnotatedStringExt.kt b/app/src/main/java/com/orange/ouds/app/ui/utilities/OudsAnnotatedStringExt.kt new file mode 100644 index 0000000000..3714a23bbf --- /dev/null +++ b/app/src/main/java/com/orange/ouds/app/ui/utilities/OudsAnnotatedStringExt.kt @@ -0,0 +1,21 @@ +/* + * Software Name: OUDS Android + * SPDX-FileCopyrightText: Copyright (c) Orange SA + * SPDX-License-Identifier: MIT + * + * This software is distributed under the MIT license, + * the text of which is available at https://opensource.org/license/MIT/ + * or see the "LICENSE" file for more details. + * + * Software description: Android library of reusable graphical components + */ + +package com.orange.ouds.app.ui.utilities + +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.fromHtml +import com.orange.ouds.core.component.common.text.OudsAnnotatedString + +fun OudsAnnotatedString.Builder.appendHtml(html: String) where T : OudsAnnotatedString { + append(AnnotatedString.fromHtml(html)) +} diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 5dd275f741..32720c6c67 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -104,6 +104,10 @@ مكونات alerte هي عناصر واجهة المستخدم التي تعرض معلومات، ردود فعل النظام أو تغييرات الحالة. Alert message (أو رسالة التحذير) هي عنصر من عناصر واجهة المستخدم التي تعرض معلومات النظام، تغييرات الحالة أو إجراء مطلوب؛ وذلك من خلال تواصل مفصل، مرئي، مستمر وقابل للاستخدام. Inline alert هو عنصر واجهة مستخدم خفيف الوزن، يتم وضعه داخل تدفّق المحتوى، ويُستخدم لعرض المعلومات أو ملاحظات النظام أو تغيّرات الحالة، من خلال رسائل قصيرة وواضحة وبارزة ومستمرّة، وغير قابلة لاتخاذ إجراء. + فشلت آخر محاولة دفع لك. يرجى التحقق من تفاصيل الدفع أو الرصيد المتاح لديك والمحاولة مرة أخرى، أو تحديث تفاصيل الدفع.]]> + فشلت عملية الدفع الخاصة بك.]]> + رصيدك المتاح قبل إعادة المحاولة.]]> + تفاصيل الدفع الخاصة بك إذا لزم الأمر.]]> Badge هي عنصر صغير في واجهة المستخدم يُستخدم لتسليط الضوء على الحالة أو الإشعارات أو التصنيف داخل الواجهة. غالبًا ما يتم عرضها كعلامة أو مؤشر بلون خلفية مميز ونص. @@ -118,6 +122,9 @@ Bullet list هي عنصر واجهة مستخدم يساعد على عرض عناصر نصية فردية ذات صلة مجمعة معًا؛ عادةً ما تبدأ برقم أو برمز نقطي. + فشلت عملية الدفع الخاصة بك.]]> + رصيدك المتاح قبل إعادة المحاولة.]]> + تفاصيل الدفع الخاصة بك إذا لزم الأمر.]]> Button هو عنصر واجهة مستخدم يُستخدم لتحفيز إجراء أو حدث، ويستخدم لبدء المهام أو تأكيد إجراء معين. @@ -126,6 +133,7 @@ Checkbox هو عنصر واجهة مستخدم يسمح باختيار خيارات متعددة من مجموعة من الخيارات غير الحصرية. وصف محتوى مربع الاختيار %s وصف محتوى مربع الاختيار غير المحدد %s + خدمة واحدة على الأقل للمتابعة.]]> تساعد Chips المستخدمين على إدخال المعلومات، القيام باختيارات، تصفية المحتوى، أو تنفيذ إجراءات. @@ -155,9 +163,13 @@ Password Input هو حقل نموذج مصمم خصيصًا لالتقاط كلمة مرور المستخدم بسرية. يقوم بإخفاء الأحرف أثناء الكتابة، عادةً باستبدالها بنقاط، لحماية الإدخال من قراءته من قبل الآخرين القريبين. بينما الهدف الأساسي هو تعزيز الخصوصية والأمان، قد يتضمن الحقل أيضًا ميزات سهولة الاستخدام مثل تبديل إظهار/إخفاء كلمة المرور ونص المساعدة لتوجيه إنشاء كلمة المرور. + فارغة.]]> + 8 و20 حرفاً.]]> إدخال رمز PIN هو عنصر واجهة مستخدم يسمح بإدخال رموز رقمية قصيرة ذات طول ثابت، عادةً لأغراض المصادقة أو التأكيد، مثل رقم التعريف الشخصي (PIN) المكون من أربعة أو ستة أو ثمانية أرقام. + فشل التحقق. تحقق من الرمز وأدخله بشكل صحيح.]]> + الرمز لمرة واحدة المرسل إلى جهازك.]]> Progress indicator يُظهر تقدم مهمة ما ويقدم تغذية بصرية. يمكن أن يُظهر قيمة محددة (محدد) أو فقط أن شيئًا ما قيد التقدم (غير محدد). @@ -167,20 +179,26 @@ Radio button هو عنصر واجهة مستخدم يسمح باختيار خيار واحد فقط من مجموعة من الخيارات المتعارضة. وصف محتوى زر الاختيار %s + طريقة اتصال واحدة للمتابعة.]]> Switch هو عنصر واجهة مستخدم يسمح بالتبديل بين حالتين، عادةً "تشغيل" و "إيقاف"، ويستخدم لتمكين أو تعطيل الميزات أو الخيارات أو الإعدادات. تبديل وصف المحتوى + الدفعات التلقائية لتفعيل هذا العرض.]]> Tag هي عنصر واجهة مستخدم يعرض معلومات قصيرة مثل تسمية أو كلمة مفتاحية أو فئة. Text area هي عنصر واجهة مستخدم يتيح للمستخدمين كتابة أو تحرير أو اختيار كتل نصية طويلة، مثل التعليقات أو الرسائل أو الوصف، من خلال التمدد عمودياً وتوفير مساحة أكبر لإدخال النص. + 20 حرفاً زائداً.]]> + وصف مفصل لطلبك.]]> Text input هو عنصر واجهة مستخدم يسمح بكتابة أو تحرير أو اختيار سطر واحد من البيانات النصية، مثل الأسماء أو البريد الإلكتروني أو استعلامات البحث. وصف محتوى أيقونة النهاية + فارغاً.]]> + معرف العميل الخاص بك في آخر فاتورة لك.]]> ُApp bar هو مكون موجه من الأعلى يعرض عنوان الشاشة ويوفر الوصول إلى الإجراءات الرئيسية وعناصر التنقل. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 48fce7e6cd..83ed4ddb41 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -102,6 +102,10 @@ Les composants d\'alerte sont des éléments d\'interface utilisateur qui affichent des informations, des retours système ou des changements d\'état. Un Alert message (ou message d\'alerte) est un élément d\'interface utilisateur qui affiche des informations système, des changements d\'état ou une action requise ; le tout grâce à une communication détaillée, visible, persistante et exploitable. Une Inline alert est un élément d\'interface utilisateur léger, placé dans le flux de contenu, qui affiche des informations, des commentaires système et des changements d\'état au moyen d\'une communication courte, visible, persistante et non exploitable. + refusée. Veuillez vérifier vos informations de paiement ou votre solde disponible et réessayer, ou mettre à jour vos informations de paiement.]]> + refusé.]]> + solde disponible avant de réessayer.]]> + informations de paiement si nécessaire.]]> Un Badge est un élément d\'interface utilisateur qui met en évidence les notifications système, l\'état ou la catégorisation d\'une information, uniquement par le biais de la couleur. @@ -116,6 +120,9 @@ La Bullet list (ou liste à puces) est un élément d\'interface utilisateur qui permet de visualiser des éléments de texte individuels liés, regroupés par ordre alphabétique ; ces éléments commencent généralement par un chiffre ou une puce. + refusé.]]> + solde disponible avant de réessayer.]]> + informations de paiement si nécessaire.]]> Un Button (ou bouton) est un élément d\'interface utilisateur qui déclenche une action ou un événement, et sert à lancer des tâches ou à confirmer une action. @@ -124,6 +131,7 @@ Une Checkbox (ou case à cocher) est un élément d\'interface utilisateur qui permet de sélectionner plusieurs options parmi un ensemble de choix non exclusifs. Description du contenu de la Checkbox %s Description du contenu de la Checkbox indéterminée %s + moins un service pour continuer.]]> Un Chip aide les utilisateurs à saisir des informations, faire des sélections, filtrer du contenu ou déclencher des actions. @@ -153,9 +161,13 @@ Un Password input (ou champ de saisie de mot de passe) est un élément d\'interface utilisateur qui permet de capturer de manière sécurisée et confidentielle le mot de passe d\'un utilisateur. + vide.]]> + 8 et 20 caractères.]]> Un PIN code input (ou champ de saisie de code PIN) est un élément d\'interface utilisateur qui permet de capturer des codes numériques courts et de longueur fixe, généralement à des fins d\'authentification ou de confirmation, tels qu\'un numéro d\'identification personnel (PIN) à quatre, six ou huit chiffres. + Échec de la vérification. Vérifiez et saisissez le code correct.]]> + code à usage unique envoyé à votre appareil.]]> Un Progress indicator affiche l\'avancement d\'une tâche et fournit un retour visuel. Il peut afficher une valeur précise (déterminée) ou simplement indiquer qu\'une action est en cours (indéterminée). @@ -165,20 +177,26 @@ Un Radio button (ou bouton radio) est un élément d\'interface utilisateur qui permet de sélectionner une seule option parmi un ensemble de choix mutuellement exclusifs. Description du contenu du bouton radio %s + un moyen de contact pour continuer.]]> Un Switch (ou interrupteur) est un élément d\'interface utilisateur qui permet de basculer entre deux états, généralement « Marche » et « Arrêt », et qui sert à activer ou désactiver des fonctionnalités, des options ou des paramètres. Description du contenu du switch + paiements automatiques pour activer cette offre.]]> Un Tag (ou étiquette) est un élément d\'interface utilisateur qui affiche de courtes informations telles qu\'un libellé, un mot-clé ou une catégorie. Un Text area (ou zone de texte) est un élément d\'interface utilisateur qui permet de saisir, de modifier ou de sélectionner des blocs de données textuelles plus longs, tels que des commentaires, des messages ou des descriptions ; en s\'étendant verticalement et en offrant plus d\'espace pour la saisie de texte. + 20 caractères en trop.]]> + description détaillée de votre demande.]]> Un Text input (ou champ de saisie de texte) est un élément d\'interface utilisateur qui permet de saisir, de modifier ou de sélectionner une seule ligne de données textuelles, telles que des noms, des adresses électroniques ou des requêtes de recherche. Description du contenu de l\'action de fin + vide.]]> + identifiant client sur votre dernière facture.]]> L\'App bar (ou barre d\'application) est un composant situé en haut de l\'écran qui affiche le titre et donne accès aux actions principales et aux éléments de navigation. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7cf1b52ccf..65d61d3cd1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -178,6 +178,10 @@ Action link Action link position Bullet %d + declined. Please check your payment details or available balance and try again, or update your payment details.]]> + declined.]]> + available balance before retrying.]]> + payment details if needed.]]> Inline alert Inline alert is a lightweight UI element, placed in the content flow, that displays information, system feedback, status changes throughout short, prominent, persistent and non actionable communication. @@ -215,6 +219,9 @@ Unordered asset Unordered asset brand color Level count + declined.]]> + available balance before retrying.]]> + payment details if needed.]]> Button @@ -230,6 +237,7 @@ Checkbox %s checkbox content description Checkbox item + least one service to continue.]]> Indeterminate checkbox %s indeterminate checkbox content description Indeterminate checkbox item @@ -288,11 +296,15 @@ Lock icon Password Text obfuscation mode + empty.]]> + 8 and 20 characters long.]]> PIN code input PIN code input is a UI element that allows to capture short, fixed-length numeric codes, typically for authentication or confirmation purposes, such as a four, six or eight-digit personal identification number (PIN). Length + Verification failed. Check and enter the correct code.]]> + one-time code sent to your device.]]> Progress indicator @@ -315,6 +327,7 @@ Radio button item Extra label Radio button %s content description + one contact method to proceed.]]> Switch @@ -322,6 +335,7 @@ Switch Switch content description Switch item + automatic payments to activate this offer.]]> Tag @@ -335,6 +349,8 @@ Text area Text area is a UI element that allows to type, edit, or select longer blocks of textual data, such as comments, messages or descriptions; by expanding vertically and offering more space to input text. Auto-resize + 20 characters too many.]]> + detailed description of your request.]]> Text input @@ -344,6 +360,8 @@ Trailing action content description Suffix Helper link + empty.]]> + customer ID on your latest invoice.]]> Top app bar From 21b9ff3a2cf5c2ebec42a7a88b2e89a655e97682 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Mon, 22 Jun 2026 11:50:43 +0200 Subject: [PATCH 08/10] Minor fixes --- .../com/orange/ouds/app/ui/components/ComponentCode.kt | 6 +++--- .../app/ui/components/bulletlist/BulletListDemoScreen.kt | 4 +--- .../app/ui/components/checkbox/CheckboxItemDemoScreen.kt | 4 ++-- .../components/radiobutton/RadioButtonItemDemoScreen.kt | 8 ++++---- .../ui/components/radiobutton/RadioButtonItemDemoState.kt | 4 ++-- .../ouds/app/ui/components/switch/SwitchItemDemoScreen.kt | 2 +- .../app/ui/components/textinput/TextInputDemoScreen.kt | 7 +++---- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt index 7c1fa06533..c0e05c34a8 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/ComponentCode.kt @@ -93,9 +93,9 @@ fun FunctionCall.Builder.tintedArgument(value: Boolean) = typedArgument(Argument fun FunctionCall.Builder.errorArgument(message: String, annotatedMessage: Boolean = false) { constructorCallArgument(Argument.Error) { if (annotatedMessage) { - annotatedStringArgument(Argument.ErrorMessage) + annotatedStringArgument(Argument.Message) } else { - typedArgument(Argument.ErrorMessage, message) + typedArgument(Argument.Message, message) } } } @@ -129,7 +129,7 @@ private object Argument { const val Content = "content" const val Enabled = "enabled" const val Error = "error" - const val ErrorMessage = "message" + const val Message = "message" const val HelperText = "helperText" const val Id = "id" const val Label = "label" diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt index 09b3186877..ac104b28d8 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt @@ -287,9 +287,7 @@ private fun OudsBulletListBuilder.bulletListDemoItem(state: BulletListDemoState, with(state) { if (annotatedText) { val annotatedLabel = buildOudsAnnotatedBulletListLabel { - if (labelHtml.isNotEmpty()) { - appendHtml(labelHtml) - } + appendHtml(labelHtml) } item(label = annotatedLabel, builder = builder) } else { diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt index cfbd6ba4e0..cbfc5c3797 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/checkbox/CheckboxItemDemoScreen.kt @@ -73,7 +73,7 @@ private fun CheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = controlItemIcon(this), + icon = controlItemIcon(state = this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, @@ -108,7 +108,7 @@ private fun IndeterminateCheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = controlItemIcon(this), + icon = controlItemIcon(state = this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt index 13e2fb1477..e53e3fc9e7 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoScreen.kt @@ -85,8 +85,8 @@ private fun RadioButtonItemDemoContent(state: RadioButtonItemDemoState) { Modifier.padding(horizontal = OudsTheme.grids.margin) }.selectableGroup() ) { - RadioButtonItemDemoState.values.forEachIndexed { index, radioButtonValue -> - val isLastItem = index == RadioButtonItemDemoState.values.lastIndex + RadioButtonItemDemoState.Values.forEachIndexed { index, radioButtonValue -> + val isLastItem = index == RadioButtonItemDemoState.Values.lastIndex OudsRadioButtonItem( selected = radioButtonValue == selectedValue, onClick = { selectedValue = radioButtonValue }, @@ -114,13 +114,13 @@ private fun RadioButtonItemDemoContent(state: RadioButtonItemDemoState) { private fun Code.Builder.radioButtonItemDemoCodeSnippet(state: RadioButtonItemDemoState, themeDrawableResources: ThemeDrawableResources) { with(state) { - RadioButtonItemDemoState.values.forEachIndexed { index, value -> + RadioButtonItemDemoState.Values.forEachIndexed { index, value -> functionCall("OudsRadioButtonItem") { typedArgument("selected", selectedValue == value) onClickArgument { comment("Change selection") } - controlItemArguments(state, themeDrawableResources, index == RadioButtonItemDemoState.values.lastIndex) + controlItemArguments(state, themeDrawableResources, index == RadioButtonItemDemoState.Values.lastIndex) if (!extraLabel.isNullOrBlank()) typedArgument("extraLabel", extraLabel) if (outlined) typedArgument("outlined", outlined) } diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt index f9d02c5665..f99a6f7b5f 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/radiobutton/RadioButtonItemDemoState.kt @@ -25,7 +25,7 @@ import com.orange.ouds.app.ui.components.controlitem.ControlItemDemoState @Composable fun rememberRadioButtonItemDemoState( - selectedValue: Int = RadioButtonItemDemoState.values.first(), + selectedValue: Int = RadioButtonItemDemoState.Values.first(), icon: ControlItemDemoState.Icon = ControlItemDemoState.Icon.None, edgeToEdge: Boolean = true, divider: Boolean = false, @@ -96,7 +96,7 @@ class RadioButtonItemDemoState( ) : ControlItemDemoState(icon, edgeToEdge, divider, reversed, enabled, readOnly, error, errorMessage, label, description, constrainedMaxWidth, annotatedText) { companion object { - val values = listOf(1, 2) + val Values = listOf(1, 2) val Saver = listSaver( save = { state -> with(state) { diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt index 3d93acd60a..6167c055c7 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/switch/SwitchItemDemoScreen.kt @@ -53,7 +53,7 @@ private fun SwitchItemDemoContent(state: SwitchItemDemoState) { label = label, onCheckedChange = { checked = it }, description = description, - icon = controlItemIcon(this), + icon = controlItemIcon(state = this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, diff --git a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt index 984015aabe..bda783508d 100644 --- a/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt +++ b/app/src/main/java/com/orange/ouds/app/ui/components/textinput/TextInputDemoScreen.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import com.orange.ouds.app.R import com.orange.ouds.app.ui.components.Component -import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.components.constrainedMaxWidthArgument import com.orange.ouds.app.ui.components.contentDescriptionArgument import com.orange.ouds.app.ui.components.enabledArgument @@ -34,6 +33,7 @@ import com.orange.ouds.app.ui.components.readOnlyArgument import com.orange.ouds.app.ui.utilities.Code import com.orange.ouds.app.ui.utilities.LocalThemeDrawableResources import com.orange.ouds.app.ui.utilities.ThemeDrawableResources +import com.orange.ouds.app.ui.utilities.appendHtml import com.orange.ouds.app.ui.utilities.composable.AppPreview import com.orange.ouds.app.ui.utilities.composable.CustomizationFilterChips import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem @@ -48,7 +48,6 @@ import com.orange.ouds.core.component.OudsTextInputTrailingIconButton import com.orange.ouds.core.component.common.OudsError import com.orange.ouds.core.component.common.text.buildOudsAnnotatedErrorMessage import com.orange.ouds.core.component.common.text.buildOudsAnnotatedHelperText -import com.orange.ouds.core.component.common.text.withStrong import com.orange.ouds.theme.OudsVersion @Composable @@ -117,7 +116,7 @@ private fun TextInputDemoBottomSheetContent(state: TextInputDemoState) { value = errorMessage, onValueChange = { value -> errorMessage = value }, enabled = errorMessageTextInputEnabled, - helperText = stringResource(R.string.app_components_common_errorMessage_tech) + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, @@ -149,7 +148,7 @@ private fun TextInputDemoBottomSheetContent(state: TextInputDemoState) { value = helperText, onValueChange = { value -> helperText = value }, enabled = helperTextTextInputEnabled, - helperText = stringResource(R.string.app_components_common_errorMessage_tech) + helperText = stringResource(id = R.string.app_components_common_annotatedTextHelperText_tech) ) CustomizationTextInput( applyTopPadding = true, From 619aad9ab6184ebb883d361df983499b2c8337ad Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 1 Jul 2026 16:07:19 +0200 Subject: [PATCH 09/10] Review: Fix a bug in alert message where the title and the icon are not centered vertically when bullet list is empty --- .../orange/ouds/core/component/OudsAlertMessage.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt b/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt index 111109a9f7..ba09247e16 100644 --- a/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt +++ b/core/src/main/java/com/orange/ouds/core/component/OudsAlertMessage.kt @@ -263,13 +263,16 @@ private fun OudsAlertMessage( } else if (!description.isNullOrBlank()) { Text(modifier = descriptionModifier, text = description, color = descriptionColor, style = descriptionStyle) } - annotatedBulletList.orElse { bulletList }?.let { list -> - Column(verticalArrangement = Arrangement.spacedBy(spaceRowGapBullet.value)) { - list.forEach { label -> - if (label.isNotBlank()) OudsAlertMessageBulletListItem(label = label, color = status.contentColor) + annotatedBulletList.orElse { bulletList } + ?.filter { it.isNotBlank() } + ?.takeIf { it.isNotEmpty() } + ?.let { list -> + Column(verticalArrangement = Arrangement.spacedBy(spaceRowGapBullet.value)) { + list.forEach { label -> + OudsAlertMessageBulletListItem(label = label, color = status.contentColor) + } } } - } } if (hasActionLink && actionLink.position == OudsAlertMessageActionLinkPosition.Bottom) { actionLink.Content(modifier = Modifier.padding(top = spaceRowGapAction.value)) From 63778e35f7ba4ead396302ece503c34c60da5772 Mon Sep 17 00:00:00 2001 From: Florent Maitre Date: Wed, 1 Jul 2026 16:16:46 +0200 Subject: [PATCH 10/10] Review: Update wording --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 65d61d3cd1..5b6b68df17 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -123,8 +123,8 @@ Line height: %s sp - Annotated text - This parameter supports annotated text. + Annotated text example + "Supports annotated text. Use switch below for example. Appearance Color Constrained max width