@actuate-media/cms-admin 0.32.0 → 0.33.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.
Files changed (28) hide show
  1. package/dist/__tests__/views/seo-settings.render.test.d.ts +2 -0
  2. package/dist/__tests__/views/seo-settings.render.test.d.ts.map +1 -0
  3. package/dist/__tests__/views/seo-settings.render.test.js +152 -0
  4. package/dist/__tests__/views/seo-settings.render.test.js.map +1 -0
  5. package/dist/__tests__/views/settings-save-footer.render.test.js +16 -9
  6. package/dist/__tests__/views/settings-save-footer.render.test.js.map +1 -1
  7. package/dist/actuate-admin.css +1 -1
  8. package/dist/components/MediaPickerModal.d.ts +15 -1
  9. package/dist/components/MediaPickerModal.d.ts.map +1 -1
  10. package/dist/components/MediaPickerModal.js +17 -1
  11. package/dist/components/MediaPickerModal.js.map +1 -1
  12. package/dist/views/Settings.js +2 -2
  13. package/dist/views/Settings.js.map +1 -1
  14. package/dist/views/settings/SeoSettingsTab.d.ts +7 -0
  15. package/dist/views/settings/SeoSettingsTab.d.ts.map +1 -0
  16. package/dist/views/settings/SeoSettingsTab.js +126 -0
  17. package/dist/views/settings/SeoSettingsTab.js.map +1 -0
  18. package/dist/views/settings/useSeoSettings.d.ts +79 -0
  19. package/dist/views/settings/useSeoSettings.d.ts.map +1 -0
  20. package/dist/views/settings/useSeoSettings.js +278 -0
  21. package/dist/views/settings/useSeoSettings.js.map +1 -0
  22. package/package.json +2 -2
  23. package/src/__tests__/views/seo-settings.render.test.tsx +219 -0
  24. package/src/__tests__/views/settings-save-footer.render.test.tsx +16 -9
  25. package/src/components/MediaPickerModal.tsx +39 -2
  26. package/src/views/Settings.tsx +2 -2
  27. package/src/views/settings/SeoSettingsTab.tsx +658 -0
  28. package/src/views/settings/useSeoSettings.ts +374 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=seo-settings.render.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-settings.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/seo-settings.render.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,152 @@
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, within } from '@testing-library/react';
5
+ // The SEO defaults tab reads four shared backends: /seo/config (meta defaults +
6
+ // verification), /seo/crawl-settings (sitemap toggles), /seo/sitemap/status
7
+ // (read-only summary), and /seo/index-status (environment).
8
+ const cmsApi = vi.fn(async (endpoint, options) => {
9
+ const method = options?.method ?? 'GET';
10
+ if (endpoint === '/seo/config' && method === 'GET') {
11
+ return {
12
+ data: {
13
+ static: { site: null },
14
+ overrides: { site: {} },
15
+ effective: {
16
+ site: {
17
+ siteName: 'Acme',
18
+ siteUrl: 'https://acme.test',
19
+ titleTemplate: '%page% — %site%',
20
+ defaultMetaDescription: 'Seed description.',
21
+ verification: { googleSearchConsole: '', bingWebmasterTools: '' },
22
+ },
23
+ },
24
+ collections: [],
25
+ },
26
+ status: 200,
27
+ };
28
+ }
29
+ if (endpoint === '/seo/crawl-settings' && method === 'GET') {
30
+ return {
31
+ data: {
32
+ autoRegenerateSitemapOnPublish: true,
33
+ pingSearchEnginesOnPublish: true,
34
+ sitemapEnabled: true,
35
+ },
36
+ status: 200,
37
+ };
38
+ }
39
+ if (endpoint === '/seo/sitemap/status' && method === 'GET') {
40
+ return {
41
+ data: {
42
+ sitemapUrl: 'https://acme.test/api/cms/sitemap.xml',
43
+ lastGeneratedAt: '2026-01-02T03:04:05.000Z',
44
+ pagesIncluded: 12,
45
+ pagesExcluded: 1,
46
+ warnings: [],
47
+ },
48
+ status: 200,
49
+ };
50
+ }
51
+ if (endpoint === '/seo/index-status' && method === 'GET') {
52
+ return { data: { environment: 'preview' }, status: 200 };
53
+ }
54
+ return { data: {}, status: 200 };
55
+ });
56
+ vi.mock('../../lib/api.js', () => ({
57
+ cmsApi: (e, o) => cmsApi(e, o),
58
+ ensureCsrfToken: vi.fn(async () => { }),
59
+ }));
60
+ // Stub the Media Center picker: when open, expose a button that selects a known
61
+ // asset (id + url), exercising the asset-id storage path without the real
62
+ // MediaBrowser/network stack.
63
+ vi.mock('../../components/MediaPickerModal.js', () => ({
64
+ MediaPickerModal: ({ open, onSelect, onSelectItem, }) => open ? (_jsx("button", { type: "button", onClick: () => {
65
+ onSelect('https://cdn.test/og.png');
66
+ onSelectItem?.({ id: 'asset-123' });
67
+ }, children: "pick-asset" })) : null,
68
+ }));
69
+ const { SeoSettingsTab } = await import('../../views/settings/SeoSettingsTab.js');
70
+ beforeEach(() => {
71
+ cmsApi.mockClear();
72
+ });
73
+ describe('SeoSettingsTab', () => {
74
+ it('renders the workspace callout and loads meta defaults from /seo/config', async () => {
75
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
76
+ expect(await screen.findByText('Looking for the full SEO workspace?')).toBeTruthy();
77
+ expect(screen.getByLabelText('Title template').value).toBe('%page% — %site%');
78
+ expect(screen.getByLabelText('Default meta description').value).toBe('Seed description.');
79
+ // Clean form → Save disabled.
80
+ expect(screen.getByRole('button', { name: 'Save Changes' }).disabled).toBe(true);
81
+ });
82
+ it('routes "Open SEO" to /seo', async () => {
83
+ const onNavigate = vi.fn();
84
+ render(_jsx(SeoSettingsTab, { role: "ADMIN", onNavigate: onNavigate }));
85
+ await screen.findByText('Looking for the full SEO workspace?');
86
+ fireEvent.click(screen.getByRole('button', { name: /Open SEO/ }));
87
+ expect(onNavigate).toHaveBeenCalledWith('/seo');
88
+ });
89
+ it('rejects an unsupported title-template token and blocks save', async () => {
90
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
91
+ const tpl = (await screen.findByLabelText('Title template'));
92
+ fireEvent.change(tpl, { target: { value: '%page% %bogus%' } });
93
+ expect((await screen.findAllByText(/Unsupported token/i)).length).toBeGreaterThan(0);
94
+ expect(screen.getByRole('button', { name: 'Save Changes' }).disabled).toBe(true);
95
+ });
96
+ it('shows a character count and warns when the description is too long', async () => {
97
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
98
+ const desc = (await screen.findByLabelText('Default meta description'));
99
+ fireEvent.change(desc, { target: { value: 'x'.repeat(200) } });
100
+ expect(screen.getByText('200/160')).toBeTruthy();
101
+ expect((await screen.findAllByText(/truncate/i)).length).toBeGreaterThan(0);
102
+ });
103
+ it('persists meta defaults + verification to the shared /seo/config', async () => {
104
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
105
+ const google = (await screen.findByLabelText('Google Search Console'));
106
+ fireEvent.change(google, { target: { value: 'goog-token' } });
107
+ const save = screen.getByRole('button', { name: 'Save Changes' });
108
+ expect(save.disabled).toBe(false);
109
+ fireEvent.click(save);
110
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/seo/config', expect.objectContaining({ method: 'PUT' })));
111
+ const putCall = cmsApi.mock.calls.find(([e, o]) => e === '/seo/config' && o?.method === 'PUT');
112
+ const body = JSON.parse(putCall[1].body);
113
+ expect(body.site.verification.googleSearchConsole).toBe('goog-token');
114
+ });
115
+ it('stores the selected social image asset id from the Media Center picker', async () => {
116
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
117
+ await screen.findByLabelText('Title template');
118
+ fireEvent.click(screen.getByRole('button', { name: 'Choose image' }));
119
+ fireEvent.click(screen.getByRole('button', { name: 'pick-asset' }));
120
+ const save = screen.getByRole('button', { name: 'Save Changes' });
121
+ expect(save.disabled).toBe(false);
122
+ fireEvent.click(save);
123
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/seo/config', expect.objectContaining({ method: 'PUT' })));
124
+ const putCall = cmsApi.mock.calls.find(([e, o]) => e === '/seo/config' && o?.method === 'PUT');
125
+ const body = JSON.parse(putCall[1].body);
126
+ expect(body.site.defaultSocialImageAssetId).toBe('asset-123');
127
+ expect(body.site.defaultOgImage).toBe('https://cdn.test/og.png');
128
+ });
129
+ it('persists sitemap toggles to the shared /seo/crawl-settings', async () => {
130
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
131
+ await screen.findByLabelText('Title template');
132
+ fireEvent.click(screen.getByRole('switch', { name: 'Ping search engines on publish' }));
133
+ fireEvent.click(screen.getByRole('button', { name: 'Save Changes' }));
134
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/seo/crawl-settings', expect.objectContaining({ method: 'PUT' })));
135
+ });
136
+ it('shows the read-only sitemap summary', async () => {
137
+ render(_jsx(SeoSettingsTab, { role: "ADMIN" }));
138
+ await screen.findByLabelText('Title template');
139
+ expect(screen.getByText('Included URLs')).toBeTruthy();
140
+ expect(screen.getByText('12')).toBeTruthy();
141
+ expect(screen.getByRole('link', { name: /View sitemap/ })).toBeTruthy();
142
+ });
143
+ it('is read-only for non-admins', async () => {
144
+ render(_jsx(SeoSettingsTab, { role: "EDITOR" }));
145
+ expect(await screen.findByText(/read-only access to SEO defaults/i)).toBeTruthy();
146
+ // Scope to the Meta Defaults region: the advanced SEOConfigPanel disclosure
147
+ // also mounts and can briefly produce overlapping label matches.
148
+ const metaCard = screen.getByRole('region', { name: 'Meta Defaults' });
149
+ expect(within(metaCard).getByLabelText('Title template').disabled).toBe(true);
150
+ });
151
+ });
152
+ //# sourceMappingURL=seo-settings.render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-settings.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/seo-settings.render.test.tsx"],"names":[],"mappings":"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"}
@@ -3,11 +3,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import { render, screen, waitFor } from '@testing-library/react';
5
5
  /**
6
- * Regression: the shared "Save Changes" footer must only render on tabs that
7
- * actually persist through `handleSave` (currently just Layout, which writes
8
- * `defaultLayout` into the settings global). On self-managed tabs like SEO it
9
- * was rendering a footer whose click toasted "Settings saved successfully!"
10
- * while persisting none of the tab's edits — misleading, no-op UI.
6
+ * Regression: the shared no-op "Save Changes" footer (driven by the settings
7
+ * global `handleSave`) must only render on tabs that actually persist through
8
+ * it (currently just Layout). On self-managed tabs like SEO it used to render a
9
+ * SECOND footer whose click toasted "Settings saved successfully!" while
10
+ * persisting none of the tab's edits — misleading, no-op UI.
11
+ *
12
+ * The SEO tab now owns a real `SettingsSaveBar` (also labelled "Save Changes")
13
+ * that persists through `/seo/config`, so the contract is: exactly ONE
14
+ * "Save Changes" button on the SEO tab — the tab's own — never the global one
15
+ * stacked on top of it.
11
16
  */
12
17
  const SEO_CONFIG = {
13
18
  static: { site: null },
@@ -38,12 +43,14 @@ beforeEach(() => {
38
43
  cmsApi.mockClear();
39
44
  });
40
45
  describe('Settings shared save footer', () => {
41
- it('does NOT render the global "Save Changes" footer on the self-managed SEO tab', async () => {
46
+ it("renders only the SEO tab's own save bar, not the global footer on top", async () => {
42
47
  render(_jsx(Settings, { config: LAYOUT_CONFIG, session: SESSION, initialTab: "seo" }));
43
- // The SEO panel owns its own persistence ("Save SEO defaults").
48
+ // The Advanced disclosure still mounts the per-collection SEO panel, which
49
+ // owns its own persistence ("Save SEO defaults").
44
50
  expect((await screen.findAllByRole('button', { name: 'Save SEO defaults' })).length).toBeGreaterThan(0);
45
- // The shared footer ("Save Changes") must be absent here.
46
- expect(screen.queryByRole('button', { name: 'Save Changes' })).toBeNull();
51
+ // The tab's own SettingsSaveBar is the ONLY "Save Changes" button the
52
+ // no-op global footer must not be stacked on top of it.
53
+ expect(screen.getAllByRole('button', { name: 'Save Changes' })).toHaveLength(1);
47
54
  });
48
55
  it('renders the global "Save Changes" footer on the Layout tab', async () => {
49
56
  render(_jsx(Settings, { config: LAYOUT_CONFIG, session: SESSION, initialTab: "layout" }));
@@ -1 +1 @@
1
- {"version":3,"file":"settings-save-footer.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/settings-save-footer.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,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAEhE;;;;;;GAMG;AACH,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACtB,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;IACxC,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE;IACvD,WAAW,EAAE,EAAE;CAChB,CAAA;AAED,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,QAAgB,EAAE,OAA6B,EAAE,EAAE;IAC7E,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,KAAK,CAAA;IACvC,IAAI,QAAQ,KAAK,mBAAmB,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACzD,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC9D,CAAC;IACD,IAAI,QAAQ,KAAK,aAAa,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACnD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC1C,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC5C,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,EAAE,MAAM,EAAE,CAAC,CAAS,EAAE,CAAW,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;AAEhG,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAA;AAE5D,MAAM,aAAa,GAAG;IACpB,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE;CAC5E,CAAA;AACD,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAA;AAE3C,UAAU,CAAC,GAAG,EAAE;IACd,MAAM,CAAC,SAAS,EAAE,CAAA;AACpB,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,CAAC,KAAC,QAAQ,IAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAC,KAAK,GAAG,CAAC,CAAA;QAE9E,gEAAgE;QAChE,MAAM,CACJ,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,MAAM,CAC7E,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;QACpB,0DAA0D;QAC1D,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,CAAC,KAAC,QAAQ,IAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAC,QAAQ,GAAG,CAAC,CAAA;QAEjF,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;IAChG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"settings-save-footer.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/settings-save-footer.render.test.tsx"],"names":[],"mappings":"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"}