@syntrologie/adapt-content 2.4.0 → 2.5.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.
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +59 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +67 -12
- package/dist/summarize.d.ts.map +1 -1
- package/dist/summarize.js +12 -1
- package/dist/types.d.ts +9 -42
- package/dist/types.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/AnchorPicker.test.js +224 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.js +102 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DetectionBadge.test.js +58 -6
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.js +18 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.js +61 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.js +478 -7
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.js +54 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.js +257 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/useTriggerWhenStatus.test.js +1015 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts +4 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +2 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts +2 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.js +20 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts +10 -8
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +350 -87
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts +3 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +5 -2
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts +24 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/{useShowWhenStatus.js → useTriggerWhenStatus.js} +18 -15
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +3 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -1
- package/package.json +4 -5
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +0 -26
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +0 -13
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +0 -1428
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +0 -142
- package/node_modules/@syntrologie/sdk-contracts/package.json +0 -33
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts +0 -24
- package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useShowWhenStatus.d.ts.map +0 -1
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { act, fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import { EditorPanelShell } from '../components/EditorPanelShell';
|
|
5
|
-
//
|
|
5
|
+
// jsdom does not support PointerEvent; polyfill it so fireEvent creates events
|
|
6
|
+
// with clientX/clientY/pointerId correctly.
|
|
7
|
+
if (typeof globalThis.PointerEvent === 'undefined') {
|
|
8
|
+
class PointerEventPolyfill extends MouseEvent {
|
|
9
|
+
constructor(type, init = {}) {
|
|
10
|
+
super(type, init);
|
|
11
|
+
this.pointerId = init.pointerId ?? 0;
|
|
12
|
+
this.pointerType = init.pointerType ?? '';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
vi.stubGlobal('PointerEvent', PointerEventPolyfill);
|
|
16
|
+
}
|
|
6
17
|
vi.stubGlobal('ResizeObserver', class {
|
|
7
18
|
observe() { }
|
|
8
19
|
unobserve() { }
|
|
@@ -10,16 +21,476 @@ vi.stubGlobal('ResizeObserver', class {
|
|
|
10
21
|
});
|
|
11
22
|
Element.prototype.setPointerCapture = vi.fn();
|
|
12
23
|
Element.prototype.releasePointerCapture = vi.fn();
|
|
24
|
+
// Node 25+ exposes a native `localStorage` that shadows jsdom's when
|
|
25
|
+
// `--localstorage-file` is absent. Stub with a proper in-memory Storage.
|
|
26
|
+
const storageMap = new Map();
|
|
27
|
+
vi.stubGlobal('localStorage', {
|
|
28
|
+
getItem: (key) => storageMap.get(key) ?? null,
|
|
29
|
+
setItem: (key, value) => storageMap.set(key, value),
|
|
30
|
+
removeItem: (key) => storageMap.delete(key),
|
|
31
|
+
clear: () => storageMap.clear(),
|
|
32
|
+
get length() {
|
|
33
|
+
return storageMap.size;
|
|
34
|
+
},
|
|
35
|
+
key: (index) => [...storageMap.keys()][index] ?? null,
|
|
36
|
+
});
|
|
13
37
|
describe('EditorPanelShell', () => {
|
|
14
|
-
|
|
15
|
-
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
localStorage.clear();
|
|
40
|
+
// Set default viewport dimensions
|
|
41
|
+
Object.defineProperty(window, 'innerWidth', { value: 1280, writable: true });
|
|
42
|
+
Object.defineProperty(window, 'innerHeight', { value: 800, writable: true });
|
|
43
|
+
});
|
|
44
|
+
it('renders panel docked left by default', () => {
|
|
45
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
16
46
|
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
17
47
|
expect(panel).toBeTruthy();
|
|
18
|
-
expect(panel.
|
|
48
|
+
expect(panel.style.left).toBe('0px');
|
|
49
|
+
expect(panel.style.top).toBe('0px');
|
|
50
|
+
});
|
|
51
|
+
it('renders panel with minimum width of 480px', () => {
|
|
52
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
53
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
54
|
+
expect(panel.style.width).toBe('480px');
|
|
55
|
+
});
|
|
56
|
+
it('renders FAB when panel is open', () => {
|
|
57
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
58
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
59
|
+
expect(fab).toBeTruthy();
|
|
19
60
|
});
|
|
20
|
-
it('
|
|
61
|
+
it('renders FAB when panel is closed (minimized)', () => {
|
|
21
62
|
const { container } = render(_jsx(EditorPanelShell, { isOpen: false, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
63
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
64
|
+
expect(fab).toBeTruthy();
|
|
22
65
|
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
23
66
|
expect(panel).toBeNull();
|
|
24
67
|
});
|
|
68
|
+
it('renders 8 resize handles when panel is open', () => {
|
|
69
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
70
|
+
const handles = container.querySelectorAll('[data-syntro-editor-ui^="resize-"]');
|
|
71
|
+
expect(handles.length).toBe(8);
|
|
72
|
+
});
|
|
73
|
+
it('does not render resize handles when closed', () => {
|
|
74
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: false, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
75
|
+
const handles = container.querySelectorAll('[data-syntro-editor-ui^="resize-"]');
|
|
76
|
+
expect(handles.length).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
it('saves geometry to localStorage on mount', () => {
|
|
79
|
+
render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
80
|
+
const stored = localStorage.getItem('syntro:editor-panel');
|
|
81
|
+
expect(stored).toBeTruthy();
|
|
82
|
+
const geo = JSON.parse(stored);
|
|
83
|
+
expect(geo.width).toBe(480);
|
|
84
|
+
expect(geo.docked).toBe('left');
|
|
85
|
+
});
|
|
86
|
+
it('loads geometry from localStorage', () => {
|
|
87
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 600, height: 500, docked: null }));
|
|
88
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
89
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
90
|
+
expect(panel.style.left).toBe('100px');
|
|
91
|
+
expect(panel.style.top).toBe('50px');
|
|
92
|
+
expect(panel.style.width).toBe('600px');
|
|
93
|
+
});
|
|
94
|
+
it('applies antialiased font smoothing', () => {
|
|
95
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
96
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
97
|
+
expect(panel.className).toContain('se-antialiased');
|
|
98
|
+
});
|
|
99
|
+
it('applies rounded corners when undocked', () => {
|
|
100
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 600, height: 500, docked: null }));
|
|
101
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
102
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
103
|
+
expect(panel.className).toContain('se-rounded-lg');
|
|
104
|
+
});
|
|
105
|
+
it('applies edge border when docked left', () => {
|
|
106
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
107
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
108
|
+
expect(panel.className).toContain('se-border-r');
|
|
109
|
+
});
|
|
110
|
+
it('uses namespaced localStorage key when panelId is provided', () => {
|
|
111
|
+
render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, panelId: "my-panel", children: _jsx("div", { children: "Content" }) }));
|
|
112
|
+
const stored = localStorage.getItem('syntro:editor-panel:my-panel');
|
|
113
|
+
expect(stored).toBeTruthy();
|
|
114
|
+
const geo = JSON.parse(stored);
|
|
115
|
+
expect(geo.width).toBe(480);
|
|
116
|
+
expect(geo.docked).toBe('left');
|
|
117
|
+
});
|
|
118
|
+
it('loads geometry from namespaced localStorage key when panelId is provided', () => {
|
|
119
|
+
localStorage.setItem('syntro:editor-panel:my-panel', JSON.stringify({ x: 200, y: 75, width: 700, height: 550, docked: null }));
|
|
120
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, panelId: "my-panel", children: _jsx("div", { children: "Content" }) }));
|
|
121
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
122
|
+
expect(panel.style.left).toBe('200px');
|
|
123
|
+
expect(panel.style.top).toBe('75px');
|
|
124
|
+
expect(panel.style.width).toBe('700px');
|
|
125
|
+
});
|
|
126
|
+
it('does not use default key when panelId is provided', () => {
|
|
127
|
+
render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, panelId: "my-panel", children: _jsx("div", { children: "Content" }) }));
|
|
128
|
+
const defaultStored = localStorage.getItem('syntro:editor-panel');
|
|
129
|
+
expect(defaultStored).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
it('FAB zIndex matches panel zIndex prop', () => {
|
|
132
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, zIndex: 10000, children: _jsx("div", { children: "Content" }) }));
|
|
133
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
134
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
135
|
+
expect(fab.style.zIndex).toBe('10000');
|
|
136
|
+
expect(panel.style.zIndex).toBe('10000');
|
|
137
|
+
});
|
|
138
|
+
it('resize handles have data-resize-dir attributes', () => {
|
|
139
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
140
|
+
const handles = container.querySelectorAll('[data-resize-dir]');
|
|
141
|
+
expect(handles.length).toBe(8);
|
|
142
|
+
const dirs = Array.from(handles).map((h) => h.dataset.resizeDir);
|
|
143
|
+
expect(dirs).toContain('n');
|
|
144
|
+
expect(dirs).toContain('se');
|
|
145
|
+
expect(dirs).toContain('nw');
|
|
146
|
+
});
|
|
147
|
+
it('renders docked right with left border and correct position', () => {
|
|
148
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 0, y: 0, width: 500, height: 800, docked: 'right' }));
|
|
149
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
150
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
151
|
+
expect(panel.className).toContain('se-border-l');
|
|
152
|
+
expect(panel.style.right).toBe('0px');
|
|
153
|
+
});
|
|
154
|
+
it('gracefully handles corrupt localStorage data', () => {
|
|
155
|
+
localStorage.setItem('syntro:editor-panel', 'not-valid-json');
|
|
156
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
157
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
158
|
+
// Falls back to default geometry
|
|
159
|
+
expect(panel).toBeTruthy();
|
|
160
|
+
expect(panel.style.width).toBe('480px');
|
|
161
|
+
});
|
|
162
|
+
it('enforces minimum width when localStorage has smaller value', () => {
|
|
163
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 0, y: 0, width: 100, height: 200, docked: null }));
|
|
164
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
165
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
166
|
+
// Width should be clamped to MIN_WIDTH (480)
|
|
167
|
+
expect(panel.style.width).toBe('480px');
|
|
168
|
+
});
|
|
169
|
+
it('enforces minimum height when localStorage has smaller value', () => {
|
|
170
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 0, y: 0, width: 600, height: 100, docked: null }));
|
|
171
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
172
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
173
|
+
// Height should be clamped to MIN_HEIGHT (400)
|
|
174
|
+
expect(panel.style.height).toBe('400px');
|
|
175
|
+
});
|
|
176
|
+
it('handles localStorage data with missing fields by using defaults', () => {
|
|
177
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ docked: null }));
|
|
178
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
179
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
180
|
+
expect(panel).toBeTruthy();
|
|
181
|
+
// x/y should default to 0, width to MIN_WIDTH, height to max(MIN_HEIGHT, innerHeight)
|
|
182
|
+
expect(panel.style.left).toBe('0px');
|
|
183
|
+
expect(panel.style.top).toBe('0px');
|
|
184
|
+
expect(panel.style.width).toBe('480px');
|
|
185
|
+
});
|
|
186
|
+
describe('FAB drag handlers', () => {
|
|
187
|
+
it('calls onToggle on FAB click (pointerDown + pointerUp without move)', () => {
|
|
188
|
+
const onToggle = vi.fn();
|
|
189
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: onToggle, children: _jsx("div", { children: "Content" }) }));
|
|
190
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
191
|
+
fireEvent.pointerDown(fab, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
192
|
+
fireEvent.pointerUp(fab, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
193
|
+
expect(onToggle).toHaveBeenCalledTimes(1);
|
|
194
|
+
});
|
|
195
|
+
it('does not call onToggle when FAB is dragged beyond threshold', () => {
|
|
196
|
+
const onToggle = vi.fn();
|
|
197
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: onToggle, children: _jsx("div", { children: "Content" }) }));
|
|
198
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
199
|
+
fireEvent.pointerDown(fab, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
200
|
+
// Move beyond DRAG_THRESHOLD (5px)
|
|
201
|
+
fireEvent.pointerMove(fab, { clientX: 200, clientY: 200, pointerId: 1 });
|
|
202
|
+
fireEvent.pointerUp(fab, { clientX: 200, clientY: 200, pointerId: 1 });
|
|
203
|
+
expect(onToggle).not.toHaveBeenCalled();
|
|
204
|
+
});
|
|
205
|
+
it('does not count as drag when movement is below threshold', () => {
|
|
206
|
+
const onToggle = vi.fn();
|
|
207
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: onToggle, children: _jsx("div", { children: "Content" }) }));
|
|
208
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
209
|
+
fireEvent.pointerDown(fab, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
210
|
+
// Move less than DRAG_THRESHOLD (5px)
|
|
211
|
+
fireEvent.pointerMove(fab, { clientX: 102, clientY: 102, pointerId: 1 });
|
|
212
|
+
fireEvent.pointerUp(fab, { clientX: 102, clientY: 102, pointerId: 1 });
|
|
213
|
+
// Still treated as click
|
|
214
|
+
expect(onToggle).toHaveBeenCalledTimes(1);
|
|
215
|
+
});
|
|
216
|
+
it('ignores pointerMove when not dragging', () => {
|
|
217
|
+
const onToggle = vi.fn();
|
|
218
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: onToggle, children: _jsx("div", { children: "Content" }) }));
|
|
219
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
220
|
+
// Move without pointerDown should be ignored
|
|
221
|
+
fireEvent.pointerMove(fab, { clientX: 200, clientY: 200, pointerId: 1 });
|
|
222
|
+
// Panel should not have moved
|
|
223
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
224
|
+
expect(panel.style.left).toBe('0px');
|
|
225
|
+
});
|
|
226
|
+
it('moves panel DOM directly during drag for performance', () => {
|
|
227
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
228
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
229
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
230
|
+
fireEvent.pointerDown(fab, { clientX: 12, clientY: 12, pointerId: 1 });
|
|
231
|
+
// Move beyond threshold
|
|
232
|
+
fireEvent.pointerMove(fab, { clientX: 112, clientY: 112, pointerId: 1 });
|
|
233
|
+
// Panel DOM should be updated directly
|
|
234
|
+
expect(panel.style.left).toBe('100px');
|
|
235
|
+
expect(panel.style.top).toBe('100px');
|
|
236
|
+
expect(panel.style.right).toBe('auto');
|
|
237
|
+
});
|
|
238
|
+
it('updates FAB position during drag', () => {
|
|
239
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
240
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
241
|
+
fireEvent.pointerDown(fab, { clientX: 12, clientY: 12, pointerId: 1 });
|
|
242
|
+
fireEvent.pointerMove(fab, { clientX: 112, clientY: 112, pointerId: 1 });
|
|
243
|
+
// FAB should be positioned with FAB_INSET (12px) offset from panel top-left
|
|
244
|
+
expect(fab.style.left).toBe('112px'); // 100 + 12
|
|
245
|
+
expect(fab.style.top).toBe('112px'); // 100 + 12
|
|
246
|
+
});
|
|
247
|
+
it('snaps to left edge when dragged close to left boundary', () => {
|
|
248
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 200, y: 100, width: 500, height: 600, docked: null }));
|
|
249
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
250
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
251
|
+
// Start drag from the panel's position
|
|
252
|
+
fireEvent.pointerDown(fab, { clientX: 212, clientY: 112, pointerId: 1 });
|
|
253
|
+
// Move to near left edge (x < SNAP_THRESHOLD=20)
|
|
254
|
+
fireEvent.pointerMove(fab, { clientX: 22, clientY: 112, pointerId: 1 });
|
|
255
|
+
fireEvent.pointerUp(fab, { clientX: 22, clientY: 112, pointerId: 1 });
|
|
256
|
+
// After snap, should be docked left
|
|
257
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
258
|
+
expect(stored.docked).toBe('left');
|
|
259
|
+
});
|
|
260
|
+
it('snaps to right edge when dragged close to right boundary', () => {
|
|
261
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 200, y: 100, width: 500, height: 600, docked: null }));
|
|
262
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
263
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
264
|
+
// Start drag
|
|
265
|
+
fireEvent.pointerDown(fab, { clientX: 212, clientY: 112, pointerId: 1 });
|
|
266
|
+
// Move to near right edge (x + width >= innerWidth - SNAP_THRESHOLD)
|
|
267
|
+
// Panel width is 500, innerWidth is 1280: need x >= 1280 - 20 - 500 = 760
|
|
268
|
+
fireEvent.pointerMove(fab, { clientX: 982, clientY: 112, pointerId: 1 });
|
|
269
|
+
fireEvent.pointerUp(fab, { clientX: 982, clientY: 112, pointerId: 1 });
|
|
270
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
271
|
+
expect(stored.docked).toBe('right');
|
|
272
|
+
});
|
|
273
|
+
it('sets undocked geometry when dragged to middle of screen', () => {
|
|
274
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
275
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
276
|
+
fireEvent.pointerDown(fab, { clientX: 12, clientY: 12, pointerId: 1 });
|
|
277
|
+
// Move to center area (not near edges)
|
|
278
|
+
fireEvent.pointerMove(fab, { clientX: 312, clientY: 112, pointerId: 1 });
|
|
279
|
+
fireEvent.pointerUp(fab, { clientX: 312, clientY: 112, pointerId: 1 });
|
|
280
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
281
|
+
expect(stored.docked).toBeNull();
|
|
282
|
+
expect(stored.x).toBe(300);
|
|
283
|
+
expect(stored.y).toBe(100);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe('resize handlers', () => {
|
|
287
|
+
it('resizes panel east (wider)', () => {
|
|
288
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
289
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
290
|
+
const handle = container.querySelector('[data-resize-dir="e"]');
|
|
291
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
292
|
+
fireEvent.pointerDown(handle, { clientX: 600, clientY: 300, pointerId: 1 });
|
|
293
|
+
fireEvent.pointerMove(handle, { clientX: 700, clientY: 300, pointerId: 1 });
|
|
294
|
+
// Width should increase by 100
|
|
295
|
+
expect(panel.style.width).toBe('600px');
|
|
296
|
+
fireEvent.pointerUp(handle, { clientX: 700, clientY: 300, pointerId: 1 });
|
|
297
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
298
|
+
expect(stored.width).toBe(600);
|
|
299
|
+
});
|
|
300
|
+
it('resizes panel south (taller)', () => {
|
|
301
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
302
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
303
|
+
const handle = container.querySelector('[data-resize-dir="s"]');
|
|
304
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
305
|
+
fireEvent.pointerDown(handle, { clientX: 300, clientY: 650, pointerId: 1 });
|
|
306
|
+
fireEvent.pointerMove(handle, { clientX: 300, clientY: 750, pointerId: 1 });
|
|
307
|
+
expect(panel.style.height).toBe('700px');
|
|
308
|
+
fireEvent.pointerUp(handle, { clientX: 300, clientY: 750, pointerId: 1 });
|
|
309
|
+
});
|
|
310
|
+
it('resizes panel west (moves left edge)', () => {
|
|
311
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 200, y: 50, width: 500, height: 600, docked: null }));
|
|
312
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
313
|
+
const handle = container.querySelector('[data-resize-dir="w"]');
|
|
314
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
315
|
+
fireEvent.pointerDown(handle, { clientX: 200, clientY: 300, pointerId: 1 });
|
|
316
|
+
fireEvent.pointerMove(handle, { clientX: 100, clientY: 300, pointerId: 1 });
|
|
317
|
+
// Width increases by 100, x decreases by 100
|
|
318
|
+
expect(panel.style.width).toBe('600px');
|
|
319
|
+
expect(panel.style.left).toBe('100px');
|
|
320
|
+
fireEvent.pointerUp(handle, { clientX: 100, clientY: 300, pointerId: 1 });
|
|
321
|
+
});
|
|
322
|
+
it('resizes panel north (moves top edge)', () => {
|
|
323
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 200, width: 500, height: 600, docked: null }));
|
|
324
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
325
|
+
const handle = container.querySelector('[data-resize-dir="n"]');
|
|
326
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
327
|
+
fireEvent.pointerDown(handle, { clientX: 300, clientY: 200, pointerId: 1 });
|
|
328
|
+
fireEvent.pointerMove(handle, { clientX: 300, clientY: 100, pointerId: 1 });
|
|
329
|
+
expect(panel.style.height).toBe('700px');
|
|
330
|
+
expect(panel.style.top).toBe('100px');
|
|
331
|
+
fireEvent.pointerUp(handle, { clientX: 300, clientY: 100, pointerId: 1 });
|
|
332
|
+
});
|
|
333
|
+
it('resizes panel southeast (corner)', () => {
|
|
334
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
335
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
336
|
+
const handle = container.querySelector('[data-resize-dir="se"]');
|
|
337
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
338
|
+
fireEvent.pointerDown(handle, { clientX: 600, clientY: 650, pointerId: 1 });
|
|
339
|
+
fireEvent.pointerMove(handle, { clientX: 700, clientY: 750, pointerId: 1 });
|
|
340
|
+
expect(panel.style.width).toBe('600px');
|
|
341
|
+
expect(panel.style.height).toBe('700px');
|
|
342
|
+
fireEvent.pointerUp(handle, { clientX: 700, clientY: 750, pointerId: 1 });
|
|
343
|
+
});
|
|
344
|
+
it('resizes panel northwest (corner, moves both edges)', () => {
|
|
345
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 200, y: 200, width: 500, height: 600, docked: null }));
|
|
346
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
347
|
+
const handle = container.querySelector('[data-resize-dir="nw"]');
|
|
348
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
349
|
+
fireEvent.pointerDown(handle, { clientX: 200, clientY: 200, pointerId: 1 });
|
|
350
|
+
fireEvent.pointerMove(handle, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
351
|
+
expect(panel.style.width).toBe('600px');
|
|
352
|
+
expect(panel.style.height).toBe('700px');
|
|
353
|
+
expect(panel.style.left).toBe('100px');
|
|
354
|
+
expect(panel.style.top).toBe('100px');
|
|
355
|
+
fireEvent.pointerUp(handle, { clientX: 100, clientY: 100, pointerId: 1 });
|
|
356
|
+
});
|
|
357
|
+
it('enforces minimum width during resize', () => {
|
|
358
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
359
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
360
|
+
const handle = container.querySelector('[data-resize-dir="e"]');
|
|
361
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
362
|
+
fireEvent.pointerDown(handle, { clientX: 600, clientY: 300, pointerId: 1 });
|
|
363
|
+
// Try to shrink below MIN_WIDTH (480)
|
|
364
|
+
fireEvent.pointerMove(handle, { clientX: 200, clientY: 300, pointerId: 1 });
|
|
365
|
+
expect(panel.style.width).toBe('480px');
|
|
366
|
+
fireEvent.pointerUp(handle, { clientX: 200, clientY: 300, pointerId: 1 });
|
|
367
|
+
});
|
|
368
|
+
it('enforces minimum height during resize', () => {
|
|
369
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
370
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
371
|
+
const handle = container.querySelector('[data-resize-dir="s"]');
|
|
372
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
373
|
+
fireEvent.pointerDown(handle, { clientX: 300, clientY: 650, pointerId: 1 });
|
|
374
|
+
// Try to shrink below MIN_HEIGHT (400)
|
|
375
|
+
fireEvent.pointerMove(handle, { clientX: 300, clientY: 100, pointerId: 1 });
|
|
376
|
+
expect(panel.style.height).toBe('400px');
|
|
377
|
+
fireEvent.pointerUp(handle, { clientX: 300, clientY: 100, pointerId: 1 });
|
|
378
|
+
});
|
|
379
|
+
it('undocks panel when resize is started on a docked panel', () => {
|
|
380
|
+
// Start with docked left
|
|
381
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
382
|
+
const handle = container.querySelector('[data-resize-dir="e"]');
|
|
383
|
+
fireEvent.pointerDown(handle, { clientX: 480, clientY: 300, pointerId: 1 });
|
|
384
|
+
// After starting resize on docked panel, it should undock
|
|
385
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
386
|
+
expect(stored.docked).toBeNull();
|
|
387
|
+
});
|
|
388
|
+
it('ignores pointerMove when no resize is active', () => {
|
|
389
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
390
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
391
|
+
const handle = container.querySelector('[data-resize-dir="e"]');
|
|
392
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
393
|
+
// Move without pointerDown
|
|
394
|
+
fireEvent.pointerMove(handle, { clientX: 700, clientY: 300, pointerId: 1 });
|
|
395
|
+
// Width should not change
|
|
396
|
+
expect(panel.style.width).toBe('500px');
|
|
397
|
+
});
|
|
398
|
+
it('updates FAB position during resize', () => {
|
|
399
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
400
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
401
|
+
const handle = container.querySelector('[data-resize-dir="s"]');
|
|
402
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
403
|
+
fireEvent.pointerDown(handle, { clientX: 300, clientY: 650, pointerId: 1 });
|
|
404
|
+
fireEvent.pointerMove(handle, { clientX: 300, clientY: 750, pointerId: 1 });
|
|
405
|
+
// FAB top should stay at top-left of panel during resize
|
|
406
|
+
// FAB top = y + FAB_INSET = 50 + 12 = 62
|
|
407
|
+
expect(fab.style.top).toBe('62px');
|
|
408
|
+
// FAB left = x + FAB_INSET = 100 + 12 = 112
|
|
409
|
+
expect(fab.style.left).toBe('112px');
|
|
410
|
+
fireEvent.pointerUp(handle, { clientX: 300, clientY: 750, pointerId: 1 });
|
|
411
|
+
});
|
|
412
|
+
it('does not commit geometry on pointerUp when no pending geo exists', () => {
|
|
413
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 50, width: 500, height: 600, docked: null }));
|
|
414
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
415
|
+
const handle = container.querySelector('[data-resize-dir="e"]');
|
|
416
|
+
// pointerDown then immediate pointerUp without pointerMove
|
|
417
|
+
fireEvent.pointerDown(handle, { clientX: 600, clientY: 300, pointerId: 1 });
|
|
418
|
+
fireEvent.pointerUp(handle, { clientX: 600, clientY: 300, pointerId: 1 });
|
|
419
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
420
|
+
// Should remain undocked with original dimensions (but undocked since it was docked)
|
|
421
|
+
expect(stored.docked).toBeNull();
|
|
422
|
+
});
|
|
423
|
+
it('resizes northeast corner correctly', () => {
|
|
424
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 100, y: 200, width: 500, height: 600, docked: null }));
|
|
425
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
426
|
+
const handle = container.querySelector('[data-resize-dir="ne"]');
|
|
427
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
428
|
+
fireEvent.pointerDown(handle, { clientX: 600, clientY: 200, pointerId: 1 });
|
|
429
|
+
fireEvent.pointerMove(handle, { clientX: 700, clientY: 100, pointerId: 1 });
|
|
430
|
+
// East: width increases by 100
|
|
431
|
+
expect(panel.style.width).toBe('600px');
|
|
432
|
+
// North: height increases by 100, top decreases by 100
|
|
433
|
+
expect(panel.style.height).toBe('700px');
|
|
434
|
+
expect(panel.style.top).toBe('100px');
|
|
435
|
+
fireEvent.pointerUp(handle, { clientX: 700, clientY: 100, pointerId: 1 });
|
|
436
|
+
});
|
|
437
|
+
it('resizes southwest corner correctly', () => {
|
|
438
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 200, y: 50, width: 500, height: 600, docked: null }));
|
|
439
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
440
|
+
const handle = container.querySelector('[data-resize-dir="sw"]');
|
|
441
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
442
|
+
fireEvent.pointerDown(handle, { clientX: 200, clientY: 650, pointerId: 1 });
|
|
443
|
+
fireEvent.pointerMove(handle, { clientX: 100, clientY: 750, pointerId: 1 });
|
|
444
|
+
// West: width increases by 100, left decreases by 100
|
|
445
|
+
expect(panel.style.width).toBe('600px');
|
|
446
|
+
expect(panel.style.left).toBe('100px');
|
|
447
|
+
// South: height increases by 100
|
|
448
|
+
expect(panel.style.height).toBe('700px');
|
|
449
|
+
fireEvent.pointerUp(handle, { clientX: 100, clientY: 750, pointerId: 1 });
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
describe('viewport clamping', () => {
|
|
453
|
+
it('clamps panel position on window resize', () => {
|
|
454
|
+
localStorage.setItem('syntro:editor-panel', JSON.stringify({ x: 1200, y: 700, width: 500, height: 600, docked: null }));
|
|
455
|
+
render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
456
|
+
// Shrink viewport
|
|
457
|
+
Object.defineProperty(window, 'innerWidth', { value: 800, writable: true });
|
|
458
|
+
Object.defineProperty(window, 'innerHeight', { value: 600, writable: true });
|
|
459
|
+
act(() => {
|
|
460
|
+
window.dispatchEvent(new Event('resize'));
|
|
461
|
+
});
|
|
462
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
463
|
+
// x should be clamped so FAB stays visible
|
|
464
|
+
expect(stored.x).toBeLessThanOrEqual(800 - 56); // vw - FAB_SIZE
|
|
465
|
+
});
|
|
466
|
+
it('does not clamp docked panels on window resize', () => {
|
|
467
|
+
// Docked panels ignore clampToViewport
|
|
468
|
+
render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
469
|
+
act(() => {
|
|
470
|
+
window.dispatchEvent(new Event('resize'));
|
|
471
|
+
});
|
|
472
|
+
const stored = JSON.parse(localStorage.getItem('syntro:editor-panel'));
|
|
473
|
+
expect(stored.docked).toBe('left');
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
it('sets id attribute on panel when panelId is provided', () => {
|
|
477
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, panelId: "test-panel", children: _jsx("div", { children: "Content" }) }));
|
|
478
|
+
const panel = container.querySelector('#test-panel');
|
|
479
|
+
expect(panel).toBeTruthy();
|
|
480
|
+
});
|
|
481
|
+
it('does not set id attribute on panel when panelId is not provided', () => {
|
|
482
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
483
|
+
const panel = container.querySelector('[data-syntro-editor-panel]');
|
|
484
|
+
expect(panel.id).toBe('');
|
|
485
|
+
});
|
|
486
|
+
it('FAB title is "Minimize panel" when open', () => {
|
|
487
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
488
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
489
|
+
expect(fab.title).toBe('Minimize panel');
|
|
490
|
+
});
|
|
491
|
+
it('FAB title is "Open panel" when closed', () => {
|
|
492
|
+
const { container } = render(_jsx(EditorPanelShell, { isOpen: false, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
|
|
493
|
+
const fab = container.querySelector('[data-syntro-fab]');
|
|
494
|
+
expect(fab.title).toBe('Open panel');
|
|
495
|
+
});
|
|
25
496
|
});
|
|
@@ -119,4 +119,58 @@ describe('ElementHighlight', () => {
|
|
|
119
119
|
const removeBtn = document.body.querySelector('[data-syntro-highlight-remove]');
|
|
120
120
|
expect(removeBtn).toBeNull();
|
|
121
121
|
});
|
|
122
|
+
it('calls onClick when Enter key is pressed', () => {
|
|
123
|
+
const onClick = vi.fn();
|
|
124
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: onClick }));
|
|
125
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
126
|
+
fireEvent.keyDown(overlay, { key: 'Enter' });
|
|
127
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
128
|
+
});
|
|
129
|
+
it('calls onClick when Space key is pressed', () => {
|
|
130
|
+
const onClick = vi.fn();
|
|
131
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: onClick }));
|
|
132
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
133
|
+
fireEvent.keyDown(overlay, { key: ' ' });
|
|
134
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
135
|
+
});
|
|
136
|
+
it('does not fire onClick on other key presses', () => {
|
|
137
|
+
const onClick = vi.fn();
|
|
138
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: onClick }));
|
|
139
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
140
|
+
fireEvent.keyDown(overlay, { key: 'Tab' });
|
|
141
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
it('does not attach onKeyDown when onClick is not provided', () => {
|
|
144
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6" }));
|
|
145
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
146
|
+
// Should not throw and should have no role/tabIndex
|
|
147
|
+
expect(overlay.getAttribute('role')).toBeNull();
|
|
148
|
+
expect(overlay.getAttribute('tabindex')).toBeNull();
|
|
149
|
+
});
|
|
150
|
+
it('has role="button" and tabIndex when onClick is provided', () => {
|
|
151
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: () => { } }));
|
|
152
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
153
|
+
expect(overlay.getAttribute('role')).toBe('button');
|
|
154
|
+
expect(overlay.getAttribute('tabindex')).toBe('0');
|
|
155
|
+
});
|
|
156
|
+
it('sets pointer cursor when onClick is provided', () => {
|
|
157
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: () => { } }));
|
|
158
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
159
|
+
expect(overlay.style.cursor).toBe('pointer');
|
|
160
|
+
});
|
|
161
|
+
it('sets default cursor when onClick is not provided', () => {
|
|
162
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6" }));
|
|
163
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
164
|
+
expect(overlay.style.cursor).toBe('default');
|
|
165
|
+
});
|
|
166
|
+
it('sets pointerEvents to auto when onClick is provided', () => {
|
|
167
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", onClick: () => { } }));
|
|
168
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
169
|
+
expect(overlay.style.pointerEvents).toBe('auto');
|
|
170
|
+
});
|
|
171
|
+
it('sets pointerEvents to none when no onClick or onRemove', () => {
|
|
172
|
+
render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6" }));
|
|
173
|
+
const overlay = document.body.querySelector('[data-syntro-highlight]');
|
|
174
|
+
expect(overlay.style.pointerEvents).toBe('none');
|
|
175
|
+
});
|
|
122
176
|
});
|
package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/selectorGenerator.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectorGenerator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/selectorGenerator.test.ts"],"names":[],"mappings":""}
|