From 376b61938fe21c7a2e976ab5a7f15e78b090d31a Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 7 Aug 2026 11:52:24 -0700 Subject: [PATCH] Fix: prevent debounce overwrites in advanced search field, also improve Esc behavior (#13602) --- .../filter-editor/filter-editor.component.spec.ts | 14 ++++++++++++++ .../filter-editor/filter-editor.component.ts | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts index 9fa7f7807..26e9bbeb3 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts @@ -2213,6 +2213,20 @@ describe('FilterEditorComponent', () => { expect(blurSpy).toHaveBeenCalled() }) + it('should only dismiss open autocomplete suggestions on Escape, keeping the query', () => { + component.textFilter = 'foo bar' + component.textFilterInput.nativeElement.value = 'foo bar' + jest.spyOn(component.searchTypeahead, 'isPopupOpen').mockReturnValue(true) + const dismissSpy = jest + .spyOn(component.searchTypeahead, 'dismissPopup') + .mockImplementation(() => {}) + component.textFilterInput.nativeElement.dispatchEvent( + new KeyboardEvent('keydown', { key: 'Escape' }) + ) + expect(dismissSpy).toHaveBeenCalled() + expect(component.textFilter).toEqual('foo bar') + }) + it('should adjust text filter targets if more like search', () => { const TEXT_FILTER_TARGET_FULLTEXT_MORELIKE = 'fulltext-morelike' // private const component.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_MORELIKE diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index 537187597..3d002b076 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -15,6 +15,7 @@ import { import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbDropdownModule, + NgbTypeahead, NgbTypeaheadModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @@ -351,6 +352,9 @@ export class FilterEditorComponent @ViewChild('textFilterInput') textFilterInput: ElementRef + @ViewChild(NgbTypeahead) + searchTypeahead: NgbTypeahead + readonly customFields = signal([]) tagDocumentCounts: SelectionDataItem[] @@ -1150,6 +1154,7 @@ export class FilterEditorComponent } set textFilter(value) { + this._textFilter = value // set immediately to prevent loss of keystrokes this.textFilterDebounce.next(value) } @@ -1242,9 +1247,9 @@ export class FilterEditorComponent distinctUntilChanged(), filter((query) => !query.length || query.length > 2) ) - .subscribe((text) => + .subscribe(() => this.updateTextFilter( - text, + this._textFilter, // use the current value, not the debounced (possibly stale) one this.textFilterTarget !== TEXT_FILTER_TARGET_FULLTEXT_QUERY ) ) @@ -1320,6 +1325,11 @@ export class FilterEditorComponent this.updateTextFilter(filterString) } } else if (event.key === 'Escape') { + if (this.searchTypeahead?.isPopupOpen()) { + // only dismiss the suggestions, so longer query can use Enter + this.searchTypeahead.dismissPopup() + return + } if (this._textFilter?.length) { this.resetTextField() } else {