Commit 792c7aa8 authored by Shik Chen's avatar Shik Chen Committed by Chromium LUCI CQ

CCA: Announce detected QR code text in ChromeVox

This CL tweaks the a11y of detected QR code text by announcing the text
content and auto focus to the expand/copy button.

Bug: b/172879638
Test: Manually with Chromevox.

Change-Id: If9d219e7e397f219566e1790c17c6dfc5ad35ff6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2600428
Auto-Submit: Shik Chen <shik@chromium.org>
Reviewed-by: default avatarWei Lee <wtlee@chromium.org>
Commit-Queue: Shik Chen <shik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839319}
parent 8a3ad768
......@@ -58,6 +58,7 @@ const struct {
{"barcode_copy_link_button", IDS_BARCODE_COPY_LINK_BUTTON},
{"barcode_copy_text_button", IDS_BARCODE_COPY_TEXT_BUTTON},
{"barcode_link_detected", IDS_BARCODE_LINK_DETECTED},
{"barcode_text_detected", IDS_BARCODE_TEXT_DETECTED},
{"expert_mode_button", IDS_EXPERT_MODE_BUTTON},
{"expert_preview_metadata", IDS_EXPERT_PREVIEW_METADATA},
{"expert_save_metadata", IDS_EXPERT_SAVE_METADATA},
......
......@@ -6,6 +6,7 @@ import {browserProxy} from './browser_proxy/browser_proxy.js';
import {assert} from './chrome_util.js';
import * as dom from './dom.js';
import * as snackbar from './snackbar.js';
import * as state from './state.js';
import {OneShotTimer} from './timer.js';
// TODO(b/172879638): Tune the duration according to the final motion spec.
......@@ -48,7 +49,13 @@ function deactivate() {
function activate(container) {
container.classList.remove('invisible');
currentChip = container;
currentTimer = new OneShotTimer(deactivate, CHIP_DURATION);
if (state.get(state.State.TAB_NAVIGATION)) {
// Do not auto dismiss the chip when using keyboard for a11y. Screen reader
// might need long time to read the detected content.
currentTimer.stop();
}
}
/**
......@@ -75,6 +82,7 @@ function isSafeUrl(s) {
* @param {string} content The content to be copied.
* @param {string} snackbarLabel The label to be displayed on snackbar when the
* content is copied.
* @return {!HTMLElement} The copy button element.
*/
function setupCopyButton(container, content, snackbarLabel) {
const copyButton =
......@@ -83,6 +91,7 @@ function setupCopyButton(container, content, snackbarLabel) {
await navigator.clipboard.writeText(content);
snackbar.show(snackbarLabel);
};
return copyButton;
}
/**
......@@ -108,7 +117,6 @@ function showUrl(url) {
}
/**
* TODO(b/172879638): Handle a11y.
* Shows an actionable text chip.
* @param {string} text
*/
......@@ -125,9 +133,19 @@ function showText(text) {
expandEl.classList.toggle('hidden', !expandable);
expandEl.onclick = () => {
container.classList.toggle('expanded');
const expanded = container.classList.contains('expanded');
expandEl.setAttribute('aria-expanded', expanded.toString());
};
setupCopyButton(container, text, 'snackbar_text_copied');
const copyButton = setupCopyButton(container, text, 'snackbar_text_copied');
// TODO(b/172879638): There is a race in ChromeVox which will speak the
// focused element twice.
if (expandable) {
expandEl.focus();
} else {
copyButton.focus();
}
}
/**
......
......@@ -34,41 +34,44 @@ export class OneShotTimer {
*/
this.timeoutId_ = 0;
this.start_();
this.start();
}
/**
* Resets the timer delay.
* Starts the timer.
*/
resetTimeout() {
this.clearPendingTimeout_();
this.start_();
start() {
assert(this.timeoutId_ === 0);
this.timeoutId_ = setTimeout(this.handler_, this.timeout_);
}
/**
* Stops the timer and runs the scheduled handler immediately.
* Stops the pending timeout.
*/
fireNow() {
this.clearPendingTimeout_();
this.handler_();
stop() {
assert(this.timeoutId_ !== 0);
clearTimeout(this.timeoutId_);
this.timeoutId_ = 0;
}
/**
* Starts the timer.
* @private
* Resets the timer delay. It's a no-op if the timer is already stopped.
*/
start_() {
assert(this.timeoutId_ === 0);
this.timeoutId_ = setTimeout(this.handler_, this.timeout_);
resetTimeout() {
if (this.timeoutId_ === 0) {
return;
}
this.stop();
this.start();
}
/**
* Clears the pending timeout.
* @private
* Stops the timer and runs the scheduled handler immediately.
*/
clearPendingTimeout_() {
assert(this.timeoutId_ !== 0);
clearTimeout(this.timeoutId_);
this.timeoutId_ = 0;
fireNow() {
if (this.timeoutId_ !== 0) {
this.stop();
}
this.handler_();
}
}
......@@ -333,9 +333,12 @@
<message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_COPY_TEXT_BUTTON">
Copy text
</message>
<message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_LINK_DETECTED">
<message desc="Label for detected link on the barcode chip." name="IDS_BARCODE_LINK_DETECTED">
Link detected. <ph name="hostname">$1<ex>www.google.com</ex></ph>
</message>
<message desc="Label for detected text on the barcode chip." name="IDS_BARCODE_TEXT_DETECTED">
Text detected.
</message>
<message desc="Label for the button of expert mode options." name="IDS_EXPERT_MODE_BUTTON">
Expert mode
</message>
......
......@@ -417,10 +417,12 @@
</div>
</div>
<div id="barcode-chip-text-container"
class="invisible barcode-chip-container">
class="invisible barcode-chip-container"
role="dialog" i18n-label="barcode_text_detected"
aria-live="polite" aria-describedby="barcode-chip-text-content">
<div class="barcode-chip-text">
<div id="barcode-chip-text-content"></div>
<button id="barcode-chip-text-expand"></button>
<button id="barcode-chip-text-expand" aria-expanded="false"></button>
</div>
<div class="circle">
<button class="barcode-copy-button" tabindex="0"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment