@@ -123,6 +123,10 @@ export function SearchOverlay({
123123 useEffect ( ( ) => {
124124 let timer : ReturnType < typeof setTimeout >
125125
126+ if ( autoCompleteSearchError ) {
127+ return setShowSpinner ( false )
128+ }
129+
126130 // If it's the initial fetch, show the spinner immediately
127131 if ( ! aiAutocompleteOptions . length && ! generalSearchResults . length ) {
128132 return setShowSpinner ( true )
@@ -137,7 +141,12 @@ export function SearchOverlay({
137141 return ( ) => {
138142 clearTimeout ( timer )
139143 }
140- } , [ searchLoading , aiAutocompleteOptions . length , generalSearchResults . length ] )
144+ } , [
145+ searchLoading ,
146+ aiAutocompleteOptions . length ,
147+ generalSearchResults . length ,
148+ autoCompleteSearchError ,
149+ ] )
141150
142151 // Filter out any options that match the local query and replace them with a custom user query option that include isUserQuery: true
143152 const filteredAIOptions = aiAutocompleteOptions . filter (
@@ -147,7 +156,14 @@ export function SearchOverlay({
147156 // Create new arrays that prepend the user input
148157 const userInputOptions =
149158 urlSearchInputQuery . trim ( ) !== ''
150- ? [ { term : urlSearchInputQuery , highlights : [ ] , isUserQuery : true } ]
159+ ? [
160+ {
161+ term : urlSearchInputQuery ,
162+ title : urlSearchInputQuery ,
163+ highlights : [ ] ,
164+ isUserQuery : true ,
165+ } ,
166+ ]
151167 : [ ]
152168
153169 // Combine options for key navigation
@@ -165,6 +181,13 @@ export function SearchOverlay({
165181 title : t ( 'search.overlay.view_all_search_results' ) ,
166182 isViewAllResults : true ,
167183 } as any )
184+ } else if ( autoCompleteSearchError ) {
185+ if ( urlSearchInputQuery . trim ( ) !== '' ) {
186+ generalOptionsWithViewStatus . push ( {
187+ ...( userInputOptions [ 0 ] || { } ) ,
188+ isSearchDocsOption : true ,
189+ } as unknown as GeneralSearchHit )
190+ }
168191 } else if ( urlSearchInputQuery . trim ( ) !== '' && ! searchLoading ) {
169192 generalOptionsWithViewStatus . push ( {
170193 title : t ( 'search.overlay.no_results_found' ) ,
@@ -205,6 +228,7 @@ export function SearchOverlay({
205228 aiSearchError ,
206229 aiReferences ,
207230 isAskAIState ,
231+ autoCompleteSearchError ,
208232 ] )
209233
210234 // Rather than use `initialFocusRef` to have our Primer <Overlay> component auto-focus our input
@@ -432,7 +456,10 @@ export function SearchOverlay({
432456 ) {
433457 const selectedItem = combinedOptions [ selectedIndex ]
434458 if ( selectedItem . group === 'general' ) {
435- if ( ( selectedItem . option as GeneralSearchHitWithOptions ) . isViewAllResults ) {
459+ if (
460+ ( selectedItem . option as GeneralSearchHitWithOptions ) . isViewAllResults ||
461+ ( selectedItem . option as GeneralSearchHitWithOptions ) . isSearchDocsOption
462+ ) {
436463 pressedOnContext = 'view-all'
437464 performGeneralSearch ( )
438465 } else {
@@ -500,7 +527,11 @@ export function SearchOverlay({
500527 className = { styles . suggestionsList }
501528 ref = { suggestionsListHeightRef }
502529 sx = { {
503- minHeight : `${ previousSuggestionsListHeight } px` ,
530+ // When there is an error and nothing is typed in by the user, show an empty list with no height
531+ minHeight :
532+ autoCompleteSearchError && ! generalOptionsWithViewStatus . length
533+ ? '0'
534+ : `${ previousSuggestionsListHeight } px` ,
504535 } }
505536 >
506537 { /* Always show the AI Search UI error message when it is needed */ }
@@ -533,27 +564,9 @@ export function SearchOverlay({
533564 < ActionList . Divider key = "error-bottom-divider" />
534565 </ >
535566 ) }
536- { /* Only show the autocomplete search UI error message in Dev */ }
537- { process . env . NODE_ENV === 'development' && autoCompleteSearchError && ! aiSearchError && (
538- < Box
539- sx = { {
540- padding : '0 16px 0 16px' ,
541- } }
542- >
543- < Banner
544- tabIndex = { 0 }
545- className = { styles . errorBanner }
546- title = { t ( 'search.failure.general_title' ) }
547- description = { t ( 'search.failure.description' ) }
548- variant = "info"
549- aria-live = "assertive"
550- role = "alert"
551- />
552- </ Box >
553- ) }
554567 { renderSearchGroups (
555568 t ,
556- autoCompleteSearchError ? [ ] : generalOptionsWithViewStatus ,
569+ generalOptionsWithViewStatus ,
557570 aiSearchError ? [ ] : aiOptionsWithUserInput ,
558571 generalSearchResultOnSelect ,
559572 aiSearchOptionOnSelect ,
@@ -713,6 +726,7 @@ interface AutocompleteSearchHitWithUserQuery extends AutocompleteSearchHit {
713726interface GeneralSearchHitWithOptions extends GeneralSearchHit {
714727 isViewAllResults ?: boolean
715728 isNoResultsFound ?: boolean
729+ isSearchDocsOption ?: boolean
716730}
717731
718732// Render the autocomplete suggestions with AI suggestions first, headings, and a divider between the two
@@ -824,6 +838,40 @@ function renderSearchGroups(
824838 )
825839 // There should be no more items after the no results found item
826840 break
841+ // This is a special case where there is an error loading search results and we want to be able to search the docs using the user's query
842+ } else if ( option . isSearchDocsOption ) {
843+ const isActive = selectedIndex === index
844+ items . push (
845+ < ActionList . Item
846+ key = { `general-${ index } ` }
847+ id = { `search-option-general-${ index } ` }
848+ role = "option"
849+ tabIndex = { - 1 }
850+ active = { isActive }
851+ onSelect = { ( ) => performGeneralSearch ( ) }
852+ aria-label = { t ( 'search.overlay.search_docs_with_query' ) . replace ( '{query}' , option . title ) }
853+ ref = { ( element ) => {
854+ if ( listElementsRef . current ) {
855+ listElementsRef . current [ index ] = element
856+ }
857+ } }
858+ >
859+ < ActionList . LeadingVisual aria-hidden >
860+ < SearchIcon />
861+ </ ActionList . LeadingVisual >
862+ { option . title }
863+ < ActionList . TrailingVisual
864+ aria-hidden
865+ sx = { {
866+ // Hold the space even when not visible to prevent layout shift
867+ visibility : isActive ? 'visible' : 'hidden' ,
868+ width : '1rem' ,
869+ } }
870+ >
871+ < ArrowRightIcon />
872+ </ ActionList . TrailingVisual >
873+ </ ActionList . Item > ,
874+ )
827875 } else if ( option . title ) {
828876 const isActive = selectedIndex === index
829877 items . push (
@@ -877,13 +925,15 @@ function renderSearchGroups(
877925 // Don't show the bottom divider if:
878926 // 1. We are in the AI could not answer state
879927 // 2. We are in the AI Search error state
928+ // 3. There are no AI suggestions to show in suggestions state
880929 if (
881930 ! askAIState . aiCouldNotAnswer &&
882931 ! askAIState . aiSearchError &&
883932 ( ! askAIState . isAskAIState ||
884933 generalSearchOptions . filter (
885934 ( option ) => ! option . isViewAllResults && ! option . isNoResultsFound ,
886- ) . length )
935+ ) . length ) &&
936+ aiOptionsWithUserInput . length
887937 ) {
888938 groups . push ( < ActionList . Divider key = "bottom-divider" /> )
889939 }
0 commit comments