app: two findings from the adversarial pass on the Settings rewrite
An outside click on the Settings window closed it unconditionally -- pipeline_window.js's own standalone modal has always refused to close that way (a group edit's draft is the only copy of it), and hosting it inside Settings silently dropped that guarantee, with no way left for a real user to reach the protected version (boot.js has no other caller of pipeline_window.js any more). Settings now holds the door the same way, for every section: an accidental backdrop click during any half-finished edit no longer costs it. filterList's icon `tone` claimed to be a closed set, the same rule el()'s style refusal already holds -- but nothing checked it, so a typo would have rendered a silent, unstyled chip instead of failing loudly the way button.js's own tone check already does for the same mistake. Now it throws, matching that sibling pattern.
This commit is contained in:
@@ -83,6 +83,14 @@ export function createSettingsWindow({ sections = [], gitStatus } = {}) {
|
||||
const body = composer.splitPane.h({ panes: [nav, contentStack], size: 25, min: 15, label: "Settings" });
|
||||
const modal = composer.modal.generic({
|
||||
title: "Settings", size: "xl", body,
|
||||
// Mirrors pipeline_window.js's own standalone modal: a half-finished
|
||||
// edit (Models and pipeline's draft, most concretely) must not vanish
|
||||
// because a click landed on the backdrop. This window has no way to
|
||||
// ask "does the section on screen have unsaved work" per section, so
|
||||
// it holds the door for all of them, the same conservative call
|
||||
// pipeline_window.js already made for itself -- the X button and every
|
||||
// section's own "Close" still work.
|
||||
closeOnOutside: false,
|
||||
onClose: () => {
|
||||
if (activeCleanup) { activeCleanup.destroy?.(); activeCleanup = null; }
|
||||
if (current === handle) current = null;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { setupDom, teardownDom } from "../../composer/tests/_dom.js";
|
||||
import { setupDom, teardownDom, fire } from "../../composer/tests/_dom.js";
|
||||
|
||||
let createSettingsWindow;
|
||||
test.before(async () => {
|
||||
@@ -119,6 +119,20 @@ test("a gitStatus rejection is shown as a hint rather than left blank or thrown"
|
||||
w.close();
|
||||
});
|
||||
|
||||
test("a click outside the window does not close it -- a half-finished edit in any section is not a click away from gone", () => {
|
||||
// The bug this guards: pipeline_window.js's own standalone modal has
|
||||
// ALWAYS refused to close on an outside click (a group edit's draft is
|
||||
// the only copy of it), but Settings' own modal was built without
|
||||
// carrying that over, so hosting Models and pipeline in here silently
|
||||
// lost the protection its own file was written to guarantee.
|
||||
const alpha = fakeSection("a", "Alpha");
|
||||
const w = createSettingsWindow({ sections: [alpha] });
|
||||
w.open("a");
|
||||
fire(document.body, "pointerdown");
|
||||
assert.ok(document.querySelector(".cx-modal"), "an outside click closed the window");
|
||||
w.close();
|
||||
});
|
||||
|
||||
test("closing tears down the active section and a later open starts fresh", () => {
|
||||
const alpha = fakeSection("a", "Alpha");
|
||||
const w = createSettingsWindow({ sections: [alpha] });
|
||||
|
||||
Reference in New Issue
Block a user