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..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 @@ -22,6 +22,9 @@ 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.OudsAnnotatedHelperText +import com.orange.ouds.core.component.common.text.OudsAnnotatedString fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.() -> Unit) { if (onColoredBox) { @@ -35,6 +38,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 +90,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(Argument.Message) + } else { + typedArgument(Argument.Message, message) + } } } @@ -96,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.isNotBlank()) { + typedArgument(Argument.HelperText, helperText) + } +} + private object Argument { const val Color = "color" @@ -104,6 +129,8 @@ private object Argument { const val Content = "content" const val Enabled = "enabled" const val Error = "error" + const val Message = "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/alert/AlertMessageDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/alert/AlertMessageDemoScreen.kt index 29c9cc9608..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 @@ -22,13 +22,14 @@ 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 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 @@ -44,6 +45,10 @@ 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.buildOudsAnnotatedAlertMessageBulletListLabel +import com.orange.ouds.core.component.common.text.buildOudsAnnotatedAlertMessageDescription import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -123,7 +128,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 +150,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 +178,59 @@ 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 alertMessageStatus = 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 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 { + 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 { + appendHtml(annotatedBullet1Html) + }, + buildOudsAnnotatedAlertMessageBulletListLabel { + appendHtml(annotatedBullet2Html) + }, + buildOudsAnnotatedAlertMessageBulletListLabel { + appendHtml(annotatedBullet3Html) + } + ) + OudsAlertMessage( + label = label, + description = annotatedDescription, + status = alertMessageStatus, + onClose = onClose, + actionLink = alertMessageActionLink, + bulletList = annotatedBulletList + ) + } else { + OudsAlertMessage( + label = label, + description = description, + status = alertMessageStatus, + onClose = onClose, + actionLink = alertMessageActionLink, + bulletList = bulletList + ) + } } } @@ -209,7 +255,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 +274,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/components/bulletlist/BulletListDemoScreen.kt b/app/src/main/java/com/orange/ouds/app/ui/components/bulletlist/BulletListDemoScreen.kt index b5e130396f..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 @@ -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 @@ -29,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 @@ -44,6 +46,8 @@ 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.buildOudsAnnotatedBulletListLabel import com.orange.ouds.foundation.extensions.toSentenceCase import com.orange.ouds.foundation.extensions.tryOrNull import com.orange.ouds.theme.OudsVersion @@ -113,7 +117,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 +132,28 @@ private fun BulletListDemoBottomSheetContent(state: BulletListDemoState) { @Composable private fun BulletListDemoContent(state: BulletListDemoState) { with(state) { - val builder: OudsBulletListBuilder.() -> Unit = remember(levelCount, label) { + 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 -> { - item(label = label) - item(label = label) - item(label = label) + bulletListDemoItem(this@with, label1Html) + bulletListDemoItem(this@with, label2Html) + bulletListDemoItem(this@with, label3Html) } 2 -> { - item(label = label) { - item(label = label) - item(label = label) + bulletListDemoItem(this@with, label1Html) { + bulletListDemoItem(this@with, label2Html) + bulletListDemoItem(this@with, label3Html) } } else -> { - item(label = label) { - item(label = label) { - item(label = label) + bulletListDemoItem(this@with, label1Html) { + bulletListDemoItem(this@with, label2Html) { + bulletListDemoItem(this@with, label3Html) } } } @@ -155,10 +170,9 @@ private fun BulletListDemoContent(state: BulletListDemoState) { } else { type }, - textStyle = OudsBulletListTextStyle(fontSize, fontWeight) - ) { - builder() - } + textStyle = OudsBulletListTextStyle(fontSize, fontWeight), + builder = builder + ) } } @@ -190,19 +204,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 +226,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 +283,19 @@ private fun getUnorderedAssetClasses() = if (LocalInspectionMode.current) { OudsBulletListUnorderedAsset::class.sealedSubclasses } +private fun OudsBulletListBuilder.bulletListDemoItem(state: BulletListDemoState, labelHtml: String, builder: (OudsBulletListBuilder.() -> Unit)? = null) { + with(state) { + if (annotatedText) { + val annotatedLabel = buildOudsAnnotatedBulletListLabel { + appendHtml(labelHtml) + } + 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 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..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 @@ -18,9 +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.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 @@ -71,13 +73,13 @@ private fun CheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(state = 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 +108,13 @@ private fun IndeterminateCheckboxItemDemoContent(state: CheckboxItemDemoState) { }, label = label, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(state = 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 +129,36 @@ private fun CheckboxItemDemoColumn(edgeToEdge: Boolean, content: @Composable () } } +@Composable +private fun checkboxItemError(state: CheckboxItemDemoState, isLastItem: Boolean): OudsError? { + return controlItemError( + state = state, + isLastItem = isLastItem, + errorMessageHtmlResId = R.string.app_components_checkbox_checkboxItem_annotatedErrorMessage_text + ) +} + 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..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,11 +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.buildOudsAnnotatedErrorMessage data class ControlItemCustomization(val index: Int, val content: @Composable () -> Unit) @@ -48,7 +52,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 +66,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 +78,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 +90,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 +101,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 +112,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 +124,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 +136,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 +149,23 @@ 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 }, + enabled = annotatedTextSwitchEnabled ) } } @@ -157,7 +175,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 +187,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 +198,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 +206,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 +214,23 @@ fun getControlItemIcon(state: ControlItemDemoState): OudsControlItemIcon? { } } +@Composable +fun controlItemError(state: ControlItemDemoState, isLastItem: Boolean, @StringRes errorMessageHtmlResId: Int): OudsError? { + return with(state) { + when { + error && !isLastItem -> OudsError("") + error && !annotatedText -> OudsError(errorMessage) + error && annotatedText -> { + val errorMessageHtml = stringResource(id = errorMessageHtmlResId) + OudsError(buildOudsAnnotatedErrorMessage { + appendHtml(errorMessageHtml) + }) + } + else -> null + } + } +} + fun FunctionCall.Builder.controlItemArguments(state: ControlItemDemoState, themeDrawableResources: ThemeDrawableResources, hasErrorMessage: Boolean = false) = with(state) { labelArgument(label) @@ -208,6 +243,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..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 @@ -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,8 @@ open class ControlItemDemoState( var label: String by mutableStateOf(label) var description: String? by mutableStateOf(description) + var annotatedText: Boolean by mutableStateOf(annotatedText) + val enabledSwitchEnabled: Boolean get() = !error @@ -93,6 +98,9 @@ open class ControlItemDemoState( get() = enabled && !readOnly val errorMessageTextInputEnabled: Boolean + get() = error && !annotatedText + + val annotatedTextSwitchEnabled: Boolean get() = error enum class Icon(@StringRes val labelRes: Int) { 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..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 @@ -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,9 +23,11 @@ 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 +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 @@ -33,6 +36,8 @@ 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.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,67 @@ 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 -> { + 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 { + appendHtml(helperTextHtml) + } + 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 +219,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..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 @@ -12,13 +12,16 @@ 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.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 @@ -26,6 +29,8 @@ 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.foundation.extensions.toSentenceCase import com.orange.ouds.theme.OudsVersion @@ -66,30 +71,66 @@ 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 -> { + 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 { + appendHtml(helperTextHtml) + } + 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 +143,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/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..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 @@ -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,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.OudsError import com.orange.ouds.core.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -85,22 +85,26 @@ 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 }, 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, + errorMessageHtmlResId = R.string.app_components_radioButtonItem_annotatedErrorMessage_text + ), constrainedMaxWidth = constrainedMaxWidth ) } @@ -109,16 +113,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..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, @@ -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,11 +91,12 @@ 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) + val Values = listOf(1, 2) val Saver = listSaver( save = { state -> with(state) { @@ -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..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 @@ -17,16 +17,17 @@ 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.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.theme.OudsTheme import com.orange.ouds.theme.OudsVersion @@ -52,13 +53,17 @@ private fun SwitchItemDemoContent(state: SwitchItemDemoState) { label = label, onCheckedChange = { checked = it }, description = description, - icon = getControlItemIcon(this), + icon = controlItemIcon(state = this), edgeToEdge = edgeToEdge, divider = divider, reversed = reversed, enabled = enabled, readOnly = readOnly, - error = if (error) OudsError(errorMessage) else null, + error = controlItemError( + state = this, + isLastItem = true, + errorMessageHtmlResId = R.string.app_components_switchItem_annotatedErrorMessage_text + ), 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 ) } } 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..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 @@ -12,15 +12,18 @@ 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 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 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 +36,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 +95,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 +114,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 +129,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 -> { + 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 { + appendHtml(helperTextHtml) + } + 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 +209,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..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 @@ -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 @@ -31,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 @@ -43,6 +46,8 @@ 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.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(id = R.string.app_components_common_annotatedTextHelperText_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(id = R.string.app_components_common_annotatedTextHelperText_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 -> { + 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 { + appendHtml(helperTextHtml) + } + 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), 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/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-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 b1f7b472a7..5b6b68df17 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 example + "Supports annotated text. Use switch below for example. Appearance Color Constrained max width @@ -176,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. @@ -213,6 +219,9 @@ Unordered asset Unordered asset brand color Level count + declined.]]> + available balance before retrying.]]> + payment details if needed.]]> Button @@ -228,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 @@ -286,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 @@ -313,6 +327,7 @@ Radio button item Extra label Radio button %s content description + one contact method to proceed.]]> Switch @@ -320,6 +335,7 @@ Switch Switch content description Switch item + automatic payments to activate this offer.]]> Tag @@ -333,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 @@ -342,6 +360,8 @@ Trailing action content description Suffix Helper link + empty.]]> + customer ID on your latest invoice.]]> Top app bar 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..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 @@ -258,18 +258,21 @@ 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 -> - 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))