diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index 612473e9567b0..abae46b6dd7a5 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -5,11 +5,21 @@ import 'dart:async'; import 'dart:typed_data'; -import '../../engine.dart' show registerHotRestartListener; +import '../../engine.dart' show registerHotRestartListener; import '../dom.dart'; import '../services.dart'; import '../util.dart'; +/// Determines the assertiveness level of the accessibility announcement. +/// +/// It is used to set the priority with which assistive technology should treat announcements. +/// +/// The order of this enum must match the order of the values in semantics_event.dart in framework. +enum Assertiveness { + polite, + assertive, +} + /// Singleton for accessing accessibility announcements from the platform. final AccessibilityAnnouncements accessibilityAnnouncements = AccessibilityAnnouncements.instance; @@ -63,15 +73,19 @@ class AccessibilityAnnouncements { final Map dataMap = inputMap.readDynamicJson('data'); final String? message = dataMap.tryString('message'); if (message != null && message.isNotEmpty) { - _initLiveRegion(message); + /// The default value for politeness is `polite`. + final int ariaLivePolitenessIndex = dataMap.tryInt('assertiveness') ?? 0; + final Assertiveness ariaLivePoliteness = Assertiveness.values[ariaLivePolitenessIndex]; + _initLiveRegion(message, ariaLivePoliteness); _removeElementTimer = Timer(durationA11yMessageIsOnDom, () { _element!.remove(); }); } } - void _initLiveRegion(String message) { - _domElement.setAttribute('aria-live', 'polite'); + void _initLiveRegion(String message, Assertiveness ariaLivePoliteness) { + final String assertiveLevel = (ariaLivePoliteness == Assertiveness.assertive) ? 'assertive' : 'polite'; + _domElement.setAttribute('aria-live', assertiveLevel); _domElement.text = message; domDocument.body!.append(_domElement); } diff --git a/lib/web_ui/test/engine/semantics/accessibility_test.dart b/lib/web_ui/test/engine/semantics/accessibility_test.dart index fde66c76fe6ad..c8b2059f202ea 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -55,5 +55,37 @@ void testMain() { () => expect(domDocument.getElementById('accessibility-element'), isNull)); }); + + test('Default value of aria-live is polite when assertiveness is not specified', () { + const Map testInput = {'data': {'message': 'message'}}; + accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); + final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; + + expect(input.getAttribute('aria-live'), equals('polite')); + }); + + test('aria-live is assertive when assertiveness is set to 1', () { + const Map testInput = {'data': {'message': 'message', 'assertiveness': 1}}; + accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); + final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; + + expect(input.getAttribute('aria-live'), equals('assertive')); + }); + + test('aria-live is polite when assertiveness is null', () { + const Map testInput = {'data': {'message': 'message', 'assertiveness': null}}; + accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); + final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; + + expect(input.getAttribute('aria-live'), equals('polite')); + }); + + test('aria-live is polite when assertiveness is set to 0', () { + const Map testInput = {'data': {'message': 'message', 'assertiveness': 0}}; + accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); + final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; + + expect(input.getAttribute('aria-live'), equals('polite')); + }); }); }