diff --git a/.changeset/healthy-jobs-act.md b/.changeset/healthy-jobs-act.md new file mode 100644 index 00000000..90f89ded --- /dev/null +++ b/.changeset/healthy-jobs-act.md @@ -0,0 +1,5 @@ +--- +"dom-accessibility-api": patch +--- + +Dynamically calculates the aria role for elements instead of always returning 'columnheader' diff --git a/sources/__tests__/getRole.js b/sources/__tests__/getRole.js index cb5b958d..3f6a5535 100644 --- a/sources/__tests__/getRole.js +++ b/sources/__tests__/getRole.js @@ -168,7 +168,13 @@ const cases = [ ["title", null, createElementFactory("title", {})], // WARNING: Only in certain contexts ["td", "cell", createElementFactory("td", {})], - ["th", "columnheader", createElementFactory("th", {})], + // default scope=auto + ["th missing scope", "columnheader", createElementFactory("th", {})], + ["th scope explicitly set to `auto`", "columnheader", createElementFactory("th", {scope:"auto"})], + ["th scope=col", "columnheader", createElementFactory("th", {scope:"col"})], + ["th scope=colgroup", "columnheader", createElementFactory("th", {scope:"colgroup"})], + ["th scope=row", "rowheader", createElementFactory("th", {scope:"row"})], + ["th scope=rowgroup", "rowheader", createElementFactory("th", {scope:"rowgroup"})], ["tr", "row", createElementFactory("tr", {})], ["track", null, createElementFactory("track", {})], ["ul", "list", createElementFactory("ul", {})], @@ -182,7 +188,7 @@ const cases = [ ["presentational
with prohibited aria attributes", null, createElementFactory("div", {'aria-label': "hello", role: "none"})], ]; -it.each(cases)("%s has the role %s", (name, role, elementFactory) => { +it.each(cases)("%s has the role %s", (_name, role, elementFactory) => { const element = elementFactory(); expect(getRole(element)).toEqual(role); diff --git a/sources/getRole.ts b/sources/getRole.ts index 14096a97..53f79276 100644 --- a/sources/getRole.ts +++ b/sources/getRole.ts @@ -59,7 +59,6 @@ const localNameToRoleMappings: Record = { tfoot: "rowgroup", // WARNING: Only in certain context td: "cell", - th: "columnheader", thead: "rowgroup", tr: "row", ul: "list", @@ -143,12 +142,13 @@ export default function getRole(element: Element): string | null { } function getImplicitRole(element: Element): string | null { - const mappedByTag = localNameToRoleMappings[getLocalName(element)]; + const elName = getLocalName(element); + const mappedByTag = localNameToRoleMappings[elName]; if (mappedByTag !== undefined) { return mappedByTag; } - switch (getLocalName(element)) { + switch (elName) { case "a": case "area": case "link": @@ -205,6 +205,17 @@ function getImplicitRole(element: Element): string | null { return "listbox"; } return "combobox"; + case "th": + switch (element.getAttribute("scope")) { + case "row": + case "rowgroup": + return "rowheader"; + case "col": + case "colgroup": + case "auto": + default: + return "columnheader"; + } } return null; }