@classic-homes/theme-svelte 1.22.0 → 1.22.1

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 (77) hide show
  1. package/dist/lib/components/Alert.svelte +8 -10
  2. package/dist/lib/components/DataPanel.svelte +19 -5
  3. package/dist/lib/components/HeaderSearch.svelte +18 -10
  4. package/dist/lib/components/Skeleton.svelte.d.ts +1 -1
  5. package/dist/lib/components/layout/AppShell.svelte +1 -1
  6. package/dist/lib/components/layout/DashboardLayout.svelte +15 -6
  7. package/dist/lib/components/layout/Footer.svelte +6 -6
  8. package/dist/lib/components/layout/FormPageLayout.svelte +4 -3
  9. package/dist/lib/components/layout/Sidebar.svelte +25 -21
  10. package/dist/lib/components/media/Image.svelte.d.ts +1 -1
  11. package/dist/lib/components/media/Lightbox.svelte +13 -9
  12. package/dist/lib/components/media/MediaCarousel.svelte +5 -0
  13. package/dist/lib/components/media/MediaGrid.svelte +4 -1
  14. package/dist/lib/components/notifications/NotificationDialog.svelte +8 -5
  15. package/dist/lib/components/notifications/NotificationItem.svelte +16 -6
  16. package/dist/lib/components/studio/InsertPalette.svelte +4 -1
  17. package/dist/lib/components/studio/StudioCanvas.svelte +23 -2
  18. package/dist/lib/components/studio/StudioCanvas.svelte.d.ts +5 -1
  19. package/dist/lib/composables/useFocusTrap.svelte.js +9 -2
  20. package/dist/lib/notices/sanitize.js +10 -3
  21. package/dist/lib/notices/store.d.ts +3 -3
  22. package/dist/lib/notices/store.js +14 -2
  23. package/dist/lib/notices/types.d.ts +8 -8
  24. package/dist/lib/notices/types.js +5 -1
  25. package/dist/lib/stores/sidebar.svelte.d.ts +6 -0
  26. package/dist/lib/stores/sidebar.svelte.js +61 -26
  27. package/dist/lib/utils/media.js +4 -3
  28. package/dist/lib/utils/url.d.ts +10 -0
  29. package/dist/lib/utils/url.js +21 -0
  30. package/dist/lib/utils.js +14 -1
  31. package/package.json +5 -4
  32. package/dist/__tests__/FileUpload.test.js +0 -45
  33. package/dist/__tests__/a11y/Alert.a11y.test.js +0 -84
  34. package/dist/__tests__/a11y/AlertDialog.a11y.test.js +0 -126
  35. package/dist/__tests__/a11y/Button.a11y.test.js +0 -66
  36. package/dist/__tests__/a11y/Card.a11y.test.js +0 -77
  37. package/dist/__tests__/a11y/Combobox.a11y.test.js +0 -209
  38. package/dist/__tests__/a11y/Dialog.a11y.test.js +0 -103
  39. package/dist/__tests__/a11y/DropdownMenu.a11y.test.js +0 -132
  40. package/dist/__tests__/a11y/FileUpload.a11y.test.js +0 -158
  41. package/dist/__tests__/a11y/FilterToolbar.a11y.test.js +0 -62
  42. package/dist/__tests__/a11y/Icon.a11y.test.js +0 -119
  43. package/dist/__tests__/a11y/Input.a11y.test.js +0 -96
  44. package/dist/__tests__/a11y/LiveRegion.a11y.test.js +0 -102
  45. package/dist/__tests__/a11y/Media.a11y.test.js +0 -145
  46. package/dist/__tests__/a11y/PublicLayout.a11y.test.js +0 -139
  47. package/dist/__tests__/a11y/SegmentedControl.a11y.test.js +0 -50
  48. package/dist/__tests__/a11y/Switch.a11y.test.js +0 -95
  49. package/dist/__tests__/a11y/Tabs.a11y.test.js +0 -116
  50. package/dist/__tests__/a11y/Textarea.a11y.test.js +0 -111
  51. package/dist/__tests__/a11y/Toolbar.a11y.test.js +0 -31
  52. package/dist/__tests__/a11y/ViewToggle.a11y.test.js +0 -40
  53. package/dist/__tests__/action-header.test.js +0 -150
  54. package/dist/__tests__/alert-dialog-dismiss.test.js +0 -54
  55. package/dist/__tests__/code-field.test.js +0 -65
  56. package/dist/__tests__/control-text-color.test.js +0 -29
  57. package/dist/__tests__/data-panel-card-gestures.test.js +0 -138
  58. package/dist/__tests__/data-panel-viewport.test.js +0 -45
  59. package/dist/__tests__/dropdown-trigger.test.js +0 -39
  60. package/dist/__tests__/header-search.test.js +0 -90
  61. package/dist/__tests__/media-components.test.js +0 -206
  62. package/dist/__tests__/media-hls.test.js +0 -273
  63. package/dist/__tests__/media-matterport.test.js +0 -296
  64. package/dist/__tests__/media-players.test.js +0 -244
  65. package/dist/__tests__/media-scrubber.test.js +0 -44
  66. package/dist/__tests__/media-utils.test.js +0 -427
  67. package/dist/__tests__/nav-access.test.js +0 -145
  68. package/dist/__tests__/page-header.test.js +0 -36
  69. package/dist/__tests__/public-layout-drawer.test.js +0 -116
  70. package/dist/__tests__/setup.js +0 -56
  71. package/dist/__tests__/sidebar-collapse.test.js +0 -58
  72. package/dist/__tests__/sidebar-docs-parity.test.js +0 -134
  73. package/dist/__tests__/sidebar-expansion.test.js +0 -59
  74. package/dist/__tests__/sidebar-items.test.js +0 -59
  75. package/dist/__tests__/sidebar-mobile.test.js +0 -86
  76. package/dist/__tests__/studio-canvas-item.svelte.test.js +0 -98
  77. package/dist/__tests__/studio.test.js +0 -240
@@ -1,240 +0,0 @@
1
- /**
2
- * Studio editor frame: outline selection and reordering (pointer-free, by
3
- * keyboard), the save and issues signals, and the responsive move of the
4
- * outline and inspector into sheets.
5
- */
6
- import { describe, it, expect, vi, afterEach } from 'vitest';
7
- import { mount, unmount, flushSync, tick, createRawSnippet } from 'svelte';
8
- import StudioOutline from '../lib/components/studio/StudioOutline.svelte';
9
- import SaveStatus from '../lib/components/studio/SaveStatus.svelte';
10
- import IssuesButton from '../lib/components/studio/IssuesButton.svelte';
11
- import Studio from '../lib/components/studio/Studio.svelte';
12
- import StudioCanvasItem from '../lib/components/studio/StudioCanvasItem.svelte';
13
- let instance = null;
14
- let host;
15
- function render(component, props) {
16
- host = document.createElement('div');
17
- document.body.appendChild(host);
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- instance = mount(component, { target: host, props });
20
- flushSync();
21
- return host;
22
- }
23
- function viewport(width) {
24
- vi.spyOn(window, 'matchMedia').mockImplementation((query) => {
25
- const min = Number(/min-width:\s*(\d+)/.exec(query)?.[1] ?? 0);
26
- return {
27
- matches: width >= min,
28
- media: query,
29
- onchange: null,
30
- addEventListener: vi.fn(),
31
- removeEventListener: vi.fn(),
32
- addListener: vi.fn(),
33
- removeListener: vi.fn(),
34
- dispatchEvent: vi.fn(),
35
- };
36
- });
37
- }
38
- const html = (markup) => createRawSnippet(() => ({ render: () => markup }));
39
- function key(target, k, init = {}) {
40
- target.dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, ...init }));
41
- flushSync();
42
- }
43
- afterEach(() => {
44
- if (instance)
45
- unmount(instance);
46
- instance = null;
47
- host?.remove();
48
- document.body.innerHTML = '';
49
- vi.restoreAllMocks();
50
- });
51
- const entries = [
52
- { id: 'a', title: 'Name' },
53
- { id: 'b', title: 'Email', issues: 2 },
54
- { id: 'c', title: 'Rating' },
55
- ];
56
- describe('StudioOutline', () => {
57
- it('selects a row and marks it current', () => {
58
- const onSelect = vi.fn();
59
- const el = render(StudioOutline, { items: entries, selected: 'a', onSelect });
60
- const rows = el.querySelectorAll('[data-outline-row]');
61
- rows[2].click();
62
- flushSync();
63
- expect(onSelect).toHaveBeenCalledWith('c');
64
- expect(rows[2].getAttribute('aria-current')).toBe('true');
65
- expect(rows[0].hasAttribute('aria-current')).toBe(false);
66
- });
67
- it('keeps one row in the tab order and moves focus with the arrow keys', () => {
68
- const el = render(StudioOutline, { items: entries, selected: 'b' });
69
- const rows = el.querySelectorAll('[data-outline-row]');
70
- expect([...rows].map((r) => r.tabIndex)).toEqual([-1, 0, -1]);
71
- rows[1].focus();
72
- key(rows[1], 'ArrowDown');
73
- expect(document.activeElement).toBe(rows[2]);
74
- });
75
- it('shows issue counts as text, not only colour', () => {
76
- const el = render(StudioOutline, { items: entries });
77
- expect(el.textContent).toContain('2');
78
- expect(el.textContent).toContain('issues');
79
- });
80
- it('reorders with the keyboard: pick up, move, drop', () => {
81
- const onReorder = vi.fn();
82
- const el = render(StudioOutline, { items: entries, onReorder });
83
- const handle = el.querySelector('[data-sortable-handle="a"]');
84
- expect(handle.getAttribute('aria-label')).toBe('Reorder Name');
85
- key(handle, ' ');
86
- expect(handle.getAttribute('aria-pressed')).toBe('true');
87
- key(handle, 'ArrowDown');
88
- expect(onReorder).toHaveBeenCalledWith(0, 1);
89
- });
90
- it('moves in one step with Alt+Arrow', () => {
91
- const onReorder = vi.fn();
92
- const el = render(StudioOutline, { items: entries, onReorder });
93
- const handle = el.querySelector('[data-sortable-handle="c"]');
94
- key(handle, 'ArrowUp', { altKey: true });
95
- expect(onReorder).toHaveBeenCalledWith(2, 1);
96
- });
97
- it('ignores arrows on a handle that is not picked up', () => {
98
- const onReorder = vi.fn();
99
- const el = render(StudioOutline, { items: entries, onReorder });
100
- key(el.querySelector('[data-sortable-handle="a"]'), 'ArrowDown');
101
- expect(onReorder).not.toHaveBeenCalled();
102
- });
103
- it('has no drag handles when reordering is off', () => {
104
- const el = render(StudioOutline, { items: entries });
105
- expect(el.querySelector('[data-sortable-handle]')).toBeNull();
106
- });
107
- it('renders the add button only when onAdd is given', () => {
108
- const onAdd = vi.fn();
109
- const el = render(StudioOutline, { items: entries, onAdd, addLabel: 'Add question' });
110
- const add = [...el.querySelectorAll('button')].find((b) => b.textContent?.includes('Add question'));
111
- add.click();
112
- expect(onAdd).toHaveBeenCalled();
113
- });
114
- });
115
- describe('SaveStatus', () => {
116
- it('shows one word for routine states and announces nothing', () => {
117
- const el = render(SaveStatus, { state: 'saved' });
118
- expect(el.textContent).toContain('Saved');
119
- expect(el.querySelector('[role="status"]').textContent).toBe('');
120
- });
121
- it('announces failures and offers Retry in its details', async () => {
122
- const onRetry = vi.fn();
123
- const el = render(SaveStatus, {
124
- state: 'error',
125
- detail: 'The server did not answer.',
126
- onRetry,
127
- });
128
- expect(el.querySelector('[role="status"]').textContent).toBe('Not saved');
129
- const trigger = el.querySelector('button');
130
- expect(trigger.getAttribute('aria-label')).toContain('Not saved');
131
- trigger.click();
132
- flushSync();
133
- await tick();
134
- const retry = [...document.querySelectorAll('button')].find((b) => b.textContent?.includes('Retry'));
135
- expect(document.body.textContent).toContain('The server did not answer.');
136
- retry.click();
137
- expect(onRetry).toHaveBeenCalled();
138
- });
139
- });
140
- describe('IssuesButton', () => {
141
- const issues = [
142
- { id: '1', message: 'Add a question', location: 'Questions' },
143
- { id: '2', message: 'Add at least two options', location: 'Question 2' },
144
- { id: '3', message: 'Consider a close date', severity: 'warning' },
145
- ];
146
- it('is hidden when there is nothing to fix', () => {
147
- const el = render(IssuesButton, { issues: [] });
148
- expect(el.querySelector('button')).toBeNull();
149
- });
150
- it('names errors and suggestions in its label', () => {
151
- const el = render(IssuesButton, { issues });
152
- expect(el.querySelector('button').getAttribute('aria-label')).toBe('2 issues to fix, 1 suggestion');
153
- });
154
- it('jumps to an issue and closes', async () => {
155
- const onSelect = vi.fn();
156
- const el = render(IssuesButton, { issues, onSelect });
157
- el.querySelector('button').click();
158
- flushSync();
159
- await tick();
160
- const entry = [...document.querySelectorAll('button')].find((b) => b.textContent?.includes('Add at least two options'));
161
- entry.click();
162
- flushSync();
163
- expect(onSelect).toHaveBeenCalledWith(issues[1]);
164
- });
165
- });
166
- describe('Studio layout', () => {
167
- const parts = {
168
- bar: html('<header>Bar</header>'),
169
- rail: html('<nav>Rail</nav>'),
170
- canvas: html('<p>Canvas</p>'),
171
- inspector: html('<div>Inspector</div>'),
172
- };
173
- it('shows rail, canvas and inspector side by side when wide', () => {
174
- viewport(1440);
175
- const el = render(Studio, parts);
176
- expect(el.querySelector('aside[aria-label="Outline"]')).not.toBeNull();
177
- expect(el.querySelector('aside[aria-label="Properties"]')).not.toBeNull();
178
- expect(el.textContent).not.toContain('Outline');
179
- });
180
- it('moves the rail into a sheet below 1280px', () => {
181
- viewport(1024);
182
- const el = render(Studio, parts);
183
- expect(el.querySelector('aside[aria-label="Outline"]')).toBeNull();
184
- expect(el.querySelector('aside[aria-label="Properties"]')).not.toBeNull();
185
- const open = [...el.querySelectorAll('button')].find((b) => b.textContent?.includes('Outline'));
186
- open.click();
187
- flushSync();
188
- expect(document.body.textContent).toContain('Rail');
189
- });
190
- it('leaves only the canvas on phones', () => {
191
- viewport(390);
192
- const el = render(Studio, parts);
193
- expect(el.querySelector('aside')).toBeNull();
194
- expect(el.textContent).toContain('Canvas');
195
- const labels = [...el.querySelectorAll('button')].map((b) => b.textContent?.trim());
196
- expect(labels).toEqual(['Outline', 'Properties']);
197
- });
198
- });
199
- describe('StudioCanvasItem', () => {
200
- it('selects on click and offers a keyboard way in', () => {
201
- const onSelect = vi.fn();
202
- const el = render(StudioCanvasItem, {
203
- label: 'Question 1',
204
- onSelect,
205
- children: html('<p>Body</p>'),
206
- });
207
- const edit = el.querySelector('button');
208
- expect(edit.textContent?.trim()).toBe('Edit Question 1');
209
- el.querySelector('p').click();
210
- expect(onSelect).toHaveBeenCalled();
211
- });
212
- it('inserts below without needing a selection change', () => {
213
- const onInsert = vi.fn();
214
- const el = render(StudioCanvasItem, {
215
- label: 'Question 1',
216
- selected: true,
217
- onInsert,
218
- children: html('<p>Body</p>'),
219
- });
220
- el.querySelector('button[aria-label="Add below Question 1"]').click();
221
- expect(onInsert).toHaveBeenCalled();
222
- });
223
- });
224
- describe('StudioCanvasItem quick actions', () => {
225
- it('does not select the item when focus lands on its "+" (a closing dialog returns it there)', () => {
226
- const onSelect = vi.fn();
227
- const el = render(StudioCanvasItem, {
228
- label: 'Question 1',
229
- onSelect,
230
- onInsert: () => { },
231
- children: html('<p>Body</p>'),
232
- });
233
- el.querySelector('button[aria-label="Add below Question 1"]').focus();
234
- flushSync();
235
- expect(onSelect).not.toHaveBeenCalled();
236
- el.querySelector('button').focus(); // the "Edit Question 1" button
237
- flushSync();
238
- expect(onSelect).toHaveBeenCalledTimes(1);
239
- });
240
- });