mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Fix: tag creation sometimes retained search text
This commit is contained in:
parent
17b85f6400
commit
0098936347
@ -2,7 +2,7 @@
|
|||||||
<label class="form-label" for="tags" i18n>Tags</label>
|
<label class="form-label" for="tags" i18n>Tags</label>
|
||||||
|
|
||||||
<div class="input-group flex-nowrap">
|
<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"
|
[disabled]="disabled"
|
||||||
[multiple]="true"
|
[multiple]="true"
|
||||||
[closeOnSelect]="false"
|
[closeOnSelect]="false"
|
||||||
@ -11,11 +11,7 @@
|
|||||||
[addTag]="allowCreate ? createTagRef : false"
|
[addTag]="allowCreate ? createTagRef : false"
|
||||||
addTagText="Add tag"
|
addTagText="Add tag"
|
||||||
i18n-addTagText
|
i18n-addTagText
|
||||||
(change)="onChange(value)"
|
(change)="onChange(value)">
|
||||||
(search)="onSearch($event)"
|
|
||||||
(focus)="clearLastSearchTerm()"
|
|
||||||
(clear)="clearLastSearchTerm()"
|
|
||||||
(blur)="onBlur()">
|
|
||||||
|
|
||||||
<ng-template ng-label-tmp let-item="item">
|
<ng-template ng-label-tmp let-item="item">
|
||||||
<span class="tag-wrap tag-wrap-delete" (mousedown)="removeTag($event, item.id)">
|
<span class="tag-wrap tag-wrap-delete" (mousedown)="removeTag($event, item.id)">
|
||||||
|
@ -15,16 +15,28 @@ import {
|
|||||||
DEFAULT_MATCHING_ALGORITHM,
|
DEFAULT_MATCHING_ALGORITHM,
|
||||||
MATCH_ALL,
|
MATCH_ALL,
|
||||||
} from 'src/app/data/matching-model'
|
} 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 { RouterTestingModule } from '@angular/router/testing'
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||||
import { of } from 'rxjs'
|
import { of } from 'rxjs'
|
||||||
import { TagService } from 'src/app/services/rest/tag.service'
|
import { TagService } from 'src/app/services/rest/tag.service'
|
||||||
import {
|
import {
|
||||||
|
NgbAccordionModule,
|
||||||
NgbModal,
|
NgbModal,
|
||||||
NgbModalModule,
|
NgbModalModule,
|
||||||
NgbModalRef,
|
NgbModalRef,
|
||||||
|
NgbPopoverModule,
|
||||||
} from '@ng-bootstrap/ng-bootstrap'
|
} 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[] = [
|
const tags: PaperlessTag[] = [
|
||||||
{
|
{
|
||||||
@ -56,12 +68,32 @@ describe('TagsComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [TagsComponent],
|
declarations: [
|
||||||
|
TagsComponent,
|
||||||
|
TagEditDialogComponent,
|
||||||
|
TextComponent,
|
||||||
|
ColorComponent,
|
||||||
|
IfOwnerDirective,
|
||||||
|
SelectComponent,
|
||||||
|
TextComponent,
|
||||||
|
PermissionsFormComponent,
|
||||||
|
ColorComponent,
|
||||||
|
CheckComponent,
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: TagService,
|
provide: TagService,
|
||||||
useValue: {
|
useValue: {
|
||||||
listAll: () => of(tags),
|
listAll: () =>
|
||||||
|
of({
|
||||||
|
results: tags,
|
||||||
|
}),
|
||||||
|
create: () =>
|
||||||
|
of({
|
||||||
|
name: 'bar',
|
||||||
|
id: 99,
|
||||||
|
color: '#fff000',
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -72,6 +104,8 @@ describe('TagsComponent', () => {
|
|||||||
RouterTestingModule,
|
RouterTestingModule,
|
||||||
HttpClientTestingModule,
|
HttpClientTestingModule,
|
||||||
NgbModalModule,
|
NgbModalModule,
|
||||||
|
NgbAccordionModule,
|
||||||
|
NgbPopoverModule,
|
||||||
],
|
],
|
||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
@ -85,7 +119,7 @@ describe('TagsComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should support suggestions', () => {
|
it('should support suggestions', () => {
|
||||||
expect(component.value).toBeUndefined()
|
expect(component.value).toHaveLength(0)
|
||||||
component.value = []
|
component.value = []
|
||||||
component.tags = tags
|
component.tags = tags
|
||||||
component.suggestions = [1, 2]
|
component.suggestions = [1, 2]
|
||||||
@ -107,19 +141,19 @@ describe('TagsComponent', () => {
|
|||||||
it('should support create new using last search term and open a modal', () => {
|
it('should support create new using last search term and open a modal', () => {
|
||||||
let activeInstances: NgbModalRef[]
|
let activeInstances: NgbModalRef[]
|
||||||
modalService.activeInstances.subscribe((v) => (activeInstances = v))
|
modalService.activeInstances.subscribe((v) => (activeInstances = v))
|
||||||
component.onSearch({ term: 'bar' })
|
component.select.searchTerm = 'foobar'
|
||||||
component.createTag()
|
component.createTag()
|
||||||
expect(modalService.hasOpenModals()).toBeTruthy()
|
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', () => {
|
it('support remove tags', () => {
|
||||||
component.tags = tags
|
component.tags = tags
|
||||||
component.value = [1, 2]
|
component.value = [1, 2]
|
||||||
@ -132,6 +166,7 @@ describe('TagsComponent', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should get tags', () => {
|
it('should get tags', () => {
|
||||||
|
component.tags = null
|
||||||
expect(component.getTag(2)).toBeNull()
|
expect(component.getTag(2)).toBeNull()
|
||||||
component.tags = tags
|
component.tags = tags
|
||||||
expect(component.getTag(2)).toEqual(tags[1])
|
expect(component.getTag(2)).toEqual(tags[1])
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
|
ViewChild,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
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 { TagEditDialogComponent } from '../../edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
|
||||||
import { TagService } from 'src/app/services/rest/tag.service'
|
import { TagService } from 'src/app/services/rest/tag.service'
|
||||||
import { EditDialogMode } from '../../edit-dialog/edit-dialog.component'
|
import { EditDialogMode } from '../../edit-dialog/edit-dialog.component'
|
||||||
|
import { first, firstValueFrom, tap } from 'rxjs'
|
||||||
|
import { NgSelectComponent } from '@ng-select/ng-select'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
providers: [
|
providers: [
|
||||||
@ -74,14 +77,14 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
@Output()
|
@Output()
|
||||||
filterDocuments = new EventEmitter<PaperlessTag[]>()
|
filterDocuments = new EventEmitter<PaperlessTag[]>()
|
||||||
|
|
||||||
value: number[]
|
@ViewChild('tagSelect') select: NgSelectComponent
|
||||||
|
|
||||||
tags: PaperlessTag[]
|
value: number[] = []
|
||||||
|
|
||||||
|
tags: PaperlessTag[] = []
|
||||||
|
|
||||||
public createTagRef: (name) => void
|
public createTagRef: (name) => void
|
||||||
|
|
||||||
private _lastSearchTerm: string
|
|
||||||
|
|
||||||
getTag(id: number) {
|
getTag(id: number) {
|
||||||
if (this.tags) {
|
if (this.tags) {
|
||||||
return this.tags.find((tag) => tag.id == id)
|
return this.tags.find((tag) => tag.id == id)
|
||||||
@ -111,15 +114,20 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
})
|
})
|
||||||
modal.componentInstance.dialogMode = EditDialogMode.CREATE
|
modal.componentInstance.dialogMode = EditDialogMode.CREATE
|
||||||
if (name) modal.componentInstance.object = { name: name }
|
if (name) modal.componentInstance.object = { name: name }
|
||||||
else if (this._lastSearchTerm)
|
else if (this.select.searchTerm)
|
||||||
modal.componentInstance.object = { name: this._lastSearchTerm }
|
modal.componentInstance.object = { name: this.select.searchTerm }
|
||||||
modal.componentInstance.succeeded.subscribe((newTag) => {
|
this.select.searchTerm = null
|
||||||
this.tagService.listAll().subscribe((tags) => {
|
this.select.detectChanges()
|
||||||
this.tags = tags.results
|
return firstValueFrom(
|
||||||
this.value = [...this.value, newTag.id]
|
(modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
|
||||||
this.onChange(this.value)
|
first(),
|
||||||
})
|
tap(() => {
|
||||||
})
|
this.tagService.listAll().subscribe((tags) => {
|
||||||
|
this.tags = tags.results
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getSuggestions() {
|
getSuggestions() {
|
||||||
@ -137,20 +145,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
this.onChange(this.value)
|
this.onChange(this.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
clearLastSearchTerm() {
|
|
||||||
this._lastSearchTerm = null
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearch($event) {
|
|
||||||
this._lastSearchTerm = $event.term
|
|
||||||
}
|
|
||||||
|
|
||||||
onBlur() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.clearLastSearchTerm()
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasPrivate(): boolean {
|
get hasPrivate(): boolean {
|
||||||
return this.value.some(
|
return this.value.some(
|
||||||
(t) => this.tags?.find((t2) => t2.id === t) === undefined
|
(t) => this.tags?.find((t2) => t2.id === t) === undefined
|
||||||
|
Loading…
x
Reference in New Issue
Block a user