From 599f2964480ffe4e5e1e77942a8b36e653cd51d1 Mon Sep 17 00:00:00 2001 From: Negar Date: Wed, 13 Jul 2022 10:41:16 -0700 Subject: [PATCH 1/3] Add support for assertive announcements in aria-live --- .../src/engine/semantics/accessibility.dart | 11 +++--- .../engine/semantics/accessibility_test.dart | 34 ++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index 612473e9567b0..0ae66904bb928 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -5,7 +5,7 @@ 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'; @@ -63,15 +63,18 @@ class AccessibilityAnnouncements { final Map dataMap = inputMap.readDynamicJson('data'); final String? message = dataMap.tryString('message'); if (message != null && message.isNotEmpty) { - _initLiveRegion(message); + final bool? assertiveAnnouncement = dataMap.tryBool('assertiveAnnouncement'); + _initLiveRegion(message, assertiveAnnouncement : assertiveAnnouncement); _removeElementTimer = Timer(durationA11yMessageIsOnDom, () { _element!.remove(); }); } } - void _initLiveRegion(String message) { - _domElement.setAttribute('aria-live', 'polite'); + void _initLiveRegion(String message, {bool? assertiveAnnouncement = true}) { + //The default is assertive. If assertiveAnnouncement is set to false, the mode will be polite. + final String assertiveLevel = (assertiveAnnouncement == null || assertiveAnnouncement)? '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..92fbe8705779b 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -46,7 +46,7 @@ void testMain() { ); final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; - expect(input.getAttribute('aria-live'), equals('polite')); + expect(input.getAttribute('aria-live'), equals('assertive')); expect(input.text, testMessage); // The element should have been removed after the duration. @@ -55,5 +55,37 @@ void testMain() { () => expect(domDocument.getElementById('accessibility-element'), isNull)); }); + + test('Default value of aria-live is assertive when assertiveAnnouncement 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('assertive')); + }); + + test('aria-live is assertive when assertiveAnnouncement is set to true', () { + const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': true}}; + 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 assertive when assertiveAnnouncement is null', () { + const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': null}}; + 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 assertiveAnnouncement is set to false', () { + const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': false}}; + accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); + final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; + + expect(input.getAttribute('aria-live'), equals('polite')); + }); }); } From 39f94451dfcd999918ae75900d6529335400953a Mon Sep 17 00:00:00 2001 From: Negar Date: Tue, 19 Jul 2022 17:36:52 -0700 Subject: [PATCH 2/3] changed aria live settings from bool to enum --- .../src/engine/semantics/accessibility.dart | 19 ++++++++++++++----- .../engine/semantics/accessibility_test.dart | 14 +++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index 0ae66904bb928..21a5e40bccf7e 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -10,6 +10,14 @@ import '../dom.dart'; import '../services.dart'; import '../util.dart'; +/// Determines the politeness setting of aria live in Flutter web. +/// +/// The order of this enum must match the order of the values in semantics_event.dart in framework. +enum AriaLivePolitenessSetting { + polite, + assertive, +} + /// Singleton for accessing accessibility announcements from the platform. final AccessibilityAnnouncements accessibilityAnnouncements = AccessibilityAnnouncements.instance; @@ -63,17 +71,18 @@ class AccessibilityAnnouncements { final Map dataMap = inputMap.readDynamicJson('data'); final String? message = dataMap.tryString('message'); if (message != null && message.isNotEmpty) { - final bool? assertiveAnnouncement = dataMap.tryBool('assertiveAnnouncement'); - _initLiveRegion(message, assertiveAnnouncement : assertiveAnnouncement); + /// The default value for politenessSetting is assertive. + final int ariaLivePolitenessSettingIndex = dataMap.tryInt('ariaLivePolitenessSetting')?? 1; + final AriaLivePolitenessSetting ariaLivePolitenessSetting = AriaLivePolitenessSetting.values[ariaLivePolitenessSettingIndex]; + _initLiveRegion(message, ariaLivePolitenessSetting); _removeElementTimer = Timer(durationA11yMessageIsOnDom, () { _element!.remove(); }); } } - void _initLiveRegion(String message, {bool? assertiveAnnouncement = true}) { - //The default is assertive. If assertiveAnnouncement is set to false, the mode will be polite. - final String assertiveLevel = (assertiveAnnouncement == null || assertiveAnnouncement)? 'assertive' : 'polite'; + void _initLiveRegion(String message, AriaLivePolitenessSetting ariaLivePolitenessSetting) { + final String assertiveLevel = (ariaLivePolitenessSetting == AriaLivePolitenessSetting.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 92fbe8705779b..25a5ce2f099b1 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -56,7 +56,7 @@ void testMain() { expect(domDocument.getElementById('accessibility-element'), isNull)); }); - test('Default value of aria-live is assertive when assertiveAnnouncement is not specified', () { + test('Default value of aria-live is assertive when ariaLivePolitenessSetting is not specified', () { const Map testInput = {'data': {'message': 'message'}}; accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; @@ -64,24 +64,24 @@ void testMain() { expect(input.getAttribute('aria-live'), equals('assertive')); }); - test('aria-live is assertive when assertiveAnnouncement is set to true', () { - const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': true}}; + test('aria-live is assertive when ariaLivePolitenessSetting is set to 1', () { + const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': 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 assertive when assertiveAnnouncement is null', () { - const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': null}}; + test('aria-live is assertive when ariaLivePolitenessSetting is null', () { + const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': null}}; 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 assertiveAnnouncement is set to false', () { - const Map testInput = {'data': {'message': 'message', 'assertiveAnnouncement': false}}; + test('aria-live is polite when ariaLivePolitenessSetting is set to 0', () { + const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': 0}}; accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage(testInput)); final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; From 4f276f11677562665f4830dc4190aeee6f930956 Mon Sep 17 00:00:00 2001 From: Negar Date: Fri, 22 Jul 2022 17:36:55 -0700 Subject: [PATCH 3/3] default value of aria-live is --- .../src/engine/semantics/accessibility.dart | 18 +++++++++-------- .../engine/semantics/accessibility_test.dart | 20 +++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index 21a5e40bccf7e..abae46b6dd7a5 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -10,10 +10,12 @@ import '../dom.dart'; import '../services.dart'; import '../util.dart'; -/// Determines the politeness setting of aria live in Flutter web. +/// 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 AriaLivePolitenessSetting { +enum Assertiveness { polite, assertive, } @@ -71,18 +73,18 @@ class AccessibilityAnnouncements { final Map dataMap = inputMap.readDynamicJson('data'); final String? message = dataMap.tryString('message'); if (message != null && message.isNotEmpty) { - /// The default value for politenessSetting is assertive. - final int ariaLivePolitenessSettingIndex = dataMap.tryInt('ariaLivePolitenessSetting')?? 1; - final AriaLivePolitenessSetting ariaLivePolitenessSetting = AriaLivePolitenessSetting.values[ariaLivePolitenessSettingIndex]; - _initLiveRegion(message, ariaLivePolitenessSetting); + /// 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, AriaLivePolitenessSetting ariaLivePolitenessSetting) { - final String assertiveLevel = (ariaLivePolitenessSetting == AriaLivePolitenessSetting.assertive)? 'assertive' : '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 25a5ce2f099b1..c8b2059f202ea 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -46,7 +46,7 @@ void testMain() { ); final DomHTMLLabelElement input = domDocument.getElementById('accessibility-element')! as DomHTMLLabelElement; - expect(input.getAttribute('aria-live'), equals('assertive')); + expect(input.getAttribute('aria-live'), equals('polite')); expect(input.text, testMessage); // The element should have been removed after the duration. @@ -56,32 +56,32 @@ void testMain() { expect(domDocument.getElementById('accessibility-element'), isNull)); }); - test('Default value of aria-live is assertive when ariaLivePolitenessSetting is not specified', () { + 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('assertive')); + expect(input.getAttribute('aria-live'), equals('polite')); }); - test('aria-live is assertive when ariaLivePolitenessSetting is set to 1', () { - const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': 1}}; + 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 assertive when ariaLivePolitenessSetting is null', () { - const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': null}}; + 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('assertive')); + expect(input.getAttribute('aria-live'), equals('polite')); }); - test('aria-live is polite when ariaLivePolitenessSetting is set to 0', () { - const Map testInput = {'data': {'message': 'message', 'ariaLivePolitenessSetting': 0}}; + 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;