Skip to content

Commit

Permalink
ANDROAPP-5531-mobile-ui-Create-Input-Percentage-component (#52)
Browse files Browse the repository at this point in the history
* Component implementation

* Add tests for component

* Add tests for component, adjust supporting text for padding values
  • Loading branch information
xavimolloy authored Sep 8, 2023
1 parent a8b30e7 commit 9420dc6
Show file tree
Hide file tree
Showing 7 changed files with 455 additions and 4 deletions.
4 changes: 3 additions & 1 deletion common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.hisp.dhis.common.screens.Components
import org.hisp.dhis.common.screens.FormShellsScreen
import org.hisp.dhis.common.screens.FormsComponentsScreen
import org.hisp.dhis.common.screens.IconButtonScreen
import org.hisp.dhis.common.screens.InputPercentageScreen
import org.hisp.dhis.common.screens.InputScreen
import org.hisp.dhis.common.screens.InputTextScreen
import org.hisp.dhis.common.screens.LegendDescriptionScreen
Expand All @@ -50,7 +51,7 @@ fun App() {

@Composable
fun Main() {
val currentScreen = remember { mutableStateOf(Components.FORM_SHELLS) }
val currentScreen = remember { mutableStateOf(Components.INPUT_PERCENTAGE) }
var expanded by remember { mutableStateOf(false) }

Column(
Expand Down Expand Up @@ -114,6 +115,7 @@ fun Main() {
Components.BOTTOM_SHEET -> BottomSheetScreen()
Components.TAGS -> TagsScreen()
Components.SECTIONS -> SectionScreen()
Components.INPUT_PERCENTAGE -> InputPercentageScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class Components(val label: String) {
LEGEND_DESCRIPTION("Legend description"),
BUTTON_BLOCK("Button block"),
INPUT_TEXT("Input Text"),
INPUT_PERCENTAGE("Input Percentage"),
FORM_SHELLS("Form Shells"),
BOTTOM_SHEET("Bottom Sheet"),
TAGS("Tags"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.hisp.dhis.common.screens

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.text.input.ImeAction
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer
import org.hisp.dhis.mobile.ui.designsystem.component.InputPercentage
import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState
import org.hisp.dhis.mobile.ui.designsystem.component.LegendData
import org.hisp.dhis.mobile.ui.designsystem.component.SubTitle
import org.hisp.dhis.mobile.ui.designsystem.component.SupportingTextData
import org.hisp.dhis.mobile.ui.designsystem.component.SupportingTextState
import org.hisp.dhis.mobile.ui.designsystem.component.Title
import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor
import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor

@Composable
fun InputPercentageScreen() {
ColumnComponentContainer {
Title("Input Percentage component", textColor = TextColor.OnSurfaceVariant)
SubTitle("Basic Percentage ", textColor = TextColor.OnSurfaceVariant)
var inputValue1 by rememberSaveable { mutableStateOf("12") }

InputPercentage(
title = "Label",
inputText = inputValue1,
onValueChanged = {
if (it != null) {
inputValue1 = it
}
},
)
SubTitle("Basic Percentage required field", textColor = TextColor.OnSurfaceVariant)
var inputValueRequired by rememberSaveable { mutableStateOf("") }

InputPercentage(
title = "Label",
inputText = inputValueRequired,
onValueChanged = {
if (it != null) {
inputValueRequired = it
}
},
state = InputShellState.ERROR,
isRequiredField = true,
)
var inputValue2 by rememberSaveable { mutableStateOf("") }
SubTitle("Input Percentage with legend", textColor = TextColor.OnSurfaceVariant)
InputPercentage(
title = "Label",
inputText = inputValue2,
legendData = LegendData(SurfaceColor.CustomGreen, "Legend"),
onValueChanged = {
if (it != null) {
inputValue2 = it
}
},
)

var inputValue3 by rememberSaveable { mutableStateOf("") }

SubTitle("Input Percentage with Supporting text", textColor = TextColor.OnSurfaceVariant)
InputPercentage(
title = "Label",
inputText = inputValue3,
supportingText = listOf(SupportingTextData("Supporting text", SupportingTextState.DEFAULT)),
onValueChanged = {
if (it != null) {
inputValue3 = it
}
},
)

var inputValue4 by rememberSaveable { mutableStateOf("") }

SubTitle("Input Percentage with Supporting text and legend", textColor = TextColor.OnSurfaceVariant)

InputPercentage(
title = "Label",
inputText = inputValue4,
supportingText = listOf(
SupportingTextData(
"Supporting text",
SupportingTextState.DEFAULT,
),
),
legendData = LegendData(SurfaceColor.CustomGreen, "Legend"),
onValueChanged = {
if (it != null) {
inputValue4 = it
}
},
)
SubTitle("Input Percentage with error, warning text and legend", textColor = TextColor.OnSurfaceVariant)
var inputValue5 by rememberSaveable { mutableStateOf("") }

InputPercentage(
title = "Label",
inputText = inputValue5,
supportingText = listOf(
SupportingTextData("Supporting text", SupportingTextState.DEFAULT),
SupportingTextData("Supporting text", SupportingTextState.WARNING),
SupportingTextData("Supporting text", SupportingTextState.ERROR),

),
legendData = LegendData(SurfaceColor.CustomGreen, "Legend"),
state = InputShellState.ERROR,
imeAction = ImeAction.Done,
onValueChanged = {
if (it != null) {
inputValue5 = it
}
},
)
var inputValue6 by rememberSaveable { mutableStateOf("") }

SubTitle("Disabled Percentage ", textColor = TextColor.OnSurfaceVariant)
InputPercentage(
title = "Label",
inputText = inputValue6,
state = InputShellState.DISABLED,
onValueChanged = {
if (it != null) {
inputValue6 = it
}
},
)

var inputValue7 by rememberSaveable { mutableStateOf("1234") }

SubTitle("Disabled Percentage with content ", textColor = TextColor.OnSurfaceVariant)
InputPercentage(
title = "Label",
inputText = inputValue7,
state = InputShellState.DISABLED,
onValueChanged = {
if (it != null) {
inputValue7 = it
}
},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Cancel
import androidx.compose.material3.Icon
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.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
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.theme.Spacing

/**
* DHIS2 Input percentage. Wraps DHIS · [InputShell].
* Only integers allowed
* @param title controls the text to be shown for the title
* @param state Manages the InputShell state
* @param supportingText is a list of SupportingTextData that
* manages all the messages to be shown
* @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 imeAction controls the imeAction button to be shown
* @param modifier allows a modifier to be passed externally
*/
@Composable
fun InputPercentage(
title: String,
state: InputShellState = InputShellState.UNFOCUSED,
supportingText: List<SupportingTextData>? = null,
legendData: LegendData? = null,
inputText: String? = null,
isRequiredField: Boolean = false,
onNextClicked: (() -> Unit)? = null,
onValueChanged: ((String?) -> Unit)? = null,
imeAction: ImeAction = ImeAction.Next,
modifier: Modifier = Modifier,
) {
val inputValue by remember(inputText) { mutableStateOf(inputText) }

var deleteButtonIsVisible by remember { mutableStateOf(!inputText.isNullOrEmpty() && state != InputShellState.DISABLED) }
val focusManager = LocalFocusManager.current
val pattern = remember { Regex("^([1-9]|[1-9][0-9]|100)\$") }
val keyboardOptions = KeyboardOptions(imeAction = imeAction, keyboardType = KeyboardType.Number)
InputShell(
modifier = modifier.testTag("INPUT_PERCENTAGE"),
isRequiredField = isRequiredField,
title = title,
primaryButton = {
if (deleteButtonIsVisible) {
IconButton(
modifier = Modifier.testTag("INPUT_PERCENTAGE_RESET_BUTTON"),
icon = {
Icon(
imageVector = Icons.Outlined.Cancel,
contentDescription = "Icon Button",
)
},
onClick = {
onValueChanged?.invoke("")
deleteButtonIsVisible = false
},
enabled = state != InputShellState.DISABLED,
)
}
},
state = state,
legend = {
legendData?.let {
Legend(legendData, Modifier.testTag("INPUT_PERCENTAGE_LEGEND"))
}
},
supportingText = {
supportingText?.forEach {
label ->
SupportingText(
label.text,
label.state,
modifier = Modifier.testTag("INPUT_PERCENTAGE_SUPPORTING_TEXT").padding(
start = Spacing.Spacing16,
top = Spacing.Spacing4,
end = Spacing.Spacing16,
),
)
}
},
inputField = {
BasicInput(
modifier = Modifier.testTag("INPUT_PERCENTAGE_FIELD"),
helper = "%",
helperStyle = InputStyle.WITH_HELPER_AFTER,
inputText = inputValue ?: "",
onInputChanged = {
if (it.matches(pattern) || it.isEmpty()) {
onValueChanged?.invoke(it)
deleteButtonIsVisible = it.isNotEmpty()
}
},
enabled = state != InputShellState.DISABLED,
state = state,
keyboardOptions = keyboardOptions,
onNextClicked = {
if (onNextClicked != null) {
onNextClicked.invoke()
} else {
focusManager.moveFocus(FocusDirection.Down)
}
},
)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down Expand Up @@ -213,10 +214,10 @@ fun SectionHeader(
)
description?.let {
SupportingText(
modifier = Modifier.padding(Spacing.Spacing0)
.testTag(SectionTestTag.DESCRIPTION),
modifier = Modifier.testTag(SectionTestTag.DESCRIPTION),
text = it,
maxLines = 2,
paddingValues = PaddingValues(Spacing.Spacing0),
onNoInteraction = {
Pair(interactionSource, onSectionClick)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.hisp.dhis.mobile.ui.designsystem.component
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.ripple.LocalRippleTheme
Expand Down Expand Up @@ -52,6 +53,11 @@ fun SupportingText(
top = Spacing.Spacing4,
end = Spacing.Spacing16,
),
paddingValues: PaddingValues = PaddingValues(
start = Spacing.Spacing16,
top = Spacing.Spacing4,
end = Spacing.Spacing16,
),
onNoInteraction: (() -> Pair<MutableInteractionSource, () -> Unit>)? = null,
) {
var isExpanded by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -150,7 +156,7 @@ fun SupportingText(
}
}
},
modifier = modifier.animateContentSize(),
modifier = modifier.animateContentSize().padding(paddingValues),
)
}
}
Expand Down
Loading

0 comments on commit 9420dc6

Please sign in to comment.