Skip to content

Commit

Permalink
fix(block, block-section): improve a11y (#7557)
Browse files Browse the repository at this point in the history
**Related Issue:** #5565 

## Summary

Updated HTML to improve a11y.

**Note**: this removes references to outdated `TEXT` constants, which
were used before translations were built-in.
  • Loading branch information
jcfranco authored Sep 1, 2023
1 parent 1ea52b7 commit 1f44f6b
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CSS, TEXT } from "./resources";
import { CSS } from "./resources";
import { accessible, defaults, focusable, hidden, reflects, renders, t9n } from "../../tests/commonTests";
import { E2EPage, newE2EPage } from "@stencil/core/testing";
import { html } from "../../../support/formatting";
Expand Down Expand Up @@ -87,9 +87,8 @@ describe("calcite-block-section", () => {
});

it("can be toggled", async () => {
const page = await newE2EPage({
html: `<calcite-block-section toggle-display="switch"></calcite-block-section>`,
});
const page = await newE2EPage();
await page.setContent(html`<calcite-block-section text="text" toggle-display="switch"></calcite-block-section>`);
await assertToggleBehavior(page);
});

Expand Down Expand Up @@ -119,7 +118,8 @@ describe("calcite-block-section", () => {
});

it("can be toggled", async () => {
const page = await newE2EPage({ html: "<calcite-block-section></calcite-block-section>" });
const page = await newE2EPage();
await page.setContent(html`<calcite-block-section text="text"></calcite-block-section>`);
await assertToggleBehavior(page);
});
});
Expand Down Expand Up @@ -173,19 +173,19 @@ describe("calcite-block-section", () => {
const toggleSpy = await element.spyOnEvent("calciteBlockSectionToggle");
const toggle = await page.find(`calcite-block-section >>> .${CSS.toggle}`);

expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand);
expect(toggle.getAttribute("aria-expanded")).toBe("false");

await toggle.click();

expect(toggleSpy).toHaveReceivedEventTimes(1);
expect(await element.getProperty("open")).toBe(true);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.collapse);
expect(toggle.getAttribute("aria-expanded")).toBe("true");

await toggle.click();

expect(toggleSpy).toHaveReceivedEventTimes(2);
expect(await element.getProperty("open")).toBe(false);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand);
expect(toggle.getAttribute("aria-expanded")).toBe("false");

const keyboardToggleEmitter =
toggle.tagName === "CALCITE-ACTION"
Expand All @@ -206,13 +206,13 @@ describe("calcite-block-section", () => {

expect(toggleSpy).toHaveReceivedEventTimes(3);
expect(await element.getProperty("open")).toBe(true);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.collapse);
expect(toggle.getAttribute("aria-expanded")).toBe("true");

await keyboardToggleEmitter.press("Enter");
await page.waitForChanges();

expect(toggleSpy).toHaveReceivedEventTimes(4);
expect(await element.getProperty("open")).toBe(false);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand);
expect(toggle.getAttribute("aria-expanded")).toBe("false");
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,18 @@
word-wrap: anywhere;
}

.toggle--switch {
calcite-switch {
@apply pointer-events-none;
.toggle--switch-container {
@apply flex items-center relative bg-transparent w-full;

.focus-guard {
--calcite-label-margin-bottom: 0;
@apply absolute pointer-events-none;
inset-inline-end: 0;
margin-inline-start: theme("margin.1");
}
}

.toggle--switch {
.status-icon {
margin-inline-start: theme("margin.2");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from "@stencil/core";

import { focusFirstTabbable, getElementDir, toAriaBoolean } from "../../utils/dom";
import { guid } from "../../utils/guid";
import { isActivationKey } from "../../utils/key";
import { connectLocalized, disconnectLocalized, LocalizedComponent } from "../../utils/locale";
import {
Expand All @@ -26,7 +25,7 @@ import {
import { Status } from "../interfaces";
import { BlockSectionMessages } from "./assets/block-section/t9n";
import { BlockSectionToggleDisplay } from "./interfaces";
import { CSS, ICONS } from "./resources";
import { CSS, ICONS, IDS } from "./resources";
import {
componentFocusable,
LoadableComponent,
Expand Down Expand Up @@ -117,8 +116,6 @@ export class BlockSection implements LocalizedComponent, T9nComponent, LoadableC

@Element() el: HTMLCalciteBlockSectionElement;

private guid = guid();

@State() effectiveLocale: string;

@Watch("effectiveLocale")
Expand Down Expand Up @@ -213,41 +210,46 @@ export class BlockSection implements LocalizedComponent, T9nComponent, LoadableC

const toggleLabel = open ? messages.collapse : messages.expand;

const { guid } = this;
const regionId = `${guid}-region`;
const buttonId = `${guid}-button`;

const headerNode =
toggleDisplay === "switch" ? (
<div
aria-controls={regionId}
aria-label={toggleLabel}
class={{
[CSS.toggle]: true,
[CSS.toggleSwitch]: true,
[CSS.toggleSwitchContainer]: true,
}}
id={buttonId}
onClick={this.toggleSection}
onKeyDown={this.handleHeaderKeyDown}
tabIndex={0}
title={toggleLabel}
>
<div class={CSS.toggleSwitchContent}>
<span class={CSS.toggleSwitchText}>{text}</span>
<div
aria-controls={IDS.content}
aria-expanded={toAriaBoolean(open)}
class={{
[CSS.toggle]: true,
[CSS.toggleSwitch]: true,
}}
id={IDS.toggle}
onClick={this.toggleSection}
onKeyDown={this.handleHeaderKeyDown}
role="button"
tabIndex={0}
title={toggleLabel}
>
<div class={CSS.toggleSwitchContent}>
<span class={CSS.toggleSwitchText}>{text}</span>
</div>
{this.renderStatusIcon()}
</div>
<calcite-switch checked={open} label={toggleLabel} scale="s" tabIndex={-1} />
{this.renderStatusIcon()}
{/* we use calcite-label to use a simple component that will allow us to prevent keyboard focus by setting tabindex="-1" on the host */}
<calcite-label class={CSS.focusGuard} layout="inline" tabIndex={-1}>
<calcite-switch checked={open} label={toggleLabel} scale="s" />
</calcite-label>
</div>
) : (
<button
aria-controls={regionId}
aria-label={toggleLabel}
aria-controls={IDS.content}
aria-expanded={toAriaBoolean(open)}
class={{
[CSS.sectionHeader]: true,
[CSS.toggle]: true,
}}
id={buttonId}
name={toggleLabel}
id={IDS.toggle}
onClick={this.toggleSection}
>
<calcite-icon icon={arrowIcon} scale="s" />
Expand All @@ -259,13 +261,7 @@ export class BlockSection implements LocalizedComponent, T9nComponent, LoadableC
return (
<Host>
{headerNode}
<section
aria-expanded={toAriaBoolean(open)}
aria-labelledby={buttonId}
class={CSS.content}
hidden={!open}
id={regionId}
>
<section aria-labelledby={IDS.toggle} class={CSS.content} hidden={!open} id={IDS.content}>
<slot />
</section>
</Host>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
export const IDS = {
content: "content",
toggle: "toggle",
};

export const CSS = {
content: "content",
focusGuard: "focus-guard",
invalid: "invalid",
sectionHeader: "section-header",
sectionHeaderText: "section-header__text",
statusIcon: "status-icon",
toggle: "toggle",
toggleSwitch: "toggle--switch",
toggleSwitchContainer: "toggle--switch-container",
toggleSwitchContent: "toggle--switch__content",
toggleSwitchText: "toggle--switch__text",
sectionHeader: "section-header",
sectionHeaderText: "section-header__text",
statusIcon: "status-icon",
valid: "valid",
};

export const TEXT = {
collapse: "Collapse",
expand: "Expand",
};

export const ICONS = {
menuOpen: "chevron-down",
menuClosedLeft: "chevron-left",
Expand Down
16 changes: 8 additions & 8 deletions packages/calcite-components/src/components/block/block.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
import { CSS, SLOTS, TEXT } from "./resources";
import { CSS, SLOTS } from "./resources";
import { accessible, defaults, disabled, focusable, hidden, renders, slots, t9n } from "../../tests/commonTests";
import { html } from "../../../support/formatting";

Expand Down Expand Up @@ -136,31 +136,31 @@ describe("calcite-block", () => {
});

it("allows toggling its content", async () => {
const page = await newE2EPage({ html: "<calcite-block collapsible></calcite-block>" });
const heading = "heading";
const page = await newE2EPage();
await page.setContent(html`<calcite-block collapsible heading=${heading}></calcite-block>`);
const messages = await import(`./assets/block/t9n/messages.json`);

const element = await page.find("calcite-block");
const toggleSpy = await element.spyOnEvent("calciteBlockToggle");
const toggle = await page.find(`calcite-block >>> .${CSS.toggle}`);

expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand);
expect(toggle.getAttribute("aria-expanded")).toBe("false");
expect(toggle.getAttribute("title")).toBe(TEXT.expand);
expect(toggle.getAttribute("title")).toBe(messages.expand);

await toggle.click();

expect(toggleSpy).toHaveReceivedEventTimes(1);
expect(await element.getProperty("open")).toBe(true);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.collapse);
expect(toggle.getAttribute("aria-expanded")).toBe("true");
expect(toggle.getAttribute("title")).toBe(TEXT.collapse);
expect(toggle.getAttribute("title")).toBe(messages.collapse);

await toggle.click();

expect(toggleSpy).toHaveReceivedEventTimes(2);
expect(await element.getProperty("open")).toBe(false);
expect(toggle.getAttribute("aria-label")).toBe(TEXT.expand);
expect(toggle.getAttribute("aria-expanded")).toBe("false");
expect(toggle.getAttribute("title")).toBe(TEXT.expand);
expect(toggle.getAttribute("title")).toBe(messages.expand);
});

describe("header", () => {
Expand Down
22 changes: 4 additions & 18 deletions packages/calcite-components/src/components/block/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
disconnectConditionalSlotComponent,
} from "../../utils/conditionalSlot";
import { focusFirstTabbable, getSlotted, toAriaBoolean } from "../../utils/dom";
import { guid } from "../../utils/guid";
import {
connectInteractive,
disconnectInteractive,
Expand All @@ -35,7 +34,7 @@ import {
import { Heading, HeadingLevel } from "../functional/Heading";
import { Status } from "../interfaces";
import { BlockMessages } from "./assets/block/t9n";
import { CSS, ICONS, SLOTS } from "./resources";
import { CSS, ICONS, IDS, SLOTS } from "./resources";
import {
componentFocusable,
LoadableComponent,
Expand Down Expand Up @@ -157,8 +156,6 @@ export class Block

@Element() el: HTMLCalciteBlockElement;

private guid = guid();

@State() effectiveLocale: string;

@Watch("effectiveLocale")
Expand Down Expand Up @@ -292,20 +289,15 @@ export class Block
const hasMenuActions = !!getSlotted(el, SLOTS.headerMenuActions);
const collapseIcon = open ? ICONS.opened : ICONS.closed;

const { guid } = this;
const regionId = `${guid}-region`;
const buttonId = `${guid}-button`;

const headerNode = (
<div class={CSS.headerContainer}>
{this.dragHandle ? <calcite-handle /> : null}
{collapsible ? (
<button
aria-controls={regionId}
aria-controls={IDS.content}
aria-expanded={collapsible ? toAriaBoolean(open) : null}
aria-label={toggleLabel}
class={CSS.toggle}
id={buttonId}
id={IDS.toggle}
onClick={this.onHeaderClick}
title={toggleLabel}
>
Expand Down Expand Up @@ -344,13 +336,7 @@ export class Block
}}
>
{headerNode}
<section
aria-expanded={toAriaBoolean(open)}
aria-labelledby={buttonId}
class={CSS.content}
hidden={!open}
id={regionId}
>
<section aria-labelledby={IDS.toggle} class={CSS.content} hidden={!open} id={IDS.content}>
{this.renderScrim()}
</section>
</article>
Expand Down
28 changes: 13 additions & 15 deletions packages/calcite-components/src/components/block/resources.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
export const IDS = {
content: "content",
toggle: "toggle",
};

export const CSS = {
button: "button",
container: "container",
content: "content",
controlContainer: "control-container",
description: "description",
header: "header",
headerContainer: "header-container",
heading: "heading",
icon: "icon",
invalid: "invalid",
statusIcon: "status-icon",
summary: "summary",
title: "title",
toggle: "toggle",
toggleIcon: "toggle-icon",
title: "title",
heading: "heading",
header: "header",
button: "button",
summary: "summary",
description: "description",
controlContainer: "control-container",
valid: "valid",
invalid: "invalid",
};

export const TEXT = {
collapse: "Collapse",
expand: "Expand",
loading: "Loading",
options: "Options",
};

export const SLOTS = {
Expand Down

0 comments on commit 1f44f6b

Please sign in to comment.