Frontend support for boolean + url custom fields
This commit is contained in:
parent
e399d4f8d3
commit
6ed740756e
@ -39,6 +39,7 @@ import { NgxFileDropModule } from 'ngx-file-drop'
|
|||||||
import { TextComponent } from './components/common/input/text/text.component'
|
import { TextComponent } from './components/common/input/text/text.component'
|
||||||
import { SelectComponent } from './components/common/input/select/select.component'
|
import { SelectComponent } from './components/common/input/select/select.component'
|
||||||
import { CheckComponent } from './components/common/input/check/check.component'
|
import { CheckComponent } from './components/common/input/check/check.component'
|
||||||
|
import { UrlComponent } from './components/common/input/url/url.component'
|
||||||
import { PasswordComponent } from './components/common/input/password/password.component'
|
import { PasswordComponent } from './components/common/input/password/password.component'
|
||||||
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'
|
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'
|
||||||
import { TagsComponent } from './components/common/input/tags/tags.component'
|
import { TagsComponent } from './components/common/input/tags/tags.component'
|
||||||
@ -203,6 +204,7 @@ function initializeApp(settings: SettingsService) {
|
|||||||
TextComponent,
|
TextComponent,
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
CheckComponent,
|
CheckComponent,
|
||||||
|
UrlComponent,
|
||||||
PasswordComponent,
|
PasswordComponent,
|
||||||
SaveViewConfigDialogComponent,
|
SaveViewConfigDialogComponent,
|
||||||
TagsComponent,
|
TagsComponent,
|
||||||
|
@ -1,5 +1,19 @@
|
|||||||
<div class="mb-3 form-check">
|
<div class="mb-3">
|
||||||
<input #inputField type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
|
<div class="row">
|
||||||
<label class="form-check-label" [for]="inputId">{{title}}</label>
|
<div *ngIf="horizontal" class="d-flex align-items-center position-relative hidden-button-container col-md-3">
|
||||||
<div *ngIf="hint" class="form-text text-muted">{{hint}}</div>
|
<label class="form-label mb-md-0" [for]="inputId">{{title}}</label>
|
||||||
|
<button type="button" *ngIf="removable" class="btn btn-sm btn-danger position-absolute left-0" (click)="removed.emit(this)">
|
||||||
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||||
|
</svg> <ng-container i18n>Remove</ng-container>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div [class.col-md-9]="horizontal">
|
||||||
|
<div class="form-check">
|
||||||
|
<input #inputField type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
|
||||||
|
<label *ngIf="!horizontal" class="form-check-label" [for]="inputId">{{title}}</label>
|
||||||
|
<div *ngIf="hint" class="form-text text-muted">{{hint}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
<div class="mb-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="d-flex align-items-center position-relative hidden-button-container" [class.col-md-3]="horizontal">
|
||||||
|
<label class="form-label mb-md-0" [for]="inputId">{{title}}</label>
|
||||||
|
<button type="button" *ngIf="removable" class="btn btn-sm btn-danger position-absolute left-0" (click)="removed.emit(this)">
|
||||||
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||||
|
</svg> <ng-container i18n>Remove</ng-container>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div [class.col-md-9]="horizontal">
|
||||||
|
<input #inputField type="url" class="form-control" [class.is-invalid]="error" placeholder="https://" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [disabled]="disabled">
|
||||||
|
<small *ngIf="hint" class="form-text text-muted" [innerHTML]="hint | safeHtml"></small>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,36 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
import {
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
} from '@angular/forms'
|
||||||
|
import { UrlComponent } from './url.component'
|
||||||
|
|
||||||
|
describe('TextComponent', () => {
|
||||||
|
let component: UrlComponent
|
||||||
|
let fixture: ComponentFixture<UrlComponent>
|
||||||
|
let input: HTMLInputElement
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [UrlComponent],
|
||||||
|
providers: [],
|
||||||
|
imports: [FormsModule, ReactiveFormsModule],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UrlComponent)
|
||||||
|
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
input = component.inputField.nativeElement
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support use of input field', () => {
|
||||||
|
expect(component.value).toBeUndefined()
|
||||||
|
// TODO: why doesnt this work?
|
||||||
|
// input.value = 'foo'
|
||||||
|
// input.dispatchEvent(new Event('change'))
|
||||||
|
// fixture.detectChanges()
|
||||||
|
// expect(component.value).toEqual('foo')
|
||||||
|
})
|
||||||
|
})
|
21
src-ui/src/app/components/common/input/url/url.component.ts
Normal file
21
src-ui/src/app/components/common/input/url/url.component.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Component, forwardRef } from '@angular/core'
|
||||||
|
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
|
import { AbstractInputComponent } from '../abstract-input'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => UrlComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selector: 'pngx-input-url',
|
||||||
|
templateUrl: './url.component.html',
|
||||||
|
styleUrls: ['./url.component.scss'],
|
||||||
|
})
|
||||||
|
export class UrlComponent extends AbstractInputComponent<string> {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
}
|
@ -102,9 +102,12 @@
|
|||||||
<pngx-input-tags formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
<pngx-input-tags formControlName="tags" [suggestions]="suggestions?.tags" [showFilter]="true" [horizontal]="true" (filterDocuments)="filterDocuments($event)" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"></pngx-input-tags>
|
||||||
<ng-container *ngFor="let fieldInstance of document?.custom_fields; let i = index">
|
<ng-container *ngFor="let fieldInstance of document?.custom_fields; let i = index">
|
||||||
<div [formGroup]="customFieldFormFields.controls[i]">
|
<div [formGroup]="customFieldFormFields.controls[i]">
|
||||||
<pngx-input-text formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.String" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField($event)" [horizontal]="true"></pngx-input-text>
|
<!-- TODO: boolean + URL -->
|
||||||
<pngx-input-date formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Date" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField($event)" [horizontal]="true"></pngx-input-date>
|
<pngx-input-text formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.String" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-text>
|
||||||
<pngx-input-number formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Integer" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField($event)" [horizontal]="true" [showAdd]="false"></pngx-input-number>
|
<pngx-input-date formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Date" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-date>
|
||||||
|
<pngx-input-number formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Integer" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true" [showAdd]="false"></pngx-input-number>
|
||||||
|
<pngx-input-check formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Boolean" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-check>
|
||||||
|
<pngx-input-url formControlName="value" *ngIf="fieldInstance.field.data_type === PaperlessCustomFieldDataType.Url" [title]="fieldInstance.field.name" [removable]="true" (removed)="removeField(fieldInstance)" [horizontal]="true"></pngx-input-url>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
@ -847,7 +847,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
initNormally()
|
initNormally()
|
||||||
const initialLength = doc.custom_fields.length
|
const initialLength = doc.custom_fields.length
|
||||||
expect(component.customFieldFormFields).toHaveLength(initialLength)
|
expect(component.customFieldFormFields).toHaveLength(initialLength)
|
||||||
component.removeField({ title: 'Field 1' } as any)
|
component.removeField(doc.custom_fields[0])
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(component.document.custom_fields).toHaveLength(initialLength - 1)
|
expect(component.document.custom_fields).toHaveLength(initialLength - 1)
|
||||||
expect(component.customFieldFormFields).toHaveLength(initialLength - 1)
|
expect(component.customFieldFormFields).toHaveLength(initialLength - 1)
|
||||||
|
@ -68,7 +68,6 @@ import {
|
|||||||
PaperlessCustomFieldDataType,
|
PaperlessCustomFieldDataType,
|
||||||
} from 'src/app/data/paperless-custom-field'
|
} from 'src/app/data/paperless-custom-field'
|
||||||
import { PaperlessCustomFieldInstance } from 'src/app/data/paperless-custom-field-instance'
|
import { PaperlessCustomFieldInstance } from 'src/app/data/paperless-custom-field-instance'
|
||||||
import { AbstractInputComponent } from '../common/input/abstract-input'
|
|
||||||
|
|
||||||
enum DocumentDetailNavIDs {
|
enum DocumentDetailNavIDs {
|
||||||
Details = 1,
|
Details = 1,
|
||||||
@ -864,14 +863,11 @@ export class DocumentDetailComponent
|
|||||||
this.updateFormForCustomFields(true)
|
this.updateFormForCustomFields(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeField(input: AbstractInputComponent<any>) {
|
removeField(fieldInstance: PaperlessCustomFieldInstance) {
|
||||||
// ok for now as custom field name unique is a constraint
|
this.document.custom_fields.splice(
|
||||||
const customFieldIndex = this.document.custom_fields.findIndex(
|
this.document.custom_fields.indexOf(fieldInstance),
|
||||||
(f) => f.field.name === input.title
|
1
|
||||||
)
|
)
|
||||||
if (customFieldIndex > -1) {
|
this.updateFormForCustomFields(true)
|
||||||
this.document.custom_fields.splice(customFieldIndex, 1)
|
|
||||||
this.updateFormForCustomFields(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user