0

personalization: accessibility in image grids

Add role=listbox to iron-list elements, and role=option to image
containers. Add aria-posinset and aria-setsize attributes so screen
readers report correct size of image grids.
Add aria-label for back button.
Add container with role=main and aria-label of current page -
"Wallpaper Collections" on main page or localized name of collection.

BUG=b/192975897
TEST=use Chromevox to navigate through new wallpaper app

Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: If7796d4405fb41eb8910da8934b912511bbbff72
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3010318
Commit-Queue: Jeffrey Young <cowmoo@chromium.org>
Reviewed-by: Tao Wu <wutao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#900177}
This commit is contained in:
Jeffrey Young
2021-07-09 22:27:54 +00:00
committed by Chromium LUCI CQ
parent e6ffb0cf42
commit 52af24caec
12 changed files with 139 additions and 20 deletions

@ -1513,6 +1513,9 @@ Try tapping the mic to ask me anything.
<message name="IDS_PERSONALIZATION_APP_BACK_BUTTON" desc="Aria label for the back button to return to a prior page">
Back to <ph name="PAGE_NAME">$1<ex>Wallpaper</ex></ph>
</message>
<message name="IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS" desc="Aria label for the wallpaper collections grid">
Wallpaper Collections
</message>
<message name="IDS_PERSONALIZATION_APP_CURRENTLY_SET" desc="Label for the currently set user wallpaper section.">
Currently set
</message>

@ -0,0 +1 @@
9886a8cbb0984357b1fc02e21d4962e63972b364

@ -60,7 +60,8 @@ void AddStrings(content::WebUIDataSource* source) {
{"title", IDS_PERSONALIZATION_APP_TITLE},
{"back", IDS_PERSONALIZATION_APP_BACK_BUTTON},
{"currentlySet", IDS_PERSONALIZATION_APP_CURRENTLY_SET},
{"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES}};
{"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES},
{"wallpaperCollections", IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS}};
source->AddLocalizedStrings(kLocalizedStrings);
source->UseStringsJs();
}

@ -2,23 +2,34 @@
:host {
overflow: hidden;
}
#main {
height: 100%;
overflow-y: auto;
width: 100%;
}
</style>
<paper-spinner-lite active="[[imagesLoading_]]">
</paper-spinner-lite>
<!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
<p hidden$="[[!hasError_]]" id="error">error</p>
<template is="dom-if" if="[[showImages_]]">
<iron-list items="[[getImages_(hidden, images_)]]" grid>
<template>
<div class="photo-container">
<div class="padding-fix">
<template is="dom-if"
if="[[shouldShowImage_(item, imageData_, imageDataLoading_)]]">
<img on-click="onClickImage_" data-id$="[[getImageKey_(item)]]"
src="[[getImageData_(item, imageData_)]]">
</template>
<div id="main" role="main" aria-label="[[i18n('myImagesLabel')]]">
<iron-list items="[[getImages_(hidden, images_)]]" grid role="listbox"
scroll-target="main"
aria-setsize$="[[getImageCount_(hidden, images_)]]">
<template>
<div class="photo-container" role="option" tabindex$="[[tabIndex]]"
aria-posinset$="[[getAriaIndex_(index)]]"
aria-selected$="[[getAriaSelected_(item)]]">
<div class="padding-fix">
<template is="dom-if"
if="[[shouldShowImage_(item, imageData_, imageDataLoading_)]]">
<img on-click="onClickImage_" data-id$="[[getImageKey_(item)]]"
src="[[getImageData_(item, imageData_)]]" alt="[[item.name]]">
</template>
</div>
</div>
</div>
</template>
</iron-list>
</template>
</iron-list>
</div>
</template>

@ -124,6 +124,25 @@ export class LocalImages extends WithPersonalizationStore {
return hidden ? [] : images;
}
/**
* @param {boolean} hidden
* @param {!Array<!chromeos.personalizationApp.mojom.LocalImage>} images
* @return {number}
*/
getImageCount_(hidden, images) {
return this.getImages_(hidden, images).length;
}
/**
* TODO(b/192975897) compare with currently selected image to return correct
* aria-selected attribute.
* @param {!chromeos.personalizationApp.mojom.LocalImage} image
* @return {string}
*/
getAriaSelected_(image) {
return 'false';
}
/**
* @private
* @param {chromeos.personalizationApp.mojom.LocalImage} image
@ -172,6 +191,16 @@ export class LocalImages extends WithPersonalizationStore {
/** @type {!chromeos.personalizationApp.mojom.LocalImage} */ (image),
getWallpaperProvider(), this.getStore());
}
/**
* @private
* @param {number} i
* @return {number}
*/
getAriaIndex_(i) {
return i + 1;
}
}
customElements.define(LocalImages.is, LocalImages);

@ -4,5 +4,6 @@
<!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
<p hidden$="[[!hasError_]]" id="error">error</p>
<iframe id="collections-iframe" frameBorder="0" hidden$="[[!showCollections_]]"
src="chrome-untrusted://personalization/untrusted/collections.html">
src="chrome-untrusted://personalization/untrusted/collections.html"
role="main" aria-label$="[[i18n('wallpaperCollections')]]">
</iframe>

@ -6,5 +6,6 @@
<!-- TODO(b/181697575) handle error cases and update error string to UI spec -->
<p hidden$="[[!hasError_]]" id="error">error</p>
<iframe frameBorder="0" id="images-iframe" hidden$="[[!showImages_]]"
src="chrome-untrusted://personalization/untrusted/images.html">
src="chrome-untrusted://personalization/untrusted/images.html" role="main"
aria-label="[[getMainAriaLabel_(collectionId, collections_)]]">
</iframe>

@ -44,6 +44,13 @@ export class WallpaperImages extends WithPersonalizationStore {
type: String,
},
/**
* @type {?Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
*/
collections_: {
type: Array,
},
/**
* @type {!Object<string,
* ?Array<!chromeos.personalizationApp.mojom.WallpaperImage>>}
@ -92,6 +99,7 @@ export class WallpaperImages extends WithPersonalizationStore {
super.connectedCallback();
this.watch('images_', state => state.backdrop.images);
this.watch('imagesLoading_', state => state.loading.images);
this.watch('collections_', state => state.backdrop.collections);
this.updateFromStore();
}
@ -145,6 +153,28 @@ export class WallpaperImages extends WithPersonalizationStore {
sendImagesFunction(iframe.contentWindow, this.images_[collectionId]);
}
}
/**
* @private
* @param {string} collectionId
* @param {?Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
* collections
* @return {string}
*/
getMainAriaLabel_(collectionId, collections) {
if (!collectionId || !Array.isArray(collections)) {
return '';
}
const collection =
collections.find(collection => collection.id === collectionId);
if (!collection) {
console.warn('Did not find collection matching collectionId');
return '';
}
return collection.name;
}
}
customElements.define(WallpaperImages.is, WallpaperImages);

@ -3,10 +3,12 @@
margin: 12px 0;
}
</style>
<iron-list items="[[tiles_]]" grid scroll-target="document">
<iron-list items="[[tiles_]]" grid scroll-target="document" role="listbox"
aria-setsize$="[[tiles_.length]]">
<template>
<div class="photo-container" data-id$="[[item.id]]"
on-click="onCollectionClicked_">
on-click="onCollectionClicked_" tabindex$="[[tabIndex]]" role="option"
aria-posinset$="[[getAriaIndex_(index)]]" aria-selected="false">
<div class="padding-fix">
<template is="dom-if" if="[[item.preview]]">
<img src="[[item.preview.url]]">

@ -232,6 +232,15 @@ class CollectionsGrid extends PolymerElement {
}
selectCollection(window.parent, id);
}
/**
* @private
* @param {number} i
* @return {number}
*/
getAriaIndex_(i) {
return i + 1;
}
}
customElements.define(CollectionsGrid.is, CollectionsGrid);

@ -3,12 +3,15 @@
margin: 12px 0;
}
</style>
<iron-list scroll-target="document" grid items="[[images_]]">
<iron-list scroll-target="document" grid items="[[images_]]" role="listbox"
aria-setsize$="[[images_.length]]">
<template>
<div class="photo-container">
<div class="photo-container" tabindex$="[[tabIndex]]" role="option"
aria-posinset$="[[getAriaIndex_(index)]]"
aria-selected$="[[getAriaSelected_(item)]]">
<div class="padding-fix">
<img data-id$="[[item.assetId]]" src="[[item.url.url]]"
on-click="onImageClicked_">
on-click="onImageClicked_" alt$="[[getImgAlt_(item)]]">
</div>
</div>
</template>

@ -68,6 +68,16 @@ class ImagesGrid extends PolymerElement {
}
}
/**
* TODO(b/192975897) compare with currently selected image to return correct
* aria-selected attribute.
* @param {!chromeos.personalizationApp.mojom.LocalImage} image
* @return {string}
*/
getAriaSelected_(image) {
return 'false';
}
/**
* Notify trusted code that a user clicked on an image.
* @private
@ -77,6 +87,24 @@ class ImagesGrid extends PolymerElement {
const img = e.currentTarget;
selectImage(window.parent, BigInt(img.dataset.id));
}
/**
* @private
* @param {!chromeos.personalizationApp.mojom.WallpaperImage} image
* @return {string}
*/
getImgAlt_(image) {
return image.attribution.join(' ');
}
/**
* @private
* @param {number} i
* @return {number}
*/
getAriaIndex_(i) {
return i + 1;
}
}
customElements.define(ImagesGrid.is, ImagesGrid);