diff --git a/list/List.js b/list/List.js index 16a78f0fc9..c5336fd109 100644 --- a/list/List.js +++ b/list/List.js @@ -136,6 +136,13 @@ define([ return function (attr, value) { sup.apply(this, arguments); if (attr === "role") { + // Only role=grid should have aria-readonly. It's meaningless for role=listbox or role=menu. + if (value === "grid") { + this.setAttribute("aria-readonly", "true"); + } else { + this.removeAttribute("aria-readonly"); + } + // Update roles of existing renderers. var renderers = this.querySelectorAll(this.itemRenderer.tag + ", " + this.categoryRenderer.tag); Array.prototype.forEach.call(renderers, function (renderer) { @@ -236,9 +243,13 @@ define([ var currentRole = this.getAttribute("role"); if (!currentRole) { this.setAttribute("role", "grid"); + currentRole = "grid"; + } + + // role=grid should have aria-disabled, but it's meaningless for role=listbox or role=menu. + if (currentRole === "grid") { + this.setAttribute("aria-readonly", "true"); } - // Might be overriden at the cell (renderer renderNode) level when developing custom renderers - this.setAttribute("aria-readonly", "true"); }, queryStoreAndInitItems: dcl.superCall(function (sup) { diff --git a/tests/unit/list/AriaListbox.js b/tests/unit/list/AriaListbox.js index 3613004142..f2b4733f5d 100644 --- a/tests/unit/list/AriaListbox.js +++ b/tests/unit/list/AriaListbox.js @@ -17,17 +17,35 @@ define([ list.destroy(); } list = new List({source: new Store()}); - list.setAttribute("role", "listbox"); - document.body.appendChild(list); - list.attachedCallback(); - list.source.filter(); list.source.add({label: "item 1"}); list.source.add({label: "item 2"}); list.source.add({label: "item 3"}); + }, + + "aria properties for role=grid": function () { + list.placeAt(document.body); list.deliver(); + + assert.strictEqual(list.getAttribute("role"), "grid", "role"); + assert(list.hasAttribute("aria-readonly"), "aria-readonly for role=grid"); + assert.strictEqual(list.children[0].getAttribute("role"), "row", "first renderer role"); + assert.strictEqual(list.children[0].renderNode.getAttribute("role"), "gridcell", + "first renderNode role"); + assert.strictEqual(list.children[1].getAttribute("role"), "row", "second renderer role"); + assert.strictEqual(list.children[1].renderNode.getAttribute("role"), "gridcell", + "second renderNode role"); + assert.strictEqual(list.children[2].getAttribute("role"), "row", "third renderer role"); + assert.strictEqual(list.children[2].renderNode.getAttribute("role"), "gridcell", + "third renderNode role"); }, - "aria properties": function () { + + "aria properties for role=listbox": function () { + list.setAttribute("role", "listbox"); + list.placeAt(document.body); + list.deliver(); + assert.strictEqual(list.getAttribute("role"), "listbox", "role"); + assert.isFalse(list.hasAttribute("aria-readonly"), "aria-readonly only for role=grid"); assert.strictEqual(list.children[0].getAttribute("role"), null, "first renderer role"); assert.strictEqual(list.children[0].renderNode.getAttribute("role"), "option", "first renderNode role"); @@ -38,10 +56,15 @@ define([ assert.strictEqual(list.children[2].renderNode.getAttribute("role"), "option", "third renderNode role"); }, + "aria properties when moving from listbox to grid": function () { + list.setAttribute("role", "listbox"); list.setAttribute("role", "grid"); + list.placeAt(document.body); list.deliver(); + assert.strictEqual(list.getAttribute("role"), "grid", "role"); + assert(list.hasAttribute("aria-readonly"), "aria-readonly for role=grid"); assert.strictEqual(list.children[0].getAttribute("role"), "row", "first renderer role"); assert.strictEqual(list.children[0].renderNode.getAttribute("role"), "gridcell", "first renderNode role"); @@ -52,6 +75,7 @@ define([ assert.strictEqual(list.children[2].renderNode.getAttribute("role"), "gridcell", "third renderNode role"); }, + teardown : function () { list.destroy(); }