Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ANDROAPP-5629-mobile-ui-Adapt-Org-Unit-component-for-implementation #100

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -1,12 +1,28 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material.icons.outlined.Cancel
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.input.ImeAction
import org.hisp.dhis.mobile.ui.designsystem.resource.provideDHIS2Icon
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing
import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor

/**
* DHIS2 Input org unit. Wraps DHIS · [BasicTextInput].
Expand All @@ -17,11 +33,9 @@ import org.hisp.dhis.mobile.ui.designsystem.resource.provideDHIS2Icon
* @param legendData manages the legendComponent
* @param inputText manages the value of the text in the input field
* @param isRequiredField controls whether the field is mandatory or not
* @param onNextClicked gives access to the imeAction event
* @param onValueChanged gives access to the onValueChanged event
* @param onFocusChanged gives access to the onFocusChanged returns true if
* item is focused
* @param imeAction controls the imeAction button to be shown
* @param modifier allows a modifier to be passed externally
* @param onOrgUnitActionCLicked callback to when org unit button is clicked
*/
Expand All @@ -33,27 +47,67 @@ fun InputOrgUnit(
legendData: LegendData? = null,
inputText: String? = null,
isRequiredField: Boolean = false,
onNextClicked: (() -> Unit)? = null,
onValueChanged: ((String?) -> Unit)? = null,
onFocusChanged: ((Boolean) -> Unit)? = null,
imeAction: ImeAction = ImeAction.Next,
modifier: Modifier = Modifier,
onOrgUnitActionCLicked: () -> Unit,
) {
BasicTextInput(
title = title,
state = state,
supportingText = supportingText,
legendData = legendData,
inputText = inputText,
val inputValue by remember(inputText) { mutableStateOf(inputText) }

var deleteButtonIsVisible by remember(inputText) { mutableStateOf(!inputText.isNullOrEmpty() && state != InputShellState.DISABLED) }
val focusRequester = remember { FocusRequester() }

val primaryButton:
@Composable()
(() -> Unit)?
if (deleteButtonIsVisible) {
primaryButton = {
IconButton(
modifier = Modifier
.testTag("INPUT_ORG_UNIT_RESET_BUTTON")
.padding(Spacing.Spacing0),
icon = {
Icon(
imageVector = Icons.Outlined.Cancel,
contentDescription = "Reset Button",
)
},
onClick = {
onValueChanged?.invoke("")
deleteButtonIsVisible = false
focusRequester.requestFocus()
},
enabled = state != InputShellState.DISABLED,
)
}
} else {
primaryButton = {
IconButton(
modifier = Modifier
.testTag("INPUT_ORG_UNIT_DROPDOWN_BUTTON")
.padding(Spacing.Spacing0),
icon = {
Icon(
imageVector = Icons.Outlined.ArrowDropDown,
contentDescription = "Dropdown Button",
)
},
onClick = {
onOrgUnitActionCLicked.invoke()
focusRequester.requestFocus()
},
enabled = state != InputShellState.DISABLED,
)
}
}
InputShell(
modifier = modifier
.testTag("INPUT_ORG_UNIT")
.focusRequester(focusRequester),
isRequiredField = isRequiredField,
onNextClicked = onNextClicked,
onValueChanged = onValueChanged,
keyboardOptions = KeyboardOptions(imeAction = imeAction),
modifier = modifier,
testTag = "ORG_UNIT",
onFocusChanged = onFocusChanged,
actionButton = {
title = title,
primaryButton = primaryButton,
secondaryButton = {
SquareIconButton(
modifier = Modifier.testTag("ORG_UNIT_BUTTON"),
enabled = state != InputShellState.DISABLED,
Expand All @@ -63,8 +117,51 @@ fun InputOrgUnit(
contentDescription = "org_unit_icon",
)
},
onClick = onOrgUnitActionCLicked,
onClick = {
onOrgUnitActionCLicked.invoke()
focusRequester.requestFocus()
},
)
},
state = state,
legend = {
legendData?.let {
Legend(legendData, Modifier.testTag("INPUT_ORG_UNIT_LEGEND"))
}
},
supportingText = {
supportingText?.forEach { label ->
SupportingText(
label.text,
label.state,
modifier = Modifier.testTag("INPUT_ORG_UNIT_SUPPORTING_TEXT"),
)
}
},
inputField = {
Box {
Text(
modifier = Modifier.testTag("INPUT_DROPDOWN_TEXT").fillMaxWidth(),
text = inputValue ?: "",
style = MaterialTheme.typography.bodyLarge.copy(
color = if (state != InputShellState.DISABLED) {
TextColor.OnSurface
} else {
TextColor.OnDisabledSurface
},
),
)
Box(
modifier = Modifier
.matchParentSize()
.alpha(0f)
.clickable(onClick = {
onOrgUnitActionCLicked.invoke()
focusRequester.requestFocus()
}),
)
}
},
onFocusChanged = onFocusChanged,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.hasText
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.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor
import org.junit.Rule
import org.junit.Test
Expand All @@ -37,7 +34,7 @@ class InputOrgUnitTest {
}

@Test
fun shouldAllowUserInputWhenEnabled() {
fun shouldDisplayDropdownButtonWhenEmpty() {
rule.setContent {
var inputValue by rememberSaveable { mutableStateOf("") }
InputOrgUnit(
Expand All @@ -52,42 +49,46 @@ class InputOrgUnitTest {
)
}
rule.onNodeWithTag("INPUT_ORG_UNIT").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").performTextInput("PHC fake")
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").assert(hasText("PHC fake"))
rule.onNodeWithTag("ORG_UNIT_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_DROPDOWN_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_DROPDOWN_BUTTON").assertIsEnabled()
}

@Test
fun shouldNotAllowUserInputWhenDisabled() {
fun shouldDisplayResetButtonWhenNotEmpty() {
rule.setContent {
var inputValue by rememberSaveable { mutableStateOf("Sample data") }
InputOrgUnit(
title = "Label",
state = InputShellState.DISABLED,
inputText = inputValue,
onValueChanged = {
if (it != null) {
inputValue = it
}
},
onOrgUnitActionCLicked = {},
)
}
rule.onNodeWithTag("INPUT_ORG_UNIT").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").assertIsNotEnabled()
rule.onNodeWithTag("ORG_UNIT_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").assertIsEnabled()
}

@Test
fun shouldShowResetButtonWhenTextFieldHasContent() {
fun shouldBeClickableIfDisabled() {
rule.setContent {
var inputValue by rememberSaveable { mutableStateOf("") }
InputOrgUnit(
title = "Label",
inputText = inputValue,
onValueChanged = {
if (it != null) {
inputValue = it
}
},
state = InputShellState.DISABLED,
onOrgUnitActionCLicked = {},
)
}
rule.onNodeWithTag("INPUT_ORG_UNIT").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").performTextInput("PHC fake")
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").assertExists()
rule.onNodeWithTag("ORG_UNIT_BUTTON").assertExists()
rule.onNodeWithTag("ORG_UNIT_BUTTON").assertIsNotEnabled()
rule.onNodeWithTag("INPUT_ORG_UNIT_DROPDOWN_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_DROPDOWN_BUTTON").assertIsNotEnabled()
}

@Test
Expand All @@ -109,7 +110,7 @@ class InputOrgUnitTest {
rule.onNodeWithTag("INPUT_ORG_UNIT").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").assertExists()
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").performClick()
rule.onNodeWithTag("INPUT_ORG_UNIT_FIELD").assertTextEquals("")
rule.onNodeWithTag("INPUT_DROPDOWN_TEXT").assertTextEquals("")
rule.onNodeWithTag("INPUT_ORG_UNIT_RESET_BUTTON").assertDoesNotExist()
}

Expand Down
Loading