Skip to content

Commit 34c265f

Browse files
authored
feat!: announce toasts via shared ARIA live region (#9672)
* feat: announce toasts via shared ARIA live region * chore: add extra space
1 parent 3389f87 commit 34c265f

2 files changed

Lines changed: 34 additions & 26 deletions

File tree

packages/blockly/core/toast.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface ToastOptions {
4545
* How prominently/interrupting the readout of the toast should be for
4646
* screenreaders. Corresponds to aria-live and defaults to polite.
4747
*/
48-
assertiveness?: Toast.Assertiveness;
48+
assertiveness?: aria.LiveRegionAssertiveness;
4949
}
5050

5151
/**
@@ -89,15 +89,13 @@ export class Toast {
8989
const {
9090
message,
9191
duration = 5,
92-
assertiveness = Toast.Assertiveness.POLITE,
92+
assertiveness = aria.LiveRegionAssertiveness.POLITE,
9393
} = options;
9494

9595
const toast = document.createElement('div');
9696
workspace.getInjectionDiv().appendChild(toast);
9797
toast.dataset.toastId = options.id;
9898
toast.className = CLASS_NAME;
99-
aria.setRole(toast, aria.Role.STATUS);
100-
aria.setState(toast, aria.State.LIVE, assertiveness);
10199

102100
const messageElement = toast.appendChild(document.createElement('div'));
103101
messageElement.className = MESSAGE_CLASS_NAME;
@@ -157,6 +155,11 @@ export class Toast {
157155
toast.addEventListener('mouseleave', setToastTimeout);
158156
setToastTimeout();
159157

158+
aria.announceDynamicAriaState(message, {
159+
assertiveness,
160+
role: aria.Role.STATUS,
161+
});
162+
160163
return toast;
161164
}
162165

@@ -174,17 +177,6 @@ export class Toast {
174177
}
175178
}
176179

177-
/**
178-
* Options for how aggressively toasts should be read out by screenreaders.
179-
* Values correspond to those for aria-live.
180-
*/
181-
export namespace Toast {
182-
export enum Assertiveness {
183-
ASSERTIVE = 'assertive',
184-
POLITE = 'polite',
185-
}
186-
}
187-
188180
Css.register(`
189181
.${CLASS_NAME} {
190182
font-size: 1.2rem;

packages/blockly/tests/mocha/toast_test.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ suite('Toasts', function () {
1414
setup(function () {
1515
sharedTestSetup.call(this);
1616
this.workspace = Blockly.inject('blocklyDiv', {});
17+
this.liveRegion = document.getElementById('blocklyAriaAnnounce');
1718
this.toastIsVisible = (message) => {
1819
const toast = this.workspace
1920
.getInjectionDiv()
@@ -97,16 +98,20 @@ suite('Toasts', function () {
9798
clock.restore();
9899
});
99100

100-
test('default to polite assertiveness', function () {
101+
test('toast announces message with status role and polite assertiveness', function () {
101102
const message = 'texas toast';
102103
Blockly.Toast.show(this.workspace, {message, id: 'test'});
103-
const toast = this.workspace
104-
.getInjectionDiv()
105-
.querySelector('.blocklyToast');
106104

105+
this.clock.tick(11);
106+
107+
assert.include(this.liveRegion.textContent, message);
107108
assert.equal(
108-
toast.getAttribute('aria-live'),
109-
Blockly.Toast.Assertiveness.POLITE,
109+
this.liveRegion.getAttribute('role'),
110+
Blockly.utils.aria.Role.STATUS,
111+
);
112+
assert.equal(
113+
this.liveRegion.getAttribute('aria-live'),
114+
Blockly.utils.aria.LiveRegionAssertiveness.POLITE,
110115
);
111116
});
112117

@@ -115,15 +120,26 @@ suite('Toasts', function () {
115120
Blockly.Toast.show(this.workspace, {
116121
message,
117122
id: 'test',
118-
assertiveness: Blockly.Toast.Assertiveness.ASSERTIVE,
123+
assertiveness: Blockly.utils.aria.LiveRegionAssertiveness.ASSERTIVE,
119124
});
125+
126+
this.clock.tick(11);
127+
128+
assert.equal(
129+
this.liveRegion.getAttribute('aria-live'),
130+
Blockly.utils.aria.LiveRegionAssertiveness.ASSERTIVE,
131+
);
132+
});
133+
134+
test('toast is not itself a live region', function () {
135+
const message = 'texas toast';
136+
Blockly.Toast.show(this.workspace, {message, id: 'test'});
137+
120138
const toast = this.workspace
121139
.getInjectionDiv()
122140
.querySelector('.blocklyToast');
123141

124-
assert.equal(
125-
toast.getAttribute('aria-live'),
126-
Blockly.Toast.Assertiveness.ASSERTIVE,
127-
);
142+
assert.isNull(toast.getAttribute('aria-live'));
143+
assert.notEqual(toast.getAttribute('role'), Blockly.utils.aria.Role.STATUS);
128144
});
129145
});

0 commit comments

Comments
 (0)