]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix: tag creation sometimes retained search text 4038/head
authorshamoon <4887959+shamoon@users.noreply.github.com>
Sun, 20 Aug 2023 20:36:46 +0000 (13:36 -0700)
committershamoon <4887959+shamoon@users.noreply.github.com>
Sun, 20 Aug 2023 23:18:34 +0000 (16:18 -0700)
src-ui/src/app/components/common/input/tags/tags.component.html
src-ui/src/app/components/common/input/tags/tags.component.spec.ts
src-ui/src/app/components/common/input/tags/tags.component.ts

index eba8ef218825a926f77f4325793203068a882cbf..497a62335b0bce0d021ffacf5cd3104427d47a72 100644 (file)
@@ -2,7 +2,7 @@
   <label class="form-label" for="tags" i18n>Tags</label>
 
   <div class="input-group flex-nowrap">
-    <ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value"
+    <ng-select #tagSelect name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value"
       [disabled]="disabled"
       [multiple]="true"
       [closeOnSelect]="false"
       [addTag]="allowCreate ? createTagRef : false"
       addTagText="Add tag"
       i18n-addTagText
-      (change)="onChange(value)"
-      (search)="onSearch($event)"
-      (focus)="clearLastSearchTerm()"
-      (clear)="clearLastSearchTerm()"
-      (blur)="onBlur()">
+      (change)="onChange(value)">
 
       <ng-template ng-label-tmp let-item="item">
         <span class="tag-wrap tag-wrap-delete" (mousedown)="removeTag($event, item.id)">
index f3ea05d5d9dd7bcb4ab7e6200a4ef57a46a0715b..85c492abae863a04862649bce2c0478b163fe8a6 100644 (file)
@@ -15,16 +15,28 @@ import {
   DEFAULT_MATCHING_ALGORITHM,
   MATCH_ALL,
 } from 'src/app/data/matching-model'
-import { NgSelectModule } from '@ng-select/ng-select'
+import { NgSelectComponent, NgSelectModule } from '@ng-select/ng-select'
 import { RouterTestingModule } from '@angular/router/testing'
 import { HttpClientTestingModule } from '@angular/common/http/testing'
 import { of } from 'rxjs'
 import { TagService } from 'src/app/services/rest/tag.service'
 import {
+  NgbAccordionModule,
   NgbModal,
   NgbModalModule,
   NgbModalRef,
+  NgbPopoverModule,
 } from '@ng-bootstrap/ng-bootstrap'
+import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
+import { CheckComponent } from '../check/check.component'
+import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
+import { TextComponent } from '../text/text.component'
+import { ColorComponent } from '../color/color.component'
+import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
+import { PermissionsFormComponent } from '../permissions/permissions-form/permissions-form.component'
+import { SelectComponent } from '../select/select.component'
+import { ColorSliderModule } from 'ngx-color/slider'
+import { By } from '@angular/platform-browser'
 
 const tags: PaperlessTag[] = [
   {
@@ -56,12 +68,32 @@ describe('TagsComponent', () => {
 
   beforeEach(async () => {
     TestBed.configureTestingModule({
-      declarations: [TagsComponent],
+      declarations: [
+        TagsComponent,
+        TagEditDialogComponent,
+        TextComponent,
+        ColorComponent,
+        IfOwnerDirective,
+        SelectComponent,
+        TextComponent,
+        PermissionsFormComponent,
+        ColorComponent,
+        CheckComponent,
+      ],
       providers: [
         {
           provide: TagService,
           useValue: {
-            listAll: () => of(tags),
+            listAll: () =>
+              of({
+                results: tags,
+              }),
+            create: () =>
+              of({
+                name: 'bar',
+                id: 99,
+                color: '#fff000',
+              }),
           },
         },
       ],
@@ -72,6 +104,8 @@ describe('TagsComponent', () => {
         RouterTestingModule,
         HttpClientTestingModule,
         NgbModalModule,
+        NgbAccordionModule,
+        NgbPopoverModule,
       ],
     }).compileComponents()
 
@@ -85,7 +119,7 @@ describe('TagsComponent', () => {
   })
 
   it('should support suggestions', () => {
-    expect(component.value).toBeUndefined()
+    expect(component.value).toHaveLength(0)
     component.value = []
     component.tags = tags
     component.suggestions = [1, 2]
@@ -107,19 +141,19 @@ describe('TagsComponent', () => {
   it('should support create new using last search term and open a modal', () => {
     let activeInstances: NgbModalRef[]
     modalService.activeInstances.subscribe((v) => (activeInstances = v))
-    component.onSearch({ term: 'bar' })
+    component.select.searchTerm = 'foobar'
     component.createTag()
     expect(modalService.hasOpenModals()).toBeTruthy()
-    expect(activeInstances[0].componentInstance.object.name).toEqual('bar')
+    expect(activeInstances[0].componentInstance.object.name).toEqual('foobar')
+    const editDialog = activeInstances[0]
+      .componentInstance as TagEditDialogComponent
+    editDialog.save() // create is mocked
+    fixture.detectChanges()
+    fixture.whenStable().then(() => {
+      expect(fixture.debugElement.nativeElement.textContent).toContain('foobar')
+    })
   })
 
-  it('should clear search term on blur after delay', fakeAsync(() => {
-    const clearSpy = jest.spyOn(component, 'clearLastSearchTerm')
-    component.onBlur()
-    tick(3000)
-    expect(clearSpy).toHaveBeenCalled()
-  }))
-
   it('support remove tags', () => {
     component.tags = tags
     component.value = [1, 2]
@@ -132,6 +166,7 @@ describe('TagsComponent', () => {
   })
 
   it('should get tags', () => {
+    component.tags = null
     expect(component.getTag(2)).toBeNull()
     component.tags = tags
     expect(component.getTag(2)).toEqual(tags[1])
index 4fb0151b684676b9a86631535ec68af5557086fd..b6bfddb3ca633e8e0564a4a8770056be45aec28b 100644 (file)
@@ -5,6 +5,7 @@ import {
   Input,
   OnInit,
   Output,
+  ViewChild,
 } from '@angular/core'
 import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -12,6 +13,8 @@ import { PaperlessTag } from 'src/app/data/paperless-tag'
 import { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
 import { TagService } from 'src/app/services/rest/tag.service'
 import { EditDialogMode } from '../../edit-dialog/edit-dialog.component'
+import { first, firstValueFrom, tap } from 'rxjs'
+import { NgSelectComponent } from '@ng-select/ng-select'
 
 @Component({
   providers: [
@@ -74,13 +77,13 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
   @Output()
   filterDocuments = new EventEmitter<PaperlessTag[]>()
 
-  value: number[]
+  @ViewChild('tagSelect') select: NgSelectComponent
 
-  tags: PaperlessTag[]
+  value: number[] = []
 
-  public createTagRef: (name) => void
+  tags: PaperlessTag[] = []
 
-  private _lastSearchTerm: string
+  public createTagRef: (name) => void
 
   getTag(id: number) {
     if (this.tags) {
@@ -111,15 +114,20 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
     })
     modal.componentInstance.dialogMode = EditDialogMode.CREATE
     if (name) modal.componentInstance.object = { name: name }
-    else if (this._lastSearchTerm)
-      modal.componentInstance.object = { name: this._lastSearchTerm }
-    modal.componentInstance.succeeded.subscribe((newTag) => {
-      this.tagService.listAll().subscribe((tags) => {
-        this.tags = tags.results
-        this.value = [...this.value, newTag.id]
-        this.onChange(this.value)
-      })
-    })
+    else if (this.select.searchTerm)
+      modal.componentInstance.object = { name: this.select.searchTerm }
+    this.select.searchTerm = null
+    this.select.detectChanges()
+    return firstValueFrom(
+      (modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
+        first(),
+        tap(() => {
+          this.tagService.listAll().subscribe((tags) => {
+            this.tags = tags.results
+          })
+        })
+      )
+    )
   }
 
   getSuggestions() {
@@ -137,20 +145,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
     this.onChange(this.value)
   }
 
-  clearLastSearchTerm() {
-    this._lastSearchTerm = null
-  }
-
-  onSearch($event) {
-    this._lastSearchTerm = $event.term
-  }
-
-  onBlur() {
-    setTimeout(() => {
-      this.clearLastSearchTerm()
-    }, 3000)
-  }
-
   get hasPrivate(): boolean {
     return this.value.some(
       (t) => this.tags?.find((t2) => t2.id === t) === undefined