Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
22 changes: 18 additions & 4 deletions lib/web_ui/lib/src/engine/semantics/accessibility.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,15 +73,19 @@ class AccessibilityAnnouncements {
final Map<dynamic, dynamic> 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);
}
Expand Down
32 changes: 32 additions & 0 deletions lib/web_ui/test/engine/semantics/accessibility_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<dynamic, dynamic> testInput = <dynamic, dynamic>{'data': <dynamic, dynamic>{'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<dynamic, dynamic> testInput = <dynamic, dynamic>{'data': <dynamic, dynamic>{'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<dynamic, dynamic> testInput = <dynamic, dynamic>{'data': <dynamic, dynamic>{'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<dynamic, dynamic> testInput = <dynamic, dynamic>{'data': <dynamic, dynamic>{'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'));
});
});
}