@syntrologie/adapt-overlays 2.2.0-canary.9 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/highlight.d.ts.map +1 -1
  2. package/dist/highlight.js +6 -0
  3. package/dist/tooltip.d.ts.map +1 -1
  4. package/dist/tooltip.js +7 -0
  5. package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts.map +1 -1
  6. package/node_modules/@syntro/design-system/dist/tailwind-preset.js +23 -2
  7. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/BeforeAfterToggle.test.js +1 -0
  8. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.d.ts +2 -0
  9. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.d.ts.map +1 -0
  10. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ConditionStatusLine.test.js +158 -0
  11. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/DismissedSection.test.js +6 -0
  12. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorCard.test.js +1 -1
  13. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorHeader.test.js +4 -5
  14. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.d.ts +2 -0
  15. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.d.ts.map +1 -0
  16. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/EditorPanelShell.test.js +25 -0
  17. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/ElementHighlight.test.js +22 -0
  18. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/TriggerJourney.test.js +77 -14
  19. package/node_modules/@syntrologie/shared-editor-ui/dist/__tests__/formatConditionLabel.test.js +1 -1
  20. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts +1 -2
  21. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts.map +1 -1
  22. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +4 -4
  23. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.d.ts.map +1 -1
  24. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.js +4 -4
  25. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +5 -5
  26. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.js +1 -1
  27. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.d.ts.map +1 -1
  28. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.js +1 -1
  29. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.d.ts.map +1 -1
  30. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.js +10 -1
  31. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.d.ts.map +1 -1
  32. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.js +1 -1
  33. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts +1 -1
  34. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts.map +1 -1
  35. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.js +5 -2
  36. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
  37. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +4 -4
  38. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts +1 -1
  39. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts.map +1 -1
  40. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.js +5 -2
  41. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts +1 -1
  42. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts.map +1 -1
  43. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.js +6 -4
  44. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.d.ts.map +1 -1
  45. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +24 -12
  46. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +1 -1
  47. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +4 -4
  48. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +1 -1
  49. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +12 -20
  50. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +1 -1
  51. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
  52. package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -1
  53. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE,gBAAgB,GACtB,eAAe,CAmKjB"}
1
+ {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE,gBAAgB,GACtB,eAAe,CAyKjB"}
package/dist/highlight.js CHANGED
@@ -57,6 +57,12 @@ export function showHighlight(anchorEl, overlayRoot, opts) {
57
57
  scrim.style.webkitClipPath = path;
58
58
  };
59
59
  const update = () => {
60
+ // Guard: if anchor was removed from DOM (e.g. React Router navigation),
61
+ // destroy the overlay gracefully instead of positioning at (0,0).
62
+ if (!anchorEl.isConnected) {
63
+ handle.destroy();
64
+ return;
65
+ }
60
66
  const rect = anchorEl.getBoundingClientRect();
61
67
  const x = Math.max(0, rect.left - padding);
62
68
  const y = Math.max(0, rect.top - padding);
@@ -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,CA+Of"}
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,CAsPf"}
package/dist/tooltip.js CHANGED
@@ -102,9 +102,16 @@ export function showTooltip(anchorEl, overlayRoot, opts) {
102
102
  ];
103
103
  const placement = opts.placement && opts.placement !== 'auto' ? opts.placement : 'top';
104
104
  const cleanup = autoUpdate(anchorEl, div, async () => {
105
+ // Guard: if anchor was removed from DOM (e.g. React Router navigation),
106
+ // destroy the overlay gracefully instead of positioning at stale coords.
107
+ if (!anchorEl.isConnected) {
108
+ handle.destroy();
109
+ return;
110
+ }
105
111
  const currentAnchorRef = getAnchorReference(anchorEl);
106
112
  const result = await computePosition(currentAnchorRef, div, {
107
113
  placement,
114
+ strategy: 'fixed',
108
115
  middleware,
109
116
  });
110
117
  const { x, y, strategy, middlewareData, placement: finalPlacement } = result;
@@ -1 +1 @@
1
- {"version":3,"file":"tailwind-preset.d.ts","sourceRoot":"","sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,CA2bxC,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"tailwind-preset.d.ts","sourceRoot":"","sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,MAAM,CAgdxC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -147,29 +147,34 @@ export const syntroPreset = {
147
147
  DEFAULT: colors.pink[4],
148
148
  },
149
149
  // Semantic tokens
150
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
150
151
  text: {
151
152
  primary: colors.text.primary,
152
153
  secondary: colors.text.secondary,
153
154
  tertiary: colors.text.tertiary,
154
155
  DEFAULT: colors.text.primary,
155
156
  },
157
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
156
158
  background: {
157
159
  primary: colors.background.primary,
158
160
  secondary: colors.background.secondary,
159
161
  DEFAULT: colors.background.primary,
160
162
  },
163
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
161
164
  border: {
162
165
  primary: colors.border.primary,
163
166
  secondary: colors.border.secondary,
164
167
  DEFAULT: colors.border.primary,
165
168
  },
166
169
  // Component tokens - Button
170
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
167
171
  'btn-primary': {
168
172
  DEFAULT: colors.button.primary.backgroundDefault,
169
173
  hover: colors.button.primary.backgroundHover,
170
174
  text: colors.button.primary.text,
171
175
  border: colors.button.primary.border,
172
176
  },
177
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
173
178
  'btn-neutral': {
174
179
  DEFAULT: colors.button.neutral.background,
175
180
  text: colors.button.neutral.text,
@@ -189,12 +194,14 @@ export const syntroPreset = {
189
194
  hover: colors.button.success.hover,
190
195
  },
191
196
  // Component tokens - Card
197
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
192
198
  card: {
193
199
  bg: colors.card.background,
194
200
  content: colors.card.content,
195
201
  border: colors.card.border,
196
202
  },
197
203
  // Component tokens - Modal
204
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
198
205
  modal: {
199
206
  bg: colors.modal.background,
200
207
  content: colors.modal.content,
@@ -211,6 +218,7 @@ export const syntroPreset = {
211
218
  'content-tertiary': colors.sidebar.contentTertiary,
212
219
  },
213
220
  // Component tokens - Tab
221
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
214
222
  tab: {
215
223
  'active-bg': colors.tab.activeBackground,
216
224
  'active-content': colors.tab.activeContent,
@@ -218,15 +226,17 @@ export const syntroPreset = {
218
226
  border: colors.tab.border,
219
227
  },
220
228
  // Component tokens - Table
229
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
221
230
  'table-header': {
222
231
  text: colors.table.header.textDefault,
223
232
  'text-hover': colors.table.header.textHover,
224
233
  bg: colors.table.header.backgroundDefault,
225
234
  },
235
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
226
236
  'table-cell': {
227
- // biome-ignore lint/plugin: These define semantic token names, not usage of shadcn color classes
237
+ // biome-ignore lint: Tailwind preset defines semantic token names, not usage of shadcn color classes
228
238
  'text-primary': colors.table.cell.textPrimary,
229
- // biome-ignore lint/plugin: These define semantic token names, not usage of shadcn color classes
239
+ // biome-ignore lint: Tailwind preset defines semantic token names, not usage of shadcn color classes
230
240
  'text-secondary': colors.table.cell.textSecondary,
231
241
  bg: colors.table.cell.backgroundDefault,
232
242
  'bg-hover': colors.table.cell.backgroundHover,
@@ -245,54 +255,63 @@ export const syntroPreset = {
245
255
  'border-error': colors.inputField.borderError,
246
256
  },
247
257
  // Component tokens - Badge (9 variants)
258
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
248
259
  'badge-slate': {
249
260
  content: colors.badge.slateGrey.content,
250
261
  outline: colors.badge.slateGrey.pillOutline,
251
262
  border: colors.badge.slateGrey.borderPrimary,
252
263
  bg: colors.badge.slateGrey.background,
253
264
  },
265
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
254
266
  'badge-brand': {
255
267
  content: colors.badge.brand.content,
256
268
  outline: colors.badge.brand.pillOutline,
257
269
  border: colors.badge.brand.borderPrimary,
258
270
  bg: colors.badge.brand.background,
259
271
  },
272
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
260
273
  'badge-green': {
261
274
  content: colors.badge.green.content,
262
275
  outline: colors.badge.green.pillOutline,
263
276
  border: colors.badge.green.borderPrimary,
264
277
  bg: colors.badge.green.background,
265
278
  },
279
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
266
280
  'badge-yellow': {
267
281
  content: colors.badge.yellow.content,
268
282
  outline: colors.badge.yellow.pillOutline,
269
283
  border: colors.badge.yellow.borderPrimary,
270
284
  bg: colors.badge.yellow.background,
271
285
  },
286
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
272
287
  'badge-red': {
273
288
  content: colors.badge.red.content,
274
289
  outline: colors.badge.red.pillOutline,
275
290
  border: colors.badge.red.borderPrimary,
276
291
  bg: colors.badge.red.background,
277
292
  },
293
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
278
294
  'badge-blue': {
279
295
  content: colors.badge.blue.content,
280
296
  outline: colors.badge.blue.pillOutline,
281
297
  border: colors.badge.blue.borderPrimary,
282
298
  bg: colors.badge.blue.background,
283
299
  },
300
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
284
301
  'badge-purple': {
285
302
  content: colors.badge.purple.content,
286
303
  outline: colors.badge.purple.pillOutline,
287
304
  border: colors.badge.purple.borderPrimary,
288
305
  bg: colors.badge.purple.background,
289
306
  },
307
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
290
308
  'badge-orange': {
291
309
  content: colors.badge.orange.content,
292
310
  outline: colors.badge.orange.pillOutline,
293
311
  border: colors.badge.orange.borderPrimary,
294
312
  bg: colors.badge.orange.background,
295
313
  },
314
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
296
315
  'badge-pink': {
297
316
  content: colors.badge.pink.content,
298
317
  outline: colors.badge.pink.pillOutline,
@@ -309,6 +328,7 @@ export const syntroPreset = {
309
328
  'on-bg-hover': colors.toggle.on.backgroundHover,
310
329
  },
311
330
  // Component tokens - Menu
331
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
312
332
  menu: {
313
333
  DEFAULT: colors.menu.backgroundDefault,
314
334
  hover: colors.menu.backgroundHover,
@@ -380,6 +400,7 @@ export const syntroPreset = {
380
400
  'focus-error': focusRings.error,
381
401
  },
382
402
  // Backdrop blur — Figma scale
403
+ // biome-ignore lint: Tailwind preset intentionally maps design tokens from source
383
404
  backdropBlur: {
384
405
  sm: backdropBlur.sm,
385
406
  md: backdropBlur.md,
@@ -23,6 +23,7 @@ describe('BeforeAfterToggle', () => {
23
23
  it('highlights active mode with blue class', () => {
24
24
  const { getByText } = render(_jsx(BeforeAfterToggle, { mode: "before", onToggle: () => { } }));
25
25
  expect(getByText('Before').className).toContain('se-text-blue-5');
26
+ expect(getByText('Before').className).toContain('se-bg-blue-5/30');
26
27
  expect(getByText('After').className).toContain('se-text-text-secondary');
27
28
  });
28
29
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ConditionStatusLine.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConditionStatusLine.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ConditionStatusLine.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,158 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { ConditionStatusLine } from '../components/ConditionStatusLine';
5
+ describe('ConditionStatusLine', () => {
6
+ it('returns null when status is null', () => {
7
+ const { container } = render(_jsx(ConditionStatusLine, { status: null }));
8
+ expect(container.innerHTML).toBe('');
9
+ });
10
+ it('renders single condition inline', () => {
11
+ const status = {
12
+ visible: true,
13
+ isFallback: false,
14
+ conditions: [
15
+ {
16
+ type: 'page_url',
17
+ passed: true,
18
+ formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
19
+ },
20
+ ],
21
+ };
22
+ render(_jsx(ConditionStatusLine, { status: status }));
23
+ expect(screen.getByText('page_url:')).toBeTruthy();
24
+ expect(screen.getByText('/pricing')).toBeTruthy();
25
+ });
26
+ it('uses at least 12px font size for condition rows', () => {
27
+ const status = {
28
+ visible: true,
29
+ isFallback: false,
30
+ conditions: [
31
+ {
32
+ type: 'page_url',
33
+ passed: true,
34
+ formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
35
+ },
36
+ ],
37
+ };
38
+ const { container } = render(_jsx(ConditionStatusLine, { status: status }));
39
+ const row = container.firstElementChild;
40
+ expect(row.className).toContain('se-text-[12px]');
41
+ expect(row.className).not.toContain('se-text-[10px]');
42
+ });
43
+ it('uses secondary text color for readability', () => {
44
+ const status = {
45
+ visible: true,
46
+ isFallback: false,
47
+ conditions: [
48
+ {
49
+ type: 'page_url',
50
+ passed: true,
51
+ formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
52
+ },
53
+ ],
54
+ };
55
+ const { container } = render(_jsx(ConditionStatusLine, { status: status }));
56
+ const row = container.firstElementChild;
57
+ expect(row.className).toContain('se-text-text-secondary');
58
+ });
59
+ it('uses medium font weight for small text', () => {
60
+ const status = {
61
+ visible: true,
62
+ isFallback: false,
63
+ conditions: [
64
+ {
65
+ type: 'page_url',
66
+ passed: true,
67
+ formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
68
+ },
69
+ ],
70
+ };
71
+ const { container } = render(_jsx(ConditionStatusLine, { status: status }));
72
+ const row = container.firstElementChild;
73
+ expect(row.className).toContain('se-font-medium');
74
+ });
75
+ it('renders multi-condition summary with expand button', () => {
76
+ const status = {
77
+ visible: false,
78
+ isFallback: false,
79
+ conditions: [
80
+ {
81
+ type: 'page_url',
82
+ passed: true,
83
+ formatted: { label: '/p', instruction: 'A', shortLabel: 'A' },
84
+ },
85
+ {
86
+ type: 'event_count',
87
+ passed: false,
88
+ formatted: { label: 'views >= 2', instruction: 'B', shortLabel: 'B' },
89
+ },
90
+ ],
91
+ };
92
+ render(_jsx(ConditionStatusLine, { status: status }));
93
+ expect(screen.getByText('1 of 2 conditions met')).toBeTruthy();
94
+ });
95
+ it('uses at least 11px for expand indicator', () => {
96
+ const status = {
97
+ visible: false,
98
+ isFallback: false,
99
+ conditions: [
100
+ {
101
+ type: 'a',
102
+ passed: true,
103
+ formatted: { label: 'a', instruction: 'A', shortLabel: 'A' },
104
+ },
105
+ {
106
+ type: 'b',
107
+ passed: false,
108
+ formatted: { label: 'b', instruction: 'B', shortLabel: 'B' },
109
+ },
110
+ ],
111
+ };
112
+ const { container } = render(_jsx(ConditionStatusLine, { status: status }));
113
+ // The expand indicator is the last span in the button
114
+ const button = container.querySelector('button');
115
+ const indicator = button.querySelector('span:last-child');
116
+ expect(indicator.className).toContain('se-text-[11px]');
117
+ expect(indicator.className).not.toContain('se-text-[8px]');
118
+ });
119
+ it('expands to show individual condition rows on click', () => {
120
+ const status = {
121
+ visible: false,
122
+ isFallback: false,
123
+ conditions: [
124
+ {
125
+ type: 'page_url',
126
+ passed: true,
127
+ formatted: { label: '/p', instruction: 'A', shortLabel: 'A' },
128
+ },
129
+ {
130
+ type: 'event_count',
131
+ passed: false,
132
+ formatted: { label: 'views >= 2', instruction: 'B', shortLabel: 'B' },
133
+ },
134
+ ],
135
+ };
136
+ render(_jsx(ConditionStatusLine, { status: status }));
137
+ fireEvent.click(screen.getByText('1 of 2 conditions met'));
138
+ expect(screen.getByText('page_url:')).toBeTruthy();
139
+ expect(screen.getByText('event_count:')).toBeTruthy();
140
+ });
141
+ it('uses secondary color (not tertiary) for condition detail labels', () => {
142
+ const status = {
143
+ visible: true,
144
+ isFallback: false,
145
+ conditions: [
146
+ {
147
+ type: 'page_url',
148
+ passed: true,
149
+ formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
150
+ },
151
+ ],
152
+ };
153
+ render(_jsx(ConditionStatusLine, { status: status }));
154
+ const label = screen.getByText('/pricing');
155
+ expect(label.className).toContain('se-text-text-secondary');
156
+ expect(label.className).not.toContain('se-text-text-tertiary');
157
+ });
158
+ });
@@ -12,6 +12,12 @@ describe('DismissedSection', () => {
12
12
  fireEvent.click(getByText('Dismissed (2)'));
13
13
  expect(queryByText('revealed')).toBeTruthy();
14
14
  });
15
+ it('uses secondary text color for readability', () => {
16
+ const { getByText } = render(_jsx(DismissedSection, { count: 2, children: _jsx("span", { children: "child" }) }));
17
+ const button = getByText('Dismissed (2)').closest('[role="button"]');
18
+ expect(button.className).toContain('se-text-text-secondary');
19
+ expect(button.className).not.toContain('se-text-text-tertiary');
20
+ });
15
21
  it('collapses again on second click', () => {
16
22
  const { getByText, queryByText } = render(_jsx(DismissedSection, { count: 1, children: _jsx("span", { children: "content" }) }));
17
23
  fireEvent.click(getByText('Dismissed (1)'));
@@ -15,7 +15,7 @@ describe('EditorCard', () => {
15
15
  it('applies green border class when validated is true', () => {
16
16
  const { container } = render(_jsx(EditorCard, { itemKey: "k", validated: true, children: "child" }));
17
17
  const card = container.firstElementChild;
18
- expect(card.className).toContain('se-border-green-4/40');
18
+ expect(card.className).toContain('se-border-green-4/50');
19
19
  });
20
20
  it('applies default border class when validated is false', () => {
21
21
  const { container } = render(_jsx(EditorCard, { itemKey: "k", validated: false, children: "child" }));
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { fireEvent, render } from '@testing-library/react';
2
+ import { render } from '@testing-library/react';
3
3
  import { describe, expect, it, vi } from 'vitest';
4
4
  import { EditorHeader } from '../components/EditorHeader';
5
5
  describe('EditorHeader', () => {
@@ -15,10 +15,9 @@ describe('EditorHeader', () => {
15
15
  const { queryByText } = render(_jsx(EditorHeader, { title: "T", onBack: () => { } }));
16
16
  expect(queryByText('Sub')).toBeNull();
17
17
  });
18
- it('calls onBack when back button is clicked', () => {
18
+ it('accepts deprecated onBack prop without rendering a back button', () => {
19
19
  const onBack = vi.fn();
20
- const { getByText } = render(_jsx(EditorHeader, { title: "T", onBack: onBack }));
21
- fireEvent.click(getByText('← Back'));
22
- expect(onBack).toHaveBeenCalledOnce();
20
+ const { queryByText } = render(_jsx(EditorHeader, { title: "T", onBack: onBack }));
21
+ expect(queryByText('← Back')).toBeNull();
23
22
  });
24
23
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=EditorPanelShell.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorPanelShell.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/EditorPanelShell.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from '@testing-library/react';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import { EditorPanelShell } from '../components/EditorPanelShell';
5
+ // Mock ResizeObserver and pointer capture for jsdom
6
+ vi.stubGlobal('ResizeObserver', class {
7
+ observe() { }
8
+ unobserve() { }
9
+ disconnect() { }
10
+ });
11
+ Element.prototype.setPointerCapture = vi.fn();
12
+ Element.prototype.releasePointerCapture = vi.fn();
13
+ describe('EditorPanelShell', () => {
14
+ it('applies antialiased font smoothing to the panel', () => {
15
+ const { container } = render(_jsx(EditorPanelShell, { isOpen: true, onToggle: () => { }, position: "right", children: _jsx("div", { children: "Content" }) }));
16
+ const panel = container.querySelector('[data-syntro-editor-panel]');
17
+ expect(panel).toBeTruthy();
18
+ expect(panel.className).toContain('se-antialiased');
19
+ });
20
+ it('does not render panel when closed', () => {
21
+ const { container } = render(_jsx(EditorPanelShell, { isOpen: false, onToggle: () => { }, children: _jsx("div", { children: "Content" }) }));
22
+ const panel = container.querySelector('[data-syntro-editor-panel]');
23
+ expect(panel).toBeNull();
24
+ });
25
+ });
@@ -92,6 +92,28 @@ describe('ElementHighlight', () => {
92
92
  expect(label.textContent).toContain('300');
93
93
  expect(label.textContent).toContain('150');
94
94
  });
95
+ it('uses at least 11px font for highlight labels', () => {
96
+ render(_jsx(ElementHighlight, { element: mockElement, color: "rgba(99,102,241,0.6)", showDimensions: true }));
97
+ const label = document.body.querySelector('[data-syntro-highlight-label]');
98
+ expect(label).toBeTruthy();
99
+ // showDimensions uses the smaller size (11px); non-dimensions uses 12px
100
+ expect(label.style.fontSize).toBe('11px');
101
+ });
102
+ it('uses 12px font for non-dimensions labels', () => {
103
+ render(_jsx(ElementHighlight, { element: mockElement, color: "#ef4444", label: "Test Label" }));
104
+ const label = document.body.querySelector('[data-syntro-highlight-label]');
105
+ expect(label).toBeTruthy();
106
+ expect(label.style.fontSize).toBe('12px');
107
+ });
108
+ it('uses 12px font for remove button', () => {
109
+ const onRemove = vi.fn();
110
+ render(_jsx(ElementHighlight, { element: mockElement, color: "#ef4444", label: "Test", showRemove: true, onRemove: onRemove }));
111
+ const removeBtn = document.body.querySelector('[data-syntro-highlight-remove]');
112
+ expect(removeBtn).toBeTruthy();
113
+ expect(removeBtn.style.fontSize).toBe('12px');
114
+ expect(removeBtn.style.width).toBe('16px');
115
+ expect(removeBtn.style.height).toBe('16px');
116
+ });
95
117
  it('does not show remove button when showRemove is false', () => {
96
118
  render(_jsx(ElementHighlight, { element: mockElement, color: "#3b82f6", label: "Test" }));
97
119
  const removeBtn = document.body.querySelector('[data-syntro-highlight-remove]');
@@ -1,26 +1,28 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { describe, it, expect } from 'vitest';
3
2
  import { render, screen } from '@testing-library/react';
3
+ import { describe, expect, it } from 'vitest';
4
4
  import { TriggerJourney } from '../components/TriggerJourney';
5
5
  describe('TriggerJourney', () => {
6
- it('returns null when status is null', () => {
7
- const { container } = render(_jsx(TriggerJourney, { status: null }));
8
- expect(container.innerHTML).toBe('');
6
+ it('renders "Always Present" when status is null', () => {
7
+ render(_jsx(TriggerJourney, { status: null }));
8
+ expect(screen.getByText('Always Present')).toBeTruthy();
9
9
  });
10
- it('returns null when conditions array is empty', () => {
10
+ it('renders "Always Present" when conditions array is empty', () => {
11
11
  const status = { visible: true, isFallback: true, conditions: [] };
12
- const { container } = render(_jsx(TriggerJourney, { status: status }));
13
- expect(container.innerHTML).toBe('');
12
+ render(_jsx(TriggerJourney, { status: status }));
13
+ expect(screen.getByText('Always Present')).toBeTruthy();
14
14
  });
15
15
  it('renders a single node for one condition', () => {
16
16
  const status = {
17
17
  visible: true,
18
18
  isFallback: false,
19
- conditions: [{
19
+ conditions: [
20
+ {
20
21
  type: 'page_url',
21
22
  passed: true,
22
23
  formatted: { label: '/pricing', instruction: 'Visit /pricing', shortLabel: '/pricing' },
23
- }],
24
+ },
25
+ ],
24
26
  };
25
27
  render(_jsx(TriggerJourney, { status: status }));
26
28
  expect(screen.getByText('/pricing')).toBeTruthy();
@@ -33,7 +35,11 @@ describe('TriggerJourney', () => {
33
35
  {
34
36
  type: 'page_url',
35
37
  passed: true,
36
- formatted: { label: '/fine-arts/', instruction: 'Visit a /fine-arts/ page', shortLabel: '/fine-arts/' },
38
+ formatted: {
39
+ label: '/fine-arts/',
40
+ instruction: 'Visit a /fine-arts/ page',
41
+ shortLabel: '/fine-arts/',
42
+ },
37
43
  },
38
44
  {
39
45
  type: 'event_count',
@@ -57,7 +63,8 @@ describe('TriggerJourney', () => {
57
63
  const status = {
58
64
  visible: false,
59
65
  isFallback: false,
60
- conditions: [{
66
+ conditions: [
67
+ {
61
68
  type: 'event_count',
62
69
  passed: false,
63
70
  formatted: {
@@ -66,7 +73,8 @@ describe('TriggerJourney', () => {
66
73
  shortLabel: '3+ clicks',
67
74
  progress: { current: 1, target: 3, operator: 'gte' },
68
75
  },
69
- }],
76
+ },
77
+ ],
70
78
  };
71
79
  render(_jsx(TriggerJourney, { status: status }));
72
80
  expect(screen.getByText('1/3')).toBeTruthy();
@@ -109,15 +117,70 @@ describe('TriggerJourney', () => {
109
117
  render(_jsx(TriggerJourney, { status: status }));
110
118
  expect(screen.queryByText('All conditions met')).toBeNull();
111
119
  });
120
+ it('uses secondary text color and medium weight for node labels', () => {
121
+ const status = {
122
+ visible: false,
123
+ isFallback: false,
124
+ conditions: [
125
+ {
126
+ type: 'page_url',
127
+ passed: false,
128
+ formatted: { label: '/p', instruction: 'Visit pricing', shortLabel: '/pricing' },
129
+ },
130
+ ],
131
+ };
132
+ render(_jsx(TriggerJourney, { status: status }));
133
+ const label = screen.getByText('/pricing');
134
+ expect(label.className).toContain('se-text-text-secondary');
135
+ expect(label.className).not.toContain('se-text-text-tertiary');
136
+ expect(label.className).toContain('se-font-medium');
137
+ });
138
+ it('uses at least 11px font size for node labels', () => {
139
+ const status = {
140
+ visible: false,
141
+ isFallback: false,
142
+ conditions: [
143
+ {
144
+ type: 'page_url',
145
+ passed: false,
146
+ formatted: { label: '/p', instruction: 'Visit pricing', shortLabel: '/pricing' },
147
+ },
148
+ ],
149
+ };
150
+ render(_jsx(TriggerJourney, { status: status }));
151
+ const label = screen.getByText('/pricing');
152
+ expect(label.className).toContain('se-text-[11px]');
153
+ });
154
+ it('uses secondary text color for "Always Present" label', () => {
155
+ render(_jsx(TriggerJourney, { status: null }));
156
+ const label = screen.getByText('Always Present');
157
+ expect(label.className).toContain('se-text-text-secondary');
158
+ expect(label.className).not.toContain('se-text-text-tertiary');
159
+ });
160
+ it('uses at least 12px for "All conditions met" text', () => {
161
+ const status = {
162
+ visible: true,
163
+ isFallback: false,
164
+ conditions: [
165
+ { type: 'a', passed: true, formatted: { label: 'a', instruction: 'A', shortLabel: 'A' } },
166
+ { type: 'b', passed: true, formatted: { label: 'b', instruction: 'B', shortLabel: 'B' } },
167
+ ],
168
+ };
169
+ const { container } = render(_jsx(TriggerJourney, { status: status }));
170
+ const badge = screen.getByText('All conditions met').closest('div');
171
+ expect(badge.className).toContain('se-text-[12px]');
172
+ });
112
173
  it('uses instruction as title attribute for tooltip', () => {
113
174
  const status = {
114
175
  visible: false,
115
176
  isFallback: false,
116
- conditions: [{
177
+ conditions: [
178
+ {
117
179
  type: 'page_url',
118
180
  passed: false,
119
181
  formatted: { label: '/p', instruction: 'Visit /pricing page', shortLabel: '/pricing' },
120
- }],
182
+ },
183
+ ],
121
184
  };
122
185
  render(_jsx(TriggerJourney, { status: status }));
123
186
  const label = screen.getByText('/pricing');
@@ -1,4 +1,4 @@
1
- import { describe, it, expect } from 'vitest';
1
+ import { describe, expect, it } from 'vitest';
2
2
  import { formatConditionLabel } from '../formatConditionLabel';
3
3
  describe('formatConditionLabel', () => {
4
4
  it('event_count: returns label with progress data', () => {