summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-12-04 07:34:59 +0100
committerBug Magnet <marco.nikic@mullvad.net>2024-12-04 07:34:59 +0100
commit638d7066edc9b50d1c0712282bc15bfd71e24d06 (patch)
treed7e35382032126dacffa1f4d2c4d2679072b8d2d
parentd44108b9e3fb403889490ccc173e085fd766e03e (diff)
parent2972a556b2982948e2eb31d6081cabd4eb2386b0 (diff)
downloadmullvadvpn-638d7066edc9b50d1c0712282bc15bfd71e24d06.tar.xz
mullvadvpn-638d7066edc9b50d1c0712282bc15bfd71e24d06.zip
Merge branch 'SingleChoiceList-AccessibilityIdentifier'
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift38
1 files changed, 35 insertions, 3 deletions
diff --git a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
index 0b48846d86..a01b51b54c 100644
--- a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
@@ -57,6 +57,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
var value: Binding<Value>
@State var initialValue: Value?
let itemDescription: (Value) -> String
+ let itemAccessibilityIdentifier: (Value) -> String
let customFieldMode: CustomFieldMode
/// The configuration for the field for a custom value row
@@ -83,6 +84,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
// this row consists of a text field into which the user can enter a custom value, which may yield a valid Value. This has accompanying text, and functions to translate between text field contents and the Value. (The fromValue method only needs to give a non-nil value if its input is a custom value that could have come from this row.)
case custom(
label: String,
+ accessibilityIdentifier: String,
prompt: String,
legend: String?,
minInputWidth: CGFloat?,
@@ -102,12 +104,14 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
optionSpecs: [OptionSpec.OptValue],
value: Binding<Value>,
itemDescription: ((Value) -> String)? = nil,
+ itemAccessibilityIdentifier: ((Value) -> String)? = nil,
customFieldMode: CustomFieldMode = .freeText
) {
self.title = title
self.options = optionSpecs.enumerated().map { OptionSpec(id: $0.offset, value: $0.element) }
self.value = value
self.itemDescription = itemDescription ?? { "\($0)" }
+ self.itemAccessibilityIdentifier = itemAccessibilityIdentifier ?? { "\($0)" }
self.customFieldMode = customFieldMode
self.initialValue = value.wrappedValue
}
@@ -118,12 +122,20 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
/// - title: The title of the list, which is typically the name of the item being chosen.
/// - options: A list of `Value`s to be presented.
/// - itemDescription: An optional function that, when given a `Value`, returns the string representation to present in the list. If not provided, this will be generated naïvely using string interpolation.
- init(title: String, options: [Value], value: Binding<Value>, itemDescription: ((Value) -> String)? = nil) {
+ /// - itemAccessibilityIdentifier: An optional function that, when given a `Value`, returns the accessibility identifier for the value's list item. If not provided, this will be generated naïvely using string interpolation.
+ init(
+ title: String,
+ options: [Value],
+ value: Binding<Value>,
+ itemDescription: ((Value) -> String)? = nil,
+ itemAccessibilityIdentifier: ((Value) -> String)? = nil
+ ) {
self.init(
title: title,
optionSpecs: options.map { .literal($0) },
value: value,
- itemDescription: itemDescription
+ itemDescription: itemDescription,
+ itemAccessibilityIdentifier: itemAccessibilityIdentifier
)
}
@@ -133,9 +145,11 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
/// - title: The title of the list, which is typically the name of the item being chosen.
/// - options: A list of fixed `Value`s to be presented.
/// - itemDescription: An optional function that, when given a `Value`, returns the string representation to present in the list. If not provided, this will be generated naïvely using string interpolation. This is only used for the non-custom values.
+ /// - itemAccessibilityIdentifier: An optional function that, when given a `Value`, returns the accessibility identifier for the value's list item. If not provided, this will be generated naïvely using string interpolation.
/// - parseCustomValue: A function that attempts to parse the text entered into the text field and produce a `Value` (typically the tagged custom value with an argument applied to it). If the text is not valid for a value, it should return `nil`
/// - formatCustomValue: A function that, when passed a `Value` containing user-entered custom data, formats that data into a string, which should match what the user would have entered. This function can expect to only be called for the custom value, and should return `nil` in the event of its argument not being a valid custom value.
/// - customLabel: The caption to display in the custom row, next to the text field.
+ /// - customAccessibilityIdentifier: The accessibility identifier to use for the custom row. If not provided, "customValue" will be used. The accessibility identifier for the text field will be this value with ".input" appended.
/// - customPrompt: The text to display, greyed, in the text field when it is empty. This also serves to set the width of the field, and should be right-padded with spaces as appropriate.
/// - customLegend: Optional text to display below the custom field, i.e., to explain sensible values
/// - customInputWidth: An optional minimum width (in pseudo-pixels) for the custom input field
@@ -146,9 +160,11 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
options: [Value],
value: Binding<Value>,
itemDescription: ((Value) -> String)? = nil,
+ itemAccessibilityIdentifier: ((Value) -> String)? = nil,
parseCustomValue: @escaping ((String) -> Value?),
formatCustomValue: @escaping ((Value) -> String?),
customLabel: String,
+ customAccessibilityIdentifier: String = "customValue",
customPrompt: String,
customLegend: String? = nil,
customInputMinWidth: CGFloat? = nil,
@@ -159,6 +175,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
title: title,
optionSpecs: options.map { .literal($0) } + [.custom(
label: customLabel,
+ accessibilityIdentifier: customAccessibilityIdentifier,
prompt: customPrompt,
legend: customLegend,
minInputWidth: customInputMinWidth,
@@ -168,6 +185,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
)],
value: value,
itemDescription: itemDescription,
+ itemAccessibilityIdentifier: itemAccessibilityIdentifier,
customFieldMode: customFieldMode
)
}
@@ -202,12 +220,14 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
customValueIsFocused = false
customValueInput = ""
}
+ .accessibilityIdentifier(itemAccessibilityIdentifier(item))
}
// Construct the one row with a custom input field for a custom value
// swiftlint:disable function_body_length
private func customRow(
label: String,
+ accessibilityIdentifier: String,
prompt: String,
inputWidth: CGFloat?,
maxInputLength: Int?,
@@ -288,6 +308,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
customValueInput = valueText
}
}
+ .accessibilityIdentifier(accessibilityIdentifier + ".input")
}
.onTapGesture {
if let v = toValue(customValueInput) {
@@ -296,6 +317,7 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
customValueIsFocused = true
}
}
+ .accessibilityIdentifier(accessibilityIdentifier)
}
// swiftlint:enable function_body_length
@@ -323,9 +345,19 @@ struct SingleChoiceList<Value>: View where Value: Equatable {
switch opt.value {
case let .literal(v):
literalRow(v)
- case let .custom(label, prompt, legend, inputWidth, maxInputLength, toValue, fromValue):
+ case let .custom(
+ label,
+ accessibilityIdentifier,
+ prompt,
+ legend,
+ inputWidth,
+ maxInputLength,
+ toValue,
+ fromValue
+ ):
customRow(
label: label,
+ accessibilityIdentifier: accessibilityIdentifier,
prompt: prompt,
inputWidth: inputWidth,
maxInputLength: maxInputLength,