-
Notifications
You must be signed in to change notification settings - Fork 28.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add aria menu roles to menu-related widgets #164741
Conversation
a026241
to
261a16f
Compare
menuItem, | ||
|
||
/// A item with a checkbox in a dropdown created by [menu] or [menuBar]. | ||
menuItemCheckbox, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order here need to be the same as semantics_node.h , so we have to either add this to the end or the enum in semantics_node.h has to be moved to the middle and shift all number after them by 2.
For now adding enum in the middle is fine since this hasn't yet expose to embedder.h. Once we expose them in embedder.h, moving enum around will be a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see! Thanks for reminding! I also noticed the dialog, alertDialog and table roles have reversed order. Just matched all of them.
engine/src/flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
Outdated
Show resolved
Hide resolved
|
||
if (semanticsObject.isFlagsDirty) { | ||
String checkedValue; | ||
if (semanticsObject.isCheckable && semanticsObject.hasFlag(ui.SemanticsFlag.isChecked)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this reuse SemanticCheckable behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems SemanticCheckable doesn't have mixed state and only contain on and off status.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SemanticCheckable is a SemanticsRole. Just added a Checkable behavior.
engine/src/flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
Outdated
Show resolved
Hide resolved
static FlutterError? _semanticsMenuBar(SemanticsNode node) { | ||
if (node.childrenCount < 1) { | ||
return FlutterError('a menu bar cannot be empty'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also check if all children are menu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the description on the mdn website, a menubar's children can be menuItem
, menuItemCheckbox
, menuItemRadio
, group
, and separator
. It looks pretty flexible, so I didn't add more checks for menu
and menuBar
, is this okay?
EDIT:
Additionally, in DropdownButton, PopupMenuButton, MenuAnchor, and MenuBar, the menu items are not menu's immediate children, so I added aria-owns
on menu and menuBar.
engine/src/flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
Outdated
Show resolved
Hide resolved
engine/src/flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
Outdated
Show resolved
Hide resolved
3c7a476
to
301f656
Compare
The PR is ready for another round of review:-) |
} | ||
|
||
bool _isMenuItem(SemanticsObject semanticsObject) { | ||
if (semanticsObject.role == ui.SemanticsRole.menuItem || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just return the condition directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also since this if for menubar, should this only owns the Menu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MenuBar in framework side also have a SingleChildScrollView to wrap the children. This _MenuPanel contains a scrollable and it is used by menu bar:)
engine/src/flutter/lib/web_ui/lib/src/engine/semantics/menus.dart
Outdated
Show resolved
Hide resolved
f95b557
to
4e50291
Compare
50c6960
to
2760e69
Compare
@@ -300,6 +300,80 @@ void main() { | |||
}); | |||
}); | |||
|
|||
group('menu', () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add failure case for menuitem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
/// A visible list of items or a widget that can be made to open and close. | ||
menu, | ||
|
||
/// A presentation of [menu] that usually remains visible and is usually |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// A presentation of [menu] that usually remains visible and is usually | |
/// A presentation of [menu]s that usually remains visible and is usually |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the doc.
/// (i.e. [SemanticsObject.isChecked] and [SemanticsObject.isMixed] are | ||
/// false). If the node is not checkable (i.e. [SemanticsObject.isCheckable] | ||
/// is false), then `aria-checked` is unset. | ||
class Checkable extends SemanticBehavior { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can some of the SemanticCheckable's logic to be replaced by this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a very straightforward refactor on SemanticCheckable as it involves all checkbox, radio and switch. Checkable behavior is more relevant to checkbox. I just added more doc for it. Maybe a separate PR for refactoring SemanticCheckable would be better?
} | ||
|
||
String attributeValue = ''; | ||
for (final int id in ids) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be simplified as
ids.map((id) => 'flt-semantic-node-$id').join(' ')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Thanks for helping simplify this.
|
||
String attributeValue = ''; | ||
for (final int id in ids) { | ||
attributeValue = '$attributeValue flt-semantic-node-$id'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as below
for (final int id in ids) { | ||
attributeValue = '$attributeValue flt-semantic-node-$id'; | ||
} | ||
setAttribute('aria-owns', attributeValue.trim()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to trim if using join
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/// Uses aria menubar role to convey this semantic information to the element. | ||
/// | ||
/// Screen-readers takes advantage of "aria-label" to describe the visual. | ||
class SemanticMenuBar extends SemanticRole { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have been reading the doc in mdn aria role menubar
Menubar interactions should be similar to the typical menu bar interaction in a desktop graphical user interface. In Google Docs, each of those menu items is a menuitem with a popup submenu, so each has an aria-haspopup attribute set to true. The menubar element does not.
This seems to me suggesting the menu in menubar should have role menuItem, but I am not sure if this is what it means. I am curious what your interpretation is on the doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is menuBar is just a horizontal menu, so they are not a parent-child relationship. And menubar should contain a list of submenu(menuItem with aria-haspopup
attribute setting to true).
@@ -29,6 +29,13 @@ class SemanticButton extends SemanticRole { | |||
} else { | |||
removeAttribute('aria-disabled'); | |||
} | |||
|
|||
// This is especially useful when the button is used to open a menu. | |||
if (semanticsObject.hasExpandedState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expansion tile can be a button, too.
Also this role already has Expandable behavior which will assign aria-expanded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense! Reverted!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created an issue for the popup flag: #165505 :)
onPressed: widget.enabled ? showButtonMenu : null, | ||
enableFeedback: enableFeedback, | ||
style: widget.style, | ||
return MergeSemantics( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you wrap the icon with semantics(expanded) instead of wraping the entire iconButton, you don't need the mergesemantics I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Related to #157177
This PR is to add the following changes:
SemanticsRole.menu
,SemanticsRole.menuItem
,SemanticsRole.menuBar
,SemanticsRole.menuItemCheckbox
, andSemanticsRole.menuItemRadio
.SemanticsRole.menu
role in,MenuAnchor
DropdownButton
, andPopupMenuButton
* AddSemanticsRole.menuBar
role toMenuBar
SemanticsRole.menuItem
to,MenuItemButton
DropdownMenuItem
andPopupMenuItem
* AddSemanticsRole.menuItemCheckbox
toCheckboxMenuButton
* AddSemanticsRole.menuItemRadio
toRadioMenuButton
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.