Skip to content

Commit efec5cb

Browse files
PM-19653: Add tooltip and subtext tupport for the switch (#4936)
1 parent 6369b20 commit efec5cb

File tree

3 files changed

+146
-79
lines changed

3 files changed

+146
-79
lines changed

app/src/main/java/com/x8bit/bitwarden/ui/platform/components/toggle/BitwardenSwitch.kt

+124-29
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.defaultMinSize
1111
import androidx.compose.foundation.layout.fillMaxWidth
1212
import androidx.compose.foundation.layout.height
1313
import androidx.compose.foundation.layout.padding
14+
import androidx.compose.foundation.layout.size
1415
import androidx.compose.foundation.layout.width
1516
import androidx.compose.material3.Switch
1617
import androidx.compose.material3.Text
@@ -23,14 +24,18 @@ import androidx.compose.ui.semantics.semantics
2324
import androidx.compose.ui.semantics.toggleableState
2425
import androidx.compose.ui.state.ToggleableState
2526
import androidx.compose.ui.text.AnnotatedString
27+
import androidx.compose.ui.text.style.TextOverflow
2628
import androidx.compose.ui.tooling.preview.Preview
29+
import androidx.compose.ui.unit.Dp
2730
import androidx.compose.ui.unit.dp
2831
import com.x8bit.bitwarden.R
2932
import com.x8bit.bitwarden.ui.platform.base.util.cardStyle
3033
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString
3134
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
3235
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
3336
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
37+
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
38+
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenRowOfActions
3439
import com.x8bit.bitwarden.ui.platform.components.toggle.color.bitwardenSwitchColors
3540
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
3641

@@ -42,8 +47,10 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
4247
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
4348
* @param cardStyle Indicates the type of card style to be applied.
4449
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
50+
* @param subtext The text to be displayed under the [label].
4551
* @param supportingText An optional supporting text to be displayed below the [label].
4652
* @param contentDescription A description of the switch's UI for accessibility purposes.
53+
* @param tooltip The data required to display a tooltip.
4754
* @param readOnly Disables the click functionality without modifying the other UI characteristics.
4855
* @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but
4956
* comes with some additional visual changes.
@@ -58,18 +65,22 @@ fun BitwardenSwitch(
5865
onCheckedChange: ((Boolean) -> Unit)?,
5966
cardStyle: CardStyle?,
6067
modifier: Modifier = Modifier,
68+
subtext: String? = null,
6169
supportingText: String? = null,
6270
contentDescription: String? = null,
71+
tooltip: TooltipData? = null,
6372
readOnly: Boolean = false,
6473
enabled: Boolean = true,
6574
actions: (@Composable RowScope.() -> Unit)? = null,
6675
) {
6776
BitwardenSwitch(
6877
modifier = modifier,
6978
label = label.toAnnotatedString(),
79+
subtext = subtext,
7080
isChecked = isChecked,
7181
onCheckedChange = onCheckedChange,
7282
contentDescription = contentDescription,
83+
tooltip = tooltip,
7384
readOnly = readOnly,
7485
enabled = enabled,
7586
cardStyle = cardStyle,
@@ -98,8 +109,10 @@ fun BitwardenSwitch(
98109
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
99110
* @param cardStyle Indicates the type of card style to be applied.
100111
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
112+
* @param subtext The text to be displayed under the [label].
101113
* @param supportingText An optional supporting text to be displayed below the [label].
102114
* @param contentDescription A description of the switch's UI for accessibility purposes.
115+
* @param tooltip The data required to display a tooltip.
103116
* @param readOnly Disables the click functionality without modifying the other UI characteristics.
104117
* @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but
105118
* comes with some additional visual changes.
@@ -114,18 +127,22 @@ fun BitwardenSwitch(
114127
onCheckedChange: ((Boolean) -> Unit)?,
115128
cardStyle: CardStyle?,
116129
modifier: Modifier = Modifier,
130+
subtext: String? = null,
117131
supportingText: String? = null,
118132
contentDescription: String? = null,
133+
tooltip: TooltipData? = null,
119134
readOnly: Boolean = false,
120135
enabled: Boolean = true,
121136
actions: (@Composable RowScope.() -> Unit)? = null,
122137
) {
123138
BitwardenSwitch(
124139
modifier = modifier,
125140
label = label,
141+
subtext = subtext,
126142
isChecked = isChecked,
127143
onCheckedChange = onCheckedChange,
128144
contentDescription = contentDescription,
145+
tooltip = tooltip,
129146
readOnly = readOnly,
130147
enabled = enabled,
131148
cardStyle = cardStyle,
@@ -154,6 +171,7 @@ fun BitwardenSwitch(
154171
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
155172
* @param cardStyle Indicates the type of card style to be applied.
156173
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
174+
* @param subtext The text to be displayed under the [label].
157175
* @param contentDescription A description of the switch's UI for accessibility purposes.
158176
* @param readOnly Disables the click functionality without modifying the other UI characteristics.
159177
* @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but
@@ -170,6 +188,7 @@ fun BitwardenSwitch(
170188
onCheckedChange: ((Boolean) -> Unit)?,
171189
cardStyle: CardStyle?,
172190
modifier: Modifier = Modifier,
191+
subtext: String? = null,
173192
contentDescription: String? = null,
174193
readOnly: Boolean = false,
175194
enabled: Boolean = true,
@@ -179,6 +198,7 @@ fun BitwardenSwitch(
179198
BitwardenSwitch(
180199
modifier = modifier,
181200
label = label.toAnnotatedString(),
201+
subtext = subtext,
182202
isChecked = isChecked,
183203
onCheckedChange = onCheckedChange,
184204
contentDescription = contentDescription,
@@ -198,7 +218,9 @@ fun BitwardenSwitch(
198218
* @param onCheckedChange A lambda that is invoked when the switch's state changes.
199219
* @param cardStyle Indicates the type of card style to be applied.
200220
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
221+
* @param subtext The text to be displayed under the [label].
201222
* @param contentDescription A description of the switch's UI for accessibility purposes.
223+
* @param tooltip The data required to display a tooltip.
202224
* @param readOnly Disables the click functionality without modifying the other UI characteristics.
203225
* @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but
204226
* comes with some additional visual changes.
@@ -207,15 +229,17 @@ fun BitwardenSwitch(
207229
* defining the layout of the actions.
208230
* @param supportingContent A lambda containing content directly below the label.
209231
*/
210-
@Suppress("LongMethod")
232+
@Suppress("LongMethod", "CyclomaticComplexMethod")
211233
@Composable
212234
fun BitwardenSwitch(
213235
label: AnnotatedString,
214236
isChecked: Boolean,
215237
onCheckedChange: ((Boolean) -> Unit)?,
216238
cardStyle: CardStyle?,
217239
modifier: Modifier = Modifier,
240+
subtext: String? = null,
218241
contentDescription: String? = null,
242+
tooltip: TooltipData? = null,
219243
readOnly: Boolean = false,
220244
enabled: Boolean = true,
221245
supportingContentPadding: PaddingValues = PaddingValues(vertical = 12.dp, horizontal = 16.dp),
@@ -240,26 +264,50 @@ fun BitwardenSwitch(
240264
) {
241265
Row(
242266
verticalAlignment = Alignment.CenterVertically,
243-
modifier = Modifier
244-
.defaultMinSize(minHeight = 36.dp)
245-
.padding(horizontal = 16.dp),
267+
modifier = Modifier.defaultMinSize(minHeight = 36.dp),
246268
) {
269+
Spacer(modifier = Modifier.width(width = 16.dp))
247270
Row(
248271
modifier = Modifier.weight(weight = 1f),
249272
verticalAlignment = Alignment.CenterVertically,
250273
) {
251-
Text(
252-
text = label,
253-
style = BitwardenTheme.typography.bodyLarge,
254-
color = if (enabled) {
255-
BitwardenTheme.colorScheme.text.primary
256-
} else {
257-
BitwardenTheme.colorScheme.filledButton.foregroundDisabled
258-
},
259-
modifier = Modifier.testTag(tag = "SwitchText"),
260-
)
261-
262-
actions?.invoke(this)
274+
Column(modifier = Modifier.weight(weight = 1f, fill = false)) {
275+
Row(verticalAlignment = Alignment.CenterVertically) {
276+
Text(
277+
text = label,
278+
style = BitwardenTheme.typography.bodyLarge,
279+
color = if (enabled) {
280+
BitwardenTheme.colorScheme.text.primary
281+
} else {
282+
BitwardenTheme.colorScheme.filledButton.foregroundDisabled
283+
},
284+
modifier = Modifier.testTag(tag = "SwitchText"),
285+
)
286+
tooltip?.let {
287+
ToolTip(
288+
tooltip = it,
289+
isVisible = subtext != null,
290+
size = 16.dp,
291+
)
292+
}
293+
}
294+
subtext?.let {
295+
Spacer(modifier = Modifier.height(height = 2.dp))
296+
Text(
297+
text = it,
298+
style = BitwardenTheme.typography.bodyMedium,
299+
color = if (enabled) {
300+
BitwardenTheme.colorScheme.text.secondary
301+
} else {
302+
BitwardenTheme.colorScheme.filledButton.foregroundDisabled
303+
},
304+
maxLines = 2,
305+
overflow = TextOverflow.Ellipsis,
306+
modifier = Modifier.testTag(tag = "SwitchSubtext"),
307+
)
308+
}
309+
}
310+
tooltip?.let { ToolTip(tooltip = it, isVisible = subtext == null) }
263311
}
264312
Spacer(modifier = Modifier.width(width = 16.dp))
265313
Switch(
@@ -271,27 +319,59 @@ fun BitwardenSwitch(
271319
onCheckedChange = null,
272320
colors = bitwardenSwitchColors(),
273321
)
322+
actions?.let { BitwardenRowOfActions(actions = it) }
323+
Spacer(modifier = Modifier.width(width = if (actions == null) 16.dp else 4.dp))
274324
}
275325
supportingContent
276326
?.let { content ->
277-
Spacer(modifier = Modifier.height(height = 12.dp))
278-
BitwardenHorizontalDivider(
279-
modifier = Modifier
280-
.fillMaxWidth()
281-
.padding(start = 16.dp),
282-
)
283-
Column(
284-
verticalArrangement = Arrangement.Center,
285-
modifier = Modifier
286-
.defaultMinSize(minHeight = 48.dp)
287-
.padding(paddingValues = supportingContentPadding),
327+
SupportingContent(
328+
paddingValues = supportingContentPadding,
288329
content = content,
289330
)
290331
}
291332
?: Spacer(modifier = Modifier.height(height = cardStyle?.let { 12.dp } ?: 0.dp))
292333
}
293334
}
294335

336+
@Composable
337+
private fun ColumnScope.SupportingContent(
338+
paddingValues: PaddingValues,
339+
content: @Composable ColumnScope.() -> Unit,
340+
) {
341+
Spacer(modifier = Modifier.height(height = 12.dp))
342+
BitwardenHorizontalDivider(
343+
modifier = Modifier
344+
.fillMaxWidth()
345+
.padding(start = 16.dp),
346+
)
347+
Column(
348+
modifier = Modifier
349+
.defaultMinSize(minHeight = 48.dp)
350+
.padding(paddingValues = paddingValues),
351+
verticalArrangement = Arrangement.Center,
352+
content = content,
353+
)
354+
}
355+
356+
@Composable
357+
private fun RowScope.ToolTip(
358+
tooltip: TooltipData,
359+
isVisible: Boolean,
360+
size: Dp = 48.dp,
361+
) {
362+
if (!isVisible) return
363+
Spacer(modifier = Modifier.width(width = 8.dp))
364+
BitwardenStandardIconButton(
365+
vectorIconRes = R.drawable.ic_question_circle_small,
366+
contentDescription = tooltip.contentDescription,
367+
onClick = tooltip.onClick,
368+
contentColor = BitwardenTheme.colorScheme.icon.secondary,
369+
modifier = Modifier
370+
.size(size = size)
371+
.testTag(tag = "SwitchTooltip"),
372+
)
373+
}
374+
295375
@Preview
296376
@Composable
297377
private fun BitwardenSwitch_preview() {
@@ -314,22 +394,37 @@ private fun BitwardenSwitch_preview() {
314394
supportingText = "description",
315395
isChecked = true,
316396
onCheckedChange = {},
397+
tooltip = TooltipData(
398+
onClick = { },
399+
contentDescription = "content description",
400+
),
317401
actions = {
318402
BitwardenStandardIconButton(
319-
vectorIconRes = R.drawable.ic_question_circle,
403+
vectorIconRes = R.drawable.ic_generate,
320404
contentDescription = "content description",
321405
onClick = {},
322406
)
323407
},
324408
cardStyle = CardStyle.Middle(),
325409
)
410+
BitwardenSwitch(
411+
label = "Label",
412+
supportingText = "description",
413+
isChecked = true,
414+
onCheckedChange = {},
415+
tooltip = TooltipData(
416+
onClick = { },
417+
contentDescription = "content description",
418+
),
419+
cardStyle = CardStyle.Middle(),
420+
)
326421
BitwardenSwitch(
327422
label = "Label",
328423
isChecked = false,
329424
onCheckedChange = {},
330425
actions = {
331426
BitwardenStandardIconButton(
332-
vectorIconRes = R.drawable.ic_question_circle,
427+
vectorIconRes = R.drawable.ic_generate,
333428
contentDescription = "content description",
334429
onClick = {},
335430
)

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditAdditionalOptions.kt

+7-12
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@ import androidx.compose.ui.res.stringResource
1313
import androidx.compose.ui.unit.dp
1414
import com.x8bit.bitwarden.R
1515
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
16-
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
1716
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
1817
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenExpandingHeader
1918
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
2019
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
20+
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
2121
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
22-
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
2322
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
2423
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
2524
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
@@ -79,16 +78,12 @@ fun LazyListScope.vaultAddEditAdditionalOptions(
7978
label = stringResource(id = R.string.password_prompt),
8079
isChecked = commonState.masterPasswordReprompt,
8180
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
82-
actions = {
83-
BitwardenStandardIconButton(
84-
vectorIconRes = R.drawable.ic_question_circle_small,
85-
contentDescription = stringResource(
86-
id = R.string.master_password_re_prompt_help,
87-
),
88-
onClick = commonTypeHandlers.onTooltipClick,
89-
contentColor = BitwardenTheme.colorScheme.icon.secondary,
90-
)
91-
},
81+
tooltip = TooltipData(
82+
onClick = commonTypeHandlers.onTooltipClick,
83+
contentDescription = stringResource(
84+
id = R.string.master_password_re_prompt_help,
85+
),
86+
),
9287
cardStyle = CardStyle.Full,
9388
modifier = Modifier
9489
.testTag(tag = "MasterPasswordRepromptToggle")

0 commit comments

Comments
 (0)