Added auto div-accessibility.type

This commit is contained in:
pkurchatov
2024-02-02 14:35:43 +03:00
parent a1d28634d1
commit a6d6972bd9
27 changed files with 272 additions and 101 deletions
-8
View File
@@ -13890,10 +13890,6 @@
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-action/firefoxMobile/with-action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-action/firefoxMobile/with-action.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-set-state-action/chromeMobile/with-set-state-action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-set-state-action/chromeMobile/with-set-state-action.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-set-state-action/firefoxMobile/with-set-state-action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-grid/with-set-state-action/firefoxMobile/with-set-state-action.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/height_wrap_content/chromeMobile/height_wrap_content.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/height_wrap_content/chromeMobile/height_wrap_content.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/height_wrap_content/firefoxMobile/height_wrap_content.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/height_wrap_content/firefoxMobile/height_wrap_content.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/width_wrap_content/chromeMobile/width_wrap_content.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/width_wrap_content/chromeMobile/width_wrap_content.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/width_wrap_content/firefoxMobile/width_wrap_content.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/width_wrap_content/firefoxMobile/width_wrap_content.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_action/chromeMobile/with_action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_action/chromeMobile/with_action.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_action/firefoxMobile/with_action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_action/firefoxMobile/with_action.png",
"client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_set_state_action/chromeMobile/with_set_state_action.png":"divkit/public/client/web/divkit/tests/hermione/screens/crossplatform/unit/div-image/with_set_state_action/chromeMobile/with_set_state_action.png",
@@ -15890,8 +15886,6 @@
"test_data/unit_test_data/div-gallery/vertical_gallery_match_parent_height_items.json":"divkit/public/test_data/unit_test_data/div-gallery/vertical_gallery_match_parent_height_items.json",
"test_data/unit_test_data/div-gallery/vertical_gallery_mixed_width_items.json":"divkit/public/test_data/unit_test_data/div-gallery/vertical_gallery_mixed_width_items.json",
"test_data/unit_test_data/div-gallery/vertical_gallery_wrap_content_height.json":"divkit/public/test_data/unit_test_data/div-gallery/vertical_gallery_wrap_content_height.json",
"test_data/unit_test_data/div-gif-image/height_wrap_content.json":"divkit/public/test_data/unit_test_data/div-gif-image/height_wrap_content.json",
"test_data/unit_test_data/div-gif-image/width_wrap_content.json":"divkit/public/test_data/unit_test_data/div-gif-image/width_wrap_content.json",
"test_data/unit_test_data/div-gif-image/with_action.json":"divkit/public/test_data/unit_test_data/div-gif-image/with_action.json",
"test_data/unit_test_data/div-gif-image/with_set_state_action.json":"divkit/public/test_data/unit_test_data/div-gif-image/with_set_state_action.json",
"test_data/unit_test_data/div-grid/incompatible-horizontal-traits.json":"divkit/public/test_data/unit_test_data/div-grid/incompatible-horizontal-traits.json",
@@ -15904,8 +15898,6 @@
"test_data/unit_test_data/div-grid/invalid-row-span3.json":"divkit/public/test_data/unit_test_data/div-grid/invalid-row-span3.json",
"test_data/unit_test_data/div-grid/with-action.json":"divkit/public/test_data/unit_test_data/div-grid/with-action.json",
"test_data/unit_test_data/div-grid/with-set-state-action.json":"divkit/public/test_data/unit_test_data/div-grid/with-set-state-action.json",
"test_data/unit_test_data/div-image/height_wrap_content.json":"divkit/public/test_data/unit_test_data/div-image/height_wrap_content.json",
"test_data/unit_test_data/div-image/width_wrap_content.json":"divkit/public/test_data/unit_test_data/div-image/width_wrap_content.json",
"test_data/unit_test_data/div-image/with_action.json":"divkit/public/test_data/unit_test_data/div-image/with_action.json",
"test_data/unit_test_data/div-image/with_set_state_action.json":"divkit/public/test_data/unit_test_data/div-image/with_set_state_action.json",
"test_data/unit_test_data/div-pager/pager_default_item.json":"divkit/public/test_data/unit_test_data/div-pager/pager_default_item.json",
@@ -5,7 +5,7 @@ extension DivAccessibility {
func resolve(
_ expressionResolver: ExpressionResolver,
id: String?,
customDescriptionProvider: (() -> String?)?
customParams: CustomAccessibilityParams
) -> AccessibilityElement {
if resolveMode(expressionResolver) == .exclude {
return AccessibilityElement(
@@ -16,17 +16,17 @@ extension DivAccessibility {
}
var label: String? = nil
if let customDescriptionProvider {
if let customDescriptionProvider = customParams.descriptionProvider {
label = customDescriptionProvider()
} else if let description = resolveDescription(expressionResolver) {
label = description
}
if label == nil, type != nil {
if label == nil, type != .auto {
label = ""
}
return AccessibilityElement(
traits: traits,
traits: traits ?? customParams.defaultTraits,
strings: AccessibilityElement.Strings(
label: label,
hint: resolveHint(expressionResolver),
@@ -38,7 +38,7 @@ extension DivAccessibility {
)
}
private var traits: AccessibilityElement.Traits {
private var traits: AccessibilityElement.Traits? {
switch type {
case .button:
return .button
@@ -54,9 +54,26 @@ extension DivAccessibility {
return .tabBar
case .select:
DivKitLogger.warning("Unsupported accessibility type")
return .none
case .list, .none?, nil:
return .none
return AccessibilityElement.Traits.none
case .none, .list:
return AccessibilityElement.Traits.none
case .auto:
return nil
}
}
}
struct CustomAccessibilityParams {
static let `default` = CustomAccessibilityParams()
let defaultTraits: AccessibilityElement.Traits
let descriptionProvider: (() -> String?)?
init(
defaultTraits: AccessibilityElement.Traits = .none,
descriptionProvider: (() -> String?)? = nil
) {
self.defaultTraits = defaultTraits
self.descriptionProvider = descriptionProvider
}
}
@@ -11,7 +11,7 @@ extension DivBase {
context: DivBlockModelingContext,
actionsHolder: DivActionsHolder?,
options: BasePropertiesOptions = [],
customA11yDescriptionProvider: (() -> String?)? = nil,
customAccessibilityParams: CustomAccessibilityParams = .default,
clipToBounds: Bool = true
) throws -> Block {
let expressionResolver = context.expressionResolver
@@ -71,12 +71,11 @@ extension DivBase {
let shadow = border?.resolveShadow(expressionResolver)
let accessibilityElement = (accessibility ?? DivAccessibility())
.resolve(
expressionResolver,
id: id,
customDescriptionProvider: customA11yDescriptionProvider
)
let accessibilityElement = (accessibility ?? DivAccessibility()).resolve(
expressionResolver,
id: id,
customParams: customAccessibilityParams
)
block = try applyBackground(
getBackground(focusState),
@@ -9,7 +9,7 @@ extension DivContainer: DivBlockModeling {
to: { try makeBaseBlock(context: context) },
context: context,
actionsHolder: self,
customA11yDescriptionProvider: { [unowned self] in
customAccessibilityParams: CustomAccessibilityParams { [unowned self] in
resolveAccessibilityDescription(context)
},
clipToBounds: resolveClipToBounds(context.expressionResolver)
@@ -8,7 +8,8 @@ extension DivGifImage: DivBlockModeling, DivImageProtocol {
try applyBaseProperties(
to: { try makeBaseBlock(context: context) },
context: context,
actionsHolder: self
actionsHolder: self,
customAccessibilityParams: CustomAccessibilityParams(defaultTraits: .image)
)
}
@@ -10,7 +10,8 @@ extension DivImage: DivBlockModeling, DivImageProtocol {
try applyBaseProperties(
to: { try makeBaseBlock(context: context) },
context: context,
actionsHolder: self
actionsHolder: self,
customAccessibilityParams: CustomAccessibilityParams(defaultTraits: .image)
)
}
@@ -5,7 +5,6 @@ import Foundation
import BasePublic
import BaseUIPublic
import LayoutKit
import NetworkingPublic
extension DivText: DivBlockModeling {
public func makeBlock(context: DivBlockModelingContext) throws -> Block {
@@ -17,7 +16,9 @@ extension DivText: DivBlockModeling {
to: { try makeBaseBlock(context: context, text: lazyText) },
context: context,
actionsHolder: self,
customA11yDescriptionProvider: { [unowned self] in
customAccessibilityParams: CustomAccessibilityParams(
defaultTraits: .staticText
) { [unowned self] in
accessibility?.resolveDescription(expressionResolver) ?? lazyText.value
}
)
@@ -16,6 +16,7 @@ public final class DivAccessibility {
case tabBar = "tab_bar"
case list = "list"
case select = "select"
case auto = "auto"
}
@frozen
@@ -30,7 +31,7 @@ public final class DivAccessibility {
public let mode: Expression<Mode> // default value: default
public let muteAfterAction: Expression<Bool> // default value: false
public let stateDescription: Expression<String>?
public let type: Kind?
public let type: Kind // default value: auto
public func resolveDescription(_ resolver: ExpressionResolver) -> String? {
resolver.resolveString(description, initializer: { $0 })
@@ -65,7 +66,7 @@ public final class DivAccessibility {
self.mode = mode ?? .value(.default)
self.muteAfterAction = muteAfterAction ?? .value(false)
self.stateDescription = stateDescription
self.type = type
self.type = type ?? .auto
}
}
@@ -99,7 +100,7 @@ extension DivAccessibility: Serializable {
result["mode"] = mode.toValidSerializationValue()
result["mute_after_action"] = muteAfterAction.toValidSerializationValue()
result["state_description"] = stateDescription?.toValidSerializationValue()
result["type"] = type?.rawValue
result["type"] = type.rawValue
return result
}
}
@@ -14,7 +14,7 @@ public final class DivAccessibilityTemplate: TemplateValue {
public let mode: Field<Expression<Mode>>? // default value: default
public let muteAfterAction: Field<Expression<Bool>>? // default value: false
public let stateDescription: Field<Expression<String>>?
public let type: Field<Kind>?
public let type: Field<Kind>? // default value: auto
public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws {
self.init(
+1 -1
View File
@@ -102,7 +102,7 @@ final class FakeImageHolderFactory: DivImageHolderFactory {
}
}
private final class FakeImageHolder: ImageHolder {
final class FakeImageHolder: ImageHolder {
var image: Image? {
nil
}
@@ -108,6 +108,34 @@ final class DivAccessibilityExtentionsTests: XCTestCase {
assertEqual(element, expectedElement)
}
func test_defaultTraits_UsedForAutoType() {
let element = DivAccessibility(
description: .value("Description"),
type: .auto
).resolve(defaultTraits: .header)
let expectedElement = AccessibilityElement(
traits: .header,
strings: AccessibilityElement.Strings(label: "Description")
)
assertEqual(element, expectedElement)
}
func test_defaultTraits_HasLowerPriorityThanType() {
let element = DivAccessibility(
description: .value("Description"),
type: .button
).resolve(defaultTraits: .header)
let expectedElement = AccessibilityElement(
traits: .button,
strings: AccessibilityElement.Strings(label: "Description")
)
assertEqual(element, expectedElement)
}
func test_customDescriptionProvider_HasHigherPriorityThanDescription() {
let element = DivAccessibility(
description: .value("Description"),
@@ -126,12 +154,16 @@ final class DivAccessibilityExtentionsTests: XCTestCase {
extension DivAccessibility {
fileprivate func resolve(
id: String? = nil,
defaultTraits: AccessibilityElement.Traits = .none,
customDescriptionProvider: (() -> String?)? = nil
) -> AccessibilityElement {
resolve(
DivBlockModelingContext.default.expressionResolver,
id: id,
customDescriptionProvider: customDescriptionProvider
customParams: CustomAccessibilityParams(
defaultTraits: defaultTraits,
descriptionProvider: customDescriptionProvider
)
)
}
}
@@ -41,7 +41,10 @@ final class DivBaseExtensionsTests: XCTestCase {
child: DecoratingBlock(
child: textBlock(text: "Hello!"),
paddings: EdgeInsets(horizontal: 20),
accessibilityElement: accessibility(label: "Hello!")
accessibilityElement: accessibility(
traits: .staticText,
label: "Hello!"
)
),
boundary: .noClip,
paddings: EdgeInsets(vertical: 10)
@@ -72,7 +72,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "Hello!".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Hello!")
accessibilityElement: accessibility(
traits: .staticText,
label: "Hello!"
)
),
]
),
@@ -112,7 +115,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "Item 1".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Item 1")
accessibilityElement: accessibility(
traits: .staticText,
label: "Item 1"
)
),
DecoratingBlock(
child: TextBlock(
@@ -120,7 +126,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "Item 2".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Item 2")
accessibilityElement: accessibility(
traits: .staticText,
label: "Item 2"
)
),
]
),
@@ -160,7 +169,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "itemBuilder".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "itemBuilder")
accessibilityElement: accessibility(
traits: .staticText,
label: "itemBuilder"
)
),
]
),
@@ -205,7 +217,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "Hello!".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Hello!")
accessibilityElement: accessibility(
traits: .staticText,
label: "Hello!"
)
),
DecoratingBlock(
child: TextBlock(
@@ -225,7 +240,10 @@ final class DivContainerExtensionsTests: XCTestCase {
text: "Nested item".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Nested item")
accessibilityElement: accessibility(
traits: .staticText,
label: "Nested item"
)
),
]
),
@@ -81,7 +81,7 @@ final class DivDataExtensionsTests: XCTestCase {
items: [
divText(
text: "0",
width: divFixedSize(10)
width: fixedSize(10)
),
]
)
@@ -3,32 +3,86 @@
import XCTest
import BasePublic
final class DivGifImageExtensionsTests: XCTestCase {
func test_WithGifUrl() {
let block = makeBlock(
divGifImage(
gifUrl: "https://image.url",
height: fixedSize(200),
width: fixedSize(100)
)
)
let expectedBlock = StateBlock(
child: DecoratingBlock(
child: AnimatableImageBlock(
imageHolder: FakeImageHolder(),
widthTrait: .fixed(100),
height: .trait(.fixed(200)),
contentMode: ImageContentMode(scale: .aspectFill)
),
accessibilityElement: accessibility(traits: .image)
),
ids: []
)
assertEqual(block, expectedBlock)
}
func test_WithAccessibilityAuto() {
let block = makeBlock(
divGifImage(
accessibility: DivAccessibility(description: .value("Description")),
gifUrl: "https://image.url",
height: fixedSize(200),
width: fixedSize(100)
)
)
let expectedBlock = StateBlock(
child: DecoratingBlock(
child: AnimatableImageBlock(
imageHolder: FakeImageHolder(),
widthTrait: .fixed(100),
height: .trait(.fixed(200)),
contentMode: ImageContentMode(scale: .aspectFill)
),
accessibilityElement: accessibility(
traits: .image,
label: "Description"
)
),
ids: []
)
assertEqual(block, expectedBlock)
}
func test_WhenWidthIsWrapContent_ThrowsError() {
XCTAssertThrowsError(
try makeBlock(fromFile: "width_wrap_content"),
try divData(divGifImage(
gifUrl: "https://image.url",
width: wrapContentSize()
)).makeBlock(context: .default),
DivBlockModelingError(
"DivGifImage has wrap_content width",
path: .root
path: .root + "0"
)
)
}
func test_WhenHeightIsWrapContent_ThrowsError() throws {
XCTAssertThrowsError(
try makeBlock(fromFile: "height_wrap_content"),
try divData(divGifImage(
gifUrl: "https://image.url",
height: wrapContentSize()
)).makeBlock(context: .default),
DivBlockModelingError(
"DivGifImage without aspect has wrap_content height",
path: .root
path: .root + "0"
)
)
}
}
private func makeBlock(fromFile filename: String) throws -> Block {
try DivGifImageTemplate.make(
fromFile: filename,
subdirectory: "div-gif-image",
context: .default
)
}
@@ -3,32 +3,86 @@
import XCTest
import BasePublic
final class DivImageExtensionsTests: XCTestCase {
func test_WithImageUrl() {
let block = makeBlock(
divImage(
height: fixedSize(200),
imageUrl: "https://image.url",
width: fixedSize(100)
)
)
let expectedBlock = StateBlock(
child: DecoratingBlock(
child: ImageBlock(
imageHolder: FakeImageHolder(),
widthTrait: .fixed(100),
heightTrait: .fixed(200),
contentMode: ImageContentMode(scale: .aspectFill)
),
accessibilityElement: accessibility(traits: .image)
),
ids: []
)
assertEqual(block, expectedBlock)
}
func test_WithAccessibilityAuto() {
let block = makeBlock(
divImage(
accessibility: DivAccessibility(description: .value("Description")),
height: fixedSize(200),
imageUrl: "https://image.url",
width: fixedSize(100)
)
)
let expectedBlock = StateBlock(
child: DecoratingBlock(
child: ImageBlock(
imageHolder: FakeImageHolder(),
widthTrait: .fixed(100),
heightTrait: .fixed(200),
contentMode: ImageContentMode(scale: .aspectFill)
),
accessibilityElement: accessibility(
traits: .image,
label: "Description"
)
),
ids: []
)
assertEqual(block, expectedBlock)
}
func test_WhenWidthIsWrapContent_ThrowsError() {
XCTAssertThrowsError(
try makeBlock(fromFile: "width_wrap_content"),
try divData(divImage(
imageUrl: "https://image.url",
width: wrapContentSize()
)).makeBlock(context: .default),
DivBlockModelingError(
"DivImage has wrap_content width",
path: .root
path: .root + "0"
)
)
}
func test_WhenHeightIsWrapContent_ThrowsError() throws {
XCTAssertThrowsError(
try makeBlock(fromFile: "height_wrap_content"),
try divData(divImage(
height: wrapContentSize(),
imageUrl: "https://image.url"
)).makeBlock(context: .default),
DivBlockModelingError(
"DivImage without aspect has wrap_content height",
path: .root
path: .root + "0"
)
)
}
}
private func makeBlock(fromFile filename: String) throws -> Block {
try DivImageTemplate.make(
fromFile: filename,
subdirectory: "div-image",
context: .default
)
}
@@ -19,7 +19,10 @@ final class DivTextExtensionsTests: XCTestCase {
text: "Hello!".withTypo(),
verticalAlignment: .leading
),
accessibilityElement: accessibility(label: "Hello!")
accessibilityElement: accessibility(
traits: .staticText,
label: "Hello!"
)
),
ids: []
)
@@ -84,7 +87,10 @@ final class DivTextExtensionsTests: XCTestCase {
uiAction(logId: "action_log_id", url: "https://some.url")
),
actionAnimation: .default,
accessibilityElement: accessibility(label: "Hello!")
accessibilityElement: accessibility(
traits: .staticText,
label: "Hello!"
)
),
ids: []
)
@@ -56,7 +56,7 @@ private let gallery = divData(
div: divGallery(
items: (0..<3).map { _ in
divContainer(
height: divFixedSize(320),
height: fixedSize(320),
items: [
divText(
fontSize: 16,
@@ -67,12 +67,12 @@ private let gallery = divData(
text: "Lorem ipsum dolor sit amet"
),
divImage(
height: divFixedSize(100),
height: fixedSize(100),
imageUrl: "https://image.url",
width: divFixedSize(100)
width: fixedSize(100)
),
],
width: divFixedSize(260)
width: fixedSize(260)
)
}
),
+21 -1
View File
@@ -22,12 +22,28 @@ func divData(_ div: Div) -> DivData {
)
}
func divGifImage(
accessibility: DivAccessibility? = nil,
gifUrl: String,
height: DivSize? = nil,
width: DivSize? = nil
) -> Div {
.divGifImage(DivGifImage(
accessibility: accessibility,
gifUrl: .value(url(gifUrl)),
height: height,
width: width
))
}
func divImage(
accessibility: DivAccessibility? = nil,
height: DivSize? = nil,
imageUrl: String,
width: DivSize? = nil
) -> Div {
.divImage(DivImage(
accessibility: accessibility,
height: height,
imageUrl: .value(url(imageUrl)),
width: width
@@ -375,6 +391,10 @@ func divTabsItem(
)
}
func divFixedSize(_ value: Int) -> DivSize {
func fixedSize(_ value: Int) -> DivSize {
.divFixedSize(DivFixedSize(value: .value(value)))
}
func wrapContentSize() -> DivSize {
.divWrapContentSize(DivWrapContentSize())
}
@@ -1,7 +0,0 @@
{
"type": "gif",
"gif_url": "https://alicekit.s3.yandex.net/images_for_divs/sheep.gif",
"height": {
"type": "wrap_content"
}
}
@@ -1,7 +0,0 @@
{
"type": "gif",
"gif_url": "https://alicekit.s3.yandex.net/images_for_divs/sheep.gif",
"width": {
"type": "wrap_content"
}
}
@@ -1,7 +0,0 @@
{
"type": "image",
"image_url": "https://yastatic.net/s3/home/divkit/chess.png",
"height": {
"type": "wrap_content"
}
}
@@ -1,7 +0,0 @@
{
"type": "image",
"image_url": "https://yastatic.net/s3/home/divkit/chess.png",
"width": {
"type": "wrap_content"
}
}