TicketDragDrop
ImplementedTwo-pane ticket picker with HTML5 drag-and-drop — available left, included right. Rows are draggable buttons; keyboard path is Tab/Enter/Space on the same buttons (WCAG 2.1.1); moves announced via aria-live (WCAG 4.1.3); tap targets >= --ep-tap-min (WCAG 2.5.5). Controlled contract matches TicketPicker (selectedIds in / onChange out). ADR-026.
components/organisms/TicketDragDrop/TicketDragDrop.tsxTwo-pane ticket picker with drag-and-drop + keyboard fallback (ADR-026).
TicketDragDrop
design-system
Two-pane ticket picker with HTML5 drag-and-drop — available left, included right. Rows are draggable buttons; the keyboard path is Tab/Enter/Space on the same buttons; moves are announced via aria-live.
- GA
Drag or click to move tickets.
Props
TicketDragDrop
groupsTicketDragDropGroup[]—Ticket types grouped by event
selectedIdsstring[]—Included ticket-type ids (controlled)
onChange(ids: string[]) => void—Emits the next selected-id array
disabledbooleanfalseDisables rows, drags, and quick actions
renderIncludedTrailing(ticketId: string) => React.ReactNode—Optional trailing control rendered as a sibling of an INCLUDED row (e.g. a per-ticket discount input on the package editor). Rendered outside the row button — a button may not contain an input, and a click on the control must not also remove the row or start a drag.
labelsTicketDragDropLabels—All visible strings — the DS ships no literals
<TicketDragDrop />Accessibility
ARIA
2 rulesa11y
1 ruleUsage
Import
import { TicketDragDrop } from "@epasslive/design-system"Example 1
<TicketDragDrop groups={groups} selectedIds={ids} onChange={setIds} labels={labels} />Example 2
<TicketDragDrop groups={groups} selectedIds={ids} onChange={setIds} labels={labels} renderIncludedTrailing={(ticketId) => <input type="number" value={discounts[ticketId]} onChange={(e) => setDiscounts({...discounts, [ticketId]: e.target.value})} />} />Anti-patterns
— Do not replace the buttons with divs for the drag path — the button IS the keyboard path
— Do not place the trailing control inside the row button — a button may not contain an input, and a click on the trailing would also fire the row's remove action