@syntrologie/adapt-overlays 2.4.1 → 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/WorkflowTracker.d.ts +10 -0
- package/dist/WorkflowTracker.d.ts.map +1 -0
- package/dist/WorkflowTracker.js +19 -0
- package/dist/WorkflowWidget.d.ts +70 -0
- package/dist/WorkflowWidget.d.ts.map +1 -0
- package/dist/WorkflowWidget.js +329 -0
- package/dist/cdn.d.ts +2 -2
- package/dist/celebrations/__tests__/engine.test.d.ts +2 -0
- package/dist/celebrations/__tests__/engine.test.d.ts.map +1 -0
- package/dist/celebrations/__tests__/engine.test.js +130 -0
- package/dist/celebrations/__tests__/executor.test.d.ts +2 -0
- package/dist/celebrations/__tests__/executor.test.d.ts.map +1 -0
- package/dist/celebrations/__tests__/executor.test.js +102 -0
- package/dist/celebrations/effects/__tests__/confetti.test.d.ts +2 -0
- package/dist/celebrations/effects/__tests__/confetti.test.d.ts.map +1 -0
- package/dist/celebrations/effects/__tests__/confetti.test.js +89 -0
- package/dist/celebrations/effects/__tests__/emoji-rain.test.d.ts +2 -0
- package/dist/celebrations/effects/__tests__/emoji-rain.test.d.ts.map +1 -0
- package/dist/celebrations/effects/__tests__/emoji-rain.test.js +88 -0
- package/dist/celebrations/effects/__tests__/fireworks.test.d.ts +2 -0
- package/dist/celebrations/effects/__tests__/fireworks.test.d.ts.map +1 -0
- package/dist/celebrations/effects/__tests__/fireworks.test.js +87 -0
- package/dist/celebrations/effects/__tests__/sparkles.test.d.ts +2 -0
- package/dist/celebrations/effects/__tests__/sparkles.test.d.ts.map +1 -0
- package/dist/celebrations/effects/__tests__/sparkles.test.js +79 -0
- package/dist/celebrations/effects/confetti.d.ts +3 -0
- package/dist/celebrations/effects/confetti.d.ts.map +1 -0
- package/dist/celebrations/effects/confetti.js +80 -0
- package/dist/celebrations/effects/emoji-rain.d.ts +3 -0
- package/dist/celebrations/effects/emoji-rain.d.ts.map +1 -0
- package/dist/celebrations/effects/emoji-rain.js +73 -0
- package/dist/celebrations/effects/fireworks.d.ts +3 -0
- package/dist/celebrations/effects/fireworks.d.ts.map +1 -0
- package/dist/celebrations/effects/fireworks.js +69 -0
- package/dist/celebrations/effects/sparkles.d.ts +3 -0
- package/dist/celebrations/effects/sparkles.d.ts.map +1 -0
- package/dist/celebrations/effects/sparkles.js +83 -0
- package/dist/celebrations/engine.d.ts +16 -0
- package/dist/celebrations/engine.d.ts.map +1 -0
- package/dist/celebrations/engine.js +89 -0
- package/dist/celebrations/index.d.ts +3 -0
- package/dist/celebrations/index.d.ts.map +1 -0
- package/dist/celebrations/index.js +73 -0
- package/dist/celebrations/types.d.ts +34 -0
- package/dist/celebrations/types.d.ts.map +1 -0
- package/dist/celebrations/types.js +1 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +59 -5
- package/dist/executors/tour.d.ts +20 -0
- package/dist/executors/tour.d.ts.map +1 -0
- package/dist/executors/tour.js +335 -0
- package/dist/modal.d.ts +2 -0
- package/dist/modal.d.ts.map +1 -1
- package/dist/modal.js +18 -8
- package/dist/runtime.d.ts +25 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +141 -24
- package/dist/schema.d.ts +684 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +36 -0
- package/dist/summarize.d.ts.map +1 -1
- package/dist/summarize.js +15 -4
- package/dist/tooltip.d.ts.map +1 -1
- package/dist/tooltip.js +26 -12
- package/dist/tour-types.d.ts +34 -0
- package/dist/tour-types.d.ts.map +1 -0
- package/dist/tour-types.js +7 -0
- package/dist/types.d.ts +20 -85
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow-types.d.ts +15 -0
- package/dist/workflow-types.d.ts.map +1 -0
- package/dist/workflow-types.js +1 -0
- 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 +3 -2
- 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
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Zod schema for validating overlays app configuration.
|
|
5
5
|
*/
|
|
6
|
+
import { TriggerWhenZ } from '@syntrologie/sdk-contracts';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
/**
|
|
8
9
|
* Overlays app config schema.
|
|
@@ -118,3 +119,38 @@ export const configSchema = z.object({
|
|
|
118
119
|
}))
|
|
119
120
|
.optional(),
|
|
120
121
|
});
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Celebrate Action Schema
|
|
124
|
+
// ============================================================================
|
|
125
|
+
const CelebrateActionZ = z
|
|
126
|
+
.object({
|
|
127
|
+
kind: z.literal('overlays:celebrate'),
|
|
128
|
+
effect: z.string(),
|
|
129
|
+
duration: z.number().optional(),
|
|
130
|
+
intensity: z.enum(['light', 'medium', 'heavy']).optional(),
|
|
131
|
+
colors: z.array(z.string()).optional(),
|
|
132
|
+
props: z.record(z.unknown()).optional(),
|
|
133
|
+
label: z.string().optional(),
|
|
134
|
+
triggerWhen: TriggerWhenZ,
|
|
135
|
+
})
|
|
136
|
+
.strict();
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Unified Schema Export
|
|
139
|
+
// ============================================================================
|
|
140
|
+
export const actionStepSchemas = [{ defName: 'celebrate', schema: CelebrateActionZ }];
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Tile Widget Definitions
|
|
143
|
+
// ============================================================================
|
|
144
|
+
/**
|
|
145
|
+
* Tile widget definitions for unified JSON Schema generation.
|
|
146
|
+
* Maps widget IDs to their props validation schema so the build script
|
|
147
|
+
* can inject if/then constraints on tile.props.
|
|
148
|
+
*/
|
|
149
|
+
const WorkflowTrackerPropsZ = z.object({}).passthrough();
|
|
150
|
+
export const tileWidgets = [
|
|
151
|
+
{
|
|
152
|
+
widget: 'adaptive-overlays:workflow-tracker',
|
|
153
|
+
defName: 'workflowTrackerProps',
|
|
154
|
+
propsSchema: WorkflowTrackerPropsZ,
|
|
155
|
+
},
|
|
156
|
+
];
|
package/dist/summarize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAc/C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAezD;AAYD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,cAAc,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAuCR"}
|
package/dist/summarize.js
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
* Pure functions — no DOM access, just string formatting from config data.
|
|
5
5
|
*/
|
|
6
6
|
const MAX_TEXT_LEN = 40;
|
|
7
|
+
/** Extract the CSS selector string from an anchorId (object or legacy string). */
|
|
8
|
+
function resolveAnchorSelector(anchorId) {
|
|
9
|
+
if (!anchorId)
|
|
10
|
+
return '';
|
|
11
|
+
if (typeof anchorId === 'string')
|
|
12
|
+
return anchorId;
|
|
13
|
+
if (typeof anchorId === 'object' && 'selector' in anchorId) {
|
|
14
|
+
return anchorId.selector;
|
|
15
|
+
}
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Convert a CSS selector into a human-friendly element description.
|
|
9
20
|
*/
|
|
@@ -37,25 +48,25 @@ function truncateQuoted(text, max) {
|
|
|
37
48
|
export function summarizeOverlayItem(type, item) {
|
|
38
49
|
switch (type) {
|
|
39
50
|
case 'tooltips': {
|
|
40
|
-
const anchorId = item.anchorId
|
|
51
|
+
const anchorId = resolveAnchorSelector(item.anchorId);
|
|
41
52
|
const desc = describeSelector(anchorId);
|
|
42
53
|
const content = item.content;
|
|
43
54
|
const body = content?.body || '';
|
|
44
55
|
return `Tooltip on ${desc}: ${truncateQuoted(body, MAX_TEXT_LEN)}`;
|
|
45
56
|
}
|
|
46
57
|
case 'highlights': {
|
|
47
|
-
const anchorId = item.anchorId
|
|
58
|
+
const anchorId = resolveAnchorSelector(item.anchorId);
|
|
48
59
|
const desc = describeSelector(anchorId);
|
|
49
60
|
return `Highlight ${desc}`;
|
|
50
61
|
}
|
|
51
62
|
case 'badges': {
|
|
52
|
-
const anchorId = item.anchorId
|
|
63
|
+
const anchorId = resolveAnchorSelector(item.anchorId);
|
|
53
64
|
const desc = describeSelector(anchorId);
|
|
54
65
|
const content = item.content || '';
|
|
55
66
|
return `Badge "${content}" on ${desc}`;
|
|
56
67
|
}
|
|
57
68
|
case 'pulses': {
|
|
58
|
-
const anchorId = item.anchorId
|
|
69
|
+
const anchorId = resolveAnchorSelector(item.anchorId);
|
|
59
70
|
const desc = describeSelector(anchorId);
|
|
60
71
|
return `Pulse on ${desc}`;
|
|
61
72
|
}
|
package/dist/tooltip.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAC;AAI1B,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,IAAI,IAAI,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAyCD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,cAAc,GACnB,aAAa,
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,SAAS,EAGf,MAAM,kBAAkB,CAAC;AAI1B,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,IAAI,IAAI,CAAC;IAAC,EAAE,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAyCD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,cAAc,GACnB,aAAa,CAqQf"}
|
package/dist/tooltip.js
CHANGED
|
@@ -184,26 +184,40 @@ export function showTooltip(anchorEl, overlayRoot, opts) {
|
|
|
184
184
|
}
|
|
185
185
|
const attachTrigger = () => {
|
|
186
186
|
if (opts.trigger === 'hover') {
|
|
187
|
-
|
|
187
|
+
let hideTimeout = null;
|
|
188
|
+
const show = () => {
|
|
189
|
+
if (hideTimeout) {
|
|
190
|
+
clearTimeout(hideTimeout);
|
|
191
|
+
hideTimeout = null;
|
|
192
|
+
}
|
|
188
193
|
div.style.visibility = 'visible';
|
|
189
194
|
div.style.opacity = '1';
|
|
190
195
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
const scheduleHide = () => {
|
|
197
|
+
hideTimeout = setTimeout(() => {
|
|
198
|
+
div.style.visibility = 'hidden';
|
|
199
|
+
div.style.opacity = '0';
|
|
200
|
+
hideTimeout = null;
|
|
201
|
+
}, 100);
|
|
194
202
|
};
|
|
195
203
|
div.style.visibility = 'hidden';
|
|
196
204
|
div.style.opacity = '0';
|
|
197
205
|
div.style.transition = 'opacity 200ms ease, visibility 200ms';
|
|
198
|
-
anchorEl.addEventListener('mouseenter',
|
|
199
|
-
anchorEl.addEventListener('mouseleave',
|
|
200
|
-
|
|
201
|
-
|
|
206
|
+
anchorEl.addEventListener('mouseenter', show);
|
|
207
|
+
anchorEl.addEventListener('mouseleave', scheduleHide);
|
|
208
|
+
div.addEventListener('mouseenter', show);
|
|
209
|
+
div.addEventListener('mouseleave', scheduleHide);
|
|
210
|
+
anchorEl.addEventListener('focus', show);
|
|
211
|
+
anchorEl.addEventListener('blur', scheduleHide);
|
|
202
212
|
return () => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
anchorEl.removeEventListener('
|
|
206
|
-
anchorEl.removeEventListener('
|
|
213
|
+
if (hideTimeout)
|
|
214
|
+
clearTimeout(hideTimeout);
|
|
215
|
+
anchorEl.removeEventListener('mouseenter', show);
|
|
216
|
+
anchorEl.removeEventListener('mouseleave', scheduleHide);
|
|
217
|
+
div.removeEventListener('mouseenter', show);
|
|
218
|
+
div.removeEventListener('mouseleave', scheduleHide);
|
|
219
|
+
anchorEl.removeEventListener('focus', show);
|
|
220
|
+
anchorEl.removeEventListener('blur', scheduleHide);
|
|
207
221
|
};
|
|
208
222
|
}
|
|
209
223
|
if (opts.trigger === 'click') {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tour & Workflow Types
|
|
3
|
+
*
|
|
4
|
+
* Canonical type definitions for tours and workflows.
|
|
5
|
+
* These are the source-of-truth types for the overlays adaptive.
|
|
6
|
+
*/
|
|
7
|
+
/** Tour step with route + action mapping */
|
|
8
|
+
export interface TourStep {
|
|
9
|
+
id: string;
|
|
10
|
+
action: Record<string, unknown>;
|
|
11
|
+
route?: string;
|
|
12
|
+
onAction?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
/** Orchestrate sequential steps with cross-page state */
|
|
15
|
+
export interface TourAction {
|
|
16
|
+
kind: 'core:tour';
|
|
17
|
+
label?: string;
|
|
18
|
+
tourId: string;
|
|
19
|
+
steps: TourStep[];
|
|
20
|
+
startStep?: string;
|
|
21
|
+
autoStart?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Metadata for a workflow tile displayed in the WorkflowTracker widget */
|
|
24
|
+
export interface WorkflowMeta {
|
|
25
|
+
title: string;
|
|
26
|
+
icon?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
stepTitles?: Record<string, string>;
|
|
29
|
+
notification: {
|
|
30
|
+
title: string;
|
|
31
|
+
body?: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=tour-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tour-types.d.ts","sourceRoot":"","sources":["../src/tour-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,4CAA4C;AAC5C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAMD,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH"}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,75 +5,14 @@
|
|
|
5
5
|
* These match the types exported from @syntrologie/runtime-sdk/types.
|
|
6
6
|
*/
|
|
7
7
|
import type { Placement } from '@floating-ui/dom';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
onChange: (config: Record<string, unknown>) => void;
|
|
11
|
-
editor: {
|
|
12
|
-
setDirty: (dirty: boolean) => void;
|
|
13
|
-
navigateHome: () => Promise<boolean>;
|
|
14
|
-
save: () => Promise<void>;
|
|
15
|
-
publish: (captureScreenshot?: boolean) => Promise<void>;
|
|
16
|
-
/** Navigate the target page to a different route (preserves editor params). */
|
|
17
|
-
navigateTo: (route: string) => Promise<void>;
|
|
18
|
-
/** Show a persistent blue highlight on the element matching this selector. */
|
|
19
|
-
highlightElement: (selector: string) => void;
|
|
20
|
-
/** Remove the persistent element highlight. */
|
|
21
|
-
clearHighlight: () => void;
|
|
22
|
-
/** Get the current page route (pathname). */
|
|
23
|
-
getCurrentRoute: () => string;
|
|
24
|
-
/** Push a temporary config to the live page preview without saving to state. */
|
|
25
|
-
previewConfig: (config: Record<string, unknown>) => void;
|
|
26
|
-
/** Global before/after preview mode set by the panel's toggle. */
|
|
27
|
-
previewMode?: 'before' | 'after';
|
|
28
|
-
/** Flat action index to open in edit mode (from accordion navigation). */
|
|
29
|
-
initialEditKey?: string;
|
|
30
|
-
/** Open the editor in create mode. */
|
|
31
|
-
initialCreate?: boolean;
|
|
32
|
-
/** Clear the initial navigation state (call after consuming). */
|
|
33
|
-
clearInitialState?: () => void;
|
|
34
|
-
/** Get dismissed keys persisted in navigation context. */
|
|
35
|
-
getDismissedKeys?: () => Set<string>;
|
|
36
|
-
/** Sync dismissed keys back to navigation context. */
|
|
37
|
-
setDismissedKeys?: (keys: Set<string>) => void;
|
|
38
|
-
/** Register a back handler shown in the panel header. Pass null to clear. */
|
|
39
|
-
setBackHandler?: (handler: (() => void) | null) => void;
|
|
40
|
-
};
|
|
41
|
-
platformClient?: unknown;
|
|
42
|
-
}
|
|
43
|
-
export interface HighlightStyle {
|
|
44
|
-
color?: string;
|
|
45
|
-
scrimOpacity?: number;
|
|
46
|
-
paddingPx?: number;
|
|
47
|
-
radiusPx?: number;
|
|
48
|
-
}
|
|
49
|
-
export type BadgePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
50
|
-
/** CTA button configuration for tooltips/modals */
|
|
51
|
-
export interface CtaButton {
|
|
52
|
-
label: string;
|
|
53
|
-
actionId: string;
|
|
54
|
-
primary?: boolean;
|
|
55
|
-
}
|
|
56
|
-
/** Modal content configuration */
|
|
57
|
-
export interface ModalContent {
|
|
58
|
-
title?: string;
|
|
59
|
-
body: string;
|
|
60
|
-
}
|
|
61
|
-
export interface TooltipContent {
|
|
62
|
-
title?: string;
|
|
63
|
-
body: string;
|
|
64
|
-
cta?: {
|
|
65
|
-
label: string;
|
|
66
|
-
action: unknown;
|
|
67
|
-
};
|
|
68
|
-
ctaButtons?: CtaButton[];
|
|
69
|
-
}
|
|
70
|
-
export type TooltipTrigger = 'immediate' | 'hover' | 'click';
|
|
8
|
+
import type { AnchorId, BadgePosition, CtaButton, EditorPanelProps, HighlightStyle, ModalContent, ModalWaitFor, TooltipContent, TooltipTrigger, TooltipWaitFor } from '@syntrologie/sdk-contracts';
|
|
9
|
+
export type { BadgePosition, CtaButton, EditorPanelProps, HighlightStyle, ModalContent, ModalWaitFor, TooltipContent, TooltipTrigger, TooltipWaitFor, };
|
|
71
10
|
interface BaseAction {
|
|
72
11
|
label?: string;
|
|
73
12
|
}
|
|
74
13
|
export interface HighlightAction extends BaseAction {
|
|
75
14
|
kind: 'overlays:highlight';
|
|
76
|
-
anchorId:
|
|
15
|
+
anchorId: AnchorId;
|
|
77
16
|
style?: HighlightStyle;
|
|
78
17
|
/** Whether the scrim blocks all page interaction (default: false) */
|
|
79
18
|
blocking?: boolean;
|
|
@@ -86,28 +25,24 @@ export interface HighlightAction extends BaseAction {
|
|
|
86
25
|
}
|
|
87
26
|
export interface PulseAction extends BaseAction {
|
|
88
27
|
kind: 'overlays:pulse';
|
|
89
|
-
anchorId:
|
|
28
|
+
anchorId: AnchorId;
|
|
90
29
|
duration?: number;
|
|
91
30
|
}
|
|
92
31
|
export interface BadgeAction extends BaseAction {
|
|
93
32
|
kind: 'overlays:badge';
|
|
94
|
-
anchorId:
|
|
33
|
+
anchorId: AnchorId;
|
|
95
34
|
content: string;
|
|
96
35
|
position?: BadgePosition;
|
|
97
36
|
}
|
|
98
|
-
/** Tooltip lifecycle - when the action completes (for sequences) */
|
|
99
|
-
export type TooltipWaitFor = 'dismissed' | 'cta-click' | `timeout:${number}`;
|
|
100
37
|
export interface TooltipAction extends BaseAction {
|
|
101
38
|
kind: 'overlays:tooltip';
|
|
102
|
-
anchorId:
|
|
39
|
+
anchorId: AnchorId;
|
|
103
40
|
content: TooltipContent;
|
|
104
41
|
trigger?: TooltipTrigger;
|
|
105
42
|
placement?: Placement;
|
|
106
43
|
/** When the action completes (for sequences). Publishes event to EventBus. */
|
|
107
44
|
waitFor?: TooltipWaitFor;
|
|
108
45
|
}
|
|
109
|
-
/** Modal lifecycle - when the action completes (for sequences) */
|
|
110
|
-
export type ModalWaitFor = 'dismissed' | 'cta-click' | `timeout:${number}`;
|
|
111
46
|
export interface ModalAction extends BaseAction {
|
|
112
47
|
kind: 'overlays:modal';
|
|
113
48
|
content: ModalContent;
|
|
@@ -125,20 +60,20 @@ export interface ModalAction extends BaseAction {
|
|
|
125
60
|
/** When the action completes (for sequences). Publishes event to EventBus. */
|
|
126
61
|
waitFor?: ModalWaitFor;
|
|
127
62
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
autoStart?: boolean;
|
|
63
|
+
export interface CelebrateAction extends BaseAction {
|
|
64
|
+
kind: 'overlays:celebrate';
|
|
65
|
+
effect: string;
|
|
66
|
+
duration?: number;
|
|
67
|
+
intensity?: 'light' | 'medium' | 'heavy';
|
|
68
|
+
colors?: string[];
|
|
69
|
+
props?: Record<string, unknown>;
|
|
70
|
+
activation?: {
|
|
71
|
+
routes?: {
|
|
72
|
+
include?: string[];
|
|
73
|
+
exclude?: string[];
|
|
74
|
+
};
|
|
75
|
+
};
|
|
142
76
|
}
|
|
77
|
+
export type { TourAction, TourStep, WorkflowMeta } from './tour-types';
|
|
143
78
|
export type { ActionExecutor, ExecutorCleanup, ExecutorContext, ExecutorResult, ExecutorUpdate, } from '@syntrologie/sdk-contracts';
|
|
144
79
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WorkflowMeta } from './tour-types';
|
|
2
|
+
export type { WorkflowMeta };
|
|
3
|
+
export interface WorkflowEntry {
|
|
4
|
+
tourId: string;
|
|
5
|
+
meta: WorkflowMeta;
|
|
6
|
+
steps: {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
}[];
|
|
10
|
+
currentStepId: string | null;
|
|
11
|
+
completedSteps: string[];
|
|
12
|
+
status: 'active' | 'completed' | 'dismissed';
|
|
13
|
+
completedAt?: number;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=workflow-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-types.d.ts","sourceRoot":"","sources":["../src/workflow-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnchorPicker.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/AnchorPicker.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { act, render } from '@testing-library/react';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { AnchorPicker } from '../components/AnchorPicker';
|
|
5
|
+
// Mock ResizeObserver for jsdom
|
|
6
|
+
vi.stubGlobal('ResizeObserver', class {
|
|
7
|
+
observe() { }
|
|
8
|
+
unobserve() { }
|
|
9
|
+
disconnect() { }
|
|
10
|
+
});
|
|
11
|
+
// Mock selectorGenerator utilities
|
|
12
|
+
vi.mock('../utils/selectorGenerator', () => ({
|
|
13
|
+
generateSelector: vi.fn((el) => `mock-selector-${el.tagName.toLowerCase()}`),
|
|
14
|
+
validateSelector: vi.fn(() => true),
|
|
15
|
+
getElementDescription: vi.fn((el) => `mock-desc-${el.tagName.toLowerCase()}`),
|
|
16
|
+
}));
|
|
17
|
+
import { generateSelector, getElementDescription, validateSelector, } from '../utils/selectorGenerator';
|
|
18
|
+
describe('AnchorPicker', () => {
|
|
19
|
+
let mockElementAtPoint;
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockElementAtPoint = document.createElement('div');
|
|
22
|
+
mockElementAtPoint.textContent = 'Target Element';
|
|
23
|
+
document.body.appendChild(mockElementAtPoint);
|
|
24
|
+
vi.spyOn(mockElementAtPoint, 'getBoundingClientRect').mockReturnValue({
|
|
25
|
+
top: 100,
|
|
26
|
+
left: 200,
|
|
27
|
+
width: 300,
|
|
28
|
+
height: 150,
|
|
29
|
+
bottom: 250,
|
|
30
|
+
right: 500,
|
|
31
|
+
x: 200,
|
|
32
|
+
y: 100,
|
|
33
|
+
toJSON: () => ({}),
|
|
34
|
+
});
|
|
35
|
+
vi.mocked(generateSelector).mockClear();
|
|
36
|
+
vi.mocked(validateSelector).mockClear();
|
|
37
|
+
vi.mocked(getElementDescription).mockClear();
|
|
38
|
+
vi.mocked(generateSelector).mockImplementation((el) => `mock-selector-${el.tagName.toLowerCase()}`);
|
|
39
|
+
vi.mocked(validateSelector).mockReturnValue(true);
|
|
40
|
+
vi.mocked(getElementDescription).mockImplementation((el) => `mock-desc-${el.tagName.toLowerCase()}`);
|
|
41
|
+
// Stub elementFromPoint on document (jsdom does not define it)
|
|
42
|
+
if (!document.elementFromPoint) {
|
|
43
|
+
document.elementFromPoint = vi.fn(() => null);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
vi.spyOn(document, 'elementFromPoint').mockReturnValue(null);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
if (mockElementAtPoint.parentNode) {
|
|
51
|
+
mockElementAtPoint.remove();
|
|
52
|
+
}
|
|
53
|
+
vi.restoreAllMocks();
|
|
54
|
+
});
|
|
55
|
+
it('renders nothing when isActive is false', () => {
|
|
56
|
+
render(_jsx(AnchorPicker, { isActive: false, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
57
|
+
const picker = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
58
|
+
expect(picker).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
it('renders overlay portal to document.body when active', () => {
|
|
61
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
62
|
+
const picker = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
63
|
+
expect(picker).toBeTruthy();
|
|
64
|
+
unmount();
|
|
65
|
+
});
|
|
66
|
+
it('sets crosshair cursor on overlay', () => {
|
|
67
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
68
|
+
const picker = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
69
|
+
expect(picker.style.cursor).toBe('crosshair');
|
|
70
|
+
unmount();
|
|
71
|
+
});
|
|
72
|
+
it('sets highest z-index on overlay', () => {
|
|
73
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
74
|
+
const picker = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
75
|
+
expect(picker.style.zIndex).toBe('2147483644');
|
|
76
|
+
unmount();
|
|
77
|
+
});
|
|
78
|
+
it('calls onCancel when Escape key is pressed', () => {
|
|
79
|
+
const onCancel = vi.fn();
|
|
80
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: onCancel }));
|
|
81
|
+
act(() => {
|
|
82
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
83
|
+
});
|
|
84
|
+
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
85
|
+
unmount();
|
|
86
|
+
});
|
|
87
|
+
it('does not call onCancel for non-Escape keys', () => {
|
|
88
|
+
const onCancel = vi.fn();
|
|
89
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: onCancel }));
|
|
90
|
+
act(() => {
|
|
91
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
92
|
+
});
|
|
93
|
+
expect(onCancel).not.toHaveBeenCalled();
|
|
94
|
+
unmount();
|
|
95
|
+
});
|
|
96
|
+
it('highlights element on mousemove via elementFromPoint', () => {
|
|
97
|
+
document.elementFromPoint.mockReturnValue(mockElementAtPoint);
|
|
98
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
99
|
+
act(() => {
|
|
100
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 250, clientY: 150, bubbles: true }));
|
|
101
|
+
});
|
|
102
|
+
// generateSelector should have been called with the element
|
|
103
|
+
expect(generateSelector).toHaveBeenCalledWith(mockElementAtPoint);
|
|
104
|
+
unmount();
|
|
105
|
+
});
|
|
106
|
+
it('clears hover state when elementFromPoint returns null', () => {
|
|
107
|
+
document.elementFromPoint.mockReturnValue(null);
|
|
108
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
109
|
+
act(() => {
|
|
110
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 250, clientY: 150, bubbles: true }));
|
|
111
|
+
});
|
|
112
|
+
// No highlight should be shown (just the overlay container + the semi-transparent bg div)
|
|
113
|
+
const picker = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
114
|
+
expect(picker.children.length).toBe(1);
|
|
115
|
+
unmount();
|
|
116
|
+
});
|
|
117
|
+
it('excludes editor panel elements from picking', () => {
|
|
118
|
+
const editorPanel = document.createElement('div');
|
|
119
|
+
editorPanel.setAttribute('data-syntro-editor-panel', '');
|
|
120
|
+
const innerEl = document.createElement('span');
|
|
121
|
+
editorPanel.appendChild(innerEl);
|
|
122
|
+
document.body.appendChild(editorPanel);
|
|
123
|
+
document.elementFromPoint.mockReturnValue(innerEl);
|
|
124
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
125
|
+
act(() => {
|
|
126
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 100, clientY: 100, bubbles: true }));
|
|
127
|
+
});
|
|
128
|
+
// Should not call generateSelector for excluded elements
|
|
129
|
+
expect(generateSelector).not.toHaveBeenCalled();
|
|
130
|
+
editorPanel.remove();
|
|
131
|
+
unmount();
|
|
132
|
+
});
|
|
133
|
+
it('excludes HTML, BODY, HEAD elements from picking', () => {
|
|
134
|
+
document.elementFromPoint.mockReturnValue(document.body);
|
|
135
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
136
|
+
act(() => {
|
|
137
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 100, clientY: 100, bubbles: true }));
|
|
138
|
+
});
|
|
139
|
+
expect(generateSelector).not.toHaveBeenCalled();
|
|
140
|
+
unmount();
|
|
141
|
+
});
|
|
142
|
+
it('calls onPick with valid selector on click after hover', () => {
|
|
143
|
+
const onPick = vi.fn();
|
|
144
|
+
document.elementFromPoint.mockReturnValue(mockElementAtPoint);
|
|
145
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: onPick, onCancel: vi.fn() }));
|
|
146
|
+
// First hover to set the hovered element
|
|
147
|
+
act(() => {
|
|
148
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 250, clientY: 150, bubbles: true }));
|
|
149
|
+
});
|
|
150
|
+
// Then click
|
|
151
|
+
act(() => {
|
|
152
|
+
document.dispatchEvent(new MouseEvent('click', { clientX: 250, clientY: 150, bubbles: true }));
|
|
153
|
+
});
|
|
154
|
+
expect(onPick).toHaveBeenCalledTimes(1);
|
|
155
|
+
expect(onPick).toHaveBeenCalledWith({
|
|
156
|
+
element: mockElementAtPoint,
|
|
157
|
+
selector: 'mock-selector-div',
|
|
158
|
+
description: 'mock-desc-div',
|
|
159
|
+
});
|
|
160
|
+
unmount();
|
|
161
|
+
});
|
|
162
|
+
it('regenerates selector when validation fails on click', () => {
|
|
163
|
+
const onPick = vi.fn();
|
|
164
|
+
document.elementFromPoint.mockReturnValue(mockElementAtPoint);
|
|
165
|
+
vi.mocked(validateSelector).mockReturnValue(false);
|
|
166
|
+
vi.mocked(generateSelector).mockReturnValue('regenerated-selector');
|
|
167
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: onPick, onCancel: vi.fn() }));
|
|
168
|
+
// Hover to set element
|
|
169
|
+
act(() => {
|
|
170
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 250, clientY: 150, bubbles: true }));
|
|
171
|
+
});
|
|
172
|
+
// Click
|
|
173
|
+
act(() => {
|
|
174
|
+
document.dispatchEvent(new MouseEvent('click', { clientX: 250, clientY: 150, bubbles: true }));
|
|
175
|
+
});
|
|
176
|
+
expect(onPick).toHaveBeenCalledWith(expect.objectContaining({
|
|
177
|
+
selector: 'regenerated-selector',
|
|
178
|
+
}));
|
|
179
|
+
unmount();
|
|
180
|
+
});
|
|
181
|
+
it('does not call onPick when no element is hovered on click', () => {
|
|
182
|
+
const onPick = vi.fn();
|
|
183
|
+
document.elementFromPoint.mockReturnValue(null);
|
|
184
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: onPick, onCancel: vi.fn() }));
|
|
185
|
+
act(() => {
|
|
186
|
+
document.dispatchEvent(new MouseEvent('click', { clientX: 250, clientY: 150, bubbles: true }));
|
|
187
|
+
});
|
|
188
|
+
expect(onPick).not.toHaveBeenCalled();
|
|
189
|
+
unmount();
|
|
190
|
+
});
|
|
191
|
+
it('removes event listeners when deactivated', () => {
|
|
192
|
+
const onCancel = vi.fn();
|
|
193
|
+
const { rerender, unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: onCancel }));
|
|
194
|
+
// Deactivate
|
|
195
|
+
rerender(_jsx(AnchorPicker, { isActive: false, onPick: vi.fn(), onCancel: onCancel }));
|
|
196
|
+
// Escape should not trigger onCancel after deactivation
|
|
197
|
+
act(() => {
|
|
198
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
199
|
+
});
|
|
200
|
+
expect(onCancel).not.toHaveBeenCalled();
|
|
201
|
+
unmount();
|
|
202
|
+
});
|
|
203
|
+
it('temporarily disables pointer events on overlay during mousemove detection', () => {
|
|
204
|
+
const overlayPointerEvents = [];
|
|
205
|
+
document.elementFromPoint.mockImplementation(() => {
|
|
206
|
+
// Capture overlay pointer events state during elementFromPoint call
|
|
207
|
+
const overlay = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
208
|
+
if (overlay) {
|
|
209
|
+
overlayPointerEvents.push(overlay.style.pointerEvents);
|
|
210
|
+
}
|
|
211
|
+
return mockElementAtPoint;
|
|
212
|
+
});
|
|
213
|
+
const { unmount } = render(_jsx(AnchorPicker, { isActive: true, onPick: vi.fn(), onCancel: vi.fn() }));
|
|
214
|
+
act(() => {
|
|
215
|
+
document.dispatchEvent(new MouseEvent('mousemove', { clientX: 250, clientY: 150, bubbles: true }));
|
|
216
|
+
});
|
|
217
|
+
// During elementFromPoint, overlay pointerEvents should be 'none'
|
|
218
|
+
expect(overlayPointerEvents).toContain('none');
|
|
219
|
+
// After the call, it should be restored to 'auto'
|
|
220
|
+
const overlay = document.body.querySelector('[data-syntro-anchor-picker]');
|
|
221
|
+
expect(overlay.style.pointerEvents).toBe('auto');
|
|
222
|
+
unmount();
|
|
223
|
+
});
|
|
224
|
+
});
|