Skip to main content

TicketDragDrop

Implemented

Two-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.

Categoryorganisms
Sourcecomponents/organisms/TicketDragDrop/TicketDragDrop.tsx
AI HintUse for any admin form that composes a package/pass/coupon from ticket types. Drop-in replacement for the storefront TicketPicker when drag parity is wanted. The renderIncludedTrailing render-prop attaches a per-row trailing control (e.g. a discount input) to every INCLUDED row — the trailing is a sibling of the row, never inside the row button, so an input can be embedded without violating the no-input-in-button rule.

Two-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.

Available tickets
Festival nightJun 12, 8:00 PM
WorkshopJun 13, 10:00 AM
Tickets included in this package
Festival nightJun 12, 8:00 PM
  • GA$30.00

Drag or click to move tickets.

Props

Preview

TicketDragDrop

Prop
Type
Required
Default
Description
groups
TicketDragDropGroup[]
Required
—

Ticket types grouped by event

selectedIds
string[]
Required
—

Included ticket-type ids (controlled)

onChange
(ids: string[]) => void
Required
—

Emits the next selected-id array

disabled
boolean
Optional
false

Disables rows, drags, and quick actions

renderIncludedTrailing
(ticketId: string) => React.ReactNode
Optional
—

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.

labels
TicketDragDropLabels
Required
—

All visible strings — the DS ships no literals

Quick use:<TicketDragDrop />

Accessibility

ARIA rolegroup

ARIA

2 rules

WCAG 2.1.1

Every row is a real button — Tab/Enter/Space move it (WCAG 2.1.1)

WCAG 4.1.3

Moves announced through aria-live=polite (WCAG 4.1.3)

a11y

1 rule

WCAG 2.5.5

Rows and quick actions >= --ep-tap-min (44px)

Usage

Import

tsxSnippet
import { TicketDragDrop } from "@epasslive/design-system"

Example 1

tsxSnippet
<TicketDragDrop groups={groups} selectedIds={ids} onChange={setIds} labels={labels} />

Example 2

tsxSnippet
<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

Design System v4.1.0 | ePassLive