You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
66 lines
7.3 KiB
66 lines
7.3 KiB
# Audit: drag-drop
|
|
audit-version: 1
|
|
audited-at: 2026-06-26
|
|
scope: (SCOPE-DRIFT → SYS-1)
|
|
method: adversarially-verified workflow; HIGH lead-verified. B7 ground-truth: Toolbar A35 + Form A36 incidents STAYED FIXED (untrack present); toast/clipboard/drag-drop use uix.timers (no A6 leak); button is clean (its archetype finding was inert — Spinner not runtime-registered); data-size/data-shape are eidos visual attrs (not contract violations).
|
|
provider: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts
|
|
cleanup-audit (A6/A35/A36): TIMERS/LISTENERS:
|
|
1. setupPointerDrag() — lines 261-294:
|
|
- this.soma.dom.listen(win, 'pointermove', onPointerMove) → cleanupPointerMove [DISPOSED]
|
|
- this.soma.dom.listen(win, 'pointerup', onPointerUp) → cleanupPointerUp [DISPOSED]
|
|
- this.soma.dom.listen(win, 'pointercancel', onPoint
|
|
|
|
## Summary
|
|
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 3 · LOW 0.
|
|
|
|
## Findings
|
|
### MEDIUM: SYS-1 SCOPE-DRIFT — drag-drop-001 <!-- id: drag-drop-001 -->
|
|
- dimension: D
|
|
- rule: SYS-1 SCOPE-DRIFT
|
|
- location: src/uix/morfo/components/drag-drop.ts:7, src/uix/eidos/components/drag-drop/
|
|
- evidence: Morfo declares scope: ['soma', 'sema'] (line 7) but eidos component directory exists at src/uix/eidos/components/drag-drop/ with drag-drop.svelte, drag-drop.css, and sub-components (draggable, droppable, preview). The recipe dir exists but morfo scope omits 'eidos'.
|
|
- impact: Framework scope validation may flag this as inconsistent; eidos layer is present but not declared in morfo scope.
|
|
- proposed-fix: Update morfo scope declaration to scope: ['soma', 'sema', 'eidos'] to match the actual component hierarchy.
|
|
- verify: [confirmed] CONFIRMED. morfo declares scope: ['soma', 'sema'] (drag-drop.ts:7) yet a full eidos implementation exists: recipe entry 'drag-drop': { 'preview-z': '99' } (recipes/base.ts:4259-4261), drag-drop.css, and 3 svelte wrappers (draggable/droppable/preview). The Morfo.scope doctrine (morfo/types.ts:798) reads 'Layers that implement this component' and 'eidos' is a valid Layer (schema.ts:49: union(literal('soma'), literal('sema'), literal('eidos'))). Omitting 'eidos' is genuine scope-drift = SYS-1 baseline MEDIUM. Note: many siblings (calendar/carousel/color-field/combobox/command) share the same drift, consistent with SYS-1 being systemic.
|
|
- fix-status: fixed (212624e0)
|
|
|
|
### MEDIUM: SYS-2 MAGIC Z-INDEX — drag-drop-002 <!-- id: drag-drop-002 -->
|
|
- dimension: B
|
|
- rule: SYS-2 MAGIC Z-INDEX
|
|
- location: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts:656, 664
|
|
- evidence: DragPreviewProvider.style $derived.by inlines 'z-index': '9999' (lines 656, 664) as a hardcoded literal in the style object. The eidos CSS correctly uses var(--drag-drop-preview-z) (drag-drop.css:97) which resolves to 99 per generated base.css:3400, but the inline style hardcodes 9999 instead.
|
|
- impact: z-index value is 9999 (extremely high, above all canonical layers) instead of 99 (--drag-drop-preview-z). This violates the canonical --z-index-* scale and may unexpectedly layer above modals (--z-index-modal: 700) and toast (--z-index-toast: 900).
|
|
- proposed-fix: Replace hardcoded '9999' with 'var(--drag-drop-preview-z)' to consume the canonical token, e.g., 'z-index': 'var(--drag-drop-preview-z)'.
|
|
- verify: [downgraded] DIVERGENCE CONFIRMED, severity DOWNGRADED HIGH->MEDIUM. DragPreviewProvider.style $derived.by hardcodes 'z-index': '9999' on both branches (drag-drop-provider.svelte.ts:656 and :664). The CSS rule [data-drag-drop-preview] uses z-index: var(--drag-drop-preview-z) (drag-drop.css:97) which resolves to 99 (generated/base.css:3400). Because the provider sets z-index inline via props.style, inline precedence wins, so effective z is 9999 and the recipe token is inert. 9999 is off the canonical scale entirely (--z-index-base 0 .. --z-index-toast 900; generated/base.css:304-311) and exceeds modal (700) and toast (900). This is a magic-literal that overrides its own dedicated token (SYS-2 magic-z), NOT a syncAttrs double-write (no data-attr involved). Per rubric magic-literal == MEDIUM and SYS-2 is baselined MEDIUM, so HIGH is unwarranted.
|
|
- fix-status: open
|
|
|
|
### MEDIUM: Test coverage for high-risk paths — drag-drop-003 <!-- id: drag-drop-003 -->
|
|
- dimension: F
|
|
- rule: Test coverage for high-risk paths
|
|
- location: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts:126-306
|
|
- evidence: Test file contains 2 test cases: (1) keyboard drag start/navigation/commit, (2) drag prevention, accept filters, cancel, pointer preview. Missing explicit coverage for: pointer drag initiation and movement (setupPointerDrag), pending pointer-down state promotion (DraggableProvider.onpointerdown moveBuffer threshold), pointer listener cleanup on premature cancellation, and live-region disposal on component unmount.
|
|
- impact: Pointer drag logic and pending state lifecycle are not directly tested; test env is jsdom-only. A/B testing (live-region dual-region toggle) is not verified.
|
|
- proposed-fix: Add test case for pointer drag from pointerdown to pointermove threshold to active drag promotion; verify cleanup on pointercancel; add test for announcer timer cancellation on provider cleanup.
|
|
- verify: [confirmed] CONFIRMED. Test file has exactly 2 it() cases (drag-drop-provider.svelte.test.ts:132 keyboard drag; :213 prevention/accept/cancel/preview). Both drive the flow by calling provider.startDrag(...) directly (e.g. lines 243-250, 256-264 with keyboard:false) rather than dispatching real pointerdown -> pointermove -> moveBuffer promotion (DraggableProvider.onpointerdown at :417-457, threshold Math.hypot < moveBuffer at :432). The setupPointerDrag window-listener flow (:261-294) and teardown/listener-cleanup (:245-257) are not exercised via real events. The preview-style assertion (:267-275 objectContaining) does NOT assert z-index, so the 9999 hardcode is also untested. jsdom-only env = SYS-3. MEDIUM stands.
|
|
- fix-status: open
|
|
|
|
## No-findings dimensions
|
|
A, C
|
|
|
|
## Theming facts (E-bis)
|
|
- magic z-index: src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts:656,664 — hardcoded '9999' instead of var(--drag-drop-preview-z)
|
|
- magic literals: none
|
|
- undeclared parts: none
|
|
- roles clean: true · variants clean: true
|
|
|
|
## Tests (F)
|
|
- exists: true · env: jsdom
|
|
- covers: keyboard drag start/activate via Space/Enter; keyboard navigation (ArrowDown/ArrowRight, ArrowUp/ArrowLeft, Tab/Shift+Tab); keyboard drop (Enter/Space on overTarget); keyboard cancel (Escape); onDragStart preventDefault block; accept filter evaluation; pointer drag cancel on invalid drop; pointer preview style positioning (fixed layout, offset from pointer)
|
|
- untested: pointer drag initiation (onpointerdown → pending state); pointer drag promotion (moveBuffer threshold exceeded); pointer listener cleanup on premature cancel; keyboard listener cleanup/disposal; announcer timer cleanup on provider dispose; live-region dual-region A/B toggle announce pattern
|
|
|
|
## Style observations (non-blocking)
|
|
- Draggable cursor transitions from grab → grabbing on :active, correct interaction feedback
|
|
- Droppable uses inset box-shadow rings for accept/dragover states, consistent with design tokens
|
|
- Preview uses --opacity-ghost (opacity-disabled), --space-* spacing, --radius-md, and role aliases correctly
|
|
- Color cascade via data-color attribute in eidos component correctly routes to --_drag-drop-accent-soft and --_drag-drop-accent-solid variables
|