[codex] tighten update center and proxy debug UX (#325)

* tighten update center and proxy debug UX

* Address update center review feedback
This commit is contained in:
Cita
2026-03-30 13:39:20 +08:00
committed by GitHub
parent 71be4cb63a
commit 26d87db2cb
16 changed files with 1252 additions and 134 deletions
+9 -5
View File
@@ -28,18 +28,22 @@ export default function CenteredModal({
showCloseButton = true,
}: CenteredModalProps) {
const presence = useAnimatedVisibility(open, 220);
const canUsePortal = typeof document !== 'undefined'
&& !!document.body
&& typeof document.body.appendChild === 'function'
&& typeof document.body.removeChild === 'function';
useEffect(() => {
if (!open || typeof document === 'undefined') return;
if (!open || !canUsePortal) return;
const previousOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {
document.body.style.overflow = previousOverflow;
};
}, [open]);
}, [canUsePortal, open]);
useEffect(() => {
if (!open || !closeOnEscape || typeof document === 'undefined') return;
if (!open || !closeOnEscape || !canUsePortal) return;
const handleKeydown = (event: KeyboardEvent) => {
if (event.key === 'Escape') onClose();
};
@@ -47,7 +51,7 @@ export default function CenteredModal({
return () => {
document.removeEventListener('keydown', handleKeydown);
};
}, [closeOnEscape, open, onClose]);
}, [canUsePortal, closeOnEscape, open, onClose]);
if (!presence.shouldRender) return null;
@@ -82,5 +86,5 @@ export default function CenteredModal({
</div>
);
return typeof document !== 'undefined' ? createPortal(modal, document.body) : modal;
return canUsePortal ? createPortal(modal, document.body) : modal;
}
+31 -1
View File
@@ -1,10 +1,16 @@
import { describe, expect, it, vi } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { act, create } from 'react-test-renderer';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import CenteredModal from './CenteredModal.js';
describe('CenteredModal component', () => {
const originalDocument = globalThis.document;
afterEach(() => {
globalThis.document = originalDocument;
});
it('does not close on backdrop click by default and exposes an explicit close button', async () => {
const onClose = vi.fn();
let root!: WebTestRenderer;
@@ -49,4 +55,28 @@ describe('CenteredModal component', () => {
expect(source).toContain('useAnimatedVisibility');
expect(source).toContain('createPortal');
});
it('renders safely without touching document.body or listeners when no usable body exists', async () => {
globalThis.document = {
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
} as unknown as Document;
let root!: WebTestRenderer;
try {
await act(async () => {
root = create(
<CenteredModal open onClose={() => {}} title="测试弹框" closeOnEscape>
<div>content</div>
</CenteredModal>,
);
});
expect(root.toJSON()).toBeTruthy();
expect(globalThis.document.addEventListener).not.toHaveBeenCalled();
} finally {
root?.unmount();
}
});
});