Skip to content
death.tech home
All recent changes

Scroll long customer search results, and keep the dialog usable on short screens

054a6f9fix(orders)merged 3d ago1 review thread

Illustrative: based on a real change, edited for clarity and privacy.

src/domains/orders/components/customer-picker-dialog.tsx+13 −4

Changes to src/domains/orders/components/customer-picker-dialog.tsx. Lines with review comments are numbered.
@@ export function CustomerSearchDialog({ onCloseAction, onSelectAction }: {
3030 const [isSearching, startTransition] = useTransition()
3131 const inputRef = useRef<HTMLInputElement>(null)
3232 const queryRef = useRef('')
33added const rowRefs = useRef<(HTMLButtonElement | null)[]>([])
3334
3435 useEffect(() => {
3536 inputRef.current?.focus()
@@ export function CustomerSearchDialog({ onCloseAction, onSelectAction }: {
6364
6465 const focused = Math.min(focusIndex, Math.max(results.length - 1, 0))
6566
67added // Arrow keys can move the highlight past the visible rows; keep it in view.
68added useEffect(() => {
69added rowRefs.current[focused]?.scrollIntoView({ block: 'nearest' })
70added }, [focused, results])
71added
6672 function handleKeyDown(e: React.KeyboardEvent) {
6773 if (e.key === 'Escape') {
6874 onCloseAction()
@@ export function CustomerSearchDialog({ onCloseAction, onSelectAction }: {
8793 role="dialog"
8894 aria-modal="true"
8995 aria-label="Select customer"
90removed className="w-full max-w-[560px] overflow-hidden rounded-xl bg-card shadow-lg"
1 line 96, review thread 1added className="flex max-h-full w-full max-w-[560px] flex-col overflow-y-auto overscroll-contain rounded-xl bg-card shadow-lg"
9197 onClick={(e) => e.stopPropagation()}
9298 onKeyDown={handleKeyDown}
9399 >
94removed <div className="flex items-center justify-between px-6 pb-4 pt-5">
100added <div className="flex shrink-0 items-center justify-between px-6 pb-4 pt-5">
95101 <h2 className="text-base font-semibold text-primary">Select customer</h2>
96102 <button type="button" onClick={onCloseAction} aria-label="Close">
97103 <X size={16} strokeWidth={2} />
98104 </button>
99105 </div>
100removed <div className="px-6 pb-4">
106added <div className="shrink-0 px-6 pb-4">
101107 <div className="relative">
102108 <Search size={15} className="absolute left-3.5 top-1/2 -translate-y-1/2" />
103109 <input
@@ export function CustomerSearchDialog({ onCloseAction, onSelectAction }: {
124130 )}
125131 {showResults && (
126132 <>
127removed <ul className="border-t">
133added <ul className="min-h-24 flex-1 overflow-y-auto overscroll-contain border-t">
128134 {results.map((customer, i) => (
129135 <li key={customer.ref} className={i > 0 ? 'border-t' : ''}>
130136 <button
137added ref={(el) => {
138added rowRefs.current[i] = el
139added }}
131140 type="button"
132141 onMouseEnter={() => setFocusIndex(i)}
133142 onClick={() => onSelectAction({ name: customer.name, ref: customer.ref })}
    1. reviewer

      Tried this on my phone with the keyboard up: only the results scroll, and the “create a new customer” link disappears off the bottom. Can the dialog itself scroll?

    2. author

      Fixed: the dialog now scrolls when its content is taller than the space available, and the results list keeps a min-h-24 floor plus its own scroll. The no-matches message and the create link stay reachable.

    3. AI reviewP1

      Height cap hardcodes spacing

      The height calculation uses a literal 1.5rem offset. The repo’s rules say spacing comes from design tokens, not inline values.

    4. author

      The calc is gone. The overlay already pads the viewport, so the dialog uses max-h-full against the overlay’s padded box.

We’d like to use PostHog to see how this site gets used. It sets cookies, so it stays off until you allow it; everything else works without. Privacy notice