@prenta/admin 0.97.4 → 0.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/__tests__/components/modal.render.test.js +62 -2
- package/dist/__tests__/components/modal.render.test.js.map +1 -1
- package/dist/__tests__/lib/seo-service-errors.test.js +34 -3
- package/dist/__tests__/lib/seo-service-errors.test.js.map +1 -1
- package/dist/__tests__/views/seo-audit-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/seo-audit-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/seo-audit-tab.render.test.js +100 -0
- package/dist/__tests__/views/seo-audit-tab.render.test.js.map +1 -0
- package/dist/__tests__/views/seo-technical-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/seo-technical-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/seo-technical-tab.render.test.js +121 -0
- package/dist/__tests__/views/seo-technical-tab.render.test.js.map +1 -0
- package/dist/components/ui/Modal.d.ts +8 -0
- package/dist/components/ui/Modal.d.ts.map +1 -1
- package/dist/components/ui/Modal.js +22 -1
- package/dist/components/ui/Modal.js.map +1 -1
- package/dist/lib/seo-service.d.ts +30 -1
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/views/seo/AuditTab.d.ts.map +1 -1
- package/dist/views/seo/AuditTab.js +3 -2
- package/dist/views/seo/AuditTab.js.map +1 -1
- package/dist/views/seo/TechnicalTab.d.ts.map +1 -1
- package/dist/views/seo/TechnicalTab.js +5 -0
- package/dist/views/seo/TechnicalTab.js.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/components/modal.render.test.tsx +109 -1
- package/src/__tests__/lib/seo-service-errors.test.ts +55 -1
- package/src/__tests__/views/seo-audit-tab.render.test.tsx +113 -0
- package/src/__tests__/views/seo-technical-tab.render.test.tsx +156 -0
- package/src/components/ui/Modal.tsx +25 -1
- package/src/lib/seo-service.ts +30 -2
- package/src/views/seo/AuditTab.tsx +60 -27
- package/src/views/seo/TechnicalTab.tsx +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @actuate-media/cms-admin
|
|
2
2
|
|
|
3
|
+
## 0.98.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b28599c: Add rendered-audit controls and clearly identify partial SEO audit coverage.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- b28599c: Harden rendered SEO audits with non-blocking cleanup, visibility-consistent HTML parsing, and a precise Admin audit response contract.
|
|
12
|
+
|
|
13
|
+
## 0.97.5
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- c3ae495: Fix Post Details (and other `Modal` + `onDismiss` surfaces) discarding the form when clicking a nested dialog such as the media picker — outside interactions now ignore events that belong to another `[role="dialog"]`.
|
|
18
|
+
|
|
3
19
|
## 0.97.4
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// @vitest-environment happy-dom
|
|
3
|
+
import * as Dialog from '@radix-ui/react-dialog';
|
|
4
|
+
import { useState } from 'react';
|
|
3
5
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
6
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
5
7
|
/**
|
|
@@ -9,7 +11,14 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
|
|
9
11
|
* Escape / close both fire `onClose`. Hand-rolled `<div>` overlays gave us none
|
|
10
12
|
* of this.
|
|
11
13
|
*/
|
|
12
|
-
const { Modal } = await import('../../components/ui/Modal.js');
|
|
14
|
+
const { Modal, isNestedDialogEvent } = await import('../../components/ui/Modal.js');
|
|
15
|
+
function NestedDialog({ onOpenChange }) {
|
|
16
|
+
const [open, setOpen] = useState(true);
|
|
17
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: (next) => {
|
|
18
|
+
setOpen(next);
|
|
19
|
+
onOpenChange?.(next);
|
|
20
|
+
}, children: _jsx(Dialog.Portal, { children: _jsxs(Dialog.Content, { "aria-describedby": undefined, children: [_jsx(Dialog.Title, { children: "Media picker" }), _jsx("button", { type: "button", children: "Choose image" })] }) }) }));
|
|
21
|
+
}
|
|
13
22
|
describe('Modal accessibility', () => {
|
|
14
23
|
it('does not render content while closed', () => {
|
|
15
24
|
render(_jsx(Modal, { open: false, onClose: () => { }, title: "Edit page", children: _jsx("p", { children: "Body" }) }));
|
|
@@ -33,5 +42,56 @@ describe('Modal accessibility', () => {
|
|
|
33
42
|
fireEvent.keyDown(document.body, { key: 'Escape' });
|
|
34
43
|
expect(onClose).toHaveBeenCalled();
|
|
35
44
|
});
|
|
45
|
+
it('routes Escape through onDismiss when provided', () => {
|
|
46
|
+
const onClose = vi.fn();
|
|
47
|
+
const onDismiss = vi.fn();
|
|
48
|
+
render(_jsx(Modal, { open: true, onClose: onClose, onDismiss: onDismiss, title: "Post details", children: _jsx("p", { children: "Body" }) }));
|
|
49
|
+
fireEvent.keyDown(screen.getByRole('dialog'), { key: 'Escape' });
|
|
50
|
+
expect(onDismiss).toHaveBeenCalled();
|
|
51
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
it('keeps the parent open when interacting with a portaled nested dialog', async () => {
|
|
54
|
+
const onDismiss = vi.fn();
|
|
55
|
+
render(_jsx(Modal, { open: true, onClose: () => { }, onDismiss: onDismiss, title: "Post details", description: "Edit post details", children: _jsx("p", { children: "Body" }) }));
|
|
56
|
+
render(_jsx(NestedDialog, {}));
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
58
|
+
onDismiss.mockClear();
|
|
59
|
+
const chooseImage = screen.getByRole('button', { name: 'Choose image' });
|
|
60
|
+
fireEvent.pointerDown(chooseImage);
|
|
61
|
+
fireEvent.focusIn(chooseImage);
|
|
62
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
it('closes only the portaled nested dialog on Escape', async () => {
|
|
65
|
+
const onDismiss = vi.fn();
|
|
66
|
+
const onNestedOpenChange = vi.fn();
|
|
67
|
+
render(_jsx(Modal, { open: true, onClose: () => { }, onDismiss: onDismiss, title: "Post details", description: "Edit post details", children: _jsx("p", { children: "Body" }) }));
|
|
68
|
+
render(_jsx(NestedDialog, { onOpenChange: onNestedOpenChange }));
|
|
69
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
70
|
+
onDismiss.mockClear();
|
|
71
|
+
onNestedOpenChange.mockClear();
|
|
72
|
+
fireEvent.keyDown(screen.getByRole('dialog', { name: 'Media picker' }), { key: 'Escape' });
|
|
73
|
+
expect(onNestedOpenChange).toHaveBeenCalledWith(false);
|
|
74
|
+
expect(screen.queryByRole('dialog', { name: 'Media picker' })).toBeNull();
|
|
75
|
+
expect(onDismiss).not.toHaveBeenCalled();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('isNestedDialogEvent (UpChat #27)', () => {
|
|
79
|
+
it('detects events that belong to a different dialog than the Modal content', () => {
|
|
80
|
+
const parent = document.createElement('div');
|
|
81
|
+
parent.setAttribute('role', 'dialog');
|
|
82
|
+
const nested = document.createElement('div');
|
|
83
|
+
nested.setAttribute('role', 'dialog');
|
|
84
|
+
const button = document.createElement('button');
|
|
85
|
+
nested.appendChild(button);
|
|
86
|
+
document.body.append(parent, nested);
|
|
87
|
+
// MediaPicker / ConfirmDialog portals sit outside this Modal's content —
|
|
88
|
+
// their clicks must not count as "interact outside" for dismiss.
|
|
89
|
+
expect(isNestedDialogEvent(button, parent)).toBe(true);
|
|
90
|
+
expect(isNestedDialogEvent(button, nested)).toBe(false);
|
|
91
|
+
expect(isNestedDialogEvent(parent, parent)).toBe(false);
|
|
92
|
+
expect(isNestedDialogEvent(null, parent)).toBe(false);
|
|
93
|
+
parent.remove();
|
|
94
|
+
nested.remove();
|
|
95
|
+
});
|
|
36
96
|
});
|
|
37
97
|
//# sourceMappingURL=modal.render.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/components/modal.render.test.tsx"],"names":[],"mappings":";AAAA,gCAAgC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAElE;;;;;;GAMG;AAEH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"modal.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/components/modal.render.test.tsx"],"names":[],"mappings":";AAAA,gCAAgC;AAChC,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAElE;;;;;;GAMG;AAEH,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAA;AAEnF,SAAS,YAAY,CAAC,EAAE,YAAY,EAA8C;IAChF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAEtC,OAAO,CACL,KAAC,MAAM,CAAC,IAAI,IACV,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;YACrB,OAAO,CAAC,IAAI,CAAC,CAAA;YACb,YAAY,EAAE,CAAC,IAAI,CAAC,CAAA;QACtB,CAAC,YAED,KAAC,MAAM,CAAC,MAAM,cACZ,MAAC,MAAM,CAAC,OAAO,wBAAmB,SAAS,aACzC,KAAC,MAAM,CAAC,KAAK,+BAA4B,EACzC,iBAAQ,IAAI,EAAC,QAAQ,6BAAsB,IAC5B,GACH,GACJ,CACf,CAAA;AACH,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CACJ,KAAC,KAAK,IAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAC,WAAW,YACtD,+BAAW,GACL,CACT,CAAA;QACD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CACJ,KAAC,KAAK,IAAC,IAAI,QAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,KAAK,EAAC,WAAW,YAC9C,+BAAW,GACL,CACT,CAAA;QACD,sEAAsE;QACtE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAA;QACtE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACvB,MAAM,CACJ,KAAC,KAAK,IAAC,IAAI,QAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAC,WAAW,YAC7C,+BAAW,GACL,CACT,CAAA;QACD,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAA;QACrE,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC1C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACvB,MAAM,CACJ,KAAC,KAAK,IAAC,IAAI,QAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAC,WAAW,YAC7C,+BAAW,GACL,CACT,CAAA;QACD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAA;QACnD,MAAM,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,CAAA;IACpC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACvB,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACzB,MAAM,CACJ,KAAC,KAAK,IAAC,IAAI,QAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAC,cAAc,YACtE,+BAAW,GACL,CACT,CAAA;QACD,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAA;QAChE,MAAM,CAAC,SAAS,CAAC,CAAC,gBAAgB,EAAE,CAAA;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACzB,MAAM,CACJ,KAAC,KAAK,IACJ,IAAI,QACJ,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EACjB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,cAAc,EACpB,WAAW,EAAC,mBAAmB,YAE/B,+BAAW,GACL,CACT,CAAA;QACD,MAAM,CAAC,KAAC,YAAY,KAAG,CAAC,CAAA;QACxB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;QACtD,SAAS,CAAC,SAAS,EAAE,CAAA;QAErB,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAA;QACxE,SAAS,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;QAClC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAE9B,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IAC1C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACzB,MAAM,kBAAkB,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAClC,MAAM,CACJ,KAAC,KAAK,IACJ,IAAI,QACJ,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,EACjB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,cAAc,EACpB,WAAW,EAAC,mBAAmB,YAE/B,+BAAW,GACL,CACT,CAAA;QACD,MAAM,CAAC,KAAC,YAAY,IAAC,YAAY,EAAE,kBAAkB,GAAI,CAAC,CAAA;QAC1D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;QACtD,SAAS,CAAC,SAAS,EAAE,CAAA;QACrB,kBAAkB,CAAC,SAAS,EAAE,CAAA;QAE9B,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAA;QAE1F,MAAM,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;QACtD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QACzE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IAC1C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QAC5C,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QAC5C,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QAC1B,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAEpC,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtD,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvD,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvD,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAErD,MAAM,CAAC,MAAM,EAAE,CAAA;QACf,MAAM,CAAC,MAAM,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
1
|
+
import { afterEach, describe, expect, expectTypeOf, it, vi } from 'vitest';
|
|
3
2
|
const cmsApi = vi.fn();
|
|
4
3
|
vi.mock('../../lib/api.js', () => ({
|
|
5
4
|
cmsApi: (...args) => cmsApi(...args),
|
|
6
5
|
}));
|
|
7
|
-
const { fetchSeoOverview, fetchSeoContentRecords, fetchSeoIssues, fetchRedirectRules, updateSeoFields, } = await import('../../lib/seo-service.js');
|
|
6
|
+
const { fetchSeoOverview, fetchSeoContentRecords, fetchSeoIssues, fetchRedirectRules, runSeoAudit, updateSeoFields, } = await import('../../lib/seo-service.js');
|
|
8
7
|
afterEach(() => {
|
|
9
8
|
cmsApi.mockReset();
|
|
10
9
|
});
|
|
@@ -43,4 +42,36 @@ describe('SEO service read fetchers surface errors', () => {
|
|
|
43
42
|
});
|
|
44
43
|
});
|
|
45
44
|
});
|
|
45
|
+
describe('SEO audit mutation contract', () => {
|
|
46
|
+
it('returns the compact POST response with top-level rendered audit data', async () => {
|
|
47
|
+
const renderedAudit = {
|
|
48
|
+
enabled: true,
|
|
49
|
+
originSource: 'config',
|
|
50
|
+
verified: 2,
|
|
51
|
+
unverified: 1,
|
|
52
|
+
notAttempted: 0,
|
|
53
|
+
partial: true,
|
|
54
|
+
bytesRead: 256,
|
|
55
|
+
durationMs: 12,
|
|
56
|
+
reasons: [{ reason: 'timeout', count: 1 }],
|
|
57
|
+
};
|
|
58
|
+
const compactRun = {
|
|
59
|
+
id: 'run-1',
|
|
60
|
+
status: 'COMPLETED',
|
|
61
|
+
siteScoreAfter: 88,
|
|
62
|
+
issuesFound: 3,
|
|
63
|
+
criticalCount: 0,
|
|
64
|
+
warningCount: 2,
|
|
65
|
+
infoCount: 1,
|
|
66
|
+
pagesScanned: 3,
|
|
67
|
+
renderedAudit,
|
|
68
|
+
};
|
|
69
|
+
cmsApi.mockResolvedValue({ data: compactRun, status: 200 });
|
|
70
|
+
const result = await runSeoAudit('technical');
|
|
71
|
+
expect(result.run?.renderedAudit).toEqual(renderedAudit);
|
|
72
|
+
expectTypeOf().toEqualTypeOf();
|
|
73
|
+
expectTypeOf().toEqualTypeOf();
|
|
74
|
+
expectTypeOf().toEqualTypeOf();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
46
77
|
//# sourceMappingURL=seo-service-errors.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo-service-errors.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/seo-service-errors.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"seo-service-errors.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/seo-service-errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-audit-tab.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/seo-audit-tab.render.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment happy-dom
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
5
|
+
const baseRun = {
|
|
6
|
+
id: 'audit-1',
|
|
7
|
+
status: 'COMPLETED',
|
|
8
|
+
scope: 'full',
|
|
9
|
+
startedAt: '2026-08-11T12:00:00.000Z',
|
|
10
|
+
finishedAt: '2026-08-11T12:00:01.000Z',
|
|
11
|
+
siteScoreBefore: 70,
|
|
12
|
+
siteScoreAfter: 82,
|
|
13
|
+
pagesScanned: 10,
|
|
14
|
+
issuesFound: 3,
|
|
15
|
+
criticalCount: 0,
|
|
16
|
+
warningCount: 2,
|
|
17
|
+
infoCount: 1,
|
|
18
|
+
errorMessage: null,
|
|
19
|
+
};
|
|
20
|
+
let latestRun = { ...baseRun };
|
|
21
|
+
const cmsApi = vi.fn(async (endpoint) => {
|
|
22
|
+
if (endpoint === '/seo/audits') {
|
|
23
|
+
return { data: { runs: [latestRun] }, status: 200 };
|
|
24
|
+
}
|
|
25
|
+
if (endpoint.startsWith('/seo/issues')) {
|
|
26
|
+
return { data: { issues: [] }, status: 200 };
|
|
27
|
+
}
|
|
28
|
+
return { data: {}, status: 200 };
|
|
29
|
+
});
|
|
30
|
+
vi.mock('../../lib/api.js', () => ({
|
|
31
|
+
cmsApi: (endpoint) => cmsApi(endpoint),
|
|
32
|
+
}));
|
|
33
|
+
vi.mock('sonner', () => ({
|
|
34
|
+
toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn(), message: vi.fn() },
|
|
35
|
+
}));
|
|
36
|
+
const { AuditTab } = await import('../../views/seo/AuditTab.js');
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
cmsApi.mockClear();
|
|
39
|
+
latestRun = { ...baseRun };
|
|
40
|
+
});
|
|
41
|
+
describe('AuditTab rendered audit coverage', () => {
|
|
42
|
+
it('shows safe coverage details and a warning for partial runs', async () => {
|
|
43
|
+
latestRun = {
|
|
44
|
+
...baseRun,
|
|
45
|
+
metadata: {
|
|
46
|
+
renderedAudit: {
|
|
47
|
+
enabled: true,
|
|
48
|
+
originSource: 'override',
|
|
49
|
+
verified: 8,
|
|
50
|
+
unverified: 2,
|
|
51
|
+
notAttempted: 0,
|
|
52
|
+
partial: true,
|
|
53
|
+
bytesRead: 2048,
|
|
54
|
+
durationMs: 50,
|
|
55
|
+
reasons: [{ reason: 'timeout', count: 2 }],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
render(_jsx(AuditTab, {}));
|
|
60
|
+
expect(await screen.findByText('8 verified')).toBeTruthy();
|
|
61
|
+
expect(screen.getByText('2 unverified')).toBeTruthy();
|
|
62
|
+
const warning = screen.getByRole('status');
|
|
63
|
+
expect(warning.textContent).toContain('The score covers 8 verified pages.');
|
|
64
|
+
expect(warning.textContent).toContain('their prior rendered-dependent issues were preserved.');
|
|
65
|
+
expect(screen.queryByText(/timeout/i)).toBeNull();
|
|
66
|
+
expect(screen.queryByText(/https?:\/\//i)).toBeNull();
|
|
67
|
+
expect(screen.queryByText(/transport error/i)).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
it('does not show a partial warning for complete coverage', async () => {
|
|
70
|
+
latestRun = {
|
|
71
|
+
...baseRun,
|
|
72
|
+
metadata: {
|
|
73
|
+
renderedAudit: {
|
|
74
|
+
enabled: true,
|
|
75
|
+
originSource: 'config',
|
|
76
|
+
verified: 10,
|
|
77
|
+
unverified: 0,
|
|
78
|
+
notAttempted: 0,
|
|
79
|
+
partial: false,
|
|
80
|
+
bytesRead: 4096,
|
|
81
|
+
durationMs: 40,
|
|
82
|
+
reasons: [],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
render(_jsx(AuditTab, {}));
|
|
87
|
+
expect(await screen.findByText('10 verified')).toBeTruthy();
|
|
88
|
+
expect(screen.getByText('0 unverified')).toBeTruthy();
|
|
89
|
+
expect(screen.queryByText('Partial rendered audit')).toBeNull();
|
|
90
|
+
expect(screen.queryByRole('status')).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
it('renders legacy audit runs without coverage or warnings', async () => {
|
|
93
|
+
render(_jsx(AuditTab, {}));
|
|
94
|
+
await waitFor(() => expect(screen.getByText('10')).toBeTruthy());
|
|
95
|
+
expect(screen.queryByText(/verified$/i)).toBeNull();
|
|
96
|
+
expect(screen.queryByText('Partial rendered audit')).toBeNull();
|
|
97
|
+
expect(screen.queryByRole('status')).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=seo-audit-tab.render.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-audit-tab.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/seo-audit-tab.render.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-technical-tab.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/seo-technical-tab.render.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment happy-dom
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
const initialCrawlSettings = {
|
|
7
|
+
indexEntireSite: true,
|
|
8
|
+
includeImagesInSitemap: true,
|
|
9
|
+
includePostsInSitemap: true,
|
|
10
|
+
sitemapEnabled: true,
|
|
11
|
+
autoRegenerateSitemapOnPublish: true,
|
|
12
|
+
pingSearchEnginesOnPublish: true,
|
|
13
|
+
auditRenderedHtml: true,
|
|
14
|
+
};
|
|
15
|
+
let crawlSettings = { ...initialCrawlSettings };
|
|
16
|
+
let failNextPut = false;
|
|
17
|
+
let pendingPut = null;
|
|
18
|
+
const cmsApi = vi.fn(async (endpoint, options) => {
|
|
19
|
+
const method = options?.method ?? 'GET';
|
|
20
|
+
if (endpoint === '/seo/crawl-settings' && method === 'GET') {
|
|
21
|
+
return { data: { ...crawlSettings }, status: 200 };
|
|
22
|
+
}
|
|
23
|
+
if (endpoint === '/seo/crawl-settings' && method === 'PUT') {
|
|
24
|
+
if (pendingPut)
|
|
25
|
+
await pendingPut;
|
|
26
|
+
if (failNextPut) {
|
|
27
|
+
failNextPut = false;
|
|
28
|
+
return { error: 'Unable to save crawl settings.', status: 500 };
|
|
29
|
+
}
|
|
30
|
+
crawlSettings = {
|
|
31
|
+
...crawlSettings,
|
|
32
|
+
...JSON.parse(String(options?.body)),
|
|
33
|
+
};
|
|
34
|
+
return { data: { ...crawlSettings }, status: 200 };
|
|
35
|
+
}
|
|
36
|
+
if (endpoint === '/seo/sitemap/status') {
|
|
37
|
+
return {
|
|
38
|
+
data: {
|
|
39
|
+
sitemapUrl: 'https://example.test/sitemap.xml',
|
|
40
|
+
lastGeneratedAt: null,
|
|
41
|
+
pagesIncluded: 10,
|
|
42
|
+
pagesExcluded: 0,
|
|
43
|
+
autoRegenerateOnPublish: true,
|
|
44
|
+
errors: [],
|
|
45
|
+
warnings: [],
|
|
46
|
+
},
|
|
47
|
+
status: 200,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (endpoint === '/seo/robots') {
|
|
51
|
+
return { data: { body: 'User-agent: *\nAllow: /', generated: true }, status: 200 };
|
|
52
|
+
}
|
|
53
|
+
if (endpoint === '/ai-crawlers') {
|
|
54
|
+
return {
|
|
55
|
+
data: { enabled: false, rows: [], totals: { hits: 0, bots: 0, honeypot: 0 } },
|
|
56
|
+
status: 200,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { data: {}, status: 200 };
|
|
60
|
+
});
|
|
61
|
+
vi.mock('../../lib/api.js', () => ({
|
|
62
|
+
cmsApi: (endpoint, options) => cmsApi(endpoint, options),
|
|
63
|
+
}));
|
|
64
|
+
vi.mock('sonner', () => ({
|
|
65
|
+
toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn(), message: vi.fn() },
|
|
66
|
+
}));
|
|
67
|
+
const { TechnicalTab } = await import('../../views/seo/TechnicalTab.js');
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
cmsApi.mockClear();
|
|
70
|
+
vi.mocked(toast.error).mockClear();
|
|
71
|
+
crawlSettings = { ...initialCrawlSettings };
|
|
72
|
+
failNextPut = false;
|
|
73
|
+
pendingPut = null;
|
|
74
|
+
});
|
|
75
|
+
describe('TechnicalTab rendered audit setting', () => {
|
|
76
|
+
it('renders the accessible setting and persists changes', async () => {
|
|
77
|
+
render(_jsx(TechnicalTab, {}));
|
|
78
|
+
const renderedAudit = await screen.findByRole('switch', {
|
|
79
|
+
name: /Audit rendered public HTML/i,
|
|
80
|
+
description: 'Fetch public page output so audits grade crawler-visible headings and content.',
|
|
81
|
+
});
|
|
82
|
+
expect(renderedAudit.getAttribute('aria-checked')).toBe('true');
|
|
83
|
+
fireEvent.click(renderedAudit);
|
|
84
|
+
await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/seo/crawl-settings', expect.objectContaining({
|
|
85
|
+
method: 'PUT',
|
|
86
|
+
body: JSON.stringify({ auditRenderedHtml: false }),
|
|
87
|
+
})));
|
|
88
|
+
});
|
|
89
|
+
it('disables crawl switches while a setting is saving', async () => {
|
|
90
|
+
let resolvePut;
|
|
91
|
+
pendingPut = new Promise((resolve) => {
|
|
92
|
+
resolvePut = () => resolve({});
|
|
93
|
+
});
|
|
94
|
+
render(_jsx(TechnicalTab, {}));
|
|
95
|
+
fireEvent.click(await screen.findByRole('switch', { name: /Audit rendered public HTML/i }));
|
|
96
|
+
await waitFor(() => {
|
|
97
|
+
for (const toggle of screen.getAllByRole('switch')) {
|
|
98
|
+
expect(toggle.disabled).toBe(true);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
resolvePut?.();
|
|
102
|
+
await waitFor(() => expect(screen.getByRole('switch', {
|
|
103
|
+
name: /Audit rendered public HTML/i,
|
|
104
|
+
}).disabled).toBe(false));
|
|
105
|
+
});
|
|
106
|
+
it('reports save failures and refetches the server value', async () => {
|
|
107
|
+
failNextPut = true;
|
|
108
|
+
render(_jsx(TechnicalTab, {}));
|
|
109
|
+
const renderedAudit = await screen.findByRole('switch', {
|
|
110
|
+
name: /Audit rendered public HTML/i,
|
|
111
|
+
});
|
|
112
|
+
const getCallsBeforeSave = cmsApi.mock.calls.filter(([endpoint, options]) => endpoint === '/seo/crawl-settings' &&
|
|
113
|
+
(options?.method ?? 'GET') === 'GET').length;
|
|
114
|
+
fireEvent.click(renderedAudit);
|
|
115
|
+
await waitFor(() => expect(toast.error).toHaveBeenCalledWith('Unable to save crawl settings.'));
|
|
116
|
+
await waitFor(() => expect(renderedAudit.getAttribute('aria-checked')).toBe('true'));
|
|
117
|
+
expect(cmsApi.mock.calls.filter(([endpoint, options]) => endpoint === '/seo/crawl-settings' &&
|
|
118
|
+
(options?.method ?? 'GET') === 'GET').length).toBeGreaterThan(getCallsBeforeSave);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=seo-technical-tab.render.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-technical-tab.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/seo-technical-tab.render.test.tsx"],"names":[],"mappings":";AAAA,gCAAgC;AAChC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE9B,MAAM,oBAAoB,GAAG;IAC3B,eAAe,EAAE,IAAI;IACrB,sBAAsB,EAAE,IAAI;IAC5B,qBAAqB,EAAE,IAAI;IAC3B,cAAc,EAAE,IAAI;IACpB,8BAA8B,EAAE,IAAI;IACpC,0BAA0B,EAAE,IAAI;IAChC,iBAAiB,EAAE,IAAI;CACxB,CAAA;AAED,IAAI,aAAa,GAAG,EAAE,GAAG,oBAAoB,EAAE,CAAA;AAC/C,IAAI,WAAW,GAAG,KAAK,CAAA;AACvB,IAAI,UAAU,GAAuD,IAAI,CAAA;AAEzE,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,QAAgB,EAAE,OAAqB,EAAE,EAAE;IACrE,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,KAAK,CAAA;IACvC,IAAI,QAAQ,KAAK,qBAAqB,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC3D,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,QAAQ,KAAK,qBAAqB,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QAC3D,IAAI,UAAU;YAAE,MAAM,UAAU,CAAA;QAChC,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,GAAG,KAAK,CAAA;YACnB,OAAO,EAAE,KAAK,EAAE,gCAAgC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;QACjE,CAAC;QACD,aAAa,GAAG;YACd,GAAG,aAAa;YAChB,GAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAA0C;SAC/E,CAAA;QACD,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,QAAQ,KAAK,qBAAqB,EAAE,CAAC;QACvC,OAAO;YACL,IAAI,EAAE;gBACJ,UAAU,EAAE,kCAAkC;gBAC9C,eAAe,EAAE,IAAI;gBACrB,aAAa,EAAE,EAAE;gBACjB,aAAa,EAAE,CAAC;gBAChB,uBAAuB,EAAE,IAAI;gBAC7B,MAAM,EAAE,EAAE;gBACV,QAAQ,EAAE,EAAE;aACb;YACD,MAAM,EAAE,GAAG;SACZ,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IACpF,CAAC;IACD,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE;YAC7E,MAAM,EAAE,GAAG;SACZ,CAAA;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;AAClC,CAAC,CAAC,CAAA;AAEF,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,MAAM,EAAE,CAAC,QAAgB,EAAE,OAAqB,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC/E,CAAC,CAAC,CAAA;AAEH,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IACvB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;CAChF,CAAC,CAAC,CAAA;AAEH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAA;AAExE,UAAU,CAAC,GAAG,EAAE;IACd,MAAM,CAAC,SAAS,EAAE,CAAA;IAClB,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;IAClC,aAAa,GAAG,EAAE,GAAG,oBAAoB,EAAE,CAAA;IAC3C,WAAW,GAAG,KAAK,CAAA;IACnB,UAAU,GAAG,IAAI,CAAA;AACnB,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,CAAC,KAAC,YAAY,KAAG,CAAC,CAAA;QAExB,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtD,IAAI,EAAE,6BAA6B;YACnC,WAAW,EAAE,gFAAgF;SAC9F,CAAC,CAAA;QACF,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAE/D,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAE9B,MAAM,OAAO,CAAC,GAAG,EAAE,CACjB,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,CACjC,qBAAqB,EACrB,MAAM,CAAC,gBAAgB,CAAC;YACtB,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;SACnD,CAAC,CACH,CACF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,IAAI,UAAoC,CAAA;QACxC,UAAU,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACnC,UAAU,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,KAAC,YAAY,KAAG,CAAC,CAAA;QAExB,SAAS,CAAC,KAAK,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC,CAAA;QAE3F,MAAM,OAAO,CAAC,GAAG,EAAE;YACjB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnD,MAAM,CAAE,MAA4B,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC3D,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,UAAU,EAAE,EAAE,CAAA;QACd,MAAM,OAAO,CAAC,GAAG,EAAE,CACjB,MAAM,CAEF,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE;YACzB,IAAI,EAAE,6BAA6B;SACpC,CACF,CAAC,QAAQ,CACX,CAAC,IAAI,CAAC,KAAK,CAAC,CACd,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,WAAW,GAAG,IAAI,CAAA;QAClB,MAAM,CAAC,KAAC,YAAY,KAAG,CAAC,CAAA;QAExB,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE;YACtD,IAAI,EAAE,6BAA6B;SACpC,CAAC,CAAA;QACF,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CACjD,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE,CACtB,QAAQ,KAAK,qBAAqB;YAClC,CAAE,OAAmC,EAAE,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,CACpE,CAAC,MAAM,CAAA;QAER,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAE9B,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAAC,gCAAgC,CAAC,CAAC,CAAA;QAC/F,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QACpF,MAAM,CACJ,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CACtB,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE,CACtB,QAAQ,KAAK,qBAAqB;YAClC,CAAE,OAAmC,EAAE,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,CACpE,CAAC,MAAM,CACT,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -32,6 +32,14 @@ export interface ModalProps {
|
|
|
32
32
|
*/
|
|
33
33
|
onDismiss?: () => void;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* True when the event originated inside a *different* `[role="dialog"]` than
|
|
37
|
+
* `content` (e.g. MediaPickerModal / ConfirmDialog portaled above this Modal).
|
|
38
|
+
* Nested dialogs must own their own dismiss; treating their clicks as "outside"
|
|
39
|
+
* would discard the parent form before the nested selection can apply
|
|
40
|
+
* (UpChat #27 / featured-image picker).
|
|
41
|
+
*/
|
|
42
|
+
export declare function isNestedDialogEvent(target: EventTarget | null, content: EventTarget | null): boolean;
|
|
35
43
|
/**
|
|
36
44
|
* The canonical admin modal. Built on Radix `Dialog`, which provides the
|
|
37
45
|
* accessibility contract for free: focus is trapped while open and restored to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/ui/Modal.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../../src/components/ui/Modal.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAU,MAAM,OAAO,CAAA;AAG9D,QAAA,MAAM,gBAAgB;;;IAGpB,mFAAmF;;CAE3E,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,gBAAgB,CAAA;AAErD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,SAAS,CAAA;IACnB,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,qDAAqD;IACrD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IAC/C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,OAAO,EAAE,WAAW,GAAG,IAAI,GAC1B,OAAO,CAIT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,EACpB,IAAI,EACJ,OAAO,EACP,KAAK,EACL,QAAQ,EACR,OAAO,EACP,IAAW,EACX,WAAW,EACX,eAAe,EACf,SAAS,GACV,EAAE,UAAU,2CA2EZ"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
4
4
|
import { X } from 'lucide-react';
|
|
5
|
+
import { useRef } from 'react';
|
|
5
6
|
import { adminPortalContainer } from '../../lib/portal-container.js';
|
|
6
7
|
const MODAL_SIZE_CLASS = {
|
|
7
8
|
md: 'max-w-lg',
|
|
@@ -9,6 +10,19 @@ const MODAL_SIZE_CLASS = {
|
|
|
9
10
|
/** Wide form dialogs (post/page field editors) — body scrolls inside the shell. */
|
|
10
11
|
xl: 'flex max-h-[min(92vh,880px)] max-w-5xl flex-col',
|
|
11
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* True when the event originated inside a *different* `[role="dialog"]` than
|
|
15
|
+
* `content` (e.g. MediaPickerModal / ConfirmDialog portaled above this Modal).
|
|
16
|
+
* Nested dialogs must own their own dismiss; treating their clicks as "outside"
|
|
17
|
+
* would discard the parent form before the nested selection can apply
|
|
18
|
+
* (UpChat #27 / featured-image picker).
|
|
19
|
+
*/
|
|
20
|
+
export function isNestedDialogEvent(target, content) {
|
|
21
|
+
if (!(target instanceof Element) || !(content instanceof Element))
|
|
22
|
+
return false;
|
|
23
|
+
const owningDialog = target.closest('[role="dialog"]');
|
|
24
|
+
return Boolean(owningDialog && owningDialog !== content);
|
|
25
|
+
}
|
|
12
26
|
/**
|
|
13
27
|
* The canonical admin modal. Built on Radix `Dialog`, which provides the
|
|
14
28
|
* accessibility contract for free: focus is trapped while open and restored to
|
|
@@ -22,6 +36,7 @@ const MODAL_SIZE_CLASS = {
|
|
|
22
36
|
*/
|
|
23
37
|
export function Modal({ open, onClose, title, children, actions, size = 'md', description, initialFocusRef, onDismiss, }) {
|
|
24
38
|
const scrollBody = size === 'xl';
|
|
39
|
+
const contentRef = useRef(null);
|
|
25
40
|
function handleDismissAttempt() {
|
|
26
41
|
if (onDismiss)
|
|
27
42
|
onDismiss();
|
|
@@ -31,7 +46,13 @@ export function Modal({ open, onClose, title, children, actions, size = 'md', de
|
|
|
31
46
|
return (_jsx(Dialog.Root, { open: open, onOpenChange: (next) => {
|
|
32
47
|
if (!next)
|
|
33
48
|
handleDismissAttempt();
|
|
34
|
-
}, children: _jsxs(Dialog.Portal, { container: adminPortalContainer(), children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-60 bg-black/50" }), _jsxs(Dialog.Content, { className: `bg-card text-card-foreground border-border fixed top-1/2 left-1/2 z-60 w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border shadow-xl focus:outline-none ${MODAL_SIZE_CLASS[size]}`, onInteractOutside: (event) => {
|
|
49
|
+
}, children: _jsxs(Dialog.Portal, { container: adminPortalContainer(), children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-60 bg-black/50" }), _jsxs(Dialog.Content, { ref: contentRef, className: `bg-card text-card-foreground border-border fixed top-1/2 left-1/2 z-60 w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border shadow-xl focus:outline-none ${MODAL_SIZE_CLASS[size]}`, onInteractOutside: (event) => {
|
|
50
|
+
// Radix dispatches this custom event on the outside target, so
|
|
51
|
+
// `event.currentTarget` is not the parent Dialog.Content.
|
|
52
|
+
if (isNestedDialogEvent(event.target, contentRef.current)) {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
35
56
|
if (!onDismiss)
|
|
36
57
|
return;
|
|
37
58
|
event.preventDefault();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/components/ui/Modal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"Modal.js","sourceRoot":"","sources":["../../../src/components/ui/Modal.tsx"],"names":[],"mappings":"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"}
|
|
@@ -179,6 +179,20 @@ export declare function applyContentSeoQuery(records: SeoContentRecord[], query:
|
|
|
179
179
|
filter?: ContentSeoFilter;
|
|
180
180
|
}): SeoContentRecord[];
|
|
181
181
|
export type SeoAuditScope = 'full' | 'content' | 'technical' | 'redirect';
|
|
182
|
+
export interface RenderedAuditSummary {
|
|
183
|
+
enabled: boolean;
|
|
184
|
+
originSource: 'override' | 'config' | null;
|
|
185
|
+
verified: number;
|
|
186
|
+
unverified: number;
|
|
187
|
+
notAttempted: number;
|
|
188
|
+
partial: boolean;
|
|
189
|
+
bytesRead: number;
|
|
190
|
+
durationMs: number;
|
|
191
|
+
reasons: Array<{
|
|
192
|
+
reason: string;
|
|
193
|
+
count: number;
|
|
194
|
+
}>;
|
|
195
|
+
}
|
|
182
196
|
export interface SeoAuditRun {
|
|
183
197
|
id: string;
|
|
184
198
|
status: 'RUNNING' | 'COMPLETED' | 'FAILED';
|
|
@@ -193,9 +207,23 @@ export interface SeoAuditRun {
|
|
|
193
207
|
warningCount: number;
|
|
194
208
|
infoCount: number;
|
|
195
209
|
errorMessage: string | null;
|
|
210
|
+
metadata: {
|
|
211
|
+
renderedAudit?: RenderedAuditSummary;
|
|
212
|
+
} | null;
|
|
213
|
+
}
|
|
214
|
+
export interface SeoAuditRunResponse {
|
|
215
|
+
id: string;
|
|
216
|
+
status: 'COMPLETED';
|
|
217
|
+
siteScoreAfter: number;
|
|
218
|
+
issuesFound: number;
|
|
219
|
+
criticalCount: number;
|
|
220
|
+
warningCount: number;
|
|
221
|
+
infoCount: number;
|
|
222
|
+
pagesScanned: number;
|
|
223
|
+
renderedAudit: RenderedAuditSummary;
|
|
196
224
|
}
|
|
197
225
|
export declare function runSeoAudit(scope?: SeoAuditScope): Promise<{
|
|
198
|
-
run?:
|
|
226
|
+
run?: SeoAuditRunResponse;
|
|
199
227
|
error?: string;
|
|
200
228
|
}>;
|
|
201
229
|
export declare function fetchSeoAuditRuns(): Promise<SeoAuditRun[]>;
|
|
@@ -365,6 +393,7 @@ export interface CrawlSettings {
|
|
|
365
393
|
sitemapEnabled: boolean;
|
|
366
394
|
autoRegenerateSitemapOnPublish: boolean;
|
|
367
395
|
pingSearchEnginesOnPublish: boolean;
|
|
396
|
+
auditRenderedHtml: boolean;
|
|
368
397
|
}
|
|
369
398
|
export declare function fetchSitemapStatus(): Promise<SitemapStatus | null>;
|
|
370
399
|
export declare function regenerateSitemap(): Promise<{
|