From e3146e535f232bdf0dd8cb8a0c42af1256134c7a Mon Sep 17 00:00:00 2001 From: lucaronin Date: Thu, 14 May 2026 11:21:59 +0200 Subject: [PATCH] fix: make mobile type picker searchable --- .../src/MobileEditablePropertyPickers.tsx | 21 +++++++++++-- apps/mobile/src/mobilePropertyCombo.test.ts | 16 ++++++++++ apps/mobile/src/mobilePropertyCombo.ts | 30 +++++++++++++++++++ apps/mobile/src/styles/propertyComboStyles.ts | 7 +++++ docs/MOBILE_PROGRESS.md | 9 ++++++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 apps/mobile/src/mobilePropertyCombo.test.ts create mode 100644 apps/mobile/src/mobilePropertyCombo.ts diff --git a/apps/mobile/src/MobileEditablePropertyPickers.tsx b/apps/mobile/src/MobileEditablePropertyPickers.tsx index cff4adec..04a3ccef 100644 --- a/apps/mobile/src/MobileEditablePropertyPickers.tsx +++ b/apps/mobile/src/MobileEditablePropertyPickers.tsx @@ -14,6 +14,7 @@ import { toggleMobileNoteTag, type MobileNotePropertyPatch, } from './mobileNoteProperties' +import { filterMobilePropertyComboOptions, resolveMobilePropertyComboValue } from './mobilePropertyCombo' import { mobilePropertyDisplayValue, type MobilePropertyPickerKey } from './mobilePropertyPicker' import { styles } from './styles' import { colors } from './theme' @@ -110,7 +111,12 @@ function EditableTextProperty({ variant?: 'chips' | 'combo' }) { const [draft, setDraft] = useState(value) - const commitDraft = () => commitTextValue({ current: value, next: draft, onCommit }) + const commitDraft = () => { + const next = variant === 'combo' + ? resolveMobilePropertyComboValue({ options: suggestions, value: draft }) + : draft + commitTextValue({ current: value, next, onCommit, onSettled: setDraft }) + } return ( @@ -138,6 +146,7 @@ function EditableTextProperty({ ? ( { @@ -348,17 +357,21 @@ function PropertyChipOptions({ children }: { children: ReactNode }) { function PropertyComboOptions({ disabled, onSelect, + query, suggestions, value, }: { disabled: boolean onSelect: (option: string) => void + query: string suggestions: readonly string[] value: string }) { + const options = filterMobilePropertyComboOptions({ options: suggestions, query }) + return ( - {suggestions.map((option) => { + {options.map((option) => { const isSelected = isMobileNotePropertySelected({ current: value, option }) return ( @@ -379,6 +392,7 @@ function PropertyComboOptions({ ) })} + {options.length === 0 ? No matching types. : null} ) } @@ -445,12 +459,15 @@ function commitTextValue({ current, next, onCommit, + onSettled, }: { current: string next: string onCommit: (value: string) => void + onSettled?: (value: string) => void }) { const normalized = next.trim() + onSettled?.(normalized) if (normalized !== current) { onCommit(normalized) } diff --git a/apps/mobile/src/mobilePropertyCombo.test.ts b/apps/mobile/src/mobilePropertyCombo.test.ts new file mode 100644 index 00000000..cae55df6 --- /dev/null +++ b/apps/mobile/src/mobilePropertyCombo.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' +import { filterMobilePropertyComboOptions, resolveMobilePropertyComboValue } from './mobilePropertyCombo' + +describe('mobile property combo', () => { + const options = ['Note', 'Essay', 'Project', 'Evergreen'] + + it('filters options from typed text', () => { + expect(filterMobilePropertyComboOptions({ options, query: 'ess' })).toEqual(['Essay']) + expect(filterMobilePropertyComboOptions({ options, query: 'e' })).toEqual(['Note', 'Essay', 'Project', 'Evergreen']) + }) + + it('normalizes exact typed selections to the canonical option label', () => { + expect(resolveMobilePropertyComboValue({ options, value: ' essay ' })).toBe('Essay') + expect(resolveMobilePropertyComboValue({ options, value: 'Custom' })).toBe('Custom') + }) +}) diff --git a/apps/mobile/src/mobilePropertyCombo.ts b/apps/mobile/src/mobilePropertyCombo.ts new file mode 100644 index 00000000..0f15887f --- /dev/null +++ b/apps/mobile/src/mobilePropertyCombo.ts @@ -0,0 +1,30 @@ +export function filterMobilePropertyComboOptions({ + options, + query, +}: { + options: readonly string[] + query: string +}) { + const normalizedQuery = normalizeComboText(query) + if (!normalizedQuery) { + return options + } + + return options.filter((option) => normalizeComboText(option).includes(normalizedQuery)) +} + +export function resolveMobilePropertyComboValue({ + options, + value, +}: { + options: readonly string[] + value: string +}) { + const trimmed = value.trim() + const exactOption = options.find((option) => normalizeComboText(option) === normalizeComboText(trimmed)) + return exactOption ?? trimmed +} + +function normalizeComboText(value: string) { + return value.trim().toLowerCase() +} diff --git a/apps/mobile/src/styles/propertyComboStyles.ts b/apps/mobile/src/styles/propertyComboStyles.ts index 19b0a718..d4aa0c50 100644 --- a/apps/mobile/src/styles/propertyComboStyles.ts +++ b/apps/mobile/src/styles/propertyComboStyles.ts @@ -27,4 +27,11 @@ export const propertyComboStyles = StyleSheet.create({ propertyComboOptionTextSelected: { color: colors.primary, }, + propertyComboEmpty: { + paddingHorizontal: spacing.md, + paddingVertical: spacing.sm, + color: colors.mutedText, + fontSize: 13, + fontWeight: '600', + }, }) diff --git a/docs/MOBILE_PROGRESS.md b/docs/MOBILE_PROGRESS.md index 084fcbc5..d46e1b24 100644 --- a/docs/MOBILE_PROGRESS.md +++ b/docs/MOBILE_PROGRESS.md @@ -515,3 +515,12 @@ Continue Phase 4 as a quality remediation pass before new feature work: - `pnpm --filter @tolaria/mobile typecheck` passed. - `pnpm --filter @tolaria/mobile test -- src/mobileEditorMessages.test.ts src/mobileWikilinkAutocomplete.test.ts` passed; the mobile test runner executed 61 files / 202 tests. - `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export-wikilink-autocomplete` passed. + +## 2026-05-14 Type Combobox Follow-Up + +- Reworked the mobile Type picker into a searchable combobox: opening Type focuses the text field, typing filters available type options, and exact typed matches normalize to the canonical type label. +- Added empty-state copy for unmatched type searches. +- Added pure mobile combobox tests for option filtering and canonical exact-match resolution. +- `pnpm --filter @tolaria/mobile typecheck` passed. +- `pnpm --filter @tolaria/mobile test -- src/mobilePropertyCombo.test.ts src/mobileNoteProperties.test.ts` passed; the mobile test runner executed 62 files / 204 tests. +- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export-type-combobox` passed.