From 8b62154b54507c29fb06f135b253ce79d675102c Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 16 Jun 2026 18:42:22 -0700 Subject: [PATCH] Use for select + tags --- .../components/common/input/select/select.component.html | 1 + .../common/input/select/select.component.spec.ts | 9 +++++++++ .../components/common/input/select/select.component.ts | 4 ++++ .../app/components/common/input/tags/tags.component.html | 1 + .../components/common/input/tags/tags.component.spec.ts | 9 +++++++++ .../app/components/common/input/tags/tags.component.ts | 9 +++++++++ 6 files changed, 33 insertions(+) diff --git a/src-ui/src/app/components/common/input/select/select.component.html b/src-ui/src/app/components/common/input/select/select.component.html index 42fb44648..5572ad33d 100644 --- a/src-ui/src/app/components/common/input/select/select.component.html +++ b/src-ui/src/app/components/common/input/select/select.component.html @@ -28,6 +28,7 @@ [notFoundText]="notFoundText" [multiple]="multiple" [bindLabel]="bindLabel" + [searchFn]="searchFn" bindValue="id" [virtualScroll]="items?.length > 100" (change)="onChange(value)" diff --git a/src-ui/src/app/components/common/input/select/select.component.spec.ts b/src-ui/src/app/components/common/input/select/select.component.spec.ts index d93703ab1..9104faf45 100644 --- a/src-ui/src/app/components/common/input/select/select.component.spec.ts +++ b/src-ui/src/app/components/common/input/select/select.component.spec.ts @@ -112,6 +112,15 @@ describe('SelectComponent', () => { expect(createNewVal).toEqual('baz') }) + it('should search items by independent normalized terms', () => { + expect( + component.searchFn('tax 26', { id: 11, name: 'Tax\u00e9s 2026' }) + ).toBeTruthy() + expect( + component.searchFn('tax receipt', { id: 11, name: 'Tax\u00e9s 2026' }) + ).toBeFalsy() + }) + it('should clear search term on blur after delay', fakeAsync(() => { const clearSpy = jest.spyOn(component, 'clearLastSearchTerm') component.onBlur() diff --git a/src-ui/src/app/components/common/input/select/select.component.ts b/src-ui/src/app/components/common/input/select/select.component.ts index aa7b169c6..4eb1395a2 100644 --- a/src-ui/src/app/components/common/input/select/select.component.ts +++ b/src-ui/src/app/components/common/input/select/select.component.ts @@ -13,6 +13,7 @@ import { import { RouterModule } from '@angular/router' import { NgSelectModule } from '@ng-select/ng-select' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' +import { matchesSearchText } from 'src/app/utils/text-search' import { AbstractInputComponent } from '../abstract-input' @Component({ @@ -99,6 +100,9 @@ export class SelectComponent extends AbstractInputComponent { @Input() bindLabel: string = 'name' + public searchFn = (term: string, item: any): boolean => + matchesSearchText(item?.[this.bindLabel], term) + @Input() showFilter: boolean = false diff --git a/src-ui/src/app/components/common/input/tags/tags.component.html b/src-ui/src/app/components/common/input/tags/tags.component.html index 960245984..e1bb093ae 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.html +++ b/src-ui/src/app/components/common/input/tags/tags.component.html @@ -14,6 +14,7 @@ [clearSearchOnAdd]="true" [hideSelected]="tags.length > 0" [addTag]="allowCreate ? createTagRef : false" + [searchFn]="searchFn" addTagText="Add tag" i18n-addTagText (add)="onAdd($event)" diff --git a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts index 21ca3eeff..1b761a5db 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts @@ -171,6 +171,15 @@ describe('TagsComponent', () => { expect(component.getTag(4)).toBeUndefined() }) + it('should search tags by independent normalized terms including parents', () => { + const parent: Tag = { id: 11, name: 'Financ\u00e9' } + const child: Tag = { id: 12, name: 'Taxes 2026', parent: parent.id } + component.tags = [parent, child] + + expect(component.searchFn('finance 26', child)).toBeTruthy() + expect(component.searchFn('finance receipt', child)).toBeFalsy() + }) + it('should emit filtered documents', () => { component.value = [10] component.tags = tags diff --git a/src-ui/src/app/components/common/input/tags/tags.component.ts b/src-ui/src/app/components/common/input/tags/tags.component.ts index c540ff9d7..6b7d3150f 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.ts @@ -21,6 +21,7 @@ import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { first, firstValueFrom, tap } from 'rxjs' import { Tag } from 'src/app/data/tag' import { TagService } from 'src/app/services/rest/tag.service' +import { matchesSearchText } from 'src/app/utils/text-search' import { EditDialogMode } from '../../edit-dialog/edit-dialog.component' import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component' import { TagComponent } from '../../tag/tag.component' @@ -114,6 +115,14 @@ export class TagsComponent implements OnInit, ControlValueAccessor { public createTagRef: (name) => void + public searchFn = (term: string, tag: Tag): boolean => + matchesSearchText( + [this.getParentChain(tag?.id).map((parent) => parent.name), tag?.name] + .flat() + .join(' '), + term + ) + getTag(id: number) { if (this.tags) { return this.tags.find((tag) => tag.id == id)