mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 11:02:51 -05:00
PM-29871: bug: Add external link callouts (#6634)
This commit is contained in:
1 parent
9e3360e421
commit
44f5f614b6
16 files changed
+96
-11
No files matched your search
+1
@@ -169,6 +169,7 @@ private fun SetupBrowserAutofillContent(
|
||||
label = stringResource(id = BitwardenString.why_is_this_step_required),
|
||||
style = BitwardenTheme.typography.labelMedium,
|
||||
onClick = onWhyIsThisStepRequiredClick,
|
||||
isExternalLink = true,
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
|
||||
+7
-2
@@ -18,6 +18,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -157,7 +159,9 @@ fun BitwardenListItem(
|
||||
selectionItems = {
|
||||
selectionDataList.forEach { itemData ->
|
||||
BitwardenBasicDialogRow(
|
||||
modifier = Modifier.testTag("AlertSelectionOption"),
|
||||
modifier = Modifier
|
||||
.semantics { contentDescription = itemData.contentDescription }
|
||||
.testTag(tag = "AlertSelectionOption"),
|
||||
text = itemData.text,
|
||||
onClick = {
|
||||
shouldShowDialog = false
|
||||
@@ -171,11 +175,12 @@ fun BitwardenListItem(
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the an individual selection item's data.
|
||||
* Wrapper for an individual selection item's data.
|
||||
*/
|
||||
data class SelectionItemData(
|
||||
val text: String,
|
||||
val onClick: () -> Unit,
|
||||
val contentDescription: String = text,
|
||||
val testTag: String? = null,
|
||||
)
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ fun SearchContent(
|
||||
.map { option ->
|
||||
SelectionItemData(
|
||||
text = option.title(),
|
||||
contentDescription = option.contentDescription(),
|
||||
onClick = {
|
||||
when (option) {
|
||||
is ListingItemOverflowAction.SendAction.DeleteClick -> {
|
||||
|
||||
+11
-2
@@ -201,11 +201,20 @@ private fun AutoFillScreenContent(
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
val autofillServicesLabel = stringResource(id = BitwardenString.autofill_services)
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = BitwardenString.autofill_services),
|
||||
label = autofillServicesLabel,
|
||||
supportingText = stringResource(
|
||||
id = BitwardenString.autofill_services_explanation_long,
|
||||
),
|
||||
contentDescription = if (state.isAutoFillServicesEnabled) {
|
||||
autofillServicesLabel
|
||||
} else {
|
||||
stringResource(
|
||||
id = BitwardenString.external_link_format,
|
||||
formatArgs = arrayOf(autofillServicesLabel),
|
||||
)
|
||||
},
|
||||
isChecked = state.isAutoFillServicesEnabled,
|
||||
onCheckedChange = autoFillHandlers.onAutofillServicesClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
@@ -397,7 +406,7 @@ private fun AutofillCallToActionCard(
|
||||
trailingContent = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(BitwardenDrawable.ic_chevron_right),
|
||||
contentDescription = null,
|
||||
contentDescription = stringResource(id = BitwardenString.external_link),
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
)
|
||||
},
|
||||
|
||||
+6
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.base.util.cardStyle
|
||||
@@ -13,6 +14,7 @@ import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.data.autofill.model.browser.BrowserPackage
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.browser.model.BrowserAutofillSettingsOption
|
||||
@@ -44,6 +46,10 @@ fun BrowserAutofillSettingsCard(
|
||||
onCheckedChange = {
|
||||
onOptionClicked(option.browserPackage)
|
||||
},
|
||||
contentDescription = stringResource(
|
||||
id = BitwardenString.external_link_format,
|
||||
formatArgs = arrayOf(option.optionText()),
|
||||
),
|
||||
cardStyle = when {
|
||||
supportingText == null -> options.toListItemCardStyle(index = index)
|
||||
index == 0 -> CardStyle.Top(dividerPadding = 16.dp)
|
||||
|
||||
+1
@@ -163,6 +163,7 @@ private fun FlightRecorderContent(
|
||||
onClick = onHelpCenterClick,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
isExternalLink = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
|
||||
+1
@@ -73,6 +73,7 @@ fun ImportLoginsInstructionStep(
|
||||
accessibilityString = stringResource(id = BitwardenString.import_help),
|
||||
onClick = onHelpClick,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
isExternalLink = true,
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
+4
-1
@@ -515,7 +515,10 @@ private fun UriField(
|
||||
if (uriData.isLaunchable) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_external_link,
|
||||
contentDescription = stringResource(id = BitwardenString.launch),
|
||||
contentDescription = stringResource(
|
||||
id = BitwardenString.external_link_format,
|
||||
formatArgs = arrayOf(stringResource(id = BitwardenString.launch)),
|
||||
),
|
||||
onClick = { onLaunchUriClick(uriData.uri) },
|
||||
modifier = Modifier.testTag(tag = "LoginLaunchUriButton"),
|
||||
)
|
||||
|
||||
+1
@@ -261,6 +261,7 @@ fun VaultItemListingContent(
|
||||
.map { option ->
|
||||
SelectionItemData(
|
||||
text = option.title(),
|
||||
contentDescription = option.contentDescription(),
|
||||
onClick = {
|
||||
when (option) {
|
||||
is ListingItemOverflowAction.SendAction.DeleteClick -> {
|
||||
|
||||
+23
@@ -18,6 +18,11 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
*/
|
||||
abstract val title: Text
|
||||
|
||||
/**
|
||||
* The content description of the option.
|
||||
*/
|
||||
abstract val contentDescription: Text
|
||||
|
||||
/**
|
||||
* Represents the send actions.
|
||||
*/
|
||||
@@ -31,6 +36,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
val sendType: SendType,
|
||||
) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.view.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +48,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
val sendType: SendType,
|
||||
) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.edit.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +57,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
@Parcelize
|
||||
data class CopyUrlClick(val sendUrl: String) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.copy_link.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,6 +66,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
@Parcelize
|
||||
data class ShareUrlClick(val sendUrl: String) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.share_link.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +75,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
@Parcelize
|
||||
data class RemovePasswordClick(val sendId: String) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.remove_password.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +84,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
@Parcelize
|
||||
data class DeleteClick(val sendId: String) : SendAction() {
|
||||
override val title: Text get() = BitwardenString.delete.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +108,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.view.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +121,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.edit.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +131,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
data class CopyUsernameClick(val username: String) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_username.asText()
|
||||
override val requiresPasswordReprompt: Boolean get() = false
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +143,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_password.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,6 +155,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_totp.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,6 +167,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_number.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,6 +179,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_security_code.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,6 +191,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
override val requiresPasswordReprompt: Boolean,
|
||||
) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.copy_notes.asText()
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,6 +201,8 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
data class LaunchClick(val url: String) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.launch.asText()
|
||||
override val requiresPasswordReprompt: Boolean get() = false
|
||||
override val contentDescription: Text
|
||||
get() = BitwardenString.external_link_format.asText(title)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +212,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
data class ArchiveClick(val cipherId: String) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.archive_verb.asText()
|
||||
override val requiresPasswordReprompt: Boolean get() = true
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,6 +222,7 @@ sealed class ListingItemOverflowAction : Parcelable {
|
||||
data class UnarchiveClick(val cipherId: String) : VaultAction() {
|
||||
override val title: Text get() = BitwardenString.unarchive.asText()
|
||||
override val requiresPasswordReprompt: Boolean get() = true
|
||||
override val contentDescription: Text get() = title
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ fun VaultEntryListItem(
|
||||
.map { option ->
|
||||
SelectionItemData(
|
||||
text = option.title(),
|
||||
contentDescription = option.contentDescription(),
|
||||
onClick = { onOverflowOptionClick(option) },
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ class FlightRecorderScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Bitwarden help center", substring = true)
|
||||
.performScrollTo()
|
||||
.performCustomAccessibilityAction(label = "Bitwarden help center")
|
||||
.performCustomAccessibilityAction(label = "Bitwarden help center, External link")
|
||||
verify(exactly = 1) {
|
||||
viewModel.trySendAction(FlightRecorderAction.HelpCenterClick)
|
||||
}
|
||||
|
||||
+3
-3
@@ -2367,7 +2367,7 @@ class VaultItemScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(uriData.uri)
|
||||
.onChildren()
|
||||
.filterToOne(hasContentDescription("Launch"))
|
||||
.filterToOne(hasContentDescription("Launch, External link"))
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -2379,7 +2379,7 @@ class VaultItemScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(uriData.uri)
|
||||
.onSiblings()
|
||||
.filterToOne(hasContentDescription("Launch"))
|
||||
.filterToOne(hasContentDescription("Launch, External link"))
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@@ -2437,7 +2437,7 @@ class VaultItemScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithTextAfterScroll(uriData.uri)
|
||||
.onChildren()
|
||||
.filterToOne(hasContentDescription("Launch"))
|
||||
.filterToOne(hasContentDescription("Launch, External link"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
|
||||
+3
@@ -17,6 +17,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.heading
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.base.util.nullableTestTag
|
||||
@@ -202,6 +204,7 @@ fun ColumnScope.BitwardenMultiSelectDialogContent(
|
||||
is MultiSelectOption.Header -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.semantics(mergeDescendants = true) { heading() }
|
||||
.nullableTestTag(tag = it.testTag)
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
|
||||
+20
-1
@@ -20,12 +20,17 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.hideFromAccessibility
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -34,6 +39,7 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param label The label for the button.
|
||||
* @param onClick The callback when the button is clicked.
|
||||
* @param modifier The [Modifier] to be applied to the button.
|
||||
* @param isExternalLink Indicates that this button launches an external link.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenClickableText(
|
||||
@@ -41,12 +47,21 @@ fun BitwardenClickableText(
|
||||
onClick: () -> Unit,
|
||||
style: TextStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
isExternalLink: Boolean = false,
|
||||
leadingIcon: Painter? = null,
|
||||
innerPadding: PaddingValues = PaddingValues(vertical = 4.dp, horizontal = 16.dp),
|
||||
isEnabled: Boolean = true,
|
||||
cornerSize: Dp = 28.dp,
|
||||
color: Color = BitwardenTheme.colorScheme.text.interaction,
|
||||
) {
|
||||
val formattedAccessibilityString = if (isExternalLink) {
|
||||
stringResource(
|
||||
id = BitwardenString.external_link_format,
|
||||
formatArgs = arrayOf(label),
|
||||
)
|
||||
} else {
|
||||
label
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
@@ -58,7 +73,10 @@ fun BitwardenClickableText(
|
||||
enabled = isEnabled,
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(paddingValues = innerPadding),
|
||||
.padding(paddingValues = innerPadding)
|
||||
.semantics(mergeDescendants = true) {
|
||||
contentDescription = formattedAccessibilityString
|
||||
},
|
||||
) {
|
||||
leadingIcon?.let {
|
||||
Icon(
|
||||
@@ -78,6 +96,7 @@ fun BitwardenClickableText(
|
||||
textAlign = TextAlign.Start,
|
||||
color = color,
|
||||
style = style,
|
||||
modifier = Modifier.semantics { hideFromAccessibility() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -5,6 +5,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.CustomAccessibilityAction
|
||||
import androidx.compose.ui.semantics.customActions
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
@@ -12,6 +13,7 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.bitwarden.ui.platform.base.util.annotatedStringResource
|
||||
import com.bitwarden.ui.platform.base.util.spanStyleOf
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -29,7 +31,16 @@ fun BitwardenHyperTextLink(
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = BitwardenTheme.typography.labelMedium,
|
||||
color: Color = BitwardenTheme.colorScheme.text.secondary,
|
||||
isExternalLink: Boolean = false,
|
||||
) {
|
||||
val formattedAccessibilityString = if (isExternalLink) {
|
||||
stringResource(
|
||||
id = BitwardenString.external_link_format,
|
||||
formatArgs = arrayOf(accessibilityString),
|
||||
)
|
||||
} else {
|
||||
accessibilityString
|
||||
}
|
||||
Text(
|
||||
text = annotatedStringResource(
|
||||
id = annotatedResId,
|
||||
@@ -48,7 +59,7 @@ fun BitwardenHyperTextLink(
|
||||
.semantics {
|
||||
customActions = listOf(
|
||||
CustomAccessibilityAction(
|
||||
label = accessibilityString,
|
||||
label = formattedAccessibilityString,
|
||||
action = {
|
||||
onClick()
|
||||
true
|
||||
|
||||
Reference in new issue
Block a user