fix: keep draft authoring dialogs visible
This commit is contained in:
@@ -18,7 +18,7 @@ export const CapabilityPalette = ({
|
||||
<header className="capability-palette__header">
|
||||
<p className="workspace-route-pending__eyebrow">Available interfaces</p>
|
||||
<h2>Capabilities</h2>
|
||||
<p>Choose a capability to inspect its contract before adding it in the next authoring slice.</p>
|
||||
<p>Choose a capability to inspect its contract, configure a step, and add it to this graph.</p>
|
||||
</header>
|
||||
{capabilities.length > 0 ? (
|
||||
<ul className="capability-palette__list">
|
||||
|
||||
@@ -128,6 +128,50 @@ beforeEach(() => {
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("CreateDraftDialog", () => {
|
||||
it("stays open when StrictMode replays effects with native dialog events", async () => {
|
||||
const originalShowModal = HTMLDialogElement.prototype.showModal;
|
||||
const originalClose = HTMLDialogElement.prototype.close;
|
||||
Object.defineProperty(HTMLDialogElement.prototype, "showModal", {
|
||||
configurable: true,
|
||||
value(this: HTMLDialogElement) {
|
||||
this.setAttribute("open", "");
|
||||
},
|
||||
});
|
||||
Object.defineProperty(HTMLDialogElement.prototype, "close", {
|
||||
configurable: true,
|
||||
value(this: HTMLDialogElement) {
|
||||
this.removeAttribute("open");
|
||||
queueMicrotask(() => this.dispatchEvent(new Event("close")));
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
renderStrictDialog(null);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(screen.getByRole("dialog", { name: "Create a draft workspace" })).toHaveAttribute(
|
||||
"open",
|
||||
);
|
||||
} finally {
|
||||
if (originalShowModal === undefined) {
|
||||
Reflect.deleteProperty(HTMLDialogElement.prototype, "showModal");
|
||||
} else {
|
||||
Object.defineProperty(HTMLDialogElement.prototype, "showModal", {
|
||||
configurable: true,
|
||||
value: originalShowModal,
|
||||
});
|
||||
}
|
||||
if (originalClose === undefined) {
|
||||
Reflect.deleteProperty(HTMLDialogElement.prototype, "close");
|
||||
} else {
|
||||
Object.defineProperty(HTMLDialogElement.prototype, "close", {
|
||||
configurable: true,
|
||||
value: originalClose,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps successful creation navigable after StrictMode effect replay", async () => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(authoringClient.createEmpty).mockResolvedValueOnce(workspace("strict-created"));
|
||||
|
||||
@@ -44,15 +44,6 @@ const showModal = (dialog: HTMLDialogElement): void => {
|
||||
dialog.setAttribute("open", "");
|
||||
};
|
||||
|
||||
const closeModal = (dialog: HTMLDialogElement): void => {
|
||||
if (!dialog.open) return;
|
||||
if (typeof dialog.close === "function") {
|
||||
dialog.close();
|
||||
return;
|
||||
}
|
||||
dialog.removeAttribute("open");
|
||||
};
|
||||
|
||||
export const CreateDraftDialog = ({
|
||||
capability,
|
||||
onClose,
|
||||
@@ -95,7 +86,10 @@ export const CreateDraftDialog = ({
|
||||
lifecycleTokenRef.current = null;
|
||||
}
|
||||
requestGenerationRef.current += 1;
|
||||
closeModal(dialog);
|
||||
// Do not call dialog.close() here. React Strict Mode replays this cleanup
|
||||
// while the same dialog remains mounted; Chrome dispatches that native
|
||||
// close event asynchronously and the remounted instance can mistake it
|
||||
// for an operator close. Removing the DOM node closes a real unmount.
|
||||
if (previouslyFocused !== null && document.contains(previouslyFocused)) {
|
||||
previouslyFocused.focus();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const DraftIndexRoute = () => {
|
||||
<header className="draft-workspaces__header">
|
||||
<p className="workspace-route-pending__eyebrow">Authoring inventory</p>
|
||||
<h1>Draft workspaces</h1>
|
||||
<p>Inspect saved workflow drafts without changing their definitions.</p>
|
||||
<p>Create a workspace or open an existing draft to build and validate its graph.</p>
|
||||
<div className="draft-workspaces__actions">
|
||||
<button onClick={() => setCreateDialogOpen(true)} type="button">
|
||||
New draft
|
||||
@@ -52,7 +52,7 @@ export const DraftIndexRoute = () => {
|
||||
<section aria-labelledby="draft-workspaces-list-heading" className="draft-workspaces__panel">
|
||||
<div className="draft-workspaces__section-heading">
|
||||
<div>
|
||||
<p className="workspace-route-pending__eyebrow">Read-only index</p>
|
||||
<p className="workspace-route-pending__eyebrow">Authoring workspaces</p>
|
||||
<h2 id="draft-workspaces-list-heading">Available drafts</h2>
|
||||
</div>
|
||||
{drafts.items.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user