Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0c3d2ae
Add PIN code input tokens
florentmaitre Mar 12, 2026
6f9c9fd
Claude version of PIN code input
florentmaitre Mar 12, 2026
b38f32a
Use Color.Transparent instead of null when there is no background col…
florentmaitre Mar 17, 2026
a6e00fe
Add OudsDigitInput
florentmaitre Mar 16, 2026
a74a653
Add OudsPinCodeInput draft
florentmaitre Mar 18, 2026
09db097
Remove text field from OudsDigitInput
florentmaitre Mar 18, 2026
9f09b43
Add focus behavior on PIN code input
florentmaitre Mar 20, 2026
c22f437
Fix various bugs in demo screen
florentmaitre Mar 20, 2026
157a0d4
Fix digit input width
florentmaitre Mar 24, 2026
563a442
Add specific values for 8-digit PIN code input
florentmaitre Mar 24, 2026
e369afd
Add cursor blink
florentmaitre Mar 24, 2026
1f5b5e9
Add snapshot tests
florentmaitre Mar 24, 2026
6aef9e8
Add samples
florentmaitre Mar 24, 2026
ca96ac5
Add instrumented tests
florentmaitre Mar 25, 2026
f1eceeb
Add paste tooltip
florentmaitre Mar 25, 2026
0fb64b4
Remove keyboardOptions parameter
florentmaitre Mar 25, 2026
c8e57db
Add KDoc
florentmaitre Mar 25, 2026
3b2745c
Refactor code for clarity
florentmaitre Mar 25, 2026
040a264
Minor fixes
florentmaitre Mar 26, 2026
fb56567
Fix snippet error for checkbox and radio button
florentmaitre Mar 26, 2026
5b437f7
Refactor code in text input
florentmaitre Mar 26, 2026
97cece5
Update snapshots
florentmaitre Mar 26, 2026
344bd18
Review: Fix typo
florentmaitre Apr 3, 2026
34c66ee
Review: Replace pin with PIN
florentmaitre Apr 3, 2026
453a6df
Review: Add comma after "By default" in KDoc
florentmaitre Apr 3, 2026
d1330d7
Review: Add hideFromAccessibility on individual digits
florentmaitre Apr 3, 2026
5e2f40d
Review: Apply specific rules for small devices
florentmaitre Apr 3, 2026
3efcf60
Review: Rename totalSpace to totalHorizontalSpace
florentmaitre Apr 3, 2026
7049216
Review: Add a TODO to remember replacing the Material tooltip with th…
florentmaitre Apr 3, 2026
0ce7367
Review: Filter out non digits
florentmaitre Apr 7, 2026
167eedc
Review: Clarify text insertion and deletion in input transformation
florentmaitre Apr 7, 2026
efe8d32
Review: Rename instrumented test
florentmaitre Apr 7, 2026
524e0e1
Review: Update Arabic translations
florentmaitre Apr 7, 2026
4e89e73
Fix the width required to apply small device specific rules
florentmaitre Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.orange.ouds.app.ui.components.floatingactionbutton.FloatingActionButt
import com.orange.ouds.app.ui.components.link.LinkDemoScreen
import com.orange.ouds.app.ui.components.navigationbar.NavigationBarDemoScreen
import com.orange.ouds.app.ui.components.passwordinput.PasswordInputDemoScreen
import com.orange.ouds.app.ui.components.pincodeinput.PinCodeInputDemoScreen
import com.orange.ouds.app.ui.components.radiobutton.RadioButtonDemoScreen
import com.orange.ouds.app.ui.components.radiobutton.RadioButtonItemDemoScreen
import com.orange.ouds.app.ui.components.switch.SwitchDemoScreen
Expand Down Expand Up @@ -142,6 +143,13 @@ sealed class Component(
demoScreen = { PasswordInputDemoScreen() }
)

data object PinCodeInput : Component(
R.string.app_components_pinCodeInput_tech,
R.string.app_components_pinCodeInput_description_text,
{ PinCodeInputIllustration() },
demoScreen = { PinCodeInputDemoScreen() }
)

data object RadioButton : Component(
R.string.app_components_radioButton_tech,
R.string.app_components_radioButton_description_text,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.toArgb
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

fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.() -> Unit) {
if (onColoredBox) {
Expand All @@ -33,9 +34,9 @@ fun Code.Builder.coloredBoxCall(onColoredBox: Boolean, content: Code.Builder.()
}
}

fun FunctionCall.Builder.painterArgument(@DrawableRes id: Int) {
fun FunctionCall.Builder.painterArgument(@DrawableRes resId: Int) {
functionCallArgument(Argument.Painter, "painterResource") {
typedArgument(Argument.Id, id)
typedArgument(Argument.Id, resId)
}
}

Expand All @@ -46,24 +47,34 @@ fun FunctionCall.Builder.colorArgument(name: String, color: Color) {
}
}

fun FunctionCall.Builder.stringArgument(name: String, @StringRes id: Int) = formattableArgument(name) { "\"${it.getString(id)}\"" }
fun FunctionCall.Builder.stringArgument(name: String, @StringRes resId: Int) = formattableArgument(name) { "\"${it.getString(resId)}\"" }

fun FunctionCall.Builder.constrainedMaxWidthArgument(value: Boolean) = typedArgument(Argument.ConstrainedMaxWidth, value)

fun FunctionCall.Builder.contentDescriptionArgument(@StringRes id: Int) = stringArgument(Argument.ContentDescription, id)
fun FunctionCall.Builder.contentDescriptionArgument(@StringRes id: Int, vararg formatArgs: Any) =
stringResourceArgument(Argument.ContentDescription, id, *formatArgs)
fun FunctionCall.Builder.contentDescriptionArgument(@StringRes resId: Int) = stringArgument(Argument.ContentDescription, resId)
fun FunctionCall.Builder.contentDescriptionArgument(@StringRes resId: Int, vararg formatArgs: Any) =
stringResourceArgument(Argument.ContentDescription, resId, *formatArgs)

fun FunctionCall.Builder.contentDescriptionArgument(@PluralsRes id: Int, count: Int, vararg formatArgs: Any) =
pluralStringResourceArgument(Argument.ContentDescription, id, count, *formatArgs)
fun FunctionCall.Builder.contentDescriptionArgument(@PluralsRes resId: Int, count: Int, vararg formatArgs: Any) =
pluralStringResourceArgument(Argument.ContentDescription, resId, count, *formatArgs)


fun FunctionCall.Builder.enabledArgument(value: Boolean) = typedArgument(Argument.Enabled, value)

fun FunctionCall.Builder.errorArgument(value: Boolean) = typedArgument(Argument.Error, value)
fun FunctionCall.Builder.errorArgument(message: String) {
constructorCallArgument<OudsError>(Argument.Error) {
typedArgument("message", message)
}
}

fun FunctionCall.Builder.errorArgument(@StringRes messageResId: Int) {
constructorCallArgument<OudsError>(Argument.Error) {
stringResourceArgument("message", messageResId)
}
}

fun FunctionCall.Builder.labelArgument(label: String?) = typedArgument(Argument.Label, label)
fun FunctionCall.Builder.labelArgument(@StringRes id: Int) = stringResourceArgument(Argument.Label, id)
fun FunctionCall.Builder.labelArgument(@StringRes resId: Int) = stringResourceArgument(Argument.Label, resId)

fun FunctionCall.Builder.onClickArgument(init: Code.Builder.() -> Unit = {}) = lambdaArgument(Argument.OnClick, init)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalCursorBlinkEnabled
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Density
Expand Down Expand Up @@ -54,6 +56,8 @@ import com.orange.ouds.core.component.OudsNavigationBar
import com.orange.ouds.core.component.OudsNavigationBarItem
import com.orange.ouds.core.component.OudsNavigationBarItemIcon
import com.orange.ouds.core.component.OudsPasswordInput
import com.orange.ouds.core.component.OudsPinCodeInput
import com.orange.ouds.core.component.OudsPinCodeInputLength
import com.orange.ouds.core.component.OudsRadioButton
import com.orange.ouds.core.component.OudsSwitch
import com.orange.ouds.core.component.OudsTag
Expand Down Expand Up @@ -201,6 +205,21 @@ fun PasswordInputIllustration() = ComponentIllustration {
)
}

@Composable
fun PinCodeInputIllustration() = ComponentIllustration {
CompositionLocalProvider(
LocalInspectionMode provides true,
LocalCursorBlinkEnabled provides false
) {
OudsPinCodeInput(
modifier = Modifier.padding(horizontal = 12.dp),
value = "12",
onValueChange = {},
length = OudsPinCodeInputLength.Four
)
}
}

@Composable
fun RadioButtonIllustration() = ComponentIllustration {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private fun Code.Builder.checkboxDemoCodeSnippet(state: CheckboxDemoState, indet
}
enabledArgument(enabled)
readOnlyArgument(readOnly)
errorArgument(error)
if (error) errorArgument(R.string.app_components_common_error_a11y)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
import com.orange.ouds.app.R
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.labelArgument
import com.orange.ouds.app.ui.components.painterArgument
import com.orange.ouds.app.ui.components.readOnlyArgument
Expand All @@ -25,7 +26,6 @@ import com.orange.ouds.app.ui.utilities.ThemeDrawableResources
import com.orange.ouds.app.ui.utilities.composable.CustomizationSwitchItem
import com.orange.ouds.app.ui.utilities.composable.CustomizationTextInput
import com.orange.ouds.core.component.OudsControlItemIcon
import com.orange.ouds.core.component.common.OudsError

data class ControlItemCustomization(val index: Int, val content: @Composable () -> Unit)

Expand Down Expand Up @@ -195,10 +195,6 @@ fun FunctionCall.Builder.controlItemArguments(state: ControlItemDemoState, theme
if (reversed) typedArgument("reversed", reversed)
if (!enabled) enabledArgument(enabled)
if (readOnly) readOnlyArgument(readOnly)
if (error) {
constructorCallArgument<OudsError>("error") {
typedArgument("message", if (hasErrorMessage) errorMessage else "")
}
}
if (error) errorArgument(if (hasErrorMessage) errorMessage else "")
if (constrainedMaxWidth) constrainedMaxWidthArgument(constrainedMaxWidth)
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ private fun Code.Builder.navigationBarDemoCodeSnippet(
lambdaArgument("onClick", {})
labelArgument(label)
functionCallArgument("icon", OudsNavigationBarItemIcon::class.simpleName.orEmpty()) {
painterArgument(id = item.iconResourceProvider.getResource(themeDrawableResources))
painterArgument(resId = item.iconResourceProvider.getResource(themeDrawableResources))
}
if (isLastItem && lastItemBadge != NavigationBarDemoState.ItemBadge.None) {
functionCallArgument("badge", OudsNavigationBarItemBadge::class.simpleName.orEmpty()) {
when (lastItemBadge) {
NavigationBarDemoState.ItemBadge.None -> {}
NavigationBarDemoState.ItemBadge.Standard -> contentDescriptionArgument(id = R.string.app_components_common_unreadNotificationsBadge_a11y)
NavigationBarDemoState.ItemBadge.Standard -> contentDescriptionArgument(resId = R.string.app_components_common_unreadNotificationsBadge_a11y)
NavigationBarDemoState.ItemBadge.Count -> contentDescriptionArgument(
id = R.plurals.app_components_common_unreadMessageCountBadge_a11y,
resId = R.plurals.app_components_common_unreadMessageCountBadge_a11y,
count = ItemBadgeCount,
ItemBadgeCount
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.orange.ouds.app.R
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.labelArgument
import com.orange.ouds.app.ui.components.readOnlyArgument
import com.orange.ouds.app.ui.utilities.Code
Expand Down Expand Up @@ -160,9 +161,6 @@ private fun Code.Builder.passwordInputDemoCodeSnippet(state: PasswordInputDemoSt
with(state) {
functionCall("OudsPasswordInput") {
functionCallArgument("state", "rememberOudsPasswordInputState")
lambdaArgument("onValueChange") {
comment("Update value")
}
if (label.isNotEmpty()) labelArgument(label)
if (placeholder.isNotEmpty()) typedArgument("placeholder", placeholder)
typedArgument("outlined", outlined)
Expand All @@ -174,11 +172,7 @@ private fun Code.Builder.passwordInputDemoCodeSnippet(state: PasswordInputDemoSt
}
if (!enabled) enabledArgument(false)
if (readOnly) readOnlyArgument(true)
if (error) {
constructorCallArgument<OudsError>("error") {
typedArgument("message", errorMessage)
}
}
if (error) errorArgument(errorMessage)
if (prefix.isNotEmpty()) typedArgument("prefix", prefix)
if (helperText.isNotEmpty()) typedArgument("helperText", helperText)
if (constrainedMaxWidth) constrainedMaxWidthArgument(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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.components.pincodeinput

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.utilities.Code
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.composable.DemoScreen
import com.orange.ouds.app.ui.utilities.toSentenceCase
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.theme.OudsVersion

@Composable
fun PinCodeInputDemoScreen() {
val state = rememberPinCodeInputDemoState()
DemoScreen(
description = stringResource(id = Component.PinCodeInput.descriptionRes),
bottomSheetContent = { PinCodeInputDemoBottomSheetContent(state = state) },
codeSnippet = { pinCodeInputDemoCodeSnippet(state = state) },
demoContent = { PinCodeInputDemoContent(state = state) },
version = OudsVersion.Component.PinCodeInput
)
}

@Composable
private fun PinCodeInputDemoBottomSheetContent(state: PinCodeInputDemoState) {
with(state) {
CustomizationFilterChips(
applyTopPadding = false,
label = stringResource(R.string.app_components_pinCodeInput_length_tech),
chipLabels = OudsPinCodeInputLength.entries.map { it.name.toSentenceCase() },
selectedChipIndex = OudsPinCodeInputLength.entries.indexOf(length),
onSelectionChange = { index -> length = OudsPinCodeInputLength.entries[index] }
)
CustomizationSwitchItem(
label = stringResource(R.string.app_components_common_outlined_tech),
checked = outlined,
onCheckedChange = { outlined = it }
)
CustomizationSwitchItem(
label = stringResource(R.string.app_components_common_error_tech),
checked = error,
onCheckedChange = { error = it }
)
CustomizationTextInput(
applyTopPadding = true,
label = stringResource(R.string.app_components_common_errorMessage_tech),
value = errorMessage,
onValueChange = { value -> errorMessage = value },
enabled = errorMessageTextInputEnabled
)
CustomizationTextInput(
applyTopPadding = true,
label = stringResource(R.string.app_components_common_helperText_tech),
value = helperText,
onValueChange = { value -> helperText = value }
)
}
}

@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() }
)
}
}

private fun Code.Builder.pinCodeInputDemoCodeSnippet(state: PinCodeInputDemoState) {
with(state) {
functionCall("OudsPinCodeInput") {
typedArgument("value", value)
lambdaArgument("onValueChange") {
comment("Update value")
}
typedArgument("length", length)
if (outlined) typedArgument("outlined", outlined)
if (error) errorArgument(errorMessage)
if (helperText.isNotEmpty()) typedArgument("helperText", helperText)
}
}

}
Loading