Wires up expanded state in web engine (#164048)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> fixes https://github.com/flutter/flutter/issues/162141 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Mouad Debbar <mouad.debbar@gmail.com>
This commit is contained in:
parent
b8f274ddce
commit
37ff5a337a
@ -42685,6 +42685,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/scene_view.dart + ../../../fl
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/expandable.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/header.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/heading.dart + ../../../flutter/LICENSE
|
||||
@ -45617,6 +45618,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/scene_view.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/expandable.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/header.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/heading.dart
|
||||
|
@ -105,6 +105,7 @@ export 'engine/scene_painting.dart';
|
||||
export 'engine/scene_view.dart';
|
||||
export 'engine/semantics/accessibility.dart';
|
||||
export 'engine/semantics/checkable.dart';
|
||||
export 'engine/semantics/expandable.dart';
|
||||
export 'engine/semantics/focusable.dart';
|
||||
export 'engine/semantics/header.dart';
|
||||
export 'engine/semantics/heading.dart';
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
export 'semantics/accessibility.dart';
|
||||
export 'semantics/checkable.dart';
|
||||
export 'semantics/expandable.dart';
|
||||
export 'semantics/focusable.dart';
|
||||
export 'semantics/header.dart';
|
||||
export 'semantics/heading.dart';
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'semantics.dart';
|
||||
|
||||
/// Adds expandability behavior to a semantic node.
|
||||
///
|
||||
/// An expandable node would have the `aria-expanded` attribute set to "true" if the node
|
||||
/// is currently expanded (i.e. [SemanticsObject.isExpanded] is true), and set
|
||||
/// to "false" if it's not expanded (i.e. [SemanticsObject.isExpanded] is
|
||||
/// false). If the node is not expandable (i.e. [SemanticsObject.isExpandable]
|
||||
/// is false), then `aria-expanded` is unset.
|
||||
class Expandable extends SemanticBehavior {
|
||||
Expandable(super.semanticsObject, super.owner);
|
||||
|
||||
@override
|
||||
void update() {
|
||||
if (semanticsObject.isFlagsDirty) {
|
||||
if (semanticsObject.isExpandable) {
|
||||
owner.setAttribute('aria-expanded', semanticsObject.isExpanded);
|
||||
} else {
|
||||
owner.removeAttribute('aria-expanded');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import '../vector_math.dart';
|
||||
import '../window.dart';
|
||||
import 'accessibility.dart';
|
||||
import 'checkable.dart';
|
||||
import 'expandable.dart';
|
||||
import 'focusable.dart';
|
||||
import 'header.dart';
|
||||
import 'heading.dart';
|
||||
@ -461,6 +462,7 @@ abstract class SemanticRole {
|
||||
addRouteName();
|
||||
addLabelAndValue(preferredRepresentation: preferredLabelRepresentation);
|
||||
addSelectableBehavior();
|
||||
addExpandableBehavior();
|
||||
}
|
||||
|
||||
/// Initializes a blank role for a [semanticsObject].
|
||||
@ -627,6 +629,10 @@ abstract class SemanticRole {
|
||||
}
|
||||
}
|
||||
|
||||
void addExpandableBehavior() {
|
||||
addSemanticBehavior(Expandable(semanticsObject, this));
|
||||
}
|
||||
|
||||
/// Adds a semantic behavior to this role.
|
||||
///
|
||||
/// This method should be called by concrete implementations of
|
||||
@ -1947,6 +1953,19 @@ class SemanticsObject {
|
||||
/// selected.
|
||||
bool get isSelected => hasFlag(ui.SemanticsFlag.isSelected);
|
||||
|
||||
/// If true, this node represents something that can be annotated as
|
||||
/// "expanded", such as a expansion tile or drop down menu
|
||||
///
|
||||
/// Expandability is managed by `aria-expanded`.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [isExpanded], which indicates whether the node is currently selected.
|
||||
bool get isExpandable => hasFlag(ui.SemanticsFlag.hasExpandedState);
|
||||
|
||||
/// Indicates whether the node is currently expanded.
|
||||
bool get isExpanded => hasFlag(ui.SemanticsFlag.isExpanded);
|
||||
|
||||
/// Role-specific adjustment of the vertical position of the child container.
|
||||
///
|
||||
/// This is used, for example, by the [SemanticScrollable] to compensate for the
|
||||
|
@ -86,6 +86,9 @@ void runSemanticsTests() {
|
||||
group('selectables', () {
|
||||
_testSelectables();
|
||||
});
|
||||
group('expandables', () {
|
||||
_testExpandables();
|
||||
});
|
||||
group('tappable', () {
|
||||
_testTappable();
|
||||
});
|
||||
@ -2328,6 +2331,77 @@ void _testSelectables() {
|
||||
});
|
||||
}
|
||||
|
||||
void _testExpandables() {
|
||||
test('renders and updates non-expandable, expanded, and unexpanded nodes', () async {
|
||||
semantics()
|
||||
..debugOverrideTimestampFunction(() => _testTime)
|
||||
..semanticsEnabled = true;
|
||||
|
||||
final tester = SemanticsTester(owner());
|
||||
tester.updateNode(
|
||||
id: 0,
|
||||
rect: const ui.Rect.fromLTRB(0, 0, 100, 60),
|
||||
children: <SemanticsNodeUpdate>[
|
||||
tester.updateNode(id: 1, isSelectable: false, rect: const ui.Rect.fromLTRB(0, 0, 100, 20)),
|
||||
tester.updateNode(
|
||||
id: 2,
|
||||
isExpandable: true,
|
||||
isExpanded: false,
|
||||
rect: const ui.Rect.fromLTRB(0, 20, 100, 40),
|
||||
),
|
||||
tester.updateNode(
|
||||
id: 3,
|
||||
isExpandable: true,
|
||||
isExpanded: true,
|
||||
rect: const ui.Rect.fromLTRB(0, 40, 100, 60),
|
||||
),
|
||||
],
|
||||
);
|
||||
tester.apply();
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
// Missing attributes cannot be expressed using HTML patterns, so check directly.
|
||||
final nonExpandable = owner().debugSemanticsTree![1]!.element;
|
||||
expect(nonExpandable.getAttribute('aria-expanded'), isNull);
|
||||
|
||||
// Flip the values and check that that ARIA attribute is updated.
|
||||
tester.updateNode(
|
||||
id: 2,
|
||||
isExpandable: true,
|
||||
isExpanded: true,
|
||||
rect: const ui.Rect.fromLTRB(0, 20, 100, 40),
|
||||
);
|
||||
tester.updateNode(
|
||||
id: 3,
|
||||
isExpandable: true,
|
||||
isExpanded: false,
|
||||
rect: const ui.Rect.fromLTRB(0, 40, 100, 60),
|
||||
);
|
||||
tester.apply();
|
||||
|
||||
expectSemanticsTree(owner(), '''
|
||||
<sem>
|
||||
<sem-c>
|
||||
<sem></sem>
|
||||
<sem aria-expanded="true"></sem>
|
||||
<sem aria-expanded="false"></sem>
|
||||
</sem-c>
|
||||
</sem>
|
||||
''');
|
||||
|
||||
semantics().semanticsEnabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _testTappable() {
|
||||
test('renders an enabled button', () async {
|
||||
semantics()
|
||||
|
@ -34,6 +34,8 @@ class SemanticsTester {
|
||||
bool? isChecked,
|
||||
bool? isSelectable,
|
||||
bool? isSelected,
|
||||
bool? isExpandable,
|
||||
bool? isExpanded,
|
||||
bool? isButton,
|
||||
bool? isLink,
|
||||
bool? isTextField,
|
||||
@ -130,6 +132,12 @@ class SemanticsTester {
|
||||
if (isSelected ?? false) {
|
||||
flags |= ui.SemanticsFlag.isSelected.index;
|
||||
}
|
||||
if (isExpandable ?? false) {
|
||||
flags |= ui.SemanticsFlag.hasExpandedState.index;
|
||||
}
|
||||
if (isExpanded ?? false) {
|
||||
flags |= ui.SemanticsFlag.isExpanded.index;
|
||||
}
|
||||
if (isButton ?? false) {
|
||||
flags |= ui.SemanticsFlag.isButton.index;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user