Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions resources/ios/NativeUISimpleRenderers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ struct NativeUIPressableRenderer: View {
NativeUIColumnRenderer(node: node, children: content)
.contentShape(Rectangle())
}
// Core skips its accessibility modifier for `:menu` pressables
// (the Menu owns the tap), so the label and hint go on the Menu
// here. Without them an icon-only menu reads as "pop up button".
.modifier(PressableMenuAccessibility(
label: node.props.getString("a11y_label"),
hint: node.props.getString("a11y_hint")
))
} else if node.props.getBool("has_context_menu") {
// `:context-menu` attached — the same items, opened by a long
// press. The tap stays core's `@press`. SwiftUI's context menu
Expand All @@ -41,6 +48,25 @@ struct NativeUIPressableRenderer: View {
}
}

/// Gives a `:menu` pressable's Menu its `a11y-label` and `a11y-hint`.
private struct PressableMenuAccessibility: ViewModifier {
let label: String
let hint: String

func body(content: Content) -> some View {
switch (label.isEmpty, hint.isEmpty) {
case (true, true):
content
case (false, true):
content.accessibilityLabel(Text(label))
case (true, false):
content.accessibilityHint(Text(hint))
case (false, false):
content.accessibilityLabel(Text(label)).accessibilityHint(Text(hint))
}
}
}

/// Render one menu item as either a Button or a Divider, mirroring the
/// `TopBarActionView` dropdown pattern from `NativeRootStackRenderer`.
@ViewBuilder
Expand Down
16 changes: 16 additions & 0 deletions tests/IosPressableMenuItemsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,19 @@
->toContain('childNodes: children')
->toContain('ForEach(children) { child in');
});

it('gives a menu pressable its accessibility label and hint on iOS', function () {
$source = file_get_contents(dirname(__DIR__).'/resources/ios/NativeUISimpleRenderers.swift');
$start = strpos($source, 'struct NativeUIPressableRenderer');
$pressable = substr($source, $start, strpos($source, 'private struct PressableMenuAccessibility') - $start);
$modifier = substr($source, strpos($source, 'private struct PressableMenuAccessibility'), strpos($source, 'private func pressableMenuItem') - strpos($source, 'private struct PressableMenuAccessibility'));

expect($pressable)
->toContain('.modifier(PressableMenuAccessibility(')
->toContain('label: node.props.getString("a11y_label")')
->toContain('hint: node.props.getString("a11y_hint")')
->and($modifier)
->toContain('case (true, true):')
->toContain('content.accessibilityLabel(Text(label))')
->toContain('content.accessibilityHint(Text(hint))');
});
Loading