@planningcenter/chat-react-native 4.4.1-qa-staging.31 → 4.4.1-qa-1036.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/build/components/conversation/message_list.d.ts.map +1 -1
  2. package/build/components/conversation/message_list.js +123 -155
  3. package/build/components/conversation/message_list.js.map +1 -1
  4. package/build/components/conversations/conversations.d.ts.map +1 -1
  5. package/build/components/conversations/conversations.js +0 -1
  6. package/build/components/conversations/conversations.js.map +1 -1
  7. package/build/components/display/keyboard_scroll_view.d.ts.map +1 -1
  8. package/build/components/display/keyboard_scroll_view.js +0 -1
  9. package/build/components/display/keyboard_scroll_view.js.map +1 -1
  10. package/build/hooks/direct_messages/use_direct_message_candidates.d.ts +1 -3
  11. package/build/hooks/direct_messages/use_direct_message_candidates.d.ts.map +1 -1
  12. package/build/hooks/direct_messages/use_direct_message_candidates.js +29 -42
  13. package/build/hooks/direct_messages/use_direct_message_candidates.js.map +1 -1
  14. package/build/navigation/index.d.ts.map +1 -1
  15. package/build/navigation/index.js +3 -11
  16. package/build/navigation/index.js.map +1 -1
  17. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  18. package/build/screens/conversation_details_screen.js +0 -1
  19. package/build/screens/conversation_details_screen.js.map +1 -1
  20. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.d.ts.map +1 -1
  21. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js +0 -1
  22. package/build/screens/conversation_select_recipients/conversation_new_entry_screen.js.map +1 -1
  23. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.d.ts.map +1 -1
  24. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js +308 -409
  25. package/build/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.js.map +1 -1
  26. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.d.ts.map +1 -1
  27. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js +0 -1
  28. package/build/screens/conversation_select_recipients/conversation_select_group_recipients_screen.js.map +1 -1
  29. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.d.ts.map +1 -1
  30. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js +0 -1
  31. package/build/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.js.map +1 -1
  32. package/build/screens/get_help_screen.js +62 -56
  33. package/build/screens/get_help_screen.js.map +1 -1
  34. package/build/screens/group_notification_settings_screen.js +2 -3
  35. package/build/screens/group_notification_settings_screen.js.map +1 -1
  36. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  37. package/build/screens/notification_settings_screen.js +0 -1
  38. package/build/screens/notification_settings_screen.js.map +1 -1
  39. package/build/screens/preferred_app_selection_screen.js +39 -45
  40. package/build/screens/preferred_app_selection_screen.js.map +1 -1
  41. package/build/screens/search/components/search_results_list.d.ts +1 -4
  42. package/build/screens/search/components/search_results_list.d.ts.map +1 -1
  43. package/build/screens/search/components/search_results_list.js +138 -176
  44. package/build/screens/search/components/search_results_list.js.map +1 -1
  45. package/build/screens/search/search_screen.d.ts.map +1 -1
  46. package/build/screens/search/search_screen.js +28 -21
  47. package/build/screens/search/search_screen.js.map +1 -1
  48. package/build/screens/settings_screen.d.ts.map +1 -1
  49. package/build/screens/settings_screen.js +6 -7
  50. package/build/screens/settings_screen.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/components/conversation/__tests__/message_list.test.tsx +0 -4
  53. package/src/components/conversation/message_list.tsx +0 -6
  54. package/src/components/conversations/conversations.tsx +0 -1
  55. package/src/components/display/keyboard_scroll_view.tsx +0 -1
  56. package/src/hooks/direct_messages/use_direct_message_candidates.ts +2 -2
  57. package/src/navigation/index.tsx +2 -11
  58. package/src/screens/conversation_details_screen.tsx +0 -1
  59. package/src/screens/conversation_select_recipients/conversation_new_entry_screen.tsx +1 -4
  60. package/src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx +69 -104
  61. package/src/screens/conversation_select_recipients/conversation_select_group_recipients_screen.tsx +0 -1
  62. package/src/screens/conversation_select_recipients/conversation_select_teams_i_lead_recipients_screen.tsx +0 -1
  63. package/src/screens/get_help_screen.tsx +4 -4
  64. package/src/screens/group_notification_settings_screen.tsx +3 -3
  65. package/src/screens/notification_settings_screen.tsx +0 -1
  66. package/src/screens/preferred_app_selection_screen.tsx +4 -4
  67. package/src/screens/search/__tests__/search_screen.test.tsx +7 -5
  68. package/src/screens/search/components/__tests__/search_results_list.test.tsx +2 -6
  69. package/src/screens/search/components/search_results_list.tsx +8 -29
  70. package/src/screens/search/search_screen.tsx +18 -8
  71. package/src/screens/settings_screen.tsx +25 -25
  72. package/src/screens/conversation_select_recipients/__tests__/conversation_select_direct_message_recipients_screen.test.tsx +0 -83
@@ -2,7 +2,7 @@ import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
3
  import type { NativeStackNavigationProp } from '@react-navigation/native-stack'
4
4
  import React, { useEffect } from 'react'
5
- import { Platform, ScrollView, StyleSheet, View } from 'react-native'
5
+ import { Platform, StyleSheet, View } from 'react-native'
6
6
  import { Heading, Icon, Text } from '../components'
7
7
  import { useTheme } from '../hooks'
8
8
  import { platformFontWeightBold } from '../utils/styles'
@@ -38,7 +38,7 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
38
38
  }
39
39
 
40
40
  return (
41
- <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.container}>
41
+ <View style={styles.container}>
42
42
  <View style={styles.sectionOuter}>
43
43
  <View style={styles.sectionInner}>
44
44
  <Heading variant="h2" style={styles.sectionHeading}>
@@ -76,7 +76,7 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
76
76
  )}
77
77
  </View>
78
78
  </PlatformPressable>
79
- </ScrollView>
79
+ </View>
80
80
  )
81
81
  }
82
82
 
@@ -123,7 +123,6 @@ export function NotificationSettingsScreen({}: NotificationSettingsScreenProps)
123
123
  return (
124
124
  <View style={styles.listContainer}>
125
125
  <FlatList
126
- contentInsetAdjustmentBehavior="automatic"
127
126
  data={listData as SectionListData}
128
127
  contentContainerStyle={styles.contentContainer}
129
128
  renderItem={({ item, index }) => {
@@ -2,7 +2,7 @@ import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps } from '@react-navigation/native'
3
3
  import { useMutation } from '@tanstack/react-query'
4
4
  import React, { type PropsWithChildren } from 'react'
5
- import { Image, ScrollView, StyleSheet, View, type ViewStyle } from 'react-native'
5
+ import { Image, StyleSheet, View, type ViewStyle } from 'react-native'
6
6
  import { Heading, Icon, Text } from '../components'
7
7
  import { useApiClient, useTheme } from '../hooks'
8
8
  import { useAppName } from '../hooks/use_app_name'
@@ -45,7 +45,7 @@ export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScre
45
45
  const sectionTitle = chatType ? chatTypeLabel(chatType.title) : undefined
46
46
 
47
47
  return (
48
- <ScrollView contentInsetAdjustmentBehavior="automatic" contentContainerStyle={styles.container}>
48
+ <View style={styles.container}>
49
49
  <View style={styles.section}>
50
50
  <Heading variant="h2" style={styles.sectionHeading}>
51
51
  {sectionTitle}
@@ -64,7 +64,7 @@ export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScre
64
64
  </PressableRow>
65
65
  ))}
66
66
  </View>
67
- </ScrollView>
67
+ </View>
68
68
  )
69
69
  }
70
70
 
@@ -118,7 +118,7 @@ const PressableRow = ({
118
118
 
119
119
  const styles = StyleSheet.create({
120
120
  container: {
121
- flexGrow: 1,
121
+ flex: 1,
122
122
  },
123
123
  section: {
124
124
  paddingTop: 24,
@@ -106,13 +106,12 @@ describe('SearchScreen', () => {
106
106
  fireEvent.changeText(input, ' budget ')
107
107
 
108
108
  expect(view.getByText('Search chat')).toBeTruthy()
109
- const searchedTerms = () =>
110
- new Set(mockUseSearchResults.mock.calls.map(([term]) => term).filter(term => term.trim()))
111
- expect([...searchedTerms()]).toEqual([])
109
+ expect(mockUseSearchResults).not.toHaveBeenCalled()
112
110
 
113
111
  act(() => jest.advanceTimersByTime(200))
114
112
 
115
- expect([...searchedTerms()]).toEqual(['budget'])
113
+ const searchedTerms = new Set(mockUseSearchResults.mock.calls.map(([term]) => term))
114
+ expect([...searchedTerms]).toEqual(['budget'])
116
115
  expect(view.queryByText('Search chat')).toBeNull()
117
116
  expect(view.getByLabelText('Loading results')).toBeTruthy()
118
117
  })
@@ -122,7 +121,10 @@ describe('SearchScreen', () => {
122
121
 
123
122
  const scrollView = view.UNSAFE_getByType(ScrollView)
124
123
  expect(scrollView.props.keyboardShouldPersistTaps).toBe('handled')
125
- expect(scrollView.props.contentContainerStyle).toMatchObject({ flexGrow: 1 })
124
+ expect(scrollView.props.contentContainerStyle).toMatchObject({
125
+ flexGrow: 1,
126
+ justifyContent: 'center',
127
+ })
126
128
  expect(view.getByText('Search chat')).toBeTruthy()
127
129
  })
128
130
 
@@ -49,13 +49,9 @@ const messageResult = (id: string): MessageSearchResultResource => ({
49
49
  },
50
50
  })
51
51
 
52
- const slots = { header: <></>, prompt: <></> }
53
-
54
52
  const renderList = (state: Partial<SearchState>, onSettled = jest.fn()) => {
55
53
  mockUseSearchResults.mockReturnValue(searchState(state))
56
- const list = () => (
57
- <SearchResultsList term="potluck" onSelect={jest.fn()} onSettled={onSettled} {...slots} />
58
- )
54
+ const list = () => <SearchResultsList term="potluck" onSelect={jest.fn()} onSettled={onSettled} />
59
55
  const view = render(list())
60
56
  const rerenderWith = (next: Partial<SearchState>) => {
61
57
  mockUseSearchResults.mockReturnValue(searchState(next))
@@ -134,7 +130,7 @@ describe('SearchResultsList', () => {
134
130
  const results = [conversationResult('1'), messageResult('1'), messageResult('2')]
135
131
  mockUseSearchResults.mockReturnValue(searchState({ results }))
136
132
  const view = render(
137
- <SearchResultsList term="potluck" onSelect={onSelect} onSettled={jest.fn()} {...slots} />
133
+ <SearchResultsList term="potluck" onSelect={onSelect} onSettled={jest.fn()} />
138
134
  )
139
135
 
140
136
  fireEvent.press(view.getByLabelText(/^Message 2/))
@@ -1,4 +1,4 @@
1
- import React, { ReactElement, useEffect, useRef } from 'react'
1
+ import React, { useEffect, useRef } from 'react'
2
2
  import { FlatList, StyleSheet, View } from 'react-native'
3
3
  import { Banner, Spinner, Text, TextButton } from '../../../components'
4
4
  import { useSearchResults } from '../../../hooks/use_search_results'
@@ -23,17 +23,9 @@ interface SearchResultsListProps {
23
23
  term: string
24
24
  onSelect: (result: SearchResultResource, position: number) => void
25
25
  onSettled: (term: string, counts: SearchResultCounts) => void
26
- header: ReactElement
27
- prompt: ReactElement
28
26
  }
29
27
 
30
- export function SearchResultsList({
31
- term,
32
- onSelect,
33
- onSettled,
34
- header,
35
- prompt,
36
- }: SearchResultsListProps) {
28
+ export function SearchResultsList({ term, onSelect, onSettled }: SearchResultsListProps) {
37
29
  const {
38
30
  results,
39
31
  failedTypes,
@@ -44,10 +36,9 @@ export function SearchResultsList({
44
36
  hasNextPage,
45
37
  isFetchingNextPage,
46
38
  } = useSearchResults(term)
47
- const searching = term.trim().length > 0
48
- const conversationResults = searching ? results.filter(isConversationResult) : []
49
- const messageResults = searching ? results.filter(isMessageResult) : []
50
- const settled = searching && !isPending && !isError
39
+ const conversationResults = results.filter(isConversationResult)
40
+ const messageResults = results.filter(isMessageResult)
41
+ const settled = !isPending && !isError
51
42
 
52
43
  const settledTermRef = useRef<string | undefined>(undefined)
53
44
 
@@ -65,7 +56,6 @@ export function SearchResultsList({
65
56
  <FlatList
66
57
  data={messageResults}
67
58
  keyExtractor={item => item.id}
68
- contentInsetAdjustmentBehavior="automatic"
69
59
  keyboardShouldPersistTaps="handled"
70
60
  keyboardDismissMode="on-drag"
71
61
  contentContainerStyle={styles.content}
@@ -74,20 +64,16 @@ export function SearchResultsList({
74
64
  )}
75
65
  ListHeaderComponent={
76
66
  <>
77
- {header}
78
67
  {conversationResults.length > 0 && (
79
68
  <ConversationResults results={conversationResults} onSelect={onSelect} />
80
69
  )}
81
- {searching && failedTypes.includes('conversation') && (
82
- <SectionFailure type="conversation" />
83
- )}
70
+ {failedTypes.includes('conversation') && <SectionFailure type="conversation" />}
84
71
  {messageResults.length > 0 && <ResultsSection title="Messages" />}
85
72
  </>
86
73
  }
87
74
  ListEmptyComponent={
88
75
  <ListEmpty
89
76
  term={term}
90
- prompt={prompt}
91
77
  isError={isError}
92
78
  isPending={isPending}
93
79
  noResults={results.length === 0 && failedTypes.length === 0}
@@ -105,7 +91,7 @@ export function SearchResultsList({
105
91
  Try again
106
92
  </TextButton>
107
93
  )}
108
- {searching && failedTypes.includes('message') && <SectionFailure type="message" />}
94
+ {failedTypes.includes('message') && <SectionFailure type="message" />}
109
95
  </>
110
96
  }
111
97
  onEndReached={() => {
@@ -118,15 +104,12 @@ export function SearchResultsList({
118
104
 
119
105
  interface ListEmptyProps {
120
106
  term: string
121
- prompt: ReactElement
122
107
  isError: boolean
123
108
  isPending: boolean
124
109
  noResults: boolean
125
110
  }
126
111
 
127
- function ListEmpty({ term, prompt, isError, isPending, noResults }: ListEmptyProps) {
128
- if (term.trim().length === 0) return <View style={styles.prompt}>{prompt}</View>
129
-
112
+ function ListEmpty({ term, isError, isPending, noResults }: ListEmptyProps) {
130
113
  if (isError) {
131
114
  return <Banner appearance="error" description={SEARCH_ERROR_MESSAGE} style={styles.banner} />
132
115
  }
@@ -166,10 +149,6 @@ const styles = StyleSheet.create({
166
149
  content: {
167
150
  flexGrow: 1,
168
151
  },
169
- prompt: {
170
- flexGrow: 1,
171
- justifyContent: 'center',
172
- },
173
152
  banner: {
174
153
  margin: 16,
175
154
  },
@@ -4,7 +4,7 @@ import {
4
4
  NativeStackScreenProps,
5
5
  } from '@react-navigation/native-stack'
6
6
  import React, { useRef, useState } from 'react'
7
- import { Keyboard, StyleSheet, View } from 'react-native'
7
+ import { Keyboard, ScrollView, StyleSheet, View } from 'react-native'
8
8
  import { KeyboardView } from '../../components'
9
9
  import {
10
10
  composeOptions,
@@ -64,13 +64,19 @@ export function SearchScreen() {
64
64
  return (
65
65
  <KeyboardView>
66
66
  <View style={styles.container} testID="search-screen">
67
- <SearchResultsList
68
- term={debouncedTerm}
69
- onSelect={openResult}
70
- onSettled={publishSearch}
71
- header={<SearchField term={term} onChange={setTerm} />}
72
- prompt={<SearchPromptBlankState />}
73
- />
67
+ <SearchField term={term} onChange={setTerm} />
68
+ {debouncedTerm.length > 0 ? (
69
+ <SearchResultsList
70
+ key={debouncedTerm}
71
+ term={debouncedTerm}
72
+ onSelect={openResult}
73
+ onSettled={publishSearch}
74
+ />
75
+ ) : (
76
+ <ScrollView contentContainerStyle={styles.prompt} keyboardShouldPersistTaps="handled">
77
+ <SearchPromptBlankState />
78
+ </ScrollView>
79
+ )}
74
80
  </View>
75
81
  </KeyboardView>
76
82
  )
@@ -80,4 +86,8 @@ const styles = StyleSheet.create({
80
86
  container: {
81
87
  flex: 1,
82
88
  },
89
+ prompt: {
90
+ flexGrow: 1,
91
+ justifyContent: 'center',
92
+ },
83
93
  })
@@ -1,7 +1,7 @@
1
1
  import { PlatformPressable } from '@react-navigation/elements'
2
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
3
  import React, { useCallback } from 'react'
4
- import { Platform, ScrollView, StyleSheet, View } from 'react-native'
4
+ import { Platform, StyleSheet, View } from 'react-native'
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
6
6
  import { Heading, Icon, Text, VersionFooter } from '../components'
7
7
  import { Switch } from '../components/display/switch'
@@ -28,31 +28,29 @@ export function SettingsScreen({}: SettingsScreenProps) {
28
28
 
29
29
  return (
30
30
  <View style={styles.container}>
31
- <ScrollView contentInsetAdjustmentBehavior="automatic">
32
- <View style={styles.sectionOuter}>
33
- <View style={styles.sectionHeader}>
34
- <Heading variant="h2">Messages</Heading>
35
- </View>
36
- {directMessagesEligible && (
37
- <View style={styles.settingRow}>
38
- <View style={styles.settingRowText}>
39
- <Text>Allow direct messages</Text>
40
- <Text variant="tertiary">
41
- When off, you can't be added to new direct messages, and any you're already in
42
- will be turned off.
43
- </Text>
44
- </View>
45
- <Switch
46
- value={!directMessagesDisabled}
47
- onValueChange={enabled => setDirectMessagesDisabled(!enabled)}
48
- disabled={isMutating}
49
- />
50
- </View>
51
- )}
52
- <NavRow label="Notification settings" onPress={handleNotificationSettings} />
53
- {appName !== 'services' && <NavRow label="Get help" onPress={handleGetHelp} />}
31
+ <View style={styles.sectionOuter}>
32
+ <View style={styles.sectionHeader}>
33
+ <Heading variant="h2">Messages</Heading>
54
34
  </View>
55
- </ScrollView>
35
+ {directMessagesEligible && (
36
+ <View style={styles.settingRow}>
37
+ <View style={styles.settingRowText}>
38
+ <Text>Allow direct messages</Text>
39
+ <Text variant="tertiary">
40
+ When off, you can't be added to new direct messages, and any you're already in will
41
+ be turned off.
42
+ </Text>
43
+ </View>
44
+ <Switch
45
+ value={!directMessagesDisabled}
46
+ onValueChange={enabled => setDirectMessagesDisabled(!enabled)}
47
+ disabled={isMutating}
48
+ />
49
+ </View>
50
+ )}
51
+ <NavRow label="Notification settings" onPress={handleNotificationSettings} />
52
+ {appName !== 'services' && <NavRow label="Get help" onPress={handleGetHelp} />}
53
+ </View>
56
54
  <View style={styles.footer}>
57
55
  <VersionFooter />
58
56
  </View>
@@ -92,6 +90,8 @@ const useStyles = () => {
92
90
  flex: 1,
93
91
  },
94
92
  footer: {
93
+ flex: 1,
94
+ justifyContent: 'flex-end',
95
95
  paddingBottom: Math.max(insets.bottom, 8),
96
96
  },
97
97
  sectionOuter: {
@@ -1,83 +0,0 @@
1
- import { fireEvent, render } from '@testing-library/react-native'
2
- import React from 'react'
3
- import { SafeAreaProvider } from 'react-native-safe-area-context'
4
- import { Log } from '../../../utils/native_adapters/configuration'
5
- import { ConversationSelectDirectMessageRecipientsScreen } from '../conversation_select_direct_message_recipients_screen'
6
-
7
- jest.mock('@react-navigation/native', () => ({
8
- ...jest.requireActual('@react-navigation/native'),
9
- useNavigation: () => ({ getParent: jest.fn() }),
10
- }))
11
- jest.mock('../../../hooks/use_features', () => ({
12
- ...jest.requireActual('../../../hooks/use_features'),
13
- useFeatures: () => ({ featureEnabled: () => false }),
14
- }))
15
- jest.mock('../../../hooks/use_debounced_search_term', () => ({
16
- useDebouncedSearchTerm: (term: string) => term,
17
- }))
18
- jest.mock('../../../hooks/direct_messages/use_direct_message_conversation_validate', () => ({
19
- useDirectMessageConversationValidate: () => ({ warnings: [], validationPending: false }),
20
- }))
21
- jest.mock('../../../hooks/direct_messages/use_find_or_create_direct_message_conversation', () => ({
22
- useFindOrCreateDirectMessageConversation: () => ({
23
- findOrCreateConversation: jest.fn(),
24
- directMessageExists: false,
25
- isFinding: false,
26
- isError: false,
27
- }),
28
- }))
29
- jest.mock('../../../hooks/direct_messages/use_direct_message_affiliations', () => ({
30
- useDirectMessageAffiliations: () => ({
31
- groups: new Map(),
32
- teams: new Map(),
33
- resultsDescription: null,
34
- }),
35
- }))
36
- jest.mock('../../../hooks/direct_messages/use_direct_message_candidates', () => ({
37
- useDirectMessageCandidates: (term: string, { throwOnError = true } = {}) => {
38
- if (term && throwOnError) throw new Error('Network request failed')
39
- return { data: undefined, isFetching: false, isError: term.length > 0 }
40
- },
41
- }))
42
- jest.mock('../../../components/display/action_button', () => ({ ActionButton: () => null }))
43
- jest.mock('../../../components/display/keyboard_view', () => ({
44
- ...jest.requireActual('../../../components/display/keyboard_view'),
45
- KeyboardView: ({ children }: { children: React.ReactNode }) => children,
46
- }))
47
-
48
- const renderScreen = () =>
49
- render(
50
- <SafeAreaProvider
51
- initialMetrics={{
52
- frame: { x: 0, y: 0, width: 390, height: 844 },
53
- insets: { top: 0, left: 0, right: 0, bottom: 0 },
54
- }}
55
- >
56
- <ConversationSelectDirectMessageRecipientsScreen route={{ params: {} } as never} />
57
- </SafeAreaProvider>
58
- )
59
-
60
- describe('ConversationSelectDirectMessageRecipientsScreen', () => {
61
- let reportError: jest.SpyInstance
62
- let consoleError: jest.SpyInstance
63
-
64
- beforeEach(() => {
65
- reportError = jest.spyOn(Log, 'reportError').mockImplementation(() => {})
66
- consoleError = jest.spyOn(console, 'error').mockImplementation(() => {})
67
- })
68
-
69
- afterEach(() => {
70
- reportError.mockRestore()
71
- consoleError.mockRestore()
72
- })
73
-
74
- it('reports a failed search and shows the banner while keeping the To field', () => {
75
- const view = renderScreen()
76
-
77
- fireEvent.changeText(view.getByPlaceholderText('Search'), 'pi')
78
-
79
- expect(view.getByText("Couldn't load people right now. Try again.")).toBeTruthy()
80
- expect(view.getByPlaceholderText('Search').props.value).toBe('pi')
81
- expect(reportError).toHaveBeenCalledTimes(1)
82
- })
83
- })