BIT-994 Use dialogs instead of dropdowns on generator (#188)

This commit is contained in:
Andrew Haisting 2023-10-31 11:27:04 -05:00 committed by Álison Fernandes
parent cb2c6495c8
commit 1873ffe985
2 changed files with 36 additions and 42 deletions

View file

@ -4,25 +4,17 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
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.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.clearAndSetSemantics
@ -31,7 +23,7 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.toDp
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
/**
@ -41,7 +33,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
* When the field is clicked, a dropdown menu appears with a list of options to select from.
*
* @param label The descriptive text label for the [OutlinedTextField].
* @param options A list of strings representing the available options in the dropdown menu.
* @param options A list of strings representing the available options in the dialog.
* @param selectedOption The currently selected option that is displayed in the [OutlinedTextField].
* @param onOptionSelected A lambda that is invoked when an option
* is selected from the dropdown menu.
@ -56,14 +48,7 @@ fun BitwardenMultiSelectButton(
onOptionSelected: (String) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
var textFieldWidth by remember { mutableIntStateOf(0) }
// Watch for changes to 'expanded' and request focus if needed
LaunchedEffect(expanded) {
if (expanded) focusRequester.requestFocus() else focusRequester.freeFocus()
}
var shouldShowDialog by remember { mutableStateOf(false) }
Box(
modifier = modifier
@ -82,12 +67,8 @@ fun BitwardenMultiSelectButton(
indication = null,
interactionSource = remember { MutableInteractionSource() },
) {
expanded = !expanded
}
.onGloballyPositioned { coordinates ->
textFieldWidth = coordinates.size.width
}
.focusRequester(focusRequester),
shouldShowDialog = !shouldShowDialog
},
textStyle = MaterialTheme.typography.bodyLarge,
readOnly = true,
label = {
@ -97,7 +78,7 @@ fun BitwardenMultiSelectButton(
},
value = selectedOption,
onValueChange = onOptionSelected,
enabled = expanded,
enabled = shouldShowDialog,
trailingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_region_select_dropdown),
@ -114,23 +95,21 @@ fun BitwardenMultiSelectButton(
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
DropdownMenu(
modifier = Modifier.width(textFieldWidth.toDp()),
expanded = expanded,
onDismissRequest = {
expanded = false
focusRequester.freeFocus()
},
) {
options.forEach { optionString ->
DropdownMenuItem(
text = { Text(text = optionString) },
onClick = {
expanded = false
onOptionSelected(optionString)
focusRequester.freeFocus()
},
)
if (shouldShowDialog) {
BitwardenSelectionDialog(
title = label.asText(),
onDismissRequest = { shouldShowDialog = false },
) {
options.forEach { optionString ->
BitwardenSelectionRow(
text = optionString.asText(),
isSelected = optionString == selectedOption,
onClick = {
shouldShowDialog = false
onOptionSelected(optionString)
},
)
}
}
}
}

View file

@ -3,12 +3,15 @@ package com.x8bit.bitwarden.ui.tools.feature.generator
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.SemanticsMatcher.Companion.expectValue
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasProgressBarRangeInfo
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onChildren
import androidx.compose.ui.test.onLast
@ -96,6 +99,7 @@ class GeneratorScreenTest : BaseComposeTest() {
.onAllNodesWithText(text = "Password")
.onLast()
.performScrollTo()
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify {
@ -103,6 +107,11 @@ class GeneratorScreenTest : BaseComposeTest() {
GeneratorAction.MainTypeOptionSelect(GeneratorState.MainTypeOption.PASSWORD),
)
}
// Make sure dialog is hidden:
composeTestRule
.onNode(isDialog())
.assertDoesNotExist()
}
@Test
@ -120,6 +129,7 @@ class GeneratorScreenTest : BaseComposeTest() {
composeTestRule
.onAllNodesWithText(text = "Passphrase")
.onLast()
.assert(hasAnyAncestor(isDialog()))
.performClick()
verify {
@ -129,6 +139,11 @@ class GeneratorScreenTest : BaseComposeTest() {
),
)
}
// Make sure dialog is hidden:
composeTestRule
.onNode(isDialog())
.assertDoesNotExist()
}
//region Passcode Password Tests