Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(button): provides context for AT users when used as reference element for collapsible content #7658

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/calcite-components/src/components/button/button.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,4 +644,27 @@ describe("calcite-button", () => {
expect(button1.textContent.length).toBeLessThan(longText.length);
expect(button1.getAttribute("title")).toEqual(longText);
});

it("should set aria-expanded attribute on shadowDOM element when used as trigger", async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you test if the accessible helper picks this up (also checking that it fails when the ARIA attribute isn't set on the internal button)? If so, I'd suggest using that to simplify this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accessible helper is unable to pick this one up.

const page = await newE2EPage();
await page.setContent(html`<calcite-button id="test-button" label="Info">Info</calcite-button>
<calcite-popover
id="popover-content"
positioning="fixed"
heading="About this data"
reference-element="test-button"
>
<p>Information</p>
</calcite-popover>`);

const calciteButton = await page.find("calcite-button");
const button = await page.find("calcite-button >>> button");
expect(button.getAttribute("aria-expanded")).toBe("false");
expect(calciteButton.getAttribute("aria-expanded")).toBe("false");

await calciteButton.click();
await page.waitForChanges();
expect(button.getAttribute("aria-expanded")).toBe("true");
expect(calciteButton.getAttribute("aria-expanded")).toBe("true");
});
});
13 changes: 13 additions & 0 deletions packages/calcite-components/src/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import { Appearance, FlipContext, Kind, Scale, Width } from "../interfaces";
import { ButtonMessages } from "./assets/button/t9n";
import { ButtonAlignment } from "./interfaces";
import { CSS } from "./resources";
import {
GlobalAttrComponent,
unwatchGlobalAttributes,
watchGlobalAttributes,
} from "../../utils/globalAttributes";

/** Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. */
/** It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission */
Expand All @@ -39,6 +44,7 @@ import { CSS } from "./resources";
})
export class Button
implements
GlobalAttrComponent,
LabelableComponent,
InteractiveComponent,
FormOwner,
Expand Down Expand Up @@ -175,6 +181,7 @@ export class Button
connectInteractive(this);
connectLocalized(this);
connectMessages(this);
watchGlobalAttributes(this, ["aria-expanded"]);
this.hasLoader = this.loading;
this.setupTextContentObserver();
connectLabel(this);
Expand All @@ -189,6 +196,7 @@ export class Button
disconnectMessages(this);
this.resizeObserver?.disconnect();
this.formEl = null;
unwatchGlobalAttributes(this);
}

async componentWillLoad(): Promise<void> {
Expand Down Expand Up @@ -268,6 +276,7 @@ export class Button
target={childElType === "a" && this.target}
title={this.tooltipText}
type={childElType === "button" && this.type}
{...this.globalAttributes}
>
{loaderNode}
{this.iconStart ? iconStartEl : null}
Expand Down Expand Up @@ -344,6 +353,10 @@ export class Button

resizeObserver = createObserver("resize", () => this.setTooltipText());

@State() globalAttributes = {
ariaExpanded: undefined,
};

//--------------------------------------------------------------------------
//
// Private Methods
Expand Down
4 changes: 2 additions & 2 deletions packages/calcite-components/src/utils/globalAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createObserver } from "./observers";

type AttributeObject = { [k: string]: any };
type AllowedGlobalAttribute = "lang" | "role";
const allowedGlobalAttributes = ["lang", "role"];
type AllowedGlobalAttribute = "lang" | "role" | "aria-expanded";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Popover also sets aria-controls, so does that also need to be set here? Not sure if we need aria-describedby to support tooltip. cc @geospatialem

Copy link
Contributor Author

@anveshmekala anveshmekala Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, aria-controls need not be parsed on to the native button for the following reasons:

  • It has minimal support across screen readers in terms of providing context as per https://a11ysupport.io/tech/aria/aria-controls_attribute.
  • For scenarios where screen reader should jump directly onto the opened element, the popover is currently doing that with or without aria-controls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that there isn't widespread coverage for aria-controls. However with the fix, JAWS doesn't provide context in all cases.

For instance, in the original use case where a popover is provided with checkboxes JAWS will only provide context that the button is not expanded when a user changes the checked state prior to pressing the escape key. Maybe the addition would help support JAWS in all cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In VoiceOver, context is provided when user check/uncheck the checkbox before closing the popup.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows OS + JAWS updates this week mitigated the issue from last week. 💪🏻

const allowedGlobalAttributes = ["lang", "role", "aria-expanded"];

const elementToComponentAndObserverOptionsMap = new Map<
HTMLElement,
Expand Down
Loading