Skip to content

Add uniform interactionPolicy support to interactive primitives #64

Description

@artiphishle

Goal

Introduce one public cooperative interaction contract for Surface-owned interactive primitives:

export type InteractionPolicy = 'enabled' | 'passive';

export interface InteractionPolicyProps {
  interactionPolicy?: InteractionPolicy;
}

enabled is the default. Omitting the prop must behave exactly like interactionPolicy="enabled".

This issue provides the lower-level behavior required by Studio Edit mode without introducing any Studio-specific concepts into Surface.

Semantics

enabled

Normal authored application behavior.

passive

Preserve:

  • layout and dimensions
  • authored appearance
  • component identity
  • current controlled or uncontrolled value/state
  • structural scrolling

Suppress user-triggered authored behavior owned by the primitive:

  • press activation
  • long press
  • text/value mutation
  • overlay dismissal
  • manipulation gestures owned by the primitive

Do not implement passive mode using disabled={true}. Passive mode must not automatically introduce disabled styling or disabled accessibility state.

Scope

Inspect actual ownership and update only Surface components that own interaction or expose an interactive wrapper. Expected candidates include the source-confirmed equivalents of:

  • ButtonBase
  • Button / IconButton primitives when owned by Surface
  • interactive Card paths
  • TextInput / Textarea primitives
  • Modal / Drawer dismissal primitives
  • Checkbox / Switch / Radio where present

Structural ScrollView/ScrollArea behavior must remain active in passive mode.

Export InteractionPolicy and InteractionPolicyProps from the public Surface entry point so ZORA can reuse the same type without duplication.

Implementation rules

  • exactly two values: enabled | passive
  • no edit, preview, studio, or editor value
  • no deprecated alias
  • no legacy adapter
  • no compatibility shim
  • no dual old/new implementation path
  • no version-detection branch
  • no TODO fallback that keeps obsolete behavior alive
  • no dependency on Runtime, ZORA, or Studio
  • no unknown prop leakage to native React Native or DOM hosts
  • preserve explicitly authored stricter behavior, e.g. editable={false} remains false in enabled mode

For text inputs, guard accepted mutation callbacks in addition to native editability flags so raw Android events cannot change accepted value in passive mode.

Tests

Cover at minimum:

  • omitted policy equals enabled
  • explicit enabled behavior
  • passive press suppression
  • passive long-press suppression
  • no disabled styling/state introduced solely by passive mode
  • passive text mutation guard
  • enabled text restoration
  • passive overlay dismissal suppression
  • current value/state preservation
  • structural scrolling remains available where testable
  • public type exports

Run package tests, type checks, lint, and build according to repository conventions.

Delivery workflow

  1. Start from current origin/main.
  2. Create a dedicated branch after this issue exists:
issue-<THIS_ISSUE_NUMBER>/interaction-policy
  1. Never implement directly on main.
  2. Commit only files belonging to this issue.
  3. Add the required changeset according to repository conventions.
  4. Push the branch to origin even before a PR is opened.
  5. Do not open or merge a PR automatically. PR creation requires explicit approval after cross-repository integration with ZORA and Studio passes.

Do not use git add ., git add -A, or git commit -a.

Acceptance criteria

  • Surface exports the single shared policy type.
  • All source-confirmed Surface interaction owners enforce passive behavior.
  • Existing consumers are unchanged when the prop is omitted.
  • No disabled visual/accessibility semantics are introduced by passive mode.
  • No legacy, deprecation, or compatibility code is added.
  • Tests, type checks, lint, and build pass.
  • A dedicated issue branch is committed and pushed.
  • No PR is opened without explicit approval.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions