Skip to content

Commit

Permalink
feat: Add computeAccessibleDescription (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Jun 9, 2020
1 parent f27618f commit fa53c51
Show file tree
Hide file tree
Showing 14 changed files with 800 additions and 611 deletions.
18 changes: 18 additions & 0 deletions .changeset/strange-windows-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"dom-accessibility-api": patch
---

Implement accessbile description computation

```ts
import { computeAccessibleDescription } from "dom-accessibility-api";

const description = computeAccessibleDescription(element);
```

Warning: It always considers `title` attributes if the description is empty.
Even if the `title` attribute was already used for the accessible name.
This is fails a web-platform-test.
The other failing test is due to `aria-label` being ignored for the description which is correct by spec.
It's likely an issue with wpt.
The other tests are passing (13/15).
35 changes: 19 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
[![Build Status](https://dev.azure.com/silbermannsebastian/dom-accessibility-api/_apis/build/status/eps1lon.dom-accessibility-api?branchName=master)](https://dev.azure.com/silbermannsebastian/dom-accessibility-api/_build/latest?definitionId=6&branchName=master)
![Azure DevOps coverage](https://img.shields.io/azure-devops/coverage/silbermannsebastian/dom-accessibility-api/6)

Computes the accessible name of a given DOM Element.
Computes the accessible name or description of a given DOM Element.
https://w3c.github.io/accname/ implemented in JavaScript for testing.

```bash
$ yarn add dom-accessibility-api
```

```js
import { computeAccessibleName } from "dom-accessibility-api";
import {
computeAccessibleName,
computeAccessibleDescription,
} from "dom-accessibility-api";
```

I'm not an editor of any of the referenced specs (nor very experience with using them) so if you got any insights, something catches
Expand All @@ -38,30 +41,30 @@ cloning. See [the test readme](/tests/README.md) for more info about the test se

### browser (Chrome)

140/144
153/159

### jsdom

<details>
<summary>report 124/159 passing of which 16 are due `::before { content }`, 15 are accessible desc, 4 are pathological </summary>
<summary>report 138/159 passing of which 15 are due `::before { content }`, one might a wrong test, 5 are pathological </summary>

```bash
web-platform-tests
accname
✓ [expected fail] description_1.0_combobox-focusable-manual.html
✓ [expected fail] description_from_content_of_describedby_element-manual.html
[expected fail] description_link-with-label-manual.html
[expected fail] description_test_case_557-manual.html
[expected fail] description_test_case_664-manual.html
[expected fail] description_test_case_665-manual.html
[expected fail] description_test_case_666-manual.html
[expected fail] description_test_case_772-manual.html
[expected fail] description_test_case_773-manual.html
[expected fail] description_test_case_774-manual.html
[expected fail] description_test_case_838-manual.html
[expected fail] description_test_case_broken_reference-manual.html
[expected fail] description_test_case_one_valid_reference-manual.html
[expected fail] description_title-same-element-manual.html
✓ description_link-with-label-manual.html
✓ description_test_case_557-manual.html
✓ description_test_case_664-manual.html
✓ description_test_case_665-manual.html
✓ description_test_case_666-manual.html
✓ description_test_case_772-manual.html
✓ description_test_case_773-manual.html
✓ description_test_case_774-manual.html
✓ description_test_case_838-manual.html
✓ description_test_case_broken_reference-manual.html
✓ description_test_case_one_valid_reference-manual.html
✓ description_title-same-element-manual.html
✓ name_1.0_combobox-focusable-alternative-manual.html
✓ name_1.0_combobox-focusable-manual.html
✓ name_checkbox-label-embedded-combobox-manual.html
Expand Down
63 changes: 63 additions & 0 deletions sources/__tests__/accessible-description.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { computeAccessibleDescription } from "../accessible-description";
import { renderIntoDocument } from "./helpers/test-utils";
import { prettyDOM } from "@testing-library/dom";
import diff from "jest-diff";

expect.extend({
toHaveAccessibleDescription(received, expected) {
if (received == null) {
return {
message: () =>
`The element was not an Element but '${String(received)}'`,
pass: false,
};
}

const actual = computeAccessibleDescription(received);
if (actual !== expected) {
return {
message: () =>
`expected ${prettyDOM(
received
)} to have accessible description '${expected}' but got '${actual}'\n${diff(
expected,
actual
)}`,
pass: false,
};
}

return {
message: () =>
`expected ${prettyDOM(
received
)} not to have accessible description '${expected}'\n${diff(
expected,
actual
)}`,
pass: true,
};
},
});

function testMarkup(markup, accessibleDescription) {
const container = renderIntoDocument(markup);

const testNode = container.querySelector("[data-test]");
expect(testNode).toHaveAccessibleDescription(accessibleDescription);
}

describe("wpt copies", () => {
test.each([
[
`<img src="foo.jpg" data-test alt="test" aria-describedby="t1"><span id="t1" role="presentation">foo</span>`,
"foo",
],
[
`<a data-test href="#" aria-label="California" title="San Francisco" >United States</a>`,
"San Francisco",
],
])(`#%#`, (markup, expectedAccessibleName) =>
testMarkup(markup, expectedAccessibleName)
);
});
37 changes: 37 additions & 0 deletions sources/accessible-description.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
computeTextAlternative,
ComputeTextAlternativeOptions,
} from "./accessible-name-and-description";
import { queryIdRefs } from "./util";

/**
* implements https://w3c.github.io/accname/#mapping_additional_nd_description
* @param root
* @param [options]
* @parma [options.getComputedStyle] - mock window.getComputedStyle. Needs `content`, `display` and `visibility`
*/
export function computeAccessibleDescription(
root: Element,
options: ComputeTextAlternativeOptions = {}
): string {
let description = queryIdRefs(root, "aria-describedby")
.map((element) => {
return computeTextAlternative(element, {
...options,
compute: "description",
});
})
.join(" ");

// TODO: Technically we need to make sure that node wasn't used for the accessible name
// This causes `description_1.0_combobox-focusable-manual` to fail
//
// https://www.w3.org/TR/html-aam-1.0/#accessible-name-and-description-computation
// says for so many elements to use the `title` that we assume all elements are considered
if (description === "") {
const title = root.getAttribute("title");
description = title === null ? "" : title;
}

return description;
}
Loading

0 comments on commit fa53c51

Please sign in to comment.