This commit is contained in:
Carson Gross 2023-12-24 16:37:51 -07:00
parent 90096a6869
commit 9fbbd17672
6 changed files with 3832 additions and 1608 deletions

4341
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,7 @@
"chai-dom": "^1.12.0",
"eslint": "^8.56.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.29.1",
"fs-extra": "^9.1.0",
"mocha": "10.1.0",
"mocha-chrome": "https://github.com/Telroshan/mocha-chrome",

View File

@ -61,9 +61,9 @@ var htmx = (function() {
triggerSpecsCache: null,
disableInheritance: false,
responseHandling: [
{code:"204", swap: false},
{code:"[23]..", swap: true},
{code:"[45]..", swap: false, error:true},
{ code: '204', swap: false },
{ code: '[23]..', swap: true },
{ code: '[45]..', swap: false, error: true }
]
},
parseInterval,
@ -208,21 +208,21 @@ var htmx = (function() {
function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) {
const attributeValue = getAttributeValue(ancestor, attributeName)
const disinherit = getAttributeValue(ancestor, 'hx-disinherit')
var inherit = getAttributeValue(ancestor, "hx-inherit");
if (initialElement !== ancestor) {
if (htmx.config.disableInheritance) {
if (inherit && (inherit === "*" || inherit.split(" ").indexOf(attributeName) >= 0)) {
return attributeValue;
} else {
return null;
}
}
if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) {
return 'unset'
}
}
return attributeValue;
var inherit = getAttributeValue(ancestor, 'hx-inherit')
if (initialElement !== ancestor) {
if (htmx.config.disableInheritance) {
if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) {
return attributeValue
} else {
return null
}
}
if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) {
return 'unset'
}
}
return attributeValue
}
/**
* @param {HTMLElement} elt
@ -844,12 +844,12 @@ var htmx = (function() {
}
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) {
const oobValue = getAttributeValue(oobElement, "hx-swap-oob");
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
oobSwap(oobValue, oobElement, settleInfo);
oobSwap(oobValue, oobElement, settleInfo)
}
});
})
}
function handleOutOfBandSwaps(elt, fragment, settleInfo) {
@ -870,7 +870,7 @@ var htmx = (function() {
}
}
findAndSwapOobElements(fragment, settleInfo)
forEach(findAll(fragment, 'template'), function (template) {
forEach(findAll(fragment, 'template'), function(template) {
findAndSwapOobElements(template.content, settleInfo)
if (template.content.childElementCount === 0) {
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
@ -3205,15 +3205,15 @@ var htmx = (function() {
}
function codeMatches(responseHandlingConfig, status) {
var regExp = new RegExp(responseHandlingConfig.code);
return regExp.test(status);
var regExp = new RegExp(responseHandlingConfig.code)
return regExp.test(status)
}
function resolveResponseHandling(xhr) {
for (var i = 0; i < htmx.config.responseHandling.length; i++) {
var responseHandlingElement = htmx.config.responseHandling[i];
var responseHandlingElement = htmx.config.responseHandling[i]
if (codeMatches(responseHandlingElement, xhr.status)) {
return responseHandlingElement;
return responseHandlingElement
}
}
// no matches, return no swap
@ -3222,293 +3222,292 @@ var htmx = (function() {
}
}
function handleAjaxResponse(elt, responseInfo) {
const xhr = responseInfo.xhr
let target = responseInfo.target
const etc = responseInfo.etc
const select = responseInfo.select
function handleAjaxResponse(elt, responseInfo) {
const xhr = responseInfo.xhr
let target = responseInfo.target
const etc = responseInfo.etc
const select = responseInfo.select
if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return
if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return
if (hasHeader(xhr, /HX-Trigger:/i)) {
handleTrigger(xhr, 'HX-Trigger', elt)
}
if (hasHeader(xhr, /HX-Trigger:/i)) {
handleTrigger(xhr, 'HX-Trigger', elt)
}
if (hasHeader(xhr, /HX-Location:/i)) {
saveCurrentPageToHistory()
let redirectPath = xhr.getResponseHeader('HX-Location')
var swapSpec
if (redirectPath.indexOf('{') === 0) {
swapSpec = parseJSON(redirectPath)
// what's the best way to throw an error if the user didn't include this
redirectPath = swapSpec.path
delete swapSpec.path
}
ajaxHelper('GET', redirectPath, swapSpec).then(function () {
pushUrlIntoHistory(redirectPath)
})
return
}
if (hasHeader(xhr, /HX-Location:/i)) {
saveCurrentPageToHistory()
let redirectPath = xhr.getResponseHeader('HX-Location')
var swapSpec
if (redirectPath.indexOf('{') === 0) {
swapSpec = parseJSON(redirectPath)
// what's the best way to throw an error if the user didn't include this
redirectPath = swapSpec.path
delete swapSpec.path
}
ajaxHelper('GET', redirectPath, swapSpec).then(function() {
pushUrlIntoHistory(redirectPath)
})
return
}
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
const shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && xhr.getResponseHeader('HX-Refresh') === 'true'
if (hasHeader(xhr, /HX-Redirect:/i)) {
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (hasHeader(xhr, /HX-Redirect:/i)) {
location.href = xhr.getResponseHeader('HX-Redirect')
shouldRefresh && location.reload()
return
}
if (shouldRefresh) {
location.reload()
return
}
if (shouldRefresh) {
location.reload()
return
}
if (hasHeader(xhr, /HX-Retarget:/i)) {
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
responseInfo.target = elt
} else {
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
}
}
if (hasHeader(xhr, /HX-Retarget:/i)) {
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
responseInfo.target = elt
} else {
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
}
}
const historyUpdate = determineHistoryUpdates(elt, responseInfo)
const historyUpdate = determineHistoryUpdates(elt, responseInfo)
const responseHandling = resolveResponseHandling(xhr)
const shouldSwap = responseHandling.swap
let isError = !!responseHandling.error
let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
let selectOverride = responseHandling.select
if (responseHandling.target) {
responseInfo.target = querySelectorExt(elt, responseHandling.target)
}
var swapOverride = etc.swapOverride
if (swapOverride == null && responseHandling.swapOverride) {
swapOverride = responseHandling.swapOverride
}
const responseHandling = resolveResponseHandling(xhr);
let shouldSwap = responseHandling.swap;
let isError = !!responseHandling.error;
let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
let selectOverride = responseHandling.select;
if (responseHandling.target) {
responseInfo.target = querySelectorExt(elt, responseHandling.target);
}
var swapOverride = etc.swapOverride;
if (swapOverride == null && responseHandling.swapOverride) {
swapOverride = responseHandling.swapOverride;
}
// response headers override response handling config
if (hasHeader(xhr, /HX-Retarget:/i)) {
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
responseInfo.target = elt
} else {
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
}
}
if (hasHeader(xhr, /HX-Reswap:/i)) {
swapOverride = xhr.getResponseHeader('HX-Reswap')
}
// response headers override response handling config
if (hasHeader(xhr, /HX-Retarget:/i)) {
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
responseInfo.target = elt
} else {
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
}
}
if (hasHeader(xhr, /HX-Reswap:/i)) {
swapOverride = xhr.getResponseHeader("HX-Reswap");
}
var serverResponse = xhr.response
var beforeSwapDetails = mergeObjects({
shouldSwap,
serverResponse,
isError,
ignoreTitle,
selectOverride
}, responseInfo)
var serverResponse = xhr.response;
var beforeSwapDetails = mergeObjects({
shouldSwap: shouldSwap,
serverResponse: serverResponse,
isError: isError,
ignoreTitle: ignoreTitle,
selectOverride: selectOverride
}, responseInfo);
if (responseHandling.event && !triggerEvent(target, responseHandling.event, beforeSwapDetails)) return
if (responseHandling.event && !triggerEvent(target, responseHandling.event, beforeSwapDetails)) return;
if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return
if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return;
target = beforeSwapDetails.target // allow re-targeting
serverResponse = beforeSwapDetails.serverResponse // allow updating content
isError = beforeSwapDetails.isError // allow updating error
ignoreTitle = beforeSwapDetails.ignoreTitle // allow updating ignoring title
selectOverride = beforeSwapDetails.selectOverride // allow updating select override
target = beforeSwapDetails.target // allow re-targeting
serverResponse = beforeSwapDetails.serverResponse // allow updating content
isError = beforeSwapDetails.isError // allow updating error
ignoreTitle = beforeSwapDetails.ignoreTitle // allow updating ignoring title
selectOverride = beforeSwapDetails.selectOverride; //allow updating select override
responseInfo.target = target // Make updated target available to response events
responseInfo.failed = isError // Make failed property available to response events
responseInfo.successful = !isError // Make successful property available to response events
responseInfo.target = target // Make updated target available to response events
responseInfo.failed = isError // Make failed property available to response events
responseInfo.successful = !isError // Make successful property available to response events
if (beforeSwapDetails.shouldSwap) {
if (xhr.status === 286) {
cancelPolling(elt)
}
if (beforeSwapDetails.shouldSwap) {
if (xhr.status === 286) {
cancelPolling(elt)
withExtensions(elt, function(extension) {
serverResponse = extension.transformResponse(serverResponse, xhr, elt)
})
// Save current page if there will be a history update
if (historyUpdate.type) {
saveCurrentPageToHistory()
}
if (hasHeader(xhr, /HX-Reswap:/i)) {
swapOverride = xhr.getResponseHeader('HX-Reswap')
}
var swapSpec = getSwapSpecification(elt, swapOverride)
if (swapSpec.hasOwnProperty('ignoreTitle')) {
ignoreTitle = swapSpec.ignoreTitle
}
target.classList.add(htmx.config.swappingClass)
// optional transition API promise callbacks
let settleResolve = null
let settleReject = null
let doSwap = function() {
try {
const activeElt = document.activeElement
let selectionInfo = {}
try {
selectionInfo = {
elt: activeElt,
// @ts-ignore
start: activeElt ? activeElt.selectionStart : null,
// @ts-ignore
end: activeElt ? activeElt.selectionEnd : null
}
} catch (e) {
// safari issue - see https://github.com/microsoft/playwright/issues/5894
}
withExtensions(elt, function (extension) {
serverResponse = extension.transformResponse(serverResponse, xhr, elt)
})
if (select) {
selectOverride = select
}
// Save current page if there will be a history update
if (hasHeader(xhr, /HX-Reselect:/i)) {
selectOverride = xhr.getResponseHeader('HX-Reselect')
}
// if we need to save history, do so, before swapping so that relative resources have the correct base URL
if (historyUpdate.type) {
saveCurrentPageToHistory()
triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo))
if (historyUpdate.type === 'push') {
pushUrlIntoHistory(historyUpdate.path)
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', { path: historyUpdate.path })
} else {
replaceUrlInHistory(historyUpdate.path)
triggerEvent(getDocument().body, 'htmx:replacedInHistory', { path: historyUpdate.path })
}
}
if (hasHeader(xhr, /HX-Reswap:/i)) {
swapOverride = xhr.getResponseHeader('HX-Reswap')
}
var swapSpec = getSwapSpecification(elt, swapOverride)
const settleInfo = makeSettleInfo(target)
selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride)
if (swapSpec.hasOwnProperty('ignoreTitle')) {
ignoreTitle = swapSpec.ignoreTitle
}
target.classList.add(htmx.config.swappingClass)
// optional transition API promise callbacks
let settleResolve = null
let settleReject = null
let doSwap = function () {
try {
const activeElt = document.activeElement
let selectionInfo = {}
try {
selectionInfo = {
elt: activeElt,
// @ts-ignore
start: activeElt ? activeElt.selectionStart : null,
// @ts-ignore
end: activeElt ? activeElt.selectionEnd : null
}
} catch (e) {
// safari issue - see https://github.com/microsoft/playwright/issues/5894
}
if (select) {
selectOverride = select
}
if (hasHeader(xhr, /HX-Reselect:/i)) {
selectOverride = xhr.getResponseHeader('HX-Reselect')
}
// if we need to save history, do so, before swapping so that relative resources have the correct base URL
if (historyUpdate.type) {
triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({history: historyUpdate}, responseInfo))
if (historyUpdate.type === 'push') {
pushUrlIntoHistory(historyUpdate.path)
triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path})
} else {
replaceUrlInHistory(historyUpdate.path)
triggerEvent(getDocument().body, 'htmx:replacedInHistory', {path: historyUpdate.path})
}
}
const settleInfo = makeSettleInfo(target)
selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride)
if (selectionInfo.elt &&
if (selectionInfo.elt &&
!bodyContains(selectionInfo.elt) &&
getRawAttribute(selectionInfo.elt, 'id')) {
const newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, 'id'))
const focusOptions = {preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll}
if (newActiveElt) {
// @ts-ignore
if (selectionInfo.start && newActiveElt.setSelectionRange) {
// @ts-ignore
try {
newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end)
} catch (e) {
// the setSelectionRange method is present on fields that don't support it, so just let this fail
}
}
newActiveElt.focus(focusOptions)
const newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, 'id'))
const focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }
if (newActiveElt) {
// @ts-ignore
if (selectionInfo.start && newActiveElt.setSelectionRange) {
// @ts-ignore
try {
newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end)
} catch (e) {
// the setSelectionRange method is present on fields that don't support it, so just let this fail
}
}
target.classList.remove(htmx.config.swappingClass)
forEach(settleInfo.elts, function (elt) {
if (elt.classList) {
elt.classList.add(htmx.config.settlingClass)
}
triggerEvent(elt, 'htmx:afterSwap', responseInfo)
})
if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
}
handleTrigger(xhr, 'HX-Trigger-After-Swap', finalElt)
}
const doSettle = function () {
forEach(settleInfo.tasks, function (task) {
task.call()
})
forEach(settleInfo.elts, function (elt) {
if (elt.classList) {
elt.classList.remove(htmx.config.settlingClass)
}
triggerEvent(elt, 'htmx:afterSettle', responseInfo)
})
if (responseInfo.pathInfo.anchor) {
const anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor)
if (anchorTarget) {
anchorTarget.scrollIntoView({block: 'start', behavior: 'auto'})
}
}
if (settleInfo.title && !ignoreTitle) {
const titleElt = find('title')
if (titleElt) {
titleElt.innerHTML = settleInfo.title
} else {
window.document.title = settleInfo.title
}
}
updateScrollState(settleInfo.elts, swapSpec)
if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
}
handleTrigger(xhr, 'HX-Trigger-After-Settle', finalElt)
}
maybeCall(settleResolve)
}
if (swapSpec.settleDelay > 0) {
setTimeout(doSettle, swapSpec.settleDelay)
} else {
doSettle()
}
} catch (e) {
triggerErrorEvent(elt, 'htmx:swapError', responseInfo)
maybeCall(settleReject)
throw e
newActiveElt.focus(focusOptions)
}
}
let shouldTransition = htmx.config.globalViewTransitions
if (swapSpec.hasOwnProperty('transition')) {
shouldTransition = swapSpec.transition
target.classList.remove(htmx.config.swappingClass)
forEach(settleInfo.elts, function(elt) {
if (elt.classList) {
elt.classList.add(htmx.config.settlingClass)
}
triggerEvent(elt, 'htmx:afterSwap', responseInfo)
})
if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
}
handleTrigger(xhr, 'HX-Trigger-After-Swap', finalElt)
}
if (shouldTransition &&
triggerEvent(elt, 'htmx:beforeTransition', responseInfo) &&
typeof Promise !== 'undefined' && document.startViewTransition) {
const settlePromise = new Promise(function (_resolve, _reject) {
settleResolve = _resolve
settleReject = _reject
const doSettle = function() {
forEach(settleInfo.tasks, function(task) {
task.call()
})
// wrap the original doSwap() in a call to startViewTransition()
const innerDoSwap = doSwap
doSwap = function () {
document.startViewTransition(function () {
innerDoSwap()
return settlePromise
})
forEach(settleInfo.elts, function(elt) {
if (elt.classList) {
elt.classList.remove(htmx.config.settlingClass)
}
triggerEvent(elt, 'htmx:afterSettle', responseInfo)
})
if (responseInfo.pathInfo.anchor) {
const anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor)
if (anchorTarget) {
anchorTarget.scrollIntoView({ block: 'start', behavior: 'auto' })
}
}
if (settleInfo.title && !ignoreTitle) {
const titleElt = find('title')
if (titleElt) {
titleElt.innerHTML = settleInfo.title
} else {
window.document.title = settleInfo.title
}
}
updateScrollState(settleInfo.elts, swapSpec)
if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) {
let finalElt = elt
if (!bodyContains(elt)) {
finalElt = getDocument().body
}
handleTrigger(xhr, 'HX-Trigger-After-Settle', finalElt)
}
maybeCall(settleResolve)
}
if (swapSpec.swapDelay > 0) {
setTimeout(doSwap, swapSpec.swapDelay)
if (swapSpec.settleDelay > 0) {
setTimeout(doSettle, swapSpec.settleDelay)
} else {
doSwap()
doSettle()
}
}
if (isError) {
triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({error: 'Response Status Error Code ' + xhr.status + ' from ' + responseInfo.pathInfo.requestPath}, responseInfo))
} catch (e) {
triggerErrorEvent(elt, 'htmx:swapError', responseInfo)
maybeCall(settleReject)
throw e
}
}
let shouldTransition = htmx.config.globalViewTransitions
if (swapSpec.hasOwnProperty('transition')) {
shouldTransition = swapSpec.transition
}
if (shouldTransition &&
triggerEvent(elt, 'htmx:beforeTransition', responseInfo) &&
typeof Promise !== 'undefined' && document.startViewTransition) {
const settlePromise = new Promise(function(_resolve, _reject) {
settleResolve = _resolve
settleReject = _reject
})
// wrap the original doSwap() in a call to startViewTransition()
const innerDoSwap = doSwap
doSwap = function() {
document.startViewTransition(function() {
innerDoSwap()
return settlePromise
})
}
}
if (swapSpec.swapDelay > 0) {
setTimeout(doSwap, swapSpec.swapDelay)
} else {
doSwap()
}
}
if (isError) {
triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({ error: 'Response Status Error Code ' + xhr.status + ' from ' + responseInfo.pathInfo.requestPath }, responseInfo))
}
}
//= ===================================================================
// Extensions API
//= ===================================================================

View File

@ -1,84 +1,79 @@
describe("hx-inherit attribute", function() {
describe('hx-inherit attribute', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
htmx.config.disableInheritance = true
})
afterEach(function() {
this.server.restore()
clearWorkArea()
htmx.config.disableInheritance = false
})
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
htmx.config.disableInheritance = true;
});
afterEach(function () {
this.server.restore();
clearWorkArea();
htmx.config.disableInheritance = false;
});
it('can disable inheritance', function () {
this.server.respondWith("GET", "/test", "Test");
var div = make('<div hx-target="#cta">' +
it('can disable inheritance', function() {
this.server.respondWith('GET', '/test', 'Test')
var div = make('<div hx-target="#cta">' +
'<button id="btn1" hx-get="/test"></button>' +
'<span id="cta">Click Me!</span>' +
'</div>')
var btn = byId("btn1");
btn.click();
this.server.respond();
btn.innerText.should.equal("Test");
})
var btn = byId('btn1')
btn.click()
this.server.respond()
btn.innerText.should.equal('Test')
})
it('can enable inheritance for all', function () {
this.server.respondWith("GET", "/test", "Test");
var div = make('<div hx-target="#cta" hx-inherit="*">' +
it('can enable inheritance for all', function() {
this.server.respondWith('GET', '/test', 'Test')
var div = make('<div hx-target="#cta" hx-inherit="*">' +
'<button id="btn1" hx-get="/test"></button>' +
'<span id="cta">Click Me!</span>' +
'</div>')
var btn = byId("btn1");
var span = byId("cta");
btn.click();
this.server.respond();
btn.innerText.should.equal("");
span.innerText.should.equal("Test");
})
var btn = byId('btn1')
var span = byId('cta')
btn.click()
this.server.respond()
btn.innerText.should.equal('')
span.innerText.should.equal('Test')
})
it('can enable inheritance by name', function () {
this.server.respondWith("GET", "/test", "Test");
var div = make('<div hx-target="#cta" hx-inherit="hx-target">' +
it('can enable inheritance by name', function() {
this.server.respondWith('GET', '/test', 'Test')
var div = make('<div hx-target="#cta" hx-inherit="hx-target">' +
'<button id="btn1" hx-get="/test"></button>' +
'<span id="cta">Click Me!</span>' +
'</div>')
var btn = byId("btn1");
var span = byId("cta");
btn.click();
this.server.respond();
btn.innerText.should.equal("");
span.innerText.should.equal("Test");
})
var btn = byId('btn1')
var span = byId('cta')
btn.click()
this.server.respond()
btn.innerText.should.equal('')
span.innerText.should.equal('Test')
})
it('can enable inheritance by name (bad name, no inheritance)', function () {
this.server.respondWith("GET", "/test", "Test");
var div = make('<div hx-target="#cta" hx-inherit="hx-swap">' +
it('can enable inheritance by name (bad name, no inheritance)', function() {
this.server.respondWith('GET', '/test', 'Test')
var div = make('<div hx-target="#cta" hx-inherit="hx-swap">' +
'<button id="btn1" hx-get="/test"></button>' +
'<span id="cta">Click Me!</span>' +
'</div>')
var btn = byId("btn1");
var span = byId("cta");
btn.click();
this.server.respond();
btn.innerText.should.equal("Test");
span.innerText.should.equal("Click Me!");
})
var btn = byId('btn1')
var span = byId('cta')
btn.click()
this.server.respond()
btn.innerText.should.equal('Test')
span.innerText.should.equal('Click Me!')
})
it('can enable inheritance by name with multiple attributes', function () {
this.server.respondWith("GET", "/test", "Test");
var div = make('<div hx-target="#cta" hx-swap="outerHTML" hx-inherit="hx-target hx-swap">' +
it('can enable inheritance by name with multiple attributes', function() {
this.server.respondWith('GET', '/test', 'Test')
var div = make('<div hx-target="#cta" hx-swap="outerHTML" hx-inherit="hx-target hx-swap">' +
'<button id="btn1" hx-get="/test"></button>' +
'<div id="d2"><span id="cta">Click Me!</span></div>' +
'</div>')
var btn = byId("btn1");
var div = byId("d2");
btn.click();
this.server.respond();
div.innerHTML.should.equal("Test");
})
});
var btn = byId('btn1')
var div = byId('d2')
btn.click()
this.server.respond()
div.innerHTML.should.equal('Test')
})
})

View File

@ -128,8 +128,8 @@ describe('hx-swap-oob attribute', function() {
should.equal(byId('d1'), null)
})
it("oob swap supports table row in fragment along other oob swap elements", function () {
this.server.respondWith("GET", "/test",
it('oob swap supports table row in fragment along other oob swap elements', function() {
this.server.respondWith('GET', '/test',
`Clicked
<div hx-swap-oob="innerHTML" id="d1">Test</div>
<button type="button" hx-swap-oob="true" id="b2">Another button</button>
@ -156,10 +156,10 @@ describe('hx-swap-oob attribute', function() {
var btn = make('<button id="b1" type="button" hx-get="/test">Click me</button>')
btn.click()
this.server.respond()
btn.innerText.should.equal("Clicked")
byId("r1").innerHTML.should.equal(`<td>bar</td>`)
byId("b2").innerHTML.should.equal(`Another button`)
byId("d1").innerHTML.should.equal(`Test`)
byId("td1").innerHTML.should.equal(`hey`)
btn.innerText.should.equal('Clicked')
byId('r1').innerHTML.should.equal('<td>bar</td>')
byId('b2').innerHTML.should.equal('Another button')
byId('d1').innerHTML.should.equal('Test')
byId('td1').innerHTML.should.equal('hey')
})
})

View File

@ -1,248 +1,236 @@
describe("htmx config test", function () {
describe('htmx config test', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
})
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
});
afterEach(function() {
this.server.restore()
clearWorkArea()
})
afterEach(function () {
this.server.restore();
clearWorkArea();
});
it('swaps normally with no config update', function() {
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
it('swaps normally with no config update', function () {
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
responseCode = 200 // 200 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('200')
responseCode = 204 // 204 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
responseCode = 200; // 200 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("200");
responseCode = 300 // 300 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('300')
responseCode = 204; // 204 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
responseCode = 400 // 400 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
responseCode = 300; // 300 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("300");
responseCode = 500 // 500 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
})
responseCode = 400; // 400 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
it('swap all config should swap everything', function() {
var originalResponseHandling = htmx.config.responseHandling
try {
htmx.config.responseHandling = [{ code: '...', swap: true }]
responseCode = 500; // 500 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
});
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
it('swap all config should swap everything', function () {
responseCode = 200 // 200 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('200')
var originalResponseHandling = htmx.config.responseHandling;
try {
responseCode = 203 // 203 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('203')
htmx.config.responseHandling = [{code: '...', swap: true}];
responseCode = 300 // 300 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('300')
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
responseCode = 400 // 400 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('400')
responseCode = 500 // 500 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('500')
} finally {
htmx.config.responseHandling = originalResponseHandling
}
})
responseCode = 200; // 200 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("200");
it('can change the target of a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: true, target: '#a-div' })
responseCode = 203; // 203 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("203");
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
responseCode = 300; // 300 should cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("300");
responseCode = 444
var div = make('<div id="a-div">Another Div</div>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
div.innerHTML.should.equal('444')
} finally {
htmx.config.responseHandling = originalResponseHandling
}
})
responseCode = 400; // 400 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("400");
it('can change the swap type of a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: true, target: '#a-div', swapOverride: 'outerHTML' })
responseCode = 500; // 500 should not cause a swap by default
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("500");
} finally {
htmx.config.responseHandling = originalResponseHandling;
}
});
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
it('can change the target of a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
try {
responseCode = 444
var div = make('<div><div id="a-div">Another Div</div></div>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
div.innerHTML.should.equal('444')
} finally {
htmx.config.responseHandling = originalResponseHandling
}
})
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap: true, target: "#a-div"})
it('can change the select of a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: true, select: '.foo' })
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, "<div><a class='foo'>" + responseCode + '</a></div>')
})
responseCode = 444;
var div = make('<div id="a-div">Another Div</div>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
div.innerHTML.should.equal("444");
} finally {
htmx.config.responseHandling = originalResponseHandling;
}
});
responseCode = 444
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('<a class="foo">444</a>')
} finally {
htmx.config.responseHandling = originalResponseHandling
}
})
it('can change the swap type of a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
try {
it('can change if the title is ignored for a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
var originalTitle = document.title
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: true, ignoreTitle: true })
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap: true, target: "#a-div", swapOverride:"outerHTML"})
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '<title>Should Not Be Set</title>' + responseCode)
})
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
responseCode = 444
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('444')
document.title.should.equal(originalTitle)
} finally {
htmx.config.responseHandling = originalResponseHandling
}
})
responseCode = 444;
var div = make('<div><div id="a-div">Another Div</div></div>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
div.innerHTML.should.equal("444");
} finally {
htmx.config.responseHandling = originalResponseHandling;
}
});
it('can change if error for a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
var errorDetected = false
var handler = htmx.on('htmx:responseError', function() {
errorDetected = true
})
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: true, error: false })
it('can change the select of a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
try {
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap: true, select: ".foo"})
responseCode = 444
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('444')
errorDetected.should.equal(false)
} finally {
htmx.off('htmx:responseError', handler)
htmx.config.responseHandling = originalResponseHandling
}
})
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "<div><a class='foo'>" + responseCode + "</a></div>");
});
it('can trigger an event for a given response code', function() {
var originalResponseHandling = htmx.config.responseHandling
var myEventWasTriggered = false
var handler = htmx.on('myEvent', function() {
myEventWasTriggered = true
})
try {
htmx.config.responseHandling = originalResponseHandling.slice()
htmx.config.responseHandling.unshift({ code: '444', swap: false, error: false, event: 'myEvent' })
responseCode = 444;
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("<a class=\"foo\">444</a>");
} finally {
htmx.config.responseHandling = originalResponseHandling;
}
});
var responseCode = null
this.server.respondWith('GET', '/test', function(xhr, id) {
xhr.respond(responseCode, { 'Content-Type': 'application/json' }, '' + responseCode)
})
it('can change if the title is ignored for a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
var originalTitle = document.title;
try {
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap:true, ignoreTitle:true})
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "<title>Should Not Be Set</title>" + responseCode);
});
responseCode = 444;
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("444");
document.title.should.equal(originalTitle);
} finally {
htmx.config.responseHandling = originalResponseHandling;
}
});
it('can change if error for a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
var errorDetected = false;
var handler = htmx.on('htmx:responseError', function() {
errorDetected = true;
});
try {
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap:true, error:false})
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
responseCode = 444;
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("444");
errorDetected.should.equal(false);
} finally {
htmx.off('htmx:responseError', handler);
htmx.config.responseHandling = originalResponseHandling;
}
});
it('can trigger an event for a given response code', function () {
var originalResponseHandling = htmx.config.responseHandling;
var myEventWasTriggered = false;
var handler = htmx.on('myEvent', function() {
myEventWasTriggered = true;
});
try {
htmx.config.responseHandling = originalResponseHandling.slice();
htmx.config.responseHandling.unshift({code: "444", swap:false, error:false, event:'myEvent'})
var responseCode = null;
this.server.respondWith("GET", "/test", function (xhr, id) {
xhr.respond(responseCode, {"Content-Type": "application/json"}, "" + responseCode);
});
responseCode = 444;
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
myEventWasTriggered.should.equal(true);
} finally {
htmx.off('htmx:responseError', handler);
htmx.config.responseHandling = originalResponseHandling;
}
});
});
responseCode = 444
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
myEventWasTriggered.should.equal(true)
} finally {
htmx.off('htmx:responseError', handler)
htmx.config.responseHandling = originalResponseHandling
}
})
})