mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
fix(share): the user picked to invite shows in the field before it is sent
The share dialog stored the pick as String(value) while its options are keyed by numeric user ids. CustomSelect looked the pick up with a strict comparison, found nothing and left the trigger on its placeholder, although the Invite button was already enabled and the invite went through. The phone opens the same dialog from the Mehr sheet, so it showed the same. CustomSelect now compares the held value and the option values as text, so a caller holding "5" finds the option keyed 5 and the other way round. A held null or undefined still matches nothing. onChange keeps handing back the option value unchanged. No other caller mixes the two kinds today; the place form had worked around the same trap by keying its options as strings, and its comment now says why the string stays. Tests: CustomSelect matches across both kinds and marks the pick in the list, the desktop dialog shows the picked name and clears after the invite, and the phone share sheet does the same through the real dialog. Refs #2478
This commit is contained in:
@@ -4,8 +4,8 @@ import { ChevronDown, Check } from 'lucide-react'
|
||||
import { useAnchoredPosition, scrollAnchorIntoView } from '../../hooks/useAnchoredPosition'
|
||||
|
||||
interface SelectOption {
|
||||
// Callers use both string keys and numeric ids (e.g. day/place ids) as values;
|
||||
// the component only does strict-equality lookups and key rendering, so either works.
|
||||
// Callers use both string keys and numeric ids (e.g. day/place ids) as values, and
|
||||
// the value they hold need not be of the same kind as the options: see sameValue.
|
||||
value: string | number
|
||||
label: string
|
||||
icon?: React.ReactNode
|
||||
@@ -36,6 +36,20 @@ interface CustomSelectProps {
|
||||
menuFit?: 'anchor' | 'content'
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether an option is the one the caller holds.
|
||||
*
|
||||
* onChange hands back the option's own value, and callers routinely store it in another
|
||||
* form: the trip share dialog kept String(value) against options keyed by numeric user
|
||||
* ids, so a strict comparison never found the pick and the trigger stayed on its
|
||||
* placeholder while the invite button already knew who was chosen (#2478). An id reads
|
||||
* the same as a number or as text, so both sides are compared as text. A caller holding
|
||||
* nothing (null or undefined at runtime) matches no option.
|
||||
*/
|
||||
function sameValue(held: string | number | null | undefined, option: string | number): boolean {
|
||||
return held != null && String(held) === String(option)
|
||||
}
|
||||
|
||||
/** How wide a content-fitted menu may get before it stops reading as a menu. */
|
||||
const MENU_MAX_WIDTH = 420
|
||||
|
||||
@@ -92,7 +106,7 @@ export default function CustomSelect({
|
||||
return () => document.removeEventListener('mousedown', handleClick)
|
||||
}, [open])
|
||||
|
||||
const selected = options.find(o => o.value === value)
|
||||
const selected = options.find(o => sameValue(value, o.value))
|
||||
const filtered = searchable && search
|
||||
? (() => {
|
||||
const q = search.toLowerCase()
|
||||
@@ -232,7 +246,7 @@ export default function CustomSelect({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const isSelected = option.value === value
|
||||
const isSelected = sameValue(value, option.value)
|
||||
return (
|
||||
<button
|
||||
key={option.value}
|
||||
|
||||
Reference in New Issue
Block a user