hx-inherit tests x documentation

This commit is contained in:
alfonsrv 2022-01-06 14:45:12 +01:00
parent fb78c826d1
commit d025afe49f
6 changed files with 145 additions and 30 deletions

View File

@ -90,7 +90,7 @@ you can then run the test suite by navigating to:
at this point you can modify `/src/htmx.js` to add features, and then add tests in the appropriate area under `/test`
* `/test/index.html` - the root test page from which all other tests are included
* `/test/attributres` - attribute specific tests
* `/test/attributes` - attribute specific tests
* `/test/core` - core functionality tests
* `/test/core/regressions.js` - regresssion tests
* `/test/ext` - extension tests

View File

@ -79,17 +79,17 @@ return (function () {
// Utilities
//====================================================================
function parseInterval(str) {
if (str == undefined) {
return undefined
}
if (str.slice(-2) == "ms") {
return parseFloat(str.slice(0,-2)) || undefined
}
if (str.slice(-1) == "s") {
return (parseFloat(str.slice(0,-1)) * 1000) || undefined
}
return parseFloat(str) || undefined
function parseInterval(str) {
if (str == undefined) {
return undefined
}
if (str.slice(-2) == "ms") {
return parseFloat(str.slice(0,-2)) || undefined
}
if (str.slice(-1) == "s") {
return (parseFloat(str.slice(0,-1)) * 1000) || undefined
}
return parseFloat(str) || undefined
}
function getRawAttribute(elt, name) {
@ -106,6 +106,15 @@ return (function () {
return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName);
}
function getAttributeInheritance(elt, closestMatch, attributeName) {
var data = getInternalData(elt);
if (elt === closestMatch || attributeName === "hx-boost" || data.boosted) return
var inheritAttr = getAttributeValue(closestMatch, "hx-inherit");
if (inheritAttr && (inheritAttr === "false" || inheritAttr.includes(attributeName))) {
return "unset";
}
}
function parentElt(elt) {
return elt.parentElement;
}
@ -127,14 +136,8 @@ return (function () {
function getClosestAttributeValue(elt, attributeName) {
var closestAttr = null;
getClosestMatch(elt, function (e) {
closestAttr = getAttributeValue(e, attributeName)
if (closestAttr && elt !== e) {
var inheritAttr = getAttributeValue(e, "hx-inherit");
if (inheritAttr && (inheritAttr === "false" || inheritAttr.includes(attributeName))) {
closestAttr = "unset";
}
}
return closestAttr;
closestAttr = getAttributeValue(e, attributeName);
return closestAttr = getAttributeInheritance(elt, e, attributeName) || closestAttr;
});
if (closestAttr !== "unset") {
return closestAttr;
@ -383,7 +386,7 @@ return (function () {
}
function querySelectorAllExt(elt, selector) {
if (selector.indexOf("closest ") === 0) {
if (selector.indexOf("closest ") === 0) {
return [closest(elt, selector.substr(8))];
} else if (selector.indexOf("find ") === 0) {
return [find(elt, selector.substr(5))];
@ -452,7 +455,7 @@ return (function () {
function getTarget(elt) {
var explicitTarget = getClosestMatch(elt, function(e){return getAttributeValue(e,"hx-target") !== null});
if (explicitTarget) {
if (explicitTarget && getAttributeInheritance(elt, explicitTarget, "hx-target") !== "unset") {
var targetStr = getAttributeValue(explicitTarget, "hx-target");
if (targetStr === "this") {
return explicitTarget;
@ -2518,9 +2521,9 @@ return (function () {
target = beforeSwapDetails.target; // allow re-targeting
serverResponse = beforeSwapDetails.serverResponse; // allow updating content
isError = beforeSwapDetails.isError; // allow updating error
responseInfo.failed = isError; // Make failed property available to response events
responseInfo.successful = !isError; // Make successful property available to response events
responseInfo.successful = !isError; // Make successful property available to response events
if (beforeSwapDetails.shouldSwap) {
if (xhr.status === 286) {

View File

@ -55,7 +55,7 @@ describe("hx-inherit attribute", function() {
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal('<span id="cta" class="">' + response + '</span>');
btn.innerHTML.should.equal(response);
});
it('same-element inheritance disable', function () {
@ -69,21 +69,67 @@ describe("hx-inherit attribute", function() {
btn.innerHTML.should.equal(response_inner);
});
/* Does not work properly yet; how can btn.innerHTML be true, but at the same time div.parentElement.innerHTML not contain the btn itself anymore? */
it('same-element inheritance disable with child nodes', function () {
var response_inner = '<div id="snowflake" class="">Hello world</div>'
var response = '<div id="unique">' + response_inner + '</div>'
this.server.respondWith("GET", "/test", response);
this.server.respondWith("GET", "/test2", 'unique-snowflake');
var div = make('<div hx-select="#snowflake" hx-target="#container" hx-get="/test" hx-swap="outerHTML" hx-inherit="false"><div id="container"><button id="bx1" hx-get="/test2" hx-trigger="click" hx-target="#target"><div id="target"></div></button></div></div>')
var div = make('<div hx-select="#snowflake" hx-target="#container" hx-get="/test" hx-swap="outerHTML" hx-trigger="keyup" hx-inherit="false"><div id="container"><button id="bx1" hx-get="/test2" hx-trigger="click" hx-target="#target"><div id="target"></div></button></div></div>')
var btn = byId("bx1");
btn.click();
this.server.respond();
btn.innerHTML.should.equal('<div id="target" class="">unique-snowflake</div>');
div.parentElement.innerHTML.should.equal('');
var count = (div.innerHTML.match(/slowflake/g) || []).length;
count.should.equal(1);
var count = (div.parentElement.innerHTML.match(/snowflake/g) || []).length;
count.should.equal(2); // hx-select of parent div and newly loaded inner content
});
it('boosted element hx-inherit sanity check', function () {
try {
var request;
var handler = htmx.on("htmx:beforeRequest", function (evt) {
request = evt;
});
var div = make('<div hx-boost="true" hx-inherit="false"><a id="a1" href="/test">Click me</a></div>');
var link = byId("a1");
link.click();
should.equal(request.detail.requestConfig.path, '/test');
should.equal(request.detail.elt["htmx-internal-data"].boosted, true);
} finally {
htmx.off("htmx:beforeRequest", handler);
}
});
it('boosted element inheritance manual unset', function () {
try {
var request;
var handler = htmx.on("htmx:beforeRequest", function (evt) {
request = evt;
});
var div = make('<div hx-boost="true" hx-get="/test"><div hx-boost="unset"><a id="a1" href="/test">Click me</a></div></div>');
var link = byId("a1");
should.equal(link["htmx-internal-data"].boosted, undefined);
} finally {
htmx.off("htmx:beforeRequest", handler);
}
});
it('nested htmx-node with boosting parent', function () {
try {
var request;
var handler = htmx.on("htmx:beforeRequest", function (evt) {
request = evt;
});
var div = make('<div hx-boost="true" hx-target="#test" hx-inherit="false"><div id="test"></div><a id="a1" href="/test" hx-get="/test2">Click me</a></div>');
var link = byId("a1");
link.click();
should.equal(request.detail.requestConfig.path, '/test2');
should.equal(request.detail.elt["htmx-internal-data"].boosted, undefined);
should.equal(request.detail.target.id, "a1");
} finally {
htmx.off("htmx:beforeRequest", handler);
}
});
});

View File

@ -0,0 +1,62 @@
---
layout: layout.njk
title: </> htmx - hx-inherit
---
## `hx-inherit`
The `hx-inherit` attribute allows you to control the automatic attribute inheritance of an element to its
children nodes. One use case here is, to allow wrapping the whole page in `hx-boost`, while not having
the configuration of that wrapper affect a more elaborated child node htmx configuration.
The default behavior for htmx is to inherit all attributes automatically.
`hx-inherit` *disables* inheritance.
htmx evaluates inheritance as follows:
* when `hx-boost` is not set
* get all elements that defines a `hx-<verb>` (`hx-get`, `hx-post`, `hx-patch`, `hx-put` or `hx-delete`)
* for each htmx attribute, either find the htmx attribute on the element the `hx-<verb>` was defined on or traverse up all the parent nodes
* the first element's value the corresponding htmx attribute has been found on is then used
* when `hx-boost` is set
* make all `<a>` tags a `hx-get` with the link defined in the `href` attribute - [details](/attributes/hx-boost) which links hx-boost handles
* then, same htmx attribute lookup procedure as above
* when `hx-inherit` is set on a parent node
* when a parent node has a matching htmx attribute during the automatic lookup outlined above
* `hx-inherit="false"` disable all attribute inheritance
* `hx-inherit="hx-select hx-get hx-target"` disable inheritance for only one or multiple specified attributes
```html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-inherit="false">
<a href="/page1">Go To Page 1</a> <!-- boosted with the attribute settings above -->
<a href="/page2" hx-boost="unset">Go To Page 1</a> <!-- not boosted -->
<button hx-get="/test" hx-target="this"></button> <!-- hx-select is not inherited -->
</div>
```
```html
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-inherit="hx-target">
<!-- hx-select is automatically set to parent's value; hx-target is not inherited -->
<button hx-get="/test"></button>
</div>
```
```html
<div hx-target="#content">
<div hx-boost="true" hx-select="#content" hx-inherit="hx-target">
<!-- hx-select is automatically set to parent's value -->
<!-- hx-target is inherited, because the direct parent does
not specify hx-target, thus cannot disable inheritance -->
<button hx-get="/test"></button>
</div>
</div>
```
### Notes
* `hx-inherit="false"` or `hx-inherit="hx-boost"` does not disable `hx-boost`; instead use `hx-boost="unset"` to disable inheritance
* `hx-inherit="false"` or `hx-inherit="<attribute"` only affects the inheritance outcome, if the element also has the htmx attribute
defined that is currently being looked up
* Find out more about [Attribute Inheritance](/docs/#inheritance)

View File

@ -511,6 +511,9 @@ be confirmed. We could add an `unset` directive on it like so:
The top two buttons would then show a confirm dialog, but the bottom cancel button would not.
Automatic inheritance can be further configured or disabled using [`hx-inherit`](/attributes/hx-inherit).
## <a name="boosting"></a>[Boosting](#boosting)
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](/attributes/hx-boost) attribute. This

View File

@ -31,6 +31,7 @@ title: </> htmx - Attributes
| [`hx-history-elt`](/attributes/hx-history-elt) | the element to snapshot and restore during history navigation
| [`hx-include`](/attributes/hx-include) | includes additional data in AJAX requests
| [`hx-indicator`](/attributes/hx-indicator) | the element to put the `htmx-request` class on during the AJAX request
| [`hx-inherit`](/attributes/hx-inherit) | control and disable automatic attribute inheritance for child nodes
| [`hx-params`](/attributes/hx-params) | filters the parameters that will be submitted with a request
| [`hx-patch`](/attributes/hx-patch) | issues a `PATCH` to the specified URL
| [`hx-post`](/attributes/hx-post) | issues a `POST` to the specified URL