Merge branch 'mbruce/bugfix' into 'develop'

CXTDT-546824 - Notification - Accessibility - Redundant text is provided for the notification icon.

See merge request BPHV_MIPS/vds_ios!222
This commit is contained in:
Bruce, Matt R 2024-05-07 16:55:53 +00:00
commit fbc3cdb2bc
14 changed files with 160 additions and 21 deletions

View File

@ -210,6 +210,7 @@ open class BadgeIndicator: View {
/// The Container's height. /// The Container's height.
open var height: CGFloat? { didSet { setNeedsUpdate() } } open var height: CGFloat? { didSet { setNeedsUpdate() } }
open var accessibilityText: String? { didSet { setNeedsUpdate() } }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Private Properties // MARK: - Private Properties
//-------------------------------------------------- //--------------------------------------------------
@ -348,7 +349,9 @@ open class BadgeIndicator: View {
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
if kind == .numbered { if let accessibilityText {
accessibilityLabel = accessibilityText
} else if kind == .numbered {
accessibilityLabel = label.text accessibilityLabel = label.text
} else { } else {
accessibilityLabel = "Simple" accessibilityLabel = "Simple"

View File

@ -103,8 +103,6 @@ open class DropdownSelect: EntryFieldBase {
open override func setup() { open override func setup() {
super.setup() super.setup()
accessibilityLabel = "Dropdown Select"
// stackview for controls in EntryFieldBase.controlContainerView // stackview for controls in EntryFieldBase.controlContainerView
let controlStackView = UIStackView().with { let controlStackView = UIStackView().with {
$0.translatesAutoresizingMaskIntoConstraints = false $0.translatesAutoresizingMaskIntoConstraints = false
@ -118,6 +116,10 @@ open class DropdownSelect: EntryFieldBase {
controlStackView.addArrangedSubview(inlineDisplayLabel) controlStackView.addArrangedSubview(inlineDisplayLabel)
controlStackView.addArrangedSubview(selectedOptionLabel) controlStackView.addArrangedSubview(selectedOptionLabel)
containerStackView.isAccessibilityElement = true
containerStackView.accessibilityLabel = "Dropdown Select"
inlineDisplayLabel.isAccessibilityElement = true
controlStackView.setCustomSpacing(0, after: dropdownField) controlStackView.setCustomSpacing(0, after: dropdownField)
controlStackView.setCustomSpacing(VDSLayout.space1X, after: inlineDisplayLabel) controlStackView.setCustomSpacing(VDSLayout.space1X, after: inlineDisplayLabel)
controlStackView.setCustomSpacing(VDSLayout.space3X, after: selectedOptionLabel) controlStackView.setCustomSpacing(VDSLayout.space3X, after: selectedOptionLabel)
@ -245,9 +247,41 @@ open class DropdownSelect: EntryFieldBase {
statusIcon.color = iconColorConfiguration.getColor(self) statusIcon.color = iconColorConfiguration.getColor(self)
} }
open override func updateAccessibility() {
super.updateAccessibility()
var selectedOption = selectedOptionLabel.text ?? ""
containerStackView.accessibilityLabel = "Dropdown Select, \(selectedOption) \(isReadOnly ? ", read only" : "")"
containerStackView.accessibilityHint = isReadOnly || !isEnabled ? "" : "Double tap to open."
}
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()
elements.append(contentsOf: [titleLabel, containerStackView])
if showError {
elements.append(statusIcon)
if let errorText, !errorText.isEmpty {
elements.append(errorLabel)
}
}
if let helperText, !helperText.isEmpty {
elements.append(helperLabel)
}
return elements
}
set { super.accessibilityElements = newValue }
}
@objc open func pickerDoneClicked() { @objc open func pickerDoneClicked() {
optionsPicker.isHidden = true optionsPicker.isHidden = true
dropdownField.resignFirstResponder() dropdownField.resignFirstResponder()
setNeedsUpdate()
UIAccessibility.post(notification: .layoutChanged, argument: containerStackView)
} }
} }
@ -258,6 +292,7 @@ extension DropdownSelect: UIPickerViewDelegate, UIPickerViewDataSource {
internal func launchPicker() { internal func launchPicker() {
if optionsPicker.isHidden { if optionsPicker.isHidden {
UIAccessibility.post(notification: .layoutChanged, argument: optionsPicker)
dropdownField.becomeFirstResponder() dropdownField.becomeFirstResponder()
} else { } else {
dropdownField.resignFirstResponder() dropdownField.resignFirstResponder()

View File

@ -544,6 +544,7 @@ open class ButtonIcon: Control, Changeable {
badgeIndicator.horizontalPadding = badgeIndicatorModel.horizontalPadding badgeIndicator.horizontalPadding = badgeIndicatorModel.horizontalPadding
badgeIndicator.hideDot = badgeIndicatorModel.hideDot badgeIndicator.hideDot = badgeIndicatorModel.hideDot
badgeIndicator.hideBorder = badgeIndicatorModel.hideBorder badgeIndicator.hideBorder = badgeIndicatorModel.hideBorder
badgeIndicator.accessibilityText = badgeIndicatorModel.accessibilityText
} }
private func updateExpandDirectionalConstraints() { private func updateExpandDirectionalConstraints() {

View File

@ -46,6 +46,9 @@ extension ButtonIcon {
/// Trailing Text height that will be used for the badge indicator. /// Trailing Text height that will be used for the badge indicator.
public var trailingText: String? public var trailingText: String?
/// Accessibliity Text
public var accessibilityText: String?
/// Dot Size that will be used for the badge indicator. /// Dot Size that will be used for the badge indicator.
public var dotSize: CGFloat? public var dotSize: CGFloat?
@ -61,7 +64,7 @@ extension ButtonIcon {
/// Hide Border that will be used for the badge indicator. /// Hide Border that will be used for the badge indicator.
public var hideBorder: Bool = false public var hideBorder: Bool = false
public init(kind: BadgeIndicator.Kind = .simple, fillColor: BadgeIndicator.FillColor = .red, expandDirection: ExpandDirection = .right, size: BadgeIndicator.Size = .xxlarge, maximumDigits: BadgeIndicator.MaximumDigits = .two, width: CGFloat? = nil, height: CGFloat? = nil, number: Int? = nil, leadingCharacter: String? = "", trailingText: String? = "", dotSize: CGFloat? = nil, verticalPadding: CGFloat? = nil, horizontalPadding: CGFloat? = nil, hideDot: Bool = false, hideBorder: Bool = false) { public init(kind: BadgeIndicator.Kind = .simple, fillColor: BadgeIndicator.FillColor = .red, expandDirection: ExpandDirection = .right, size: BadgeIndicator.Size = .xxlarge, maximumDigits: BadgeIndicator.MaximumDigits = .two, width: CGFloat? = nil, height: CGFloat? = nil, number: Int? = nil, leadingCharacter: String? = "", trailingText: String? = "", accessibilityText: String? = nil, dotSize: CGFloat? = nil, verticalPadding: CGFloat? = nil, horizontalPadding: CGFloat? = nil, hideDot: Bool = false, hideBorder: Bool = false) {
self.kind = kind self.kind = kind
self.fillColor = fillColor self.fillColor = fillColor
self.expandDirection = expandDirection self.expandDirection = expandDirection
@ -70,6 +73,7 @@ extension ButtonIcon {
self.width = width self.width = width
self.height = height self.height = height
self.number = number self.number = number
self.accessibilityText = accessibilityText
self.leadingCharacter = leadingCharacter self.leadingCharacter = leadingCharacter
self.trailingText = trailingText self.trailingText = trailingText
self.dotSize = dotSize self.dotSize = dotSize

View File

@ -77,7 +77,7 @@ public class TooltipLabelAttribute: ActionLabelAttributeModel, TooltipLaunchable
self.subscriber = subscriber self.subscriber = subscriber
self.surface = surface self.surface = surface
self.model = model self.model = model
self.accessibleText = accessibleText self.accessibleText = accessibleText ?? model.accessibleText
self.presenter = presenter self.presenter = presenter
//create the tooltip click event //create the tooltip click event

View File

@ -104,6 +104,7 @@ open class Notification: View {
open var typeIcon = Icon().with { open var typeIcon = Icon().with {
$0.name = .infoBold $0.name = .infoBold
$0.size = UIDevice.isIPad ? .medium : .small $0.size = UIDevice.isIPad ? .medium : .small
$0.accessibilityTraits.remove(.image)
} }
/// Icon used for the close. /// Icon used for the close.

View File

@ -207,7 +207,7 @@ open class EntryFieldBase: Control, Changeable, FormFieldInternalValidatable {
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = true isAccessibilityElement = false
addSubview(stackView) addSubview(stackView)
//create the wrapping view //create the wrapping view

View File

@ -148,7 +148,6 @@ open class InputField: EntryFieldBase {
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = false
minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 0)
minWidthConstraint?.isActive = true minWidthConstraint?.isActive = true
@ -373,11 +372,29 @@ open class InputField: EntryFieldBase {
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
textField.accessibilityLabel = showError ? "error" : nil textField.accessibilityLabel = showError ? "error" : nil
if showError { }
accessibilityElements = [titleLabel, textField, statusIcon, errorLabel, helperLabel]
} else { open override var accessibilityElements: [Any]? {
accessibilityElements = [titleLabel, textField, helperLabel] get {
var elements = [Any]()
elements.append(contentsOf: [titleLabel, textField])
if showError {
elements.append(statusIcon)
if let errorText, !errorText.isEmpty {
elements.append(errorLabel)
}
} else if showSuccess, let successText, !successText.isEmpty {
elements.append(successLabel)
}
if let helperText, !helperText.isEmpty {
elements.append(helperLabel)
}
return elements
} }
set { super.accessibilityElements = newValue }
} }
open override var canBecomeFirstResponder: Bool { true } open override var canBecomeFirstResponder: Bool { true }

View File

@ -10,6 +10,25 @@ import UIKit
@objc(VDSTextField) @objc(VDSTextField)
open class TextField: UITextField { open class TextField: UITextField {
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
required public init() {
super.init(frame: .zero)
initialSetup()
}
public override init(frame: CGRect) {
super.init(frame: .zero)
initialSetup()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
initialSetup()
}
var horizontalPadding: CGFloat = 0 var horizontalPadding: CGFloat = 0
open override func textRect(forBounds bounds: CGRect) -> CGRect { open override func textRect(forBounds bounds: CGRect) -> CGRect {
@ -35,6 +54,25 @@ open class TextField: UITextField {
} }
} }
open func initialSetup() {
let doneToolbar: UIToolbar = UIToolbar()
doneToolbar.translatesAutoresizingMaskIntoConstraints = false
doneToolbar.barStyle = .default
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))
done.accessibilityHint = "Double tap to finish editing."
doneToolbar.items = [flexSpace, done]
doneToolbar.sizeToFit()
inputAccessoryView = doneToolbar
}
@objc func doneButtonAction() {
// Resigns the first responder status when 'Done' is tapped
resignFirstResponder()
}
open override func becomeFirstResponder() -> Bool { open override func becomeFirstResponder() -> Bool {
let success = super.becomeFirstResponder() let success = super.becomeFirstResponder()
if isSecureTextEntry, let text { if isSecureTextEntry, let text {

View File

@ -161,7 +161,6 @@ open class TextArea: EntryFieldBase {
/// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations. /// Called once when a view is initialized and is used to Setup additional UI or other constants and configurations.
open override func setup() { open override func setup() {
super.setup() super.setup()
isAccessibilityElement = false
containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset)) containerStackView.pinToSuperView(.uniform(VDSFormControls.spaceInset))
minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width) minWidthConstraint = containerView.widthAnchor.constraint(greaterThanOrEqualToConstant: containerSize.width)
minWidthConstraint?.isActive = true minWidthConstraint?.isActive = true
@ -264,13 +263,31 @@ open class TextArea: EntryFieldBase {
open override func updateAccessibility() { open override func updateAccessibility() {
super.updateAccessibility() super.updateAccessibility()
textView.accessibilityLabel = showError ? "error" : nil textView.accessibilityLabel = showError ? "error" : nil
if showError {
accessibilityElements = [titleLabel, textView, statusIcon, errorLabel, helperLabel]
} else {
accessibilityElements = [titleLabel, textView, helperLabel]
}
} }
open override var accessibilityElements: [Any]? {
get {
var elements = [Any]()
elements.append(contentsOf: [titleLabel, textView])
if showError {
elements.append(statusIcon)
if let errorText, !errorText.isEmpty {
elements.append(errorLabel)
}
}
if let helperText, !helperText.isEmpty {
elements.append(helperLabel)
}
return elements
}
set { super.accessibilityElements = newValue }
}
open override var canBecomeFirstResponder: Bool { true } open override var canBecomeFirstResponder: Bool { true }
open override func resignFirstResponder() -> Bool { open override func resignFirstResponder() -> Bool {

View File

@ -97,7 +97,6 @@ open class TextView: UITextView, ViewProtocol {
initialSetupPerformed = true initialSetupPerformed = true
backgroundColor = .clear backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
accessibilityCustomActions = []
setup() setup()
setNeedsUpdate() setNeedsUpdate()
} }
@ -106,6 +105,23 @@ open class TextView: UITextView, ViewProtocol {
open func setup() { open func setup() {
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
let doneToolbar: UIToolbar = UIToolbar()
doneToolbar.translatesAutoresizingMaskIntoConstraints = false
doneToolbar.barStyle = .default
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))
done.accessibilityHint = "Double tap to finish editing."
doneToolbar.items = [flexSpace, done]
doneToolbar.sizeToFit()
inputAccessoryView = doneToolbar
}
@objc func doneButtonAction() {
// Resigns the first responder status when 'Done' is tapped
resignFirstResponder()
} }
open func updateView() { open func updateView() {
@ -118,7 +134,6 @@ open class TextView: UITextView, ViewProtocol {
shouldUpdateView = false shouldUpdateView = false
surface = .light surface = .light
text = nil text = nil
accessibilityCustomActions = []
shouldUpdateView = true shouldUpdateView = true
setNeedsUpdate() setNeedsUpdate()
} }

View File

@ -48,7 +48,6 @@ open class TooltipDialog: View, UIScrollViewDelegate {
lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with { lazy var primaryAccessibilityElement = UIAccessibilityElement(accessibilityContainer: self).with {
$0.accessibilityLabel = "Modal" $0.accessibilityLabel = "Modal"
$0.accessibilityFrameInContainerSpace = .init(origin: .zero, size: .init(width: fullWidth, height: VDSLayout.space1X))
} }
//-------------------------------------------------- //--------------------------------------------------
@ -222,6 +221,7 @@ open class TooltipDialog: View, UIScrollViewDelegate {
super.updateAccessibility() super.updateAccessibility()
primaryAccessibilityElement.accessibilityHint = "Double tap on the \(tooltipModel.closeButtonText) button to close." primaryAccessibilityElement.accessibilityHint = "Double tap on the \(tooltipModel.closeButtonText) button to close."
primaryAccessibilityElement.accessibilityFrameInContainerSpace = .init(origin: .zero, size: frame.size)
var elements: [Any] = [primaryAccessibilityElement] var elements: [Any] = [primaryAccessibilityElement]
contentStackView.arrangedSubviews.forEach{ elements.append($0) } contentStackView.arrangedSubviews.forEach{ elements.append($0) }

View File

@ -17,16 +17,19 @@ extension Tooltip {
public var title: String? public var title: String?
public var content: String? public var content: String?
public var contentView: UIView? public var contentView: UIView?
public var accessibleText: String?
public var contentViewAlignment: UIStackView.Alignment? public var contentViewAlignment: UIStackView.Alignment?
public init(closeButtonText: String = "Close", public init(closeButtonText: String = "Close",
title: String? = nil, title: String? = nil,
content: String? = nil, content: String? = nil,
contentView: UIView? = nil, contentView: UIView? = nil,
accessibleText: String? = "Tooltip",
contentViewAlignment: UIStackView.Alignment = .leading) { contentViewAlignment: UIStackView.Alignment = .leading) {
self.closeButtonText = closeButtonText self.closeButtonText = closeButtonText
self.title = title self.title = title
self.content = content self.content = content
self.contentView = contentView self.contentView = contentView
self.accessibleText = accessibleText
self.contentViewAlignment = contentViewAlignment self.contentViewAlignment = contentViewAlignment
} }
} }

View File

@ -1,3 +1,8 @@
1.0.62
----------------
- CXTDT-546824 - Notification - Accessibility - Redundant text is provided for the notification icon.
- CXTDT-553663 - DropdownSelect - Accessibility - 5 issues
1.0.61 1.0.61
---------------- ----------------
- CXTDT-552068 - Text Area - Text padding - CXTDT-552068 - Text Area - Text padding