[lensoverlay] Add a11y description for ghost loader
This CL plumbs through the state of the ghost loader to then be
accessible by the screen reader focused on the searchbox input field.
Note that aria-describedby or similar do not work across ShadowDOM,
boundaries, so this hacky solution is needed.
NO_IFTTT= I added the check for future changes.
Change-Id: Ic79adad79384969bc93539dc405d963d5201df6e
Fixed: 384052844
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6111918
Auto-Submit: Duncan Mercer <mercerd@google.com>
Commit-Queue: Duncan Mercer <mercerd@google.com>
Reviewed-by: Nihar Majmudar <niharm@google.com>
Cr-Commit-Position: refs/heads/main@{#1400365}
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.html b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
index 9f58cd5..b5613bb4 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.html
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.html
@@ -349,8 +349,12 @@
</lens-selection-overlay>
<initial-gradient id="initialGradient"></initial-gradient>
<div id="searchboxContainer">
- <cr-searchbox id="searchbox" on-focus="handleSearchboxFocused"
- on-blur="handleSearchboxBlurred">
+ <cr-searchbox
+ id="searchbox"
+ on-focus="handleSearchboxFocused"
+ on-blur="handleSearchboxBlurred"
+ searchbox-aria-description="[[getSearchboxAriaDescription(showErrorState, showGhostLoader)]]">
+ </cr-searchbox>
</cr-searchbox>
<cr-searchbox-ghost-loader id="searchboxGhostLoader"
show-error-state="{{showErrorState}}">
diff --git a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
index 062526b..2d875351 100644
--- a/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
+++ b/chrome/browser/resources/lens/overlay/lens_overlay_app.ts
@@ -277,7 +277,6 @@
});
this.eventTracker_.add(
document, 'pointermove', this.updateCursorPosition.bind(this));
-
this.performanceTracker.startSession();
}
@@ -551,6 +550,12 @@
return `${r}, ${g}, ${b}`;
}
+ private getSearchboxAriaDescription(): string {
+ // Get the the text from the ghost loader to add to the searchbox aria
+ // description.
+ return this.$.searchboxGhostLoader.getText();
+ }
+
setSearchboxFocusForTesting(isFocused: boolean) {
this.isSearchboxFocused = isFocused;
}
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
index cd4ef96c..462945c2 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.html
@@ -197,8 +197,16 @@
on-click="onBackArrowClick" aria-label="$i18n{backButton}">
</cr-icon-button>
</template>
- <cr-searchbox id="searchbox" placeholder-text="{{placeholderText}}"></cr-searchbox>
- <cr-searchbox-ghost-loader show-error-state="{{showErrorState}}"></cr-searchbox-ghost-loader>
+ <cr-searchbox
+ id="searchbox"
+ placeholder-text="{{placeholderText}}"
+ searchbox-aria-description="[[getSearchboxAriaDescription(showErrorState, showGhostLoader)]]"
+ >
+ </cr-searchbox>
+ <cr-searchbox-ghost-loader
+ id="searchboxGhostLoader"
+ show-error-state="{{showErrorState}}">
+ </cr-searchbox-ghost-loader>
</div>
<!-- Network error UI that is initially hidden -->
diff --git a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
index 2b776c85..28819c5 100644
--- a/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
+++ b/chrome/browser/resources/lens/overlay/side_panel/side_panel_app.ts
@@ -17,6 +17,7 @@
import {loadTimeData} from '//resources/js/load_time_data.js';
import type {Url} from '//resources/mojo/url/mojom/url.mojom-webui.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {SearchboxGhostLoaderElement} from '/lens/shared/searchbox_ghost_loader.js';
import type {LensSidePanelPageHandlerInterface} from '../lens_side_panel.mojom-webui.js';
import {handleEscapeSearchbox, onSearchboxKeydown} from '../searchbox_utils.js';
@@ -36,6 +37,7 @@
ghostLoader: SidePanelGhostLoaderElement,
networkErrorPage: HTMLDivElement,
searchbox: SearchboxElement,
+ searchboxGhostLoader: SearchboxGhostLoaderElement,
};
}
@@ -271,6 +273,12 @@
'';
}
+ private getSearchboxAriaDescription(): string {
+ // Get the the text from the ghost loader to add to the searchbox aria
+ // description.
+ return this.$.searchboxGhostLoader.getText();
+ }
+
private suppressGhostLoader_() {
// If page bytes weren't successfully uploaded, ghost loader shouldn't be
// visible.
diff --git a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
index 99d3eca0..dff2983 100644
--- a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
+++ b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.html
@@ -116,6 +116,7 @@
}
}
</style>
+<!-- LINT.IfChange(GhostLoaderText) -->
<div id="content">
<template is="dom-if" if="[[showContextualSearchboxLoadingState]]">
<div id="loadingState" class="status-container">
@@ -143,3 +144,4 @@
</div>
</template>
</div>
+<!-- LINT.ThenChange(//chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts:GhostLoaderText) -->
diff --git a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
index 5d19c113..babe573 100644
--- a/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
+++ b/chrome/browser/resources/lens/shared/searchbox_ghost_loader.ts
@@ -6,6 +6,7 @@
import '/strings.m.js';
import './searchbox_shared_style.css.js';
+import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
import {assert} from '//resources/js/assert.js';
import {loadTimeData} from '//resources/js/load_time_data.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -14,8 +15,11 @@
import {BrowserProxyImpl} from './searchbox_ghost_loader_browser_proxy.js';
import type {BrowserProxy} from './searchbox_ghost_loader_browser_proxy.js';
+const SearchboxGhostLoaderElementBase = I18nMixin(PolymerElement);
+
// Displays a loading preview while waiting on autocomplete to return matches.
-export class SearchboxGhostLoaderElement extends PolymerElement {
+export class SearchboxGhostLoaderElement extends
+ SearchboxGhostLoaderElementBase {
static get is() {
return 'cr-searchbox-ghost-loader';
}
@@ -43,6 +47,7 @@
// Whether the autocomplete stop timer has triggered. If it has, we should
// hide the ghost loader. We also show the error text in this case.
private showErrorState: boolean;
+ private showContextualSearchboxLoadingState: boolean;
private browserProxy: BrowserProxy = BrowserProxyImpl.getInstance();
private listenerIds: number[];
@@ -64,6 +69,20 @@
this.listenerIds = [];
}
+ // LINT.IfChange(GhostLoaderText)
+ getText(): string {
+ if (!this.showContextualSearchboxLoadingState) {
+ return this.i18n('searchboxGhostLoaderNoSuggestText');
+ }
+
+ if (this.showErrorState) {
+ return this.i18n('searchboxGhostLoaderErrorText');
+ }
+
+ return this.i18n('searchboxGhostLoaderHintTextPrimary');
+ }
+ // LINT.ThenChange(//chrome/browser/resources/lens/shared/searchbox_ghost_loader.html:GhostLoaderText)
+
showErrorStateForTesting() {
this.showErrorState = true;
}
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.html b/ui/webui/resources/cr_components/searchbox/searchbox.html
index 95c92c0e..566318d 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.html
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.html
@@ -323,6 +323,7 @@
<input id="input" class="truncate" type="search" autocomplete="off"
spellcheck="false" aria-live="[[inputAriaLive_]]" role="combobox"
aria-expanded="[[dropdownIsVisible]]" aria-controls="matches"
+ aria-description="[[searchboxAriaDescription]]"
placeholder="[[computePlaceholderText_(placeholderText, showThumbnail)]]"
on-copy="onInputCutCopy_"
on-cut="onInputCutCopy_" on-focus="onInputFocus_"
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.ts b/ui/webui/resources/cr_components/searchbox/searchbox.ts
index 2ff283eb..9871b755 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.ts
@@ -16,11 +16,11 @@
import {NavigationPredictor} from './omnibox.mojom-webui.js';
import {getTemplate} from './searchbox.html.js';
+import type {AutocompleteMatch, AutocompleteResult, PageCallbackRouter, PageHandlerInterface} from './searchbox.mojom-webui.js';
+import {SideType} from './searchbox.mojom-webui.js';
import {SearchboxBrowserProxy} from './searchbox_browser_proxy.js';
import type {SearchboxDropdownElement} from './searchbox_dropdown.js';
import type {SearchboxIconElement} from './searchbox_icon.js';
-import type {AutocompleteMatch, AutocompleteResult, PageCallbackRouter, PageHandlerInterface} from './searchbox.mojom-webui.js';
-import {SideType} from './searchbox.mojom-webui.js';
import {decodeString16, mojoString16} from './utils.js';
interface Input {
@@ -112,6 +112,12 @@
reflectToAttribute: true,
},
+ /** The aria description to include on the input element. */
+ searchboxAriaDescription: {
+ type: String,
+ value: '',
+ },
+
/** Whether the Google Lens icon should be visible in the searchbox. */
searchboxLensSearchEnabled: {
type: Boolean,
@@ -503,7 +509,7 @@
if (loadTimeData.getBoolean('reportMetrics')) {
const metricsReporter = MetricsReporterImpl.getInstance();
if (!metricsReporter.hasLocalMark('CharTyped')) {
- metricsReporter.mark('CharTyped');
+ metricsReporter.mark('CharTyped');
}
}