Skip to content

Commit

Permalink
Add input phone number component
Browse files Browse the repository at this point in the history
  • Loading branch information
msasikanth committed Sep 19, 2023
1 parent d60fe7e commit f816ea3
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 1 deletion.
2 changes: 2 additions & 0 deletions common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.hisp.dhis.common.screens.InputLongTextScreen
import org.hisp.dhis.common.screens.InputNegativeIntegerScreen
import org.hisp.dhis.common.screens.InputNumberScreen
import org.hisp.dhis.common.screens.InputPercentageScreen
import org.hisp.dhis.common.screens.InputPhoneNumberScreen
import org.hisp.dhis.common.screens.InputPositiveIntegerOrZeroScreen
import org.hisp.dhis.common.screens.InputPositiveIntegerScreen
import org.hisp.dhis.common.screens.InputRadioButtonScreen
Expand Down Expand Up @@ -139,6 +140,7 @@ fun Main() {
Components.BADGES -> BadgesScreen()
Components.SWITCH -> SwitchScreen()
Components.INPUT_RADIO_BUTTON -> InputRadioButtonScreen()
Components.INPUT_PHONE_NUMBER -> InputPhoneNumberScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ enum class Components(val label: String) {
ICON_CARDS("Icon Cards"),
INPUT_RADIO_BUTTON("Input Radio Button"),
SWITCH("Switch"),
INPUT_PHONE_NUMBER("Input Phone Number"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.hisp.dhis.common.screens

import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer
import org.hisp.dhis.mobile.ui.designsystem.component.Description
import org.hisp.dhis.mobile.ui.designsystem.component.InputPhoneNumber
import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing
import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor

@Composable
fun InputPhoneNumberScreen() {
ColumnComponentContainer(
title = "Input Phone Number",
) {
var inputValue1 by rememberSaveable { mutableStateOf("") }
Description("Default Input Phone Number", textColor = TextColor.OnSurfaceVariant)
InputPhoneNumber(
title = "Label",
inputText = inputValue1,
onValueChanged = {
if (it != null) {
inputValue1 = it
}
},
onCallActionClicked = {
// no-op
},
)
Spacer(Modifier.size(Spacing.Spacing18))

var inputValue2 by rememberSaveable { mutableStateOf("") }
Description("Disabled Input Phone Number Without Phone Number", textColor = TextColor.OnSurfaceVariant)
InputPhoneNumber(
title = "Label",
inputText = inputValue2,
state = InputShellState.DISABLED,
onValueChanged = {
if (it != null) {
inputValue2 = it
}
},
onCallActionClicked = {
// no-op
},
)
Spacer(Modifier.size(Spacing.Spacing18))

var inputValue3 by rememberSaveable { mutableStateOf("1111111111") }
Description("Disabled Input Phone Number With Phone Number", textColor = TextColor.OnSurfaceVariant)
InputPhoneNumber(
title = "Label",
inputText = inputValue3,
state = InputShellState.DISABLED,
onValueChanged = {
if (it != null) {
inputValue3 = it
}
},
onCallActionClicked = {
// no-op
},
)
Spacer(Modifier.size(Spacing.Spacing18))

var inputValue4 by rememberSaveable { mutableStateOf("") }
Description("Error Input Phone Number", textColor = TextColor.OnSurfaceVariant)
InputPhoneNumber(
title = "Label",
inputText = inputValue4,
state = InputShellState.ERROR,
isRequiredField = true,
onValueChanged = {
if (it != null) {
inputValue4 = it
}
},
onCallActionClicked = {
// no-op
},
)
Spacer(Modifier.size(Spacing.Spacing18))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import org.hisp.dhis.mobile.ui.designsystem.component.internal.RegExValidations
import org.hisp.dhis.mobile.ui.designsystem.resource.provideDHIS2Icon
import org.hisp.dhis.mobile.ui.designsystem.resource.provideStringResource

@Composable
fun InputPhoneNumber(
title: String,
onCallActionClicked: () -> Unit,
modifier: Modifier = Modifier,
characterLimit: Int = 10,
state: InputShellState = InputShellState.UNFOCUSED,
legendData: LegendData? = null,
inputText: String? = null,
isRequiredField: Boolean = false,
onNextClicked: (() -> Unit)? = null,
onValueChanged: ((String?) -> Unit)? = null,
imeAction: ImeAction = ImeAction.Next,
notation: RegExValidations = RegExValidations.ONLY_INTEGERS,
) {
val hasPhoneNumber = inputText?.length == characterLimit
val supportingText = if (state == InputShellState.ERROR) {
listOf(
SupportingTextData(
text = provideStringResource("enter_phone_number"),
state = SupportingTextState.ERROR,
),
)
} else {
emptyList()
}

BasicTextInput(
title = title,
state = state,
supportingText = supportingText,
legendData = legendData,
inputText = inputText,
isRequiredField = isRequiredField,
onNextClicked = onNextClicked,
onValueChanged = {
if ((it?.length ?: 0) <= characterLimit) {
onValueChanged?.invoke(it)
} else {
// no-op
}
},
keyboardOptions = KeyboardOptions(imeAction = imeAction, keyboardType = KeyboardType.Number),
allowedCharacters = notation.regex,
modifier = modifier,
testTag = "PHONE_NUMBER",
actionButton = {
SquareIconButton(
modifier = Modifier.testTag("CALL_PHONE_NUMBER_BUTTON"),
enabled = hasPhoneNumber,
icon = {
Icon(
painter = provideDHIS2Icon("dhis2_phone_positive"),
contentDescription = null,
)
},
onClick = onCallActionClicked,
)
},
)
}
3 changes: 2 additions & 1 deletion designsystem/src/commonMain/resources/values/strings_en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
<string name="show_fields">Show fields</string>
<string name="hide_fields">Hide fields</string>
<string name="action_next">Next</string>
</resources>
<string name="enter_phone_number">Enter phone number</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import org.junit.Rule
import org.junit.Test

class InputPhoneNumberTest {

@get:Rule
val rule = createComposeRule()

@Test
fun shouldAllowDigitsInputOnly() {
rule.setContent {
var inputValue by remember { mutableStateOf("") }

InputPhoneNumber(
title = "Phone Number",
inputText = inputValue,
onValueChanged = {
if (it != null) {
inputValue = it
}
},
onCallActionClicked = {
// no-op
},
)
}
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").assertTextEquals("")
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").performTextInput("1111")
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").assertTextEquals("1111")
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").performTextInput("1111a")
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").assertTextEquals("1111")
}

@Test
fun shouldEnableCallActionButtonAfterInputTextReachesCharacterLimit() {
rule.setContent {
var inputValue by remember { mutableStateOf("") }

InputPhoneNumber(
title = "Phone Number",
inputText = inputValue,
onValueChanged = {
if (it != null) {
inputValue = it
}
},
onCallActionClicked = {
// no-op
},
)
}
rule.onNodeWithTag("CALL_PHONE_NUMBER_BUTTON").assertIsNotEnabled()
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").performTextInput("1111111111")
rule.onNodeWithTag("CALL_PHONE_NUMBER_BUTTON").assertIsEnabled()
}

@Test
fun shouldDisplaySupportTextIfInputStateIsError() {
rule.setContent {
InputPhoneNumber(
title = "Phone Number",
inputText = "",
state = InputShellState.UNFOCUSED,
onValueChanged = {
// no-op
},
onCallActionClicked = {
// no-op
},
)
}
rule.onNodeWithTag("INPUT_PHONE_NUMBER_SUPPORTING_TEXT").assertDoesNotExist()

rule.setContent {
InputPhoneNumber(
title = "Phone Number",
inputText = "",
onValueChanged = {
// no-op
},
onCallActionClicked = {
// no-op
},
)
}
rule.onNodeWithTag("INPUT_PHONE_NUMBER_SUPPORTING_TEXT").assertExists()
}

@Test
fun shouldClearPhoneNumberWhenClearButtonIsClicked() {
rule.setContent {
var inputValue by remember { mutableStateOf("1234") }

InputPhoneNumber(
title = "Phone Number",
inputText = inputValue,
onValueChanged = {
if (it != null) inputValue = it
},
onCallActionClicked = {
// no-op
},
)
}
rule.onNodeWithTag("INPUT_PHONE_NUMBER_RESET_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_PHONE_NUMBER_RESET_BUTTON").performClick()
rule.onNodeWithTag("INPUT_PHONE_NUMBER_FIELD").assertTextEquals("")
rule.onNodeWithTag("INPUT_PHONE_NUMBER_RESET_BUTTON").assertDoesNotExist()
}
}

0 comments on commit f816ea3

Please sign in to comment.