BIT-870: Updating the BitwardenWideSwitch ripple (#119)

This commit is contained in:
joshua-livefront 2023-10-17 10:27:06 -04:00 committed by Álison Fernandes
parent 3fddc3d285
commit 83c057af29
5 changed files with 78 additions and 50 deletions

View file

@ -67,11 +67,11 @@ fun BitwardenMultiSelectButton(
Box(
modifier = modifier
.semantics(mergeDescendants = true) {}
.fillMaxWidth()) {
.semantics(mergeDescendants = true) {},
) {
OutlinedTextField(
// TODO: Update with final accessibility reading (BIT-752)
modifier = modifier
modifier = Modifier
.clearAndSetSemantics {
this.role = Role.DropdownList
contentDescription = "$label, $selectedOption"

View file

@ -47,13 +47,13 @@ fun BitwardenTextFieldWithTwoIcons(
onSecondIconClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Box(modifier = Modifier.semantics(mergeDescendants = true) {}) {
Box(modifier = modifier.semantics(mergeDescendants = true) {}) {
Row(
modifier = modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
OutlinedTextField(
modifier = modifier
modifier = Modifier
.weight(1f)
.clearAndSetSemantics {
contentDescription = "$label, $value"
@ -70,7 +70,6 @@ fun BitwardenTextFieldWithTwoIcons(
},
)
RowOfIconButtons(
modifier = modifier,
firstIconResource = firstIconResource,
onFirstIconClick = onFirstIconClick,
secondIconResource = secondIconResource,
@ -83,7 +82,6 @@ fun BitwardenTextFieldWithTwoIcons(
/**
* A row of two customizable icon buttons.
*
* @param modifier Modifier for the Row.
* @param firstIconResource The resource data for the first icon button.
* @param onFirstIconClick Callback for when the first icon button is clicked.
* @param secondIconResource The resource data for the second icon button.
@ -91,14 +89,13 @@ fun BitwardenTextFieldWithTwoIcons(
*/
@Composable
private fun RowOfIconButtons(
modifier: Modifier,
firstIconResource: IconResource,
onFirstIconClick: () -> Unit,
secondIconResource: IconResource,
onSecondIconClick: () -> Unit,
) {
Row(
modifier = modifier.padding(start = 8.dp),
modifier = Modifier.padding(start = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
@ -126,7 +123,6 @@ private fun IconButtonWithResource(iconRes: IconResource, onClick: () -> Unit) {
Icon(
painter = iconRes.iconPainter,
contentDescription = iconRes.contentDescription,
modifier = Modifier.padding(8.dp),
)
}
}

View file

@ -1,14 +1,18 @@
package com.x8bit.bitwarden.ui.platform.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
@ -34,10 +38,16 @@ fun BitwardenWideSwitch(
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.semantics(mergeDescendants = true) { }
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
.wrapContentHeight()
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
onClick = { onCheckedChange?.invoke(!isChecked) },
)
.semantics(mergeDescendants = true) { }
.then(modifier),
) {
Text(
text = label,
@ -49,7 +59,7 @@ fun BitwardenWideSwitch(
modifier = Modifier
.height(56.dp),
checked = isChecked,
onCheckedChange = onCheckedChange,
onCheckedChange = null,
)
}
}

View file

@ -141,7 +141,6 @@ private fun ScrollContent(
modifier = modifier
.fillMaxHeight()
.background(color = MaterialTheme.colorScheme.surface)
.padding(start = 16.dp, end = 16.dp)
.verticalScroll(rememberScrollState()),
) {
@ -165,6 +164,7 @@ private fun ScrollContent(
text = stringResource(id = R.string.options),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -204,6 +204,7 @@ private fun GeneratedStringItem(
contentDescription = stringResource(id = R.string.generate_password),
),
onSecondIconClick = onRegenerateClick,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -225,6 +226,9 @@ private fun MainStateOptionsItem(
optionsWithStrings.entries.first { it.value == selectedOption }.key
onMainStateOptionClicked(selectedOptionId)
},
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
)
}
@ -276,6 +280,9 @@ private fun PasscodeOptionsItem(
optionsWithStrings.entries.first { it.value == selectedOption }.key
onSubStateOptionClicked(selectedOptionId)
},
modifier = Modifier
.padding(horizontal = 16.dp)
.fillMaxWidth(),
)
}
@ -344,6 +351,7 @@ private fun PasswordLengthSliderItem(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.semantics(mergeDescendants = true) {},
) {
OutlinedTextField(
@ -383,6 +391,7 @@ private fun PasswordCapitalLettersToggleItem(
label = stringResource(id = R.string.uppercase_ato_z),
isChecked = useCapitals,
onCheckedChange = onPasswordToggleCapitalLettersChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -395,6 +404,7 @@ private fun PasswordLowercaseLettersToggleItem(
label = stringResource(id = R.string.lowercase_ato_z),
isChecked = useLowercase,
onCheckedChange = onPasswordToggleLowercaseLettersChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -407,6 +417,7 @@ private fun PasswordNumbersToggleItem(
label = stringResource(id = R.string.numbers_zero_to_nine),
isChecked = useNumbers,
onCheckedChange = onPasswordToggleNumbersChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -419,6 +430,7 @@ private fun PasswordSpecialCharactersToggleItem(
label = stringResource(id = R.string.special_characters),
isChecked = useSpecialChars,
onCheckedChange = onPasswordToggleSpecialCharactersChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -444,6 +456,7 @@ private fun PasswordMinNumbersCounterItem(
onSecondIconClick = {
onPasswordMinNumbersCounterChange(minNumbers + 1)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -469,6 +482,7 @@ private fun PasswordMinSpecialCharactersCounterItem(
onSecondIconClick = {
onPasswordMinSpecialCharactersChange(minSpecial + 1)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -481,6 +495,7 @@ private fun PasswordAvoidAmbiguousCharsToggleItem(
label = stringResource(id = R.string.avoid_ambiguous_characters),
isChecked = avoidAmbiguousChars,
onCheckedChange = onPasswordToggleAvoidAmbiguousCharsChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -541,6 +556,7 @@ private fun PassphraseNumWordsCounterItem(
onSecondIconClick = {
onPassphraseNumWordsCounterChange(numWords + 1)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -555,7 +571,9 @@ private fun PassphraseWordSeparatorInputItem(
onValueChange = {
onPassphraseWordSeparatorChange(it.toCharArray().firstOrNull())
},
modifier = Modifier.width(267.dp),
modifier = Modifier
.width(267.dp)
.padding(horizontal = 16.dp),
)
}
@ -568,6 +586,7 @@ private fun PassphraseCapitalizeToggleItem(
label = stringResource(id = R.string.capitalize),
isChecked = capitalize,
onCheckedChange = onPassphraseCapitalizeToggleChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
@ -580,6 +599,7 @@ private fun PassphraseIncludeNumberToggleItem(
label = stringResource(id = R.string.include_number),
isChecked = includeNumber,
onCheckedChange = onPassphraseIncludeNumberToggleChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}

View file

@ -3,13 +3,9 @@
package com.x8bit.bitwarden.ui.tools.feature.generator
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.Role.Companion.Switch
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.SemanticsProperties.Role
import androidx.compose.ui.test.SemanticsMatcher.Companion.expectValue
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.hasContentDescription
import androidx.compose.ui.test.hasProgressBarRangeInfo
@ -46,6 +42,32 @@ class GeneratorScreenTest : BaseComposeTest() {
every { stateFlow } returns mutableStateFlow
}
@Test
fun `clicking the Regenerate button should send RegenerateClick action`() {
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule.onNodeWithContentDescription(label = "Generate password").performClick()
verify {
viewModel.trySendAction(GeneratorAction.RegenerateClick)
}
}
@Test
fun `clicking the Copy button should send CopyClick action`() {
composeTestRule.setContent {
GeneratorScreen(viewModel = viewModel)
}
composeTestRule.onNodeWithContentDescription(label = "Copy").performClick()
verify {
viewModel.trySendAction(GeneratorAction.CopyClick)
}
}
@Test
fun `clicking a MainStateOption should send MainTypeOptionSelect action`() {
composeTestRule.setContent {
@ -115,27 +137,23 @@ class GeneratorScreenTest : BaseComposeTest() {
composeTestRule
.onNodeWithText("Uppercase (A to Z)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.assertIsOn()
.performScrollTo()
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Lowercase (A to Z)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.assertIsOn()
.performScrollTo()
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Numbers (0 to 9)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.assertIsOn()
.performScrollTo()
.assertIsDisplayed()
composeTestRule
.onNodeWithText("Special characters (!@#$%^*)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.assertIsOff()
.performScrollTo()
.assertIsDisplayed()
composeTestRule
.onNodeWithContentDescription("Minimum numbers, 1")
@ -153,10 +171,8 @@ class GeneratorScreenTest : BaseComposeTest() {
composeTestRule
.onNodeWithText("Avoid ambiguous characters")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.assertIsOff()
.assertIsDisplayed()
}
@Test
@ -198,8 +214,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Uppercase (A to Z)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -219,8 +233,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Lowercase (A to Z)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -240,8 +252,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Numbers (0 to 9)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -261,8 +271,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Special characters (!@#$%^*)")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -398,8 +406,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Avoid ambiguous characters")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -492,8 +498,6 @@ class GeneratorScreenTest : BaseComposeTest() {
composeTestRule
.onNodeWithText("Capitalize")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()
@ -520,8 +524,6 @@ class GeneratorScreenTest : BaseComposeTest() {
}
composeTestRule.onNodeWithText("Include number")
.onChildren()
.filterToOne(expectValue(Role, Switch))
.performScrollTo()
.performClick()