Load Alpine's CSP build so the UI's directives run (closes #371)
check / check (push) Waiting to run
check / check (push) Waiting to run
Every page's Content-Security-Policy forbids eval, which the standard Alpine.js build needs, so no directive ran in a browser: both add forms on the webhook page showed open, and events in the event log could not be collapsed. The UI now loads Alpine's CSP build (@alpinejs/csp 3.14.9 in 3p/); the policy is unchanged. Each directive names a property or method of a component registered in static/js/app.js (collapsible, targetForm), and each card holds its own x-data. A browser test, built only with the browser tag, loads the webhook page and the event log under the real headers; make test-browser runs it in Docker. New test-only dependency chromedp, which raises golang.org/x/sys to 0.47.0. Model: opus-5-5
This commit was merged in pull request #411.
This commit is contained in:
@@ -57,3 +57,73 @@
|
||||
init();
|
||||
}
|
||||
})();
|
||||
|
||||
// Alpine.js components.
|
||||
//
|
||||
// The pages' Content-Security-Policy forbids eval, so the UI loads
|
||||
// Alpine's CSP build, which cannot run expressions written in the
|
||||
// markup: a directive in templates/ may only name a property or method,
|
||||
// and each x-data names a component registered here. This script runs
|
||||
// before Alpine, whose script tag is deferred, so this listener is in
|
||||
// place when Alpine starts.
|
||||
document.addEventListener("alpine:init", function () {
|
||||
"use strict";
|
||||
|
||||
// Something a click shows and hides: the mobile menu, an add form,
|
||||
// an event in the event log, a delivery's attempts.
|
||||
window.Alpine.data("collapsible", function () {
|
||||
return {
|
||||
open: false,
|
||||
toggle() {
|
||||
this.open = !this.open;
|
||||
},
|
||||
get closed() {
|
||||
return !this.open;
|
||||
},
|
||||
// Turns a downward caret up while open.
|
||||
get caretClass() {
|
||||
return { "rotate-180": this.open };
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// The add target form. Only the chosen type's fields show, and the
|
||||
// others are disabled so that the form does not submit them.
|
||||
//
|
||||
// The type is read from the type select when Alpine starts, when the
|
||||
// select changes, and on pageshow. Going back to the page, the
|
||||
// browser restores the type chosen before without a change event,
|
||||
// in some browsers only after Alpine has started, but always before
|
||||
// pageshow.
|
||||
window.Alpine.data("targetForm", function () {
|
||||
return {
|
||||
targetType: "",
|
||||
init() {
|
||||
this.readType();
|
||||
},
|
||||
readType() {
|
||||
this.targetType = this.$root.querySelector(
|
||||
'select[name="type"]'
|
||||
).value;
|
||||
},
|
||||
get isHttp() {
|
||||
return this.targetType === "http";
|
||||
},
|
||||
get isSlack() {
|
||||
return this.targetType === "slack";
|
||||
},
|
||||
get isDatabase() {
|
||||
return this.targetType === "database";
|
||||
},
|
||||
get notHttp() {
|
||||
return !this.isHttp;
|
||||
},
|
||||
get notSlack() {
|
||||
return !this.isSlack;
|
||||
},
|
||||
get notDatabase() {
|
||||
return !this.isDatabase;
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user