fix(studio): make canvas selection hit intended elements (#1907)

* test(studio): add design-panel QA fixture and triage matrix

Fixture project covering all panel-editable element archetypes,
plus the QA findings matrix from the design-panel bug campaign.

* fix(studio): make canvas selection hit intended elements

- honor author pointer-events:none in hit-testing (was selecting invisible overlays)
- pause playback before mousedown sampling; fall back to hover selection on null resolve
- invalidate committed selection when the active composition changes
- double-click keeps selection and defers to multi-candidate click cycling

* fix(studio): close remaining selection-layer review findings

- hoverSelection fallback now wired at all 3 mousedown call sites (box-click,
  blocked-drag, plain overlay click) instead of just the overlay path
- pointer-events override detection reads computed style, not inline style,
  so a CSS-class opt-in (not just inline style=) on a descendant is honored
- defensively remove the pointer-events override before the group-fallback
  check too, closing a theoretical gap in the no-elementsFromPoint branch
- a click that resolves to nothing (dead-zone / deselect) no longer leaves
  playback paused if it was already playing
This commit is contained in:
Miguel Ángel
2026-07-03 17:41:01 -07:00
committed by GitHub
parent 4d199c0f3a
commit 02e9d6142d
11 changed files with 831 additions and 52 deletions
@@ -0,0 +1,40 @@
// Shared harness helpers for selection hook tests (useDomSelection,
// usePreviewInteraction). Test-only module.
import type { DomEditSelection } from "../components/editor/domEditing";
export function installReactActEnvironment(): void {
Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
value: true,
configurable: true,
});
}
export function makeSelection(label: string, element: HTMLElement): DomEditSelection {
return {
element,
id: element.id || undefined,
selector: `#${element.id || label}`,
selectorIndex: 0,
sourceFile: "index.html",
compositionPath: "index.html",
label,
tagName: element.tagName.toLowerCase(),
isCompositionHost: false,
isInsideLockedComposition: false,
boundingBox: { x: 0, y: 0, width: 100, height: 40 },
textContent: element.textContent,
dataAttributes: {},
inlineStyles: {},
computedStyles: {},
textFields: [],
capabilities: {
canSelect: true,
canEditStyles: true,
canMove: true,
canResize: true,
canApplyManualOffset: true,
canApplyManualSize: true,
canApplyManualRotation: false,
},
};
}