behaves-like-button-with-command.tentative.html (9259B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-vendor.js"></script> 8 <script src="/resources/testdriver-actions.js"></script> 9 <script src="/wai-aria/scripts/aria-utils.js"></script> 10 </head> 11 <body> 12 <custom-element id="customElement"></custom-element> 13 <custom-button id="customButton"></custom-button> 14 15 <!-- Targets --> 16 <div id="target" popover>Popover content</div> 17 <dialog id="dialog">Dialog content</dialog> 18 19 <script> 20 class CustomElement extends HTMLElement { 21 constructor() { 22 super(); 23 this.internals_ = this.attachInternals(); 24 } 25 } 26 customElements.define('custom-element', CustomElement); 27 28 class CustomButton extends HTMLElement { 29 static formAssociated = true; 30 constructor() { 31 super(); 32 this.internals_ = this.attachInternals(); 33 this.internals_.type = "button"; 34 } 35 36 get disabled() { 37 return this.hasAttribute('disabled'); 38 } 39 40 set disabled(value) { 41 if (value) { 42 this.setAttribute('disabled', ''); 43 } else { 44 this.removeAttribute('disabled'); 45 } 46 } 47 } 48 customElements.define('custom-button', CustomButton); 49 50 function resetState() { 51 document.getElementById("target").hidePopover(); 52 document.getElementById("dialog").close(); 53 document.getElementById('customButton').outerHTML = '<custom-button id="customButton">'; 54 document.getElementById('customElement').outerHTML = '<custom-element id="customElement">'; 55 } 56 57 // Test popover commands 58 test(t => { 59 const customElement = document.getElementById("customElement"); 60 const target = document.getElementById("target"); 61 customElement.setAttribute('commandfor', 'target'); 62 customElement.setAttribute('command', 'toggle-popover'); 63 t.add_cleanup(() => resetState()); 64 65 // Custom element without type=button should not work with commandfor 66 assert_false(target.matches(':popover-open'), "Popover should start closed"); 67 customElement.click(); 68 assert_false(target.matches(':popover-open'), "Custom element without type=button should not trigger popover"); 69 }, "Custom element without type=button does not support commandfor attribute."); 70 71 test(t => { 72 const customButton = document.getElementById("customButton"); 73 const target = document.getElementById("target"); 74 customButton.setAttribute('commandfor', 'target'); 75 customButton.setAttribute('command', 'toggle-popover'); 76 t.add_cleanup(() => resetState()); 77 78 // Custom button with type=button should work 79 assert_false(target.matches(':popover-open'), "Popover should start closed"); 80 customButton.click(); 81 assert_true(target.matches(':popover-open'), "Custom button with type=button should open popover"); 82 83 // Second click should close 84 customButton.click(); 85 assert_false(target.matches(':popover-open'), "Second click should close popover"); 86 }, "Custom button with type=button and command='toggle-popover' toggles popover."); 87 88 test(t => { 89 const customButton = document.getElementById("customButton"); 90 const target = document.getElementById("target"); 91 customButton.setAttribute('commandfor', 'target'); 92 customButton.setAttribute('command', 'show-popover'); 93 t.add_cleanup(() => resetState()); 94 95 assert_false(target.matches(':popover-open'), "Popover should start closed"); 96 customButton.click(); 97 assert_true(target.matches(':popover-open'), "show-popover should open popover"); 98 99 // Second click should not close (show action only opens) 100 customButton.click(); 101 assert_true(target.matches(':popover-open'), "show-popover should not close popover on second click"); 102 }, "Custom button with command='show-popover' only opens popover."); 103 104 test(t => { 105 const customButton = document.getElementById("customButton"); 106 const target = document.getElementById("target"); 107 customButton.setAttribute('commandfor', 'target'); 108 customButton.setAttribute('command', 'hide-popover'); 109 t.add_cleanup(() => resetState()); 110 111 // Start with popover open 112 target.showPopover(); 113 assert_true(target.matches(':popover-open'), "Popover should start open"); 114 115 customButton.click(); 116 assert_false(target.matches(':popover-open'), "hide-popover should close popover"); 117 118 // Second click should not open (hide action only closes) 119 customButton.click(); 120 assert_false(target.matches(':popover-open'), "hide-popover should not open popover on second click"); 121 }, "Custom button with command='hide-popover' only closes popover."); 122 123 test(t => { 124 const customButton = document.getElementById("customButton"); 125 const target = document.getElementById("target"); 126 customButton.setAttribute('commandfor', 'target'); 127 customButton.setAttribute('command', 'toggle-popover'); 128 customButton.disabled = true; 129 t.add_cleanup(() => resetState()); 130 131 // Disabled custom button should not work 132 assert_true(customButton.disabled, "Custom button should be disabled"); 133 assert_false(target.matches(':popover-open'), "Popover should start closed"); 134 customButton.click(); 135 assert_false(target.matches(':popover-open'), "Disabled custom button should not open popover"); 136 }, "Disabled custom button does not support commandfor."); 137 138 // Test dialog commands 139 test(t => { 140 const customButton = document.getElementById("customButton"); 141 const dialog = document.getElementById("dialog"); 142 customButton.setAttribute('commandfor', 'dialog'); 143 customButton.setAttribute('command', 'show-modal'); 144 t.add_cleanup(() => resetState()); 145 146 assert_false(dialog.open, "Dialog should start closed"); 147 customButton.click(); 148 assert_true(dialog.open, "show-modal should open dialog"); 149 }, "Custom button with command='show-modal' opens dialog."); 150 151 test(t => { 152 const customButton = document.getElementById("customButton"); 153 const dialog = document.getElementById("dialog"); 154 customButton.setAttribute('commandfor', 'dialog'); 155 customButton.setAttribute('command', 'close'); 156 t.add_cleanup(() => resetState()); 157 158 // Start with dialog open 159 dialog.showModal(); 160 assert_true(dialog.open, "Dialog should start open"); 161 customButton.click(); 162 assert_false(dialog.open, "close command should close dialog"); 163 }, "Custom button with command='close' closes dialog."); 164 165 test(t => { 166 const customButton = document.getElementById("customButton"); 167 const dialog = document.getElementById("dialog"); 168 customButton.setAttribute('commandfor', 'dialog'); 169 customButton.setAttribute('command', 'request-close'); 170 t.add_cleanup(() => resetState()); 171 172 // Test with dialog open - should close it 173 dialog.showModal(); 174 assert_true(dialog.open, "Dialog should start open"); 175 customButton.click(); 176 assert_false(dialog.open, "request-close command should close open dialog"); 177 178 // Test with dialog already closed - should do nothing 179 assert_false(dialog.open, "Dialog should be closed"); 180 customButton.click(); 181 assert_false(dialog.open, "request-close command should not open closed dialog"); 182 }, "Custom button with command='request-close' closes open dialog and does nothing when dialog is already closed."); 183 184 // Test custom commands 185 test(t => { 186 const customButton = document.getElementById("customButton"); 187 const target = document.getElementById("target"); 188 let commandEventFired = false; 189 let receivedCommand = ''; 190 customButton.setAttribute('commandfor', 'target'); 191 customButton.setAttribute('command', '--my-custom-action'); 192 193 const commandListener = (e) => { 194 commandEventFired = true; 195 receivedCommand = e.command; 196 }; 197 target.addEventListener('command', commandListener, { signal: t.get_signal() }); 198 t.add_cleanup(() => resetState()); 199 200 customButton.click(); 201 assert_true(commandEventFired, "Custom command should fire command event"); 202 assert_equals(receivedCommand, '--my-custom-action', "Command event should contain custom command"); 203 }, "Custom button with custom command fires command event."); 204 205 // Test invalid commands 206 test(t => { 207 const customButton = document.getElementById("customButton"); 208 const target = document.getElementById("target"); 209 let commandEventFired = false; 210 customButton.setAttribute('commandfor', 'target'); 211 customButton.setAttribute('command', 'invalid-action'); 212 const commandListener = (e) => { 213 commandEventFired = true; 214 }; 215 target.addEventListener('command', commandListener, { signal: t.get_signal() }); 216 t.add_cleanup(() => resetState()); 217 218 customButton.click(); 219 assert_false(commandEventFired, "Invalid command should not fire command event"); 220 }, "Custom button with invalid command does not fire command event."); 221 </script> 222 </body> 223 </html>