Improve title & accessibility label support.

This commit is contained in:
Dermot Duffy
2022-01-15 09:31:03 -08:00
parent 2097402c9d
commit 745d0c2e71
8 changed files with 48 additions and 48 deletions
+2 -1
View File
@@ -164,9 +164,10 @@ export class FrigateCardGalleryCore extends LitElement {
</div>` </div>`
: child.thumbnail : child.thumbnail
? html`<img ? html`<img
title="${child.title}" aria-label="${child.title}"
class="mdc-image-list__image" class="mdc-image-list__image"
src="${child.thumbnail}" src="${child.thumbnail}"
title="${child.title}"
@click=${() => { @click=${() => {
if (this.view) { if (this.view) {
this.view this.view
+15 -6
View File
@@ -1,3 +1,4 @@
// TODO clip autoplay not working as expected
// TODO verify README links worked correctly (e.g. basic cameras configuration) // TODO verify README links worked correctly (e.g. basic cameras configuration)
import { import {
@@ -388,11 +389,11 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
return html` <div class="embla__slide"> return html` <div class="embla__slide">
<frigate-card-live-provider <frigate-card-live-provider
.title=${getCameraTitle(this.hass, cameraConfig)}
.hass=${this.hass}
.cameraConfig=${cameraConfig}
.liveConfig=${config}
?disabled=${this._isLazyLoading()} ?disabled=${this._isLazyLoading()}
.cameraConfig=${cameraConfig}
.label=${getCameraTitle(this.hass, cameraConfig)}
.liveConfig=${config}
.hass=${this.hass}
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) => @frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
this._mediaShowEventHandler(slideIndex, e)} this._mediaShowEventHandler(slideIndex, e)}
> >
@@ -470,7 +471,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
${ref(this._previousControlRef)} ${ref(this._previousControlRef)}
.direction=${'previous'} .direction=${'previous'}
.controlConfig=${config.controls.next_previous} .controlConfig=${config.controls.next_previous}
.title=${getCameraTitle(this.hass, prev)} .label=${getCameraTitle(this.hass, prev)}
.icon=${getCameraIcon(this.hass, prev)} .icon=${getCameraIcon(this.hass, prev)}
?disabled=${prev == null} ?disabled=${prev == null}
@click=${() => { @click=${() => {
@@ -485,7 +486,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
${ref(this._nextControlRef)} ${ref(this._nextControlRef)}
.direction=${'next'} .direction=${'next'}
.controlConfig=${config.controls.next_previous} .controlConfig=${config.controls.next_previous}
.title=${getCameraTitle(this.hass, next)} .label=${getCameraTitle(this.hass, next)}
.icon=${getCameraIcon(this.hass, next)} .icon=${getCameraIcon(this.hass, next)}
?disabled=${next == null} ?disabled=${next == null}
@click=${() => { @click=${() => {
@@ -514,6 +515,10 @@ export class FrigateCardLiveProvider extends LitElement {
@property({ attribute: true, type: Boolean }) @property({ attribute: true, type: Boolean })
public disabled = false; public disabled = false;
// Label that is used for ARIA support and as tooltip.
@property({ attribute: false })
public label = "";
protected _getResolvedProvider(): LiveProvider { protected _getResolvedProvider(): LiveProvider {
if (this.cameraConfig?.live_provider === 'auto') { if (this.cameraConfig?.live_provider === 'auto') {
if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) { if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) {
@@ -537,6 +542,10 @@ export class FrigateCardLiveProvider extends LitElement {
return; return;
} }
// Set title and ariaLabel from the provided label property.
this.title = this.label;
this.ariaLabel = this.label;
const provider = this._getResolvedProvider(); const provider = this._getResolvedProvider();
return html` return html`
+4 -10
View File
@@ -1,4 +1,4 @@
import { HomeAssistant, handleAction, hasAction } from 'custom-card-helpers'; import { HASSDomEvent, HomeAssistant, handleAction, hasAction } from 'custom-card-helpers';
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit'; import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
@@ -65,7 +65,7 @@ export class FrigateCardMenu extends LitElement {
* @param button The button configuration. * @param button The button configuration.
*/ */
protected _actionHandler( protected _actionHandler(
ev: CustomEvent<{ action: string; config?: Actions }>, ev: HASSDomEvent<{ action: string; config?: Actions }>,
config?: Actions, config?: Actions,
): void { ): void {
if (!ev) { if (!ev) {
@@ -152,21 +152,15 @@ export class FrigateCardMenu extends LitElement {
button: true, button: true,
}; };
// TODO: Upon a safe distance from the release of HA 2021.11 these
// attributes can be removed from the <ha-icon-button>.
// - icon (replaced with the embedded <ha-icon>)
// - title (replaced with .label)
return html` <ha-icon-button return html` <ha-icon-button
class="${classMap(classes)}" class="${classMap(classes)}"
style="${styleMap(stateParameters.style || {})}" style="${styleMap(stateParameters.style || {})}"
icon=${stateParameters.icon || 'mdi:gesture-tap-button'}
.label=${stateParameters.title || ''}
title=${stateParameters.title || ''}
@action=${(ev) => this._actionHandler(ev, button)}
.actionHandler=${actionHandler({ .actionHandler=${actionHandler({
hasHold: hasHold, hasHold: hasHold,
hasDoubleClick: hasDoubleClick, hasDoubleClick: hasDoubleClick,
})} })}
.label=${stateParameters.title || ''}
@action=${(ev) => this._actionHandler(ev, button)}
> >
<ha-icon icon="${stateParameters.icon || 'mdi:gesture-tap-button'}"></ha-icon> <ha-icon icon="${stateParameters.icon || 'mdi:gesture-tap-button'}"></ha-icon>
</ha-icon-button>`; </ha-icon-button>`;
+6 -8
View File
@@ -30,6 +30,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
@property({ attribute: true, type: Boolean }) @property({ attribute: true, type: Boolean })
public disabled = false; public disabled = false;
// Label that is used for ARIA support and as tooltip.
@property() label = "";
protected render(): TemplateResult { protected render(): TemplateResult {
if (this.disabled || !this._controlConfig || this._controlConfig.style == 'none') { if (this.disabled || !this._controlConfig || this._controlConfig.style == 'none') {
return html``; return html``;
@@ -55,15 +58,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
icon = this.icon icon = this.icon
} }
// TODO: Upon a safe distance from the release of HA 2021.11 these
// attributes can be removed from the <ha-icon-button>.
// - icon (replaced with the embedded <ha-icon>)
// - title (replaced with .label)
return html` <ha-icon-button return html` <ha-icon-button
icon=${icon}
class="${classMap(classes)}" class="${classMap(classes)}"
.label=${this.title} .label=${this.label}
title=${this.title}
> >
<ha-icon icon=${icon}></ha-icon> <ha-icon icon=${icon}></ha-icon>
</ha-icon-button>`; </ha-icon-button>`;
@@ -75,7 +72,8 @@ export class FrigateCardNextPreviousControl extends LitElement {
return html`<img return html`<img
src="${this.thumbnail}" src="${this.thumbnail}"
class="${classMap(classes)}" class="${classMap(classes)}"
title="${this.title}" title="${this.label}"
aria-label="${this.label}"
/>`; />`;
} }
+11 -7
View File
@@ -25,7 +25,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
@property({ attribute: false }) @property({ attribute: false })
set config(config: ThumbnailsControlConfig | undefined) { set config(config: ThumbnailsControlConfig | undefined) {
if (config) { if (config) {
if (config && (config.size !== undefined && config.size !== null)) { if (config && config.size !== undefined && config.size !== null) {
this.style.setProperty('--frigate-card-carousel-thumbnail-size', config.size); this.style.setProperty('--frigate-card-carousel-thumbnail-size', config.size);
} }
this._config = config; this._config = config;
@@ -35,7 +35,10 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
@property({ attribute: false }) @property({ attribute: false })
set highlightSelected(value: boolean) { set highlightSelected(value: boolean) {
this.style.setProperty('--frigate-card-carousel-thumbnail-opacity', value ? '0.6' : '1.0'); this.style.setProperty(
'--frigate-card-carousel-thumbnail-opacity',
value ? '0.6' : '1.0',
);
} }
/** /**
@@ -79,10 +82,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
const slides: TemplateResult[] = []; const slides: TemplateResult[] = [];
for (let i = 0; i < this.target.children.length; ++i) { for (let i = 0; i < this.target.children.length; ++i) {
const thumbnail = this._renderThumbnail( const thumbnail = this._renderThumbnail(this.target, i, slides.length);
this.target,
i,
slides.length);
if (thumbnail) { if (thumbnail) {
slides.push(thumbnail); slides.push(thumbnail);
} }
@@ -121,7 +121,11 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
} }
}} }}
> >
<img src="${mediaToRender.thumbnail}" title="${mediaToRender.title}" /> <img
aria-label="${mediaToRender.title}"
src="${mediaToRender.thumbnail}"
title="${mediaToRender.title}"
/>
</div>`; </div>`;
} }
+10 -8
View File
@@ -601,7 +601,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
.direction=${'previous'} .direction=${'previous'}
.controlConfig=${this.viewerConfig?.controls.next_previous} .controlConfig=${this.viewerConfig?.controls.next_previous}
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined} .thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
.title=${prev ? prev.title : ''} .label=${prev ? prev.title : ''}
?disabled=${!prev} ?disabled=${!prev}
@click=${() => { @click=${() => {
this._nextPreviousHandler('previous'); this._nextPreviousHandler('previous');
@@ -615,7 +615,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
.direction=${'next'} .direction=${'next'}
.controlConfig=${this.viewerConfig?.controls.next_previous} .controlConfig=${this.viewerConfig?.controls.next_previous}
.thumbnail=${next && next.thumbnail ? next.thumbnail : undefined} .thumbnail=${next && next.thumbnail ? next.thumbnail : undefined}
.title=${next ? next.title : ''} .label=${next ? next.title : ''}
?disabled=${!next} ?disabled=${!next}
@click=${() => { @click=${() => {
this._nextPreviousHandler('next'); this._nextPreviousHandler('next');
@@ -657,19 +657,21 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
<div class="embla__slide"> <div class="embla__slide">
${this.view.isClipRelatedView() ${this.view.isClipRelatedView()
? html`<frigate-card-ha-hls-player ? html`<frigate-card-ha-hls-player
.hass=${this.hass}
url=${ifDefined(lazyLoad ? undefined : resolvedMedia?.url)}
title="${mediaToRender.title}"
muted
controls
playsinline
allow-exoplayer allow-exoplayer
?autoplay="${autoplay}" ?autoplay="${autoplay}"
aria-label="${mediaToRender.title}"
controls
muted
playsinline
title="${mediaToRender.title}"
url=${ifDefined(lazyLoad ? undefined : resolvedMedia?.url)}
.hass=${this.hass}
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) => @frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
this._mediaShowEventHandler(slideIndex, e)} this._mediaShowEventHandler(slideIndex, e)}
> >
</frigate-card-ha-hls-player>` </frigate-card-ha-hls-player>`
: html`<img : html`<img
aria-label="${mediaToRender.title}"
src=${ifDefined(lazyLoad ? IMG_EMPTY : resolvedMedia?.url)} src=${ifDefined(lazyLoad ? IMG_EMPTY : resolvedMedia?.url)}
title="${mediaToRender.title}" title="${mediaToRender.title}"
@click=${() => { @click=${() => {
-4
View File
@@ -48,10 +48,6 @@ customElements.whenDefined('ha-camera-stream').then(() => {
? html` ? html`
<img <img
@load=${(e) => { @load=${(e) => {
// TODO: This block can be removed a safe distance from HA 2021.11.
if (typeof this._elementResized != 'undefined') {
this._elementResized();
}
dispatchMediaShowEvent(this, e); dispatchMediaShowEvent(this, e);
}} }}
.src=${typeof this._connected == 'undefined' || this._connected .src=${typeof this._connected == 'undefined' || this._connected
-4
View File
@@ -34,10 +34,6 @@ customElements.whenDefined('ha-hls-player').then(() => {
?playsinline=${this.playsInline} ?playsinline=${this.playsInline}
?controls=${this.controls} ?controls=${this.controls}
@loadeddata=${(e) => { @loadeddata=${(e) => {
// TODO: This block can be removed a safe distance from HA 2021.11.
if (typeof this._elementResized != 'undefined') {
this._elementResized();
}
dispatchMediaShowEvent(this, e); dispatchMediaShowEvent(this, e);
}} }}
@pause=${() => dispatchPauseEvent(this)} @pause=${() => dispatchPauseEvent(this)}