Extension.handleSwap: trigger events for extensions defined on parents

If an element is replaced by an extension in `handleSwap`, the events
(`afterSwap.htmx` and `afterSettle.htmx`) were not received by the
`onLoad` method of extensions defined on parents of the target, because
theses extensions were retrieved after the replacement, and so it was
not possible to get through the parents, the target not being in the dom
anymore.

This commits loads the extensions for the target and save them in
`eventDetail` before doing the swap, so they are accessible in
`triggerEvent`, and passed to `withExtensions` that use this list if
passed (else load them).

A new test is added that fails without the updates in `htmx.js`.
This commit is contained in:
Stephane Angel (Twidi) 2020-06-12 00:49:07 +02:00
parent 7a0010f43c
commit 86febd3efc
No known key found for this signature in database
GPG Key ID: CDD585D4431044D5
3 changed files with 56 additions and 4 deletions

View File

@ -954,8 +954,8 @@ return (function () {
return eventName === "processedNode.htmx"
}
function withExtensions(elt, toDo) {
forEach(getExtensions(elt), function(extension){
function withExtensions(elt, toDo, extensions) {
forEach(typeof extensions === 'undefined' ? getExtensions(elt) : extensions, function(extension){
try {
toDo(extension);
} catch (e) {
@ -988,7 +988,7 @@ return (function () {
var eventResult = elt.dispatchEvent(event);
withExtensions(elt, function (extension) {
eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
});
}, detail.extensions);
return eventResult;
}
@ -1407,7 +1407,7 @@ return (function () {
}
}
var eventDetail = {xhr: xhr, target: target};
var eventDetail = {xhr: xhr, target: target, extensions: getExtensions(elt)};
xhr.onload = function () {
try {
if (!triggerEvent(elt, 'beforeOnLoad.htmx', eventDetail)) return;

50
test/ext/index.js Normal file
View File

@ -0,0 +1,50 @@
describe("default extensions behavior", function() {
var loadCalls, afterSwapCalls, afterSettleCalls;
beforeEach(function () {
loadCalls = afterSwapCalls = afterSettleCalls = 0;
this.server = makeServer();
clearWorkArea();
htmx.defineExtension("ext-testswap", {
onEvent : function(name, evt) {
if (name === "load.htmx") {
loadCalls++;
}
if (name === "afterSwap.htmx") {
afterSwapCalls++;
}
if (name === "afterSettle.htmx") {
afterSettleCalls++;
}
},
handleSwap: function (swapStyle, target, fragment, settleInfo) {
// simple outerHTML replacement for tests
var parentEl = target.parentElement;
parentEl.removeChild(target);
parentEl.appendChild(fragment)
return true;
}
});
});
afterEach(function () {
this.server.restore();
clearWorkArea();
htmx.removeExtension("ext-testswap");
});
it('handleSwap: afterSwap and afterSettle triggered if extension defined on parent', function () {
this.server.respondWith("GET", "/test", '<button>Clicked!</button>');
var div = make('<div hx-ext="ext-testswap"><button hx-get="/test" hx-swap="testswap">Click Me!</button></div>');
var btn = div.firstChild;
btn.click()
this.server.respond();
afterSwapCalls.should.equal(1);
afterSettleCalls.should.equal(1);
loadCalls.should.equal(0); // load.htmlx event on new added button is not triggered
});
});

View File

@ -83,6 +83,8 @@
<script src="attributes/hx-ws.js"></script>
<!-- extension tests -->
<script src="ext/index.js"></script>
<script src="../src/ext/method-override.js"></script>
<script src="ext/method-override.js"></script>