Skip to content

Commit

Permalink
fix(contentauth#145): ManifestSummary/Tooltip/Popover accessibility
Browse files Browse the repository at this point in the history
Tooltip component used in ManifestSummary should:

1. Trigger should be keyboard accessible as a button.
2. Trigger should be labeled by the Tooltip and the header.
3. Popover for Tooltip should expand on focus and close on blur.
4. Popover should hide with Escape key and toggle on Trigger click.
5. Fix text color contrast for --cai-secondary-color.
6. Fix color contrast for  --cai-icon-fill
7. Add focus outline color and outline-offset to Tooltip button and View More
8. Use `<section>` with aria-labelledby for PanelSection.
9. Add role="heading" and a heading level for the .heading-text element within PanelSection
10. Add role="img" and appropriate aria-label for each of the Icons
  • Loading branch information
majornista committed Mar 5, 2024
1 parent bf94d07 commit 34026a8
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 28 deletions.
45 changes: 36 additions & 9 deletions packages/c2pa-wc/src/components/Icon/Icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,66 @@ export class Icon extends LitElement {
static readonly matchers = [
{
pattern: /photoshop/i,
icon: html`<cai-icon-photoshop></cai-icon-photoshop>`,
icon: html`<cai-icon-photoshop
role="img"
aria-label="Photoshop"
></cai-icon-photoshop>`,
},
{
pattern: /adobe\sstock/i,
icon: html`<cai-icon-adobe-stock></cai-icon-adobe-stock>`,
icon: html`<cai-icon-adobe-stock
role="img"
aria-label="Adobe Stock"
></cai-icon-adobe-stock>`,
},
{
pattern: /adobe/i,
icon: html`<cai-icon-adobe></cai-icon-adobe>`,
icon: html`<cai-icon-adobe
role="img"
aria-label="Adobe"
></cai-icon-adobe>`,
},
{
pattern: /behance\.net/i,
icon: html`<cai-icon-behance></cai-icon-behance>`,
icon: html`<cai-icon-behance
role="img"
aria-label="Behance"
></cai-icon-behance>`,
},
{
pattern: /facebook\.com/i,
icon: html`<cai-icon-facebook></cai-icon-facebook>`,
icon: html`<cai-icon-facebook
role="img"
aria-label="Facebook"
></cai-icon-facebook>`,
},
{
pattern: /instagram\.com/i,
icon: html`<cai-icon-instagram></cai-icon-instagram>`,
icon: html`<cai-icon-instagram
role="img"
aria-label="Instagram"
></cai-icon-instagram>`,
},
{
pattern: /truepic/i,
icon: html`<cai-icon-truepic></cai-icon-truepic>`,
icon: html`<cai-icon-truepic
role="img"
aria-label="TruePic"
></cai-icon-truepic>`,
},
{
pattern: /twitter\.com/i,
icon: html`<cai-icon-twitter></cai-icon-twitter>`,
icon: html`<cai-icon-twitter
role="img"
aria-label="Twitter"
></cai-icon-twitter>`,
},
{
pattern: /lightroom/i,
icon: html`<cai-icon-lightroom></cai-icon-lightroom>`,
icon: html`<cai-icon-lightroom
role="img"
aria-label="Lightroom"
></cai-icon-lightroom>`,
},
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ export class ManifestSummary extends Configurable(LitElement, defaultConfig) {
transition: all 150ms ease-in-out;
background-color: transparent;
border-radius: 9999px;
border: 2px solid #b3b3b3;
border: 2px solid #909090;
padding: 8px 0;
font-weight: bold;
text-align: center;
text-decoration: none;
width: 100%;
color: var(--cai-primary-color);
outline-offset: 3px;
outline-color: var(--cai-focus-ring-color, #1473e6);
}
#view-more:hover {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class MinimumViableProvenance extends Configurable(
: undefined;

return html` <cai-panel-section
headingLevel=${2}
header=${this._config.stringMap['minimum-viable-provenance.header']}
helpText=${this._config.stringMap['minimum-viable-provenance.helpText']}
>
Expand All @@ -112,6 +113,7 @@ export class MinimumViableProvenance extends Configurable(
<cai-icon
slot="icon"
source=${this.manifestStore?.signature?.issuer}
aria-hidden="true"
></cai-icon>
<span> ${this.manifestStore?.signature?.issuer} </span>
</div>
Expand Down
18 changes: 14 additions & 4 deletions packages/c2pa-wc/src/components/PanelSection/PanelSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export class PanelSection extends LitElement {
@property({ type: String })
helpText: string | null = null;

@property({ type: String })
headingLevel: '1' | '2' | '3' | '4' | '5' | '6' = '3';

static get styles() {
return [
defaultStyles,
Expand Down Expand Up @@ -63,19 +66,26 @@ export class PanelSection extends LitElement {

render() {
return html`
<div class="layout">
<section class="layout" aria-labelledby="heading-text-id">
<div class="heading">
<div class="heading-text">${this.header}</div>
<div
class="heading-text"
id="heading-text-id"
role="heading"
aria-level=${this.headingLevel}
>
${this.header}
</div>
<slot name="help">
${this.helpText
? html`<cai-tooltip autoPlacement=${false}>
? html`<cai-tooltip label=${this.header} autoPlacement=${false}>
<div slot="content">${this.helpText}</div>
</cai-tooltip>`
: nothing}
</slot>
</div>
<slot></slot>
</div>
</section>
`;
}
}
63 changes: 60 additions & 3 deletions packages/c2pa-wc/src/components/Popover/Popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,47 @@ export class Popover extends LitElement {
this._isShown = false;
}

private _onKeyDown(e: KeyboardEvent) {
switch (e.key) {
case 'Escape':
if (this._isShown) {
e.stopPropagation();
e.preventDefault();
this._hideTooltip();
this.triggerElement!.focus();
}
break;
case 'Enter':
case ' ':
if (
e.target !== this.hostElement &&
e.composedPath().includes(this.triggerElement as EventTarget)
) {
e.stopPropagation();
e.preventDefault();
this._onClick();
}
break;
}
}

private _onClick() {
if (!this._isShown) {
this._showTooltip();
} else {
this._hideTooltip();
}
}

private _cleanupTriggers() {
while (this._eventCleanupFns.length) {
const cleanup = this._eventCleanupFns.shift();
cleanup?.();
}

this.triggerElement!.removeEventListener('click', this._onClick);

this.hostElement!.removeEventListener('keydown', this._onKeyDown, true);
}

private _setTriggers() {
Expand All @@ -215,27 +251,48 @@ export class Popover extends LitElement {

this._eventCleanupFns = triggers.map((trigger) => {
const [show, hide] = trigger.split(':');
this.triggerElement!.addEventListener(show, this._showTooltip.bind(this));
this.triggerElement!.addEventListener(
show,
this._showTooltip.bind(this),
show === 'focus',
);
if (this.interactive && hide === 'mouseleave') {
this.hostElement!.addEventListener(hide, this._hideTooltip.bind(this));
} else {
this.triggerElement!.addEventListener(
hide,
this._hideTooltip.bind(this),
hide === 'blur',
);
}
return () => {
this.triggerElement!.removeEventListener(show, this._showTooltip);
this.triggerElement!.removeEventListener(
show,
this._showTooltip,
show === 'focus',
);
if (this.interactive && hide === 'mouseleave') {
this.contentElement!.addEventListener(
hide,
this._hideTooltip.bind(this),
);
} else {
this.triggerElement!.removeEventListener(hide, this._hideTooltip);
this.triggerElement!.removeEventListener(
hide,
this._hideTooltip,
hide === 'blur',
);
}
};
});

this.triggerElement!.addEventListener('click', this._onClick.bind(this));

this.hostElement!.addEventListener(
'keydown',
this._onKeyDown.bind(this),
true,
);
}

private async _updatePosition() {
Expand Down
13 changes: 9 additions & 4 deletions packages/c2pa-wc/src/components/SocialMedia/SocialMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import defaultStringMap from '../SocialMedia/SocialMedia.str.json';
import { baseSectionStyles, defaultStyles } from '../../styles';
import { ConfigurablePanelSection } from '../../mixins/configurablePanelSection';
Expand Down Expand Up @@ -54,7 +55,6 @@ export class SocialMedia extends ConfigurablePanelSection(LitElement, {
list-style: none;
padding: 0;
margin: 0;
overflow: hidden;
}
.section-social-media-list-item {
Expand All @@ -77,14 +77,19 @@ export class SocialMedia extends ConfigurablePanelSection(LitElement, {
header=${this._config.stringMap['social-media.header']}
helpText=${this._config.stringMap['social-media.helpText']}
>
<ul class="section-social-media-list">
<ul
class="section-social-media-list"
aria-label=${this._config.stringMap['social-media.header']}
>
${this._data?.map(
(socialAccount) => html`
<li class="section-social-media-list-item">
<cai-icon source="${socialAccount['@id']}"></cai-icon>
<cai-icon
source=${ifDefined(socialAccount['@id'] ?? undefined)}
></cai-icon>
<a
class="section-social-media-list-item-link"
href=${socialAccount['@id']}
href=${ifDefined(socialAccount['@id'] ?? undefined)}
target="_blank"
>
@${socialAccount.name}
Expand Down
1 change: 1 addition & 0 deletions packages/c2pa-wc/src/components/Thumbnail/Thumbnail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export class Thumbnail extends LitElement {
}
cai-tooltip.badge-tooltip,
.badge-no-tooltip {
--cai-popover-icon-border-radius: 50% 50% 0 50%;
position: absolute;
top: var(--cai-thumbnail-badge-icon-top, 1px);
right: var(--cai-thumbnail-badge-icon-right, 1px);
Expand Down
3 changes: 3 additions & 0 deletions packages/c2pa-wc/src/components/Tooltip/Tooltip.str.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"tooltip.information": "Information: "
}
50 changes: 44 additions & 6 deletions packages/c2pa-wc/src/components/Tooltip/Tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import { autoPlacement } from '@floating-ui/dom';
import { css, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import '../../../assets/svg/monochrome/help.svg';
import { defaultStyles } from '../../styles';
import { Configurable } from '../../mixins/configurable';
import defaultStringMap from './Tooltip.str.json';

import '../Icon';
import '../Popover';
Expand All @@ -29,8 +32,16 @@ declare global {
}
}

export interface TooltipConfig {
stringMap: Record<string, string>;
}

const defaultConfig: TooltipConfig = {
stringMap: defaultStringMap,
};

@customElement('cai-tooltip')
export class Tooltip extends LitElement {
export class Tooltip extends Configurable(LitElement, defaultConfig) {
@state()
protected _isShown = false;

Expand All @@ -43,6 +54,9 @@ export class Tooltip extends LitElement {
@property({ type: Boolean })
arrow = true;

@property({ type: String })
label?: string;

static get styles() {
return [
defaultStyles,
Expand All @@ -51,8 +65,15 @@ export class Tooltip extends LitElement {
display: flex;
--cai-icon-width: var(--cai-popover-icon-width, 16px);
--cai-icon-height: var(--cai-popover-icon-height, 16px);
--cai-icon-fill: var(--cai-popover-icon-fill, #a8a8a8);
--cai-icon-border-radius: var(--cai-popover-icon-border-radius, 50%);
--cai-icon-fill: var(--cai-popover-icon-fill, #909090);
cursor: pointer;
height: var(--cai-icon-height);
width: var(--cai-icon-width);
line-height: 0;
border-radius: var(--cai-icon-border-radius);
outline-color: var(--cai-focus-ring-color);
outline-offset: 1px;
}
.content {
min-width: 165px;
Expand Down Expand Up @@ -85,16 +106,33 @@ export class Tooltip extends LitElement {
return html`
<cai-popover
id="popover"
arrow=${this.arrow}
?arrow=${this.arrow}
.autoPlacement=${this.autoPlacement}
?interactive=${false}
>
<div id="trigger" slot="trigger">
<div
id="trigger"
slot="trigger"
role="button"
tabindex="0"
aria-label=${ifDefined(this.label ?? undefined)}
aria-labelledby=${`${this.label ? 'trigger' : ''} icon`}
aria-describedby="tooltip"
>
<slot name="trigger">
<cai-icon-help></cai-icon-help>
<cai-icon-help
id="icon"
role="img"
aria-label=${this._config.stringMap['tooltip.information']}
></cai-icon-help>
</slot>
</div>
<div class=${classMap(contentClassMap)} slot="content">
<div
class=${classMap(contentClassMap)}
slot="content"
role="tooltip"
id="tooltip"
>
<slot name="content"></slot>
</div>
</cai-popover>
Expand Down
Loading

0 comments on commit 34026a8

Please sign in to comment.