BIT-989 Show region selector as dialog (#180)

This commit is contained in:
Andrew Haisting 2023-10-31 07:42:31 -05:00 committed by Álison Fernandes
parent 1bd09e42b3
commit b1457bc499
4 changed files with 166 additions and 18 deletions

View file

@ -17,8 +17,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@ -44,8 +42,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenSelectionDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenSelectionRow
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
@ -226,12 +227,12 @@ private fun EnvironmentSelector(
modifier: Modifier,
) {
val options = Environment.Type.values()
var expanded by remember { mutableStateOf(false) }
var shouldShowDialog by remember { mutableStateOf(false) }
Box(modifier = modifier) {
Row(
modifier = Modifier
.clickable { expanded = !expanded }
.clickable { shouldShowDialog = !shouldShowDialog }
.fillMaxWidth()
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically,
@ -255,18 +256,21 @@ private fun EnvironmentSelector(
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEach { optionString ->
DropdownMenuItem(
text = { Text(text = optionString.label()) },
onClick = {
expanded = false
onOptionSelected(optionString)
},
)
if (shouldShowDialog) {
BitwardenSelectionDialog(
title = R.string.logging_in_on.asText(),
onDismissRequest = { shouldShowDialog = false },
) {
options.forEach {
BitwardenSelectionRow(
text = it.label,
onClick = {
onOptionSelected.invoke(it)
shouldShowDialog = false
},
isSelected = it == selectedOption,
)
}
}
}
}

View file

@ -0,0 +1,86 @@
package com.x8bit.bitwarden.ui.platform.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.Text
/**
* Displays a dialog with a title and "Cancel" button.
*
* @param title Title to display.
* @param onDismissRequest Invoked when the user dismisses the dialog.
* @param selectionItems Lambda containing selection items to show to the user. See
* [BitwardenSelectionRow].
*/
@Composable
fun BitwardenSelectionDialog(
title: Text,
onDismissRequest: () -> Unit,
selectionItems: @Composable ColumnScope.() -> Unit = {},
) {
Dialog(
onDismissRequest = onDismissRequest,
) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier.background(
color = MaterialTheme.colorScheme.surfaceContainerHigh,
shape = RoundedCornerShape(28.dp),
),
horizontalAlignment = Alignment.End,
) {
Text(
modifier = Modifier
.padding(24.dp)
.fillMaxWidth(),
text = title(),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.headlineSmall,
)
if (scrollState.canScrollBackward) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(MaterialTheme.colorScheme.outlineVariant),
)
}
Column(
modifier = Modifier
.weight(1f, fill = false)
.verticalScroll(scrollState),
content = selectionItems,
)
if (scrollState.canScrollForward) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(MaterialTheme.colorScheme.outlineVariant),
)
}
BitwardenTextButton(
modifier = Modifier.padding(24.dp),
label = stringResource(id = R.string.cancel),
onClick = onDismissRequest,
)
}
}
}

View file

@ -0,0 +1,52 @@
package com.x8bit.bitwarden.ui.platform.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.selected
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.ui.platform.base.util.Text
/**
* A clickable item that displays a radio button and text.
*
* @param text The text to display.
* @param onClick Invoked when either the radio button or text is clicked.
* @param isSelected Whether or not the radio button should be checked.
*/
@Composable
fun BitwardenSelectionRow(
text: Text,
onClick: () -> Unit,
isSelected: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.semantics(mergeDescendants = true) {
selected = isSelected
},
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(
modifier = Modifier.padding(16.dp),
selected = isSelected,
onClick = null,
)
Text(
text = text(),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodyLarge,
)
}
}

View file

@ -240,19 +240,25 @@ class LandingScreenTest : BaseComposeTest() {
)
}
// Clicking to open dropdown
// Clicking to open dialog
composeTestRule
.onNodeWithText(Environment.Us.label.toString(resources))
.performClick()
// Clicking item from the dropdown menu
// Clicking item on dialog
composeTestRule
.onNodeWithText(selectedEnvironment.label.toString(resources))
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify {
viewModel.trySendAction(LandingAction.EnvironmentTypeSelect(selectedEnvironment.type))
}
// Make sure dialog is hidden:
composeTestRule
.onNode(isDialog())
.assertDoesNotExist()
}
@Test