element-internals-aria-element-reflection.html (12941B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>Element Reflection for aria-activedescendant and aria-errormessage on ElementInternals</title> 6 <link rel=help href="https://whatpr.org/html/3917/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:element"> 7 <link rel="author" title="Alice Boxhall" href="alice@igalia.com"> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 <script src="/resources/testdriver-actions.js"></script> 11 <script src="/resources/testharness.js"></script> 12 <script src="/resources/testharnessreport.js"></script> 13 </head> 14 15 <body> 16 <script> 17 class CustomElement extends HTMLElement { 18 constructor() { 19 super(); 20 this.i = this.attachInternals(); 21 } 22 } 23 customElements.define('custom-element', CustomElement); 24 </script> 25 26 <custom-element id="custom1"></custom-element> 27 28 <div id="activedescendant">Active descendant</div> 29 <div id="controls">Controls</div> 30 <div id="describedby">Described by</div> 31 <div id="details">Details</div> 32 <div id="errormessage">Error message</div> 33 <div id="flowto">Flow to</div> 34 <div id="labelledby">Labelled by</div> 35 <div id="owns">Owns</div> 36 37 <div id="labelledby_content">Labelled by from content attribute</div> 38 <div id="labelledby_element">Labelled by from IDL attribute</div> 39 40 <div id="shadowhost"> 41 <template shadowrootmode="open"> 42 <div id="labelledby_element_shadow">Invalid labelled by in shadow root</div> 43 </template> 44 </div> 45 46 <script> 47 const element_properties = [ 48 ['ariaActiveDescendantElement', 'activedescendant']]; 49 const array_properties = [ 50 ['ariaControlsElements', 'controls'], 51 ['ariaDescribedByElements', 'describedby'], 52 ['ariaDetailsElements', 'details'], 53 ['ariaErrorMessageElements', 'errormessage'], 54 ['ariaFlowToElements', 'flowto'], 55 ['ariaLabelledByElements', 'labelledby'], 56 ['ariaOwnsElements', 'owns']]; 57 58 test(t => { 59 const custom = document.getElementById('custom1'); 60 for (const [property, id] of element_properties) { 61 assert_equals(custom.i[property], null); 62 } 63 for (const [property, id] of array_properties) { 64 assert_equals(custom.i[property], null); 65 } 66 }, "Getting previously-unset ARIA element reflection properties on ElementInternals should return null."); 67 68 test(t => { 69 const custom = document.getElementById('custom1'); 70 for (const [property, id] of element_properties) { 71 const related = document.getElementById(id); 72 custom.i[property] = related; 73 assert_equals(custom.i[property], related); 74 } 75 for (const [property, id] of array_properties) { 76 const related = document.getElementById(id); 77 custom.i[property] = [related]; 78 assert_array_equals(custom.i[property], [related]); 79 } 80 }, "Getting ARIA element reflection properties on ElementInternals should return the value that was set."); 81 82 test(t => { 83 const custom = document.getElementById('custom1'); 84 for (const [property, id] of array_properties) { 85 custom.i[property] = []; 86 assert_array_equals(custom.i[property], []); 87 } 88 }, "Setting ARIA element reflection properties to an empty array should work as expected."); 89 90 test(t => { 91 const custom = document.getElementById('custom1'); 92 for (const [property, id] of element_properties) { 93 const related = document.getElementById(id); 94 custom.i[property] = related; 95 assert_equals(custom.i[property], related); 96 97 custom.i[property] = null; 98 assert_equals(custom.i[property], null); 99 } 100 for (const [property, id] of array_properties) { 101 const related = document.getElementById(id); 102 custom.i[property] = [related]; 103 assert_array_equals(custom.i[property], [related]); 104 105 custom.i[property] = null; 106 assert_equals(custom.i[property], null); 107 } 108 }, "Setting ARIA element reflection properties on ElementInternals to null should delete any previous value, and not crash"); 109 110 promise_test(async t => { 111 const custom = document.getElementById('custom1'); 112 custom.i.ariaLabelledByElements = null; 113 const label_before_labelledby_set = await test_driver.get_computed_label(custom); 114 assert_equals(label_before_labelledby_set, "", "Before ariaLabelledByElements is set, accessible label should be empty."); 115 116 const labelledBy = document.getElementById('labelledby'); 117 custom.i.ariaLabelledByElements = [labelledBy]; 118 const label_after_labelledby_set = await test_driver.get_computed_label(custom); 119 assert_equals(label_after_labelledby_set, "Labelled by", "After ariaLabelledByElements is set, accessible label should be 'Labelled by'"); 120 }, "Setting ariaLabelledByElements on ElementInternals should change the accessible name of the custom element") 121 122 promise_test(async t => { 123 const custom = document.getElementById('custom1'); 124 custom.i.ariaLabelledByElements = null; 125 const label_before_labelledby_set = await test_driver.get_computed_label(custom); 126 assert_equals(label_before_labelledby_set, "", "Before ariaLabelledByElements is set, accessible label should be empty."); 127 128 const labelledBy = document.getElementById('labelledby'); 129 custom.i.ariaLabelledByElements = [labelledBy]; 130 const label_after_internals_labelledby_set = await test_driver.get_computed_label(custom); 131 assert_equals(label_after_internals_labelledby_set, "Labelled by", "After ariaLabelledByElements is set, accessible label should be 'Labelled by'"); 132 133 custom.setAttribute('aria-labelledby', 'labelledby_content'); 134 const label_after_content_labelledby_set = await test_driver.get_computed_label(custom); 135 assert_equals(label_after_content_labelledby_set, "Labelled by from content attribute", "aria-labelledby content attribute supersedes ariaLabelledByElements on internals"); 136 137 const labelledby_element = document.getElementById('labelledby_element'); 138 custom.ariaLabelledByElements = [labelledby_element]; 139 const label_after_idl_arialabelledbyelements_set = await test_driver.get_computed_label(custom); 140 assert_equals(label_after_idl_arialabelledbyelements_set, "Labelled by from IDL attribute", "ariaLabelledByElements on element supersedes ariaLabelledByElements on internals"); 141 142 custom.setAttribute('aria-labelledby', 'bad_id'); 143 const label_after_content_labelledby_set_to_bad_id = await test_driver.get_computed_label(custom); 144 assert_equals(label_after_content_labelledby_set_to_bad_id, "", "aria-labelledby content attribute supersedes ariaLabelledByElements on internals even when invalid"); 145 146 const shadowhost = document.getElementById('shadowhost'); 147 const labelledby_element_invalid = shadowhost.shadowRoot.getElementById('labelledby_element_shadow'); 148 custom.ariaLabelledByElements = [labelledby_element_invalid]; 149 const label_after_idl_arialabelledbyelements_set_to_invalid_element = await test_driver.get_computed_label(custom); 150 assert_equals(label_after_idl_arialabelledbyelements_set_to_invalid_element, "", "ariaLabelledByElements on element supersedes ariaLabelledByElements on internals even when invalid"); 151 }, "Setting aria-labelledby or ariaLabelledByElements on the custom element should supersede the value of ariaLabelledByElements on ElementInternals"); 152 </script> 153 154 <custom-element id="cachingInvariantMain"></custom-element> 155 <div id="cachingInvariantElement1"></div> 156 <div id="cachingInvariantElement2"></div> 157 <div id="cachingInvariantElement3"></div> 158 <div id="cachingInvariantElement4"></div> 159 <div id="cachingInvariantElement5"></div> 160 161 <script> 162 test(function(t) { 163 cachingInvariantMain.i.ariaControlsElements = [cachingInvariantElement1, cachingInvariantElement2]; 164 cachingInvariantMain.i.ariaDescribedByElements = [cachingInvariantElement3, cachingInvariantElement4]; 165 cachingInvariantMain.i.ariaDetailsElements = [cachingInvariantElement5]; 166 cachingInvariantMain.i.ariaFlowToElements = [cachingInvariantElement1, cachingInvariantElement3]; 167 cachingInvariantMain.i.ariaLabelledByElements = [cachingInvariantElement2, cachingInvariantElement4]; 168 cachingInvariantMain.i.ariaOwnsElements = [cachingInvariantElement1, cachingInvariantElement2, cachingInvariantElement3]; 169 170 let ariaControlsElementsArray = cachingInvariantMain.i.ariaControlsElements; 171 let ariaDescribedByElementsArray = cachingInvariantMain.i.ariaDescribedByElements; 172 let ariaDetailsElementsArray = cachingInvariantMain.i.ariaDetailsElements; 173 let ariaFlowToElementsArray = cachingInvariantMain.i.ariaFlowToElements; 174 let ariaLabelledByElementsArray = cachingInvariantMain.i.ariaLabelledByElements; 175 let ariaOwnsElementsArray = cachingInvariantMain.i.ariaOwnsElements; 176 177 assert_equals(ariaControlsElementsArray, cachingInvariantMain.i.ariaControlsElements, "Caching invariant for ariaControlsElements"); 178 assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements"); 179 assert_equals(ariaDetailsElementsArray, cachingInvariantMain.i.ariaDetailsElements, "Caching invariant for ariaDetailsElements"); 180 assert_equals(ariaFlowToElementsArray, cachingInvariantMain.i.ariaFlowToElements, "Caching invariant for ariaFlowToElements"); 181 assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.i.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements"); 182 assert_equals(ariaOwnsElementsArray, cachingInvariantMain.i.ariaOwnsElements, "Caching invariant for ariaOwnsElements"); 183 184 // Ensure that cached values don't become stale 185 cachingInvariantMain.i.ariaControlsElements = [cachingInvariantElement4, cachingInvariantElement5]; 186 cachingInvariantMain.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2]; 187 cachingInvariantMain.i.ariaDetailsElements = [cachingInvariantElement3]; 188 cachingInvariantMain.i.ariaFlowToElements = [cachingInvariantElement4, cachingInvariantElement5]; 189 cachingInvariantMain.i.ariaLabelledByElements = [cachingInvariantElement1, cachingInvariantElement2]; 190 cachingInvariantMain.i.ariaOwnsElements = [cachingInvariantElement3, cachingInvariantElement4, cachingInvariantElement1]; 191 192 ariaControlsElementsArray = cachingInvariantMain.i.ariaControlsElements; 193 ariaDescribedByElementsArray = cachingInvariantMain.i.ariaDescribedByElements; 194 ariaDetailsElementsArray = cachingInvariantMain.i.ariaDetailsElements; 195 ariaFlowToElementsArray = cachingInvariantMain.i.ariaFlowToElements; 196 ariaLabelledByElementsArray = cachingInvariantMain.i.ariaLabelledByElements; 197 ariaOwnsElementsArray = cachingInvariantMain.i.ariaOwnsElements; 198 199 assert_equals(ariaControlsElementsArray, cachingInvariantMain.i.ariaControlsElements, "Caching invariant for ariaControlsElements"); 200 assert_equals(ariaDescribedByElementsArray, cachingInvariantMain.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements"); 201 assert_equals(ariaDetailsElementsArray, cachingInvariantMain.i.ariaDetailsElements, "Caching invariant for ariaDetailsElements"); 202 assert_equals(ariaFlowToElementsArray, cachingInvariantMain.i.ariaFlowToElements, "Caching invariant for ariaFlowToElements"); 203 assert_equals(ariaLabelledByElementsArray, cachingInvariantMain.i.ariaLabelledByElements, "Caching invariant for ariaLabelledByElements"); 204 assert_equals(ariaOwnsElementsArray, cachingInvariantMain.i.ariaOwnsElements, "Caching invariant for ariaOwnsElements"); 205 206 }, "Caching invariant different attributes."); 207 </script> 208 209 <custom-element id="cachingInvariantMain1"></custom-element> 210 <custom-element id="cachingInvariantMain2"></custom-element> 211 212 <script> 213 test(function(t) { 214 cachingInvariantMain1.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2]; 215 cachingInvariantMain2.i.ariaDescribedByElements = [cachingInvariantElement1, cachingInvariantElement2]; 216 217 let ariaDescribedByElementsArray1 = cachingInvariantMain1.i.ariaDescribedByElements; 218 let ariaDescribedByElementsArray2 = cachingInvariantMain2.i.ariaDescribedByElements; 219 220 assert_equals(ariaDescribedByElementsArray1, cachingInvariantMain1.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in one elemnt"); 221 assert_equals(ariaDescribedByElementsArray2, cachingInvariantMain2.i.ariaDescribedByElements, "Caching invariant for ariaDescribedByElements in onother elemnt"); 222 assert_not_equals(cachingInvariantMain1.i.ariaDescribedByElements, cachingInvariantMain2.i.ariaDescribedByElements); 223 }, "Caching invariant different elements."); 224 </script> 225 226 </body> 227 </html>