@@ -8,15 +8,20 @@ import {
8
8
TextInput ,
9
9
css ,
10
10
cx ,
11
+ focusRing ,
11
12
palette ,
12
13
spacing ,
13
14
useDarkMode ,
14
15
} from '@mongodb-js/compass-components' ;
15
16
import { connect } from 'react-redux' ;
16
17
17
- import { RobotSVG } from './robot-svg' ;
18
+ import { DEFAULT_ROBOT_SIZE , RobotSVG } from './robot-svg' ;
18
19
import type { RootState } from '../../stores/query-bar-store' ;
19
- import { cancelAIQuery , runAIQuery } from '../../stores/ai-query-reducer' ;
20
+ import {
21
+ cancelAIQuery ,
22
+ changeAIPromptText ,
23
+ runAIQuery ,
24
+ } from '../../stores/ai-query-reducer' ;
20
25
21
26
const containerStyles = css ( {
22
27
display : 'flex' ,
@@ -95,6 +100,30 @@ const buttonHighlightLightModeStyles = css({
95
100
color : palette . gray . dark1 ,
96
101
} ) ;
97
102
103
+ const loaderContainerStyles = css ( {
104
+ padding : spacing [ 1 ] ,
105
+ display : 'inline-flex' ,
106
+ width : DEFAULT_ROBOT_SIZE + spacing [ 2 ] ,
107
+ justifyContent : 'space-around' ,
108
+ } ) ;
109
+
110
+ const buttonResetStyles = css ( {
111
+ margin : 0 ,
112
+ padding : 0 ,
113
+ border : 'none' ,
114
+ background : 'none' ,
115
+ cursor : 'pointer' ,
116
+ } ) ;
117
+
118
+ const closeAIButtonStyles = css (
119
+ buttonResetStyles ,
120
+ {
121
+ padding : spacing [ 1 ] ,
122
+ display : 'inline-flex' ,
123
+ } ,
124
+ focusRing
125
+ ) ;
126
+
98
127
const closeText = 'Close AI Query' ;
99
128
100
129
const SubmitArrowSVG = ( { darkMode } : { darkMode ?: boolean } ) => (
@@ -122,25 +151,28 @@ const SubmitArrowSVG = ({ darkMode }: { darkMode?: boolean }) => (
122
151
) ;
123
152
124
153
type AITextInputProps = {
154
+ aiPromptText : string ;
125
155
onCancelAIQuery : ( ) => void ;
126
156
isFetching ?: boolean ;
127
157
didSucceed : boolean ;
128
158
errorMessage ?: string ;
129
159
show : boolean ;
160
+ onChangeAIPromptText : ( text : string ) => void ;
130
161
onClose : ( ) => void ;
131
162
onSubmitText : ( text : string ) => void ;
132
163
} ;
133
164
134
165
function AITextInput ( {
166
+ aiPromptText,
135
167
onCancelAIQuery,
136
168
isFetching,
137
169
didSucceed,
138
170
errorMessage,
139
171
show,
140
172
onClose,
173
+ onChangeAIPromptText,
141
174
onSubmitText,
142
175
} : AITextInputProps ) {
143
- const [ text , setText ] = useState ( '' ) ;
144
176
const promptTextInputRef = useRef < HTMLInputElement > ( null ) ;
145
177
const [ showSuccess , setShowSuccess ] = useState ( false ) ;
146
178
const darkMode = useDarkMode ( ) ;
@@ -149,12 +181,12 @@ function AITextInput({
149
181
( evt : React . KeyboardEvent < HTMLInputElement > ) => {
150
182
if ( evt . key === 'Enter' ) {
151
183
evt . preventDefault ( ) ;
152
- onSubmitText ( text ) ;
184
+ onSubmitText ( aiPromptText ) ;
153
185
} else if ( evt . key === 'Escape' ) {
154
186
isFetching ? onCancelAIQuery ( ) : onClose ( ) ;
155
187
}
156
188
} ,
157
- [ text , onClose , onSubmitText , isFetching , onCancelAIQuery ]
189
+ [ aiPromptText , onClose , onSubmitText , isFetching , onCancelAIQuery ]
158
190
) ;
159
191
160
192
useEffect ( ( ) => {
@@ -196,23 +228,21 @@ function AITextInput({
196
228
sizeVariant = "small"
197
229
aria-label = "Enter a plain text query that the AI will translate into MongoDB query language."
198
230
placeholder = "Tell Compass what documents to find (e.g. how many users signed up last month)"
199
- value = { text }
231
+ value = { aiPromptText }
200
232
onChange = { ( evt : React . ChangeEvent < HTMLInputElement > ) =>
201
- setText ( evt . currentTarget . value )
233
+ onChangeAIPromptText ( evt . currentTarget . value )
202
234
}
203
235
onKeyDown = { onTextInputKeyDown }
204
236
/>
205
237
< div className = { floatingButtonsContainerStyles } >
206
- { isFetching && < SpinLoader /> }
207
- { showSuccess && (
208
- < Icon
209
- className = {
210
- darkMode
211
- ? successIndicatorDarkModeStyles
212
- : successIndicatorLightModeStyles
213
- }
214
- glyph = "CheckmarkWithCircle"
215
- />
238
+ { aiPromptText && (
239
+ < IconButton
240
+ aria-label = "Clear query prompt"
241
+ onClick = { ( ) => onChangeAIPromptText ( '' ) }
242
+ data-testid = "ai-text-clear-prompt"
243
+ >
244
+ < Icon glyph = "X" />
245
+ </ IconButton >
216
246
) }
217
247
< Button
218
248
size = "small"
@@ -221,7 +251,7 @@ function AITextInput({
221
251
! darkMode && generateButtonLightModeStyles
222
252
) }
223
253
onClick = { ( ) =>
224
- isFetching ? onCancelAIQuery ( ) : onSubmitText ( text )
254
+ isFetching ? onCancelAIQuery ( ) : onSubmitText ( aiPromptText )
225
255
}
226
256
>
227
257
{ isFetching ? (
@@ -245,13 +275,32 @@ function AITextInput({
245
275
</ >
246
276
) }
247
277
</ Button >
248
- < IconButton
249
- aria-label = { closeText }
250
- title = { closeText }
251
- onClick = { ( ) => onClose ( ) }
252
- >
253
- < RobotSVG />
254
- </ IconButton >
278
+ { isFetching ? (
279
+ < div className = { loaderContainerStyles } >
280
+ < SpinLoader />
281
+ </ div >
282
+ ) : showSuccess ? (
283
+ < div className = { loaderContainerStyles } >
284
+ < Icon
285
+ className = {
286
+ darkMode
287
+ ? successIndicatorDarkModeStyles
288
+ : successIndicatorLightModeStyles
289
+ }
290
+ glyph = "CheckmarkWithCircle"
291
+ />
292
+ </ div >
293
+ ) : (
294
+ < button
295
+ className = { closeAIButtonStyles }
296
+ data-testid = "close-ai-query-button"
297
+ aria-label = { closeText }
298
+ title = { closeText }
299
+ onClick = { ( ) => onClose ( ) }
300
+ >
301
+ { isFetching ? < SpinLoader /> : < RobotSVG /> }
302
+ </ button >
303
+ ) }
255
304
</ div >
256
305
</ div >
257
306
{ errorMessage && (
@@ -266,12 +315,14 @@ function AITextInput({
266
315
const ConnectedAITextInput = connect (
267
316
( state : RootState ) => {
268
317
return {
318
+ aiPromptText : state . aiQuery . aiPromptText ,
269
319
isFetching : state . aiQuery . status === 'fetching' ,
270
320
didSucceed : state . aiQuery . status === 'success' ,
271
321
errorMessage : state . aiQuery . errorMessage ,
272
322
} ;
273
323
} ,
274
324
{
325
+ onChangeAIPromptText : changeAIPromptText ,
275
326
onCancelAIQuery : cancelAIQuery ,
276
327
onSubmitText : runAIQuery ,
277
328
}
0 commit comments