From 317d4bc600172519dbcac4b5bba763730c5937c6 Mon Sep 17 00:00:00 2001 From: wmcparla <183443443+wmcparla@users.noreply.github.com> Date: Fri, 13 Nov 2026 14:17:53 -0500 Subject: [PATCH 1/2] fix: show the X in the mail search field once it has focus, and let it close the search #127 --- src/screens/EmailListScreen.tsx | 16 ++++++++++++-- .../__tests__/search-close-wiring.test.ts | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/screens/__tests__/search-close-wiring.test.ts diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index 49d2488d..c7d824fe 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -1122,6 +1122,18 @@ export default function EmailListScreen({ const id = setTimeout(() => setSearchQuery(searchInput), 600); return () => clearTimeout(id); }, [searchInput, storeSearchQuery, setSearchQuery]); + // The X in the field clears the text and keeps the keyboard up; on an empty + // field, which has an X too while it has focus, it closes the search and + // the panel under it (#127). iOS has no back button to leave the field. + const clearOrCloseSearch = React.useCallback(() => { + if (searchInput.length > 0) { + setSearchInput(''); + setSearchQuery(''); + return; + } + setSearchFocused(false); + searchInputRef.current?.blur(); + }, [searchInput, setSearchQuery]); const submitSearch = React.useCallback((value: string) => { setSearchInput(value); setSearchQuery(value); @@ -1471,9 +1483,9 @@ export default function EmailListScreen({ autoCorrect={false} returnKeyType="search" /> - {searchInput.length > 0 && ( + {(searchInput.length > 0 || searchFocused) && ( { setSearchInput(''); setSearchQuery(''); }} + onPress={clearOrCloseSearch} hitSlop={8} style={styles.searchClearButton} accessibilityRole="button" diff --git a/src/screens/__tests__/search-close-wiring.test.ts b/src/screens/__tests__/search-close-wiring.test.ts new file mode 100644 index 00000000..57d46b1a --- /dev/null +++ b/src/screens/__tests__/search-close-wiring.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; + +// #127: the X in the mail list's search field shows as soon as the field has +// focus: it clears the text, and on an empty field closes the search. iOS has +// no back button to leave an empty, focused field. No React Native renderer in the test env, so this checks the +// wiring. + +const src = fs.readFileSync(path.resolve(__dirname, '..', 'EmailListScreen.tsx'), 'utf8'); + +describe('mail search close wiring', () => { + it('shows the X while the field has focus or holds text', () => { + expect(src).toMatch(/\{\(searchInput\.length > 0 \|\| searchFocused\) && \(\s+ { + expect(src).toMatch( + /const clearOrCloseSearch = React\.useCallback\(\(\) => \{\s+if \(searchInput\.length > 0\) \{\s+setSearchInput\(''\);\s+setSearchQuery\(''\);\s+return;\s+\}\s+setSearchFocused\(false\);\s+searchInputRef\.current\?\.blur\(\);/, + ); + }); +}); From f11b30d6ba70f6e32b9c486a5ee528d6dd3baee5 Mon Sep 17 00:00:00 2001 From: wmcparla <183443443+wmcparla@users.noreply.github.com> Date: Fri, 13 Nov 2026 15:50:40 -0500 Subject: [PATCH 2/2] fix: read the search X out as Close when it closes an empty field #127 --- src/screens/EmailListScreen.tsx | 3 ++- src/screens/__tests__/search-close-wiring.test.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index c7d824fe..5ac72ac6 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -1489,7 +1489,8 @@ export default function EmailListScreen({ hitSlop={8} style={styles.searchClearButton} accessibilityRole="button" - accessibilityLabel={t('contacts.clear_search', 'Clear search')} + // Named for what it does: on an empty field it closes the search. + accessibilityLabel={searchInput.length > 0 ? t('contacts.clear_search', 'Clear search') : t('common.close', 'Close')} > diff --git a/src/screens/__tests__/search-close-wiring.test.ts b/src/screens/__tests__/search-close-wiring.test.ts index 57d46b1a..8b21fc0c 100644 --- a/src/screens/__tests__/search-close-wiring.test.ts +++ b/src/screens/__tests__/search-close-wiring.test.ts @@ -14,6 +14,12 @@ describe('mail search close wiring', () => { expect(src).toMatch(/\{\(searchInput\.length > 0 \|\| searchFocused\) && \(\s+ { + expect(src).toMatch( + /onPress=\{clearOrCloseSearch\}[\s\S]{0,200}?accessibilityLabel=\{searchInput\.length > 0 \? t\('contacts\.clear_search', 'Clear search'\) : t\('common\.close', 'Close'\)\}/, + ); + }); + it('clears typed text first, then closes the panel and lets go of the keyboard', () => { expect(src).toMatch( /const clearOrCloseSearch = React\.useCallback\(\(\) => \{\s+if \(searchInput\.length > 0\) \{\s+setSearchInput\(''\);\s+setSearchQuery\(''\);\s+return;\s+\}\s+setSearchFocused\(false\);\s+searchInputRef\.current\?\.blur\(\);/,