Skip to content
Snippets Groups Projects
Verified Commit 48fefe20 authored by Sebastian Bergandy's avatar Sebastian Bergandy :keyboard:
Browse files

OZG-5977 use multi upload in comments

parent bac71359
No related branches found
No related tags found
1 merge request!67OZG-5977 add multi option to file upload button
......@@ -22,32 +22,20 @@
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BinaryFileService } from '@alfa-client/binary-file-shared';
import {
CommandOrder,
CommandResource,
CommandService,
CreateCommand,
} from '@alfa-client/command-shared';
import { CommandOrder, CommandResource, CommandService, CreateCommand } from '@alfa-client/command-shared';
import { NavigationService } from '@alfa-client/navigation-shared';
import {
StateResource,
createEmptyStateResource,
createStateResource,
} from '@alfa-client/tech-shared';
import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared';
import { Mock, mock, useFromMock } from '@alfa-client/test-utils';
import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared';
import { expect } from '@jest/globals';
import { cold, hot } from 'jest-marbles';
import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel';
import { createCommandResource } from 'libs/command-shared/test/command';
import {
createKommentar,
createKommentarListResource,
createKommentarResource,
} from 'libs/kommentar-shared/test/kommentar';
import { createKommentar, createKommentarListResource, createKommentarResource } from 'libs/kommentar-shared/test/kommentar';
import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang';
import { of } from 'rxjs';
import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel';
import { Kommentar, KommentarListResource, KommentarResource } from './kommentar.model';
import { Kommentar, KOMMENTAR_UPLOADED_ATTACHMENTS, KommentarListResource, KommentarResource } from './kommentar.model';
import { KommentarRepository } from './kommentar.repository';
import { KommentarService } from './kommentar.service';
......@@ -120,9 +108,7 @@ describe('KommentarService', () => {
it('should be return', () => {
const result = service.createKommentar(kommentar);
expect(result).toBeObservable(
cold('ab', { a: createEmptyStateResource(true), b: commandStateResource }),
);
expect(result).toBeObservable(cold('ab', { a: createEmptyStateResource(true), b: commandStateResource }));
});
});
......@@ -174,9 +160,7 @@ describe('KommentarService', () => {
it('should be return', () => {
const result = service.editKommentar(kommentarResource, kommentar);
expect(result).toBeObservable(
cold('ab', { a: createEmptyStateResource(true), b: commandStateResource }),
);
expect(result).toBeObservable(cold('ab', { a: createEmptyStateResource(true), b: commandStateResource }));
});
});
......@@ -298,10 +282,7 @@ describe('KommentarService', () => {
const kommentarResource = createKommentarResource([KommentarLinkRel.ATTACHMENTS]);
service.getAttachments(kommentarResource);
expect(binaryFileService.getFiles).toHaveBeenCalledWith(
kommentarResource,
KommentarLinkRel.ATTACHMENTS,
);
expect(binaryFileService.getFiles).toHaveBeenCalledWith(kommentarResource, KommentarLinkRel.ATTACHMENTS);
});
it('should not be loaded if no link available', () => {
......@@ -315,9 +296,7 @@ describe('KommentarService', () => {
it('should create new Kommentare', () => {
const canCreateNewKommentar$ = cold('a', { a: true });
const observable = service.canCreateNewKommentar(
createKommentarListResource([KommentarListLinkRel.CREATE_KOMMENTAR]),
);
const observable = service.canCreateNewKommentar(createKommentarListResource([KommentarListLinkRel.CREATE_KOMMENTAR]));
expect(observable).toBeObservable(canCreateNewKommentar$);
});
......@@ -326,9 +305,7 @@ describe('KommentarService', () => {
const canCreateNewKommentar$ = cold('a', { a: false });
service.formularVisibility$.next(true);
const observable = service.canCreateNewKommentar(
createKommentarListResource([KommentarListLinkRel.CREATE_KOMMENTAR]),
);
const observable = service.canCreateNewKommentar(createKommentarListResource([KommentarListLinkRel.CREATE_KOMMENTAR]));
expect(observable).toBeObservable(canCreateNewKommentar$);
});
......@@ -342,4 +319,12 @@ describe('KommentarService', () => {
expect(observable).toBeObservable(canCreateNewKommentar$);
});
});
describe('clearUploadedFiles', () => {
it('should call binary file service', () => {
service.clearUploadedFiles(KOMMENTAR_UPLOADED_ATTACHMENTS);
expect(binaryFileService.clearUploadedFiles).toHaveBeenCalledWith(KOMMENTAR_UPLOADED_ATTACHMENTS);
});
});
});
......@@ -21,27 +21,16 @@
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BinaryFileListResource, BinaryFileService } from '@alfa-client/binary-file-shared';
import {
CommandOrder,
CommandResource,
CommandService,
CreateCommand,
isDone,
} from '@alfa-client/command-shared';
import { BinaryFileListResource, BinaryFileService, FileUploadType } from '@alfa-client/binary-file-shared';
import { CommandOrder, CommandResource, CommandService, CreateCommand, isDone } from '@alfa-client/command-shared';
import { NavigationService } from '@alfa-client/navigation-shared';
import {
StateResource,
createEmptyStateResource,
createStateResource,
doIfLoadingRequired,
} from '@alfa-client/tech-shared';
import { createEmptyStateResource, createStateResource, doIfLoadingRequired, StateResource } from '@alfa-client/tech-shared';
import { VorgangResource, VorgangService } from '@alfa-client/vorgang-shared';
import { Injectable } from '@angular/core';
import { Params } from '@angular/router';
import { Resource, hasLink } from '@ngxp/rest';
import { hasLink, Resource } from '@ngxp/rest';
import { isNil } from 'lodash-es';
import { BehaviorSubject, Observable, Subscription, of } from 'rxjs';
import { BehaviorSubject, Observable, of, Subscription } from 'rxjs';
import { map, startWith, tap } from 'rxjs/operators';
import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel';
import { Kommentar, KommentarListResource, KommentarResource } from './kommentar.model';
......@@ -49,8 +38,9 @@ import { KommentarRepository } from './kommentar.repository';
@Injectable({ providedIn: 'root' })
export class KommentarService {
readonly kommentarList$: BehaviorSubject<StateResource<KommentarListResource>> =
new BehaviorSubject(createEmptyStateResource<KommentarListResource>());
readonly kommentarList$: BehaviorSubject<StateResource<KommentarListResource>> = new BehaviorSubject(
createEmptyStateResource<KommentarListResource>(),
);
readonly formularVisibility$: BehaviorSubject<boolean> = new BehaviorSubject(false);
private navigationSub: Subscription;
......@@ -67,9 +57,7 @@ export class KommentarService {
private listenToNavigation(): void {
this.unsubscribe();
this.navigationSub = this.navigationService
.urlChanged()
.subscribe((params: Params) => this.onNavigation(params));
this.navigationSub = this.navigationService.urlChanged().subscribe((params: Params) => this.onNavigation(params));
}
private unsubscribe(): void {
......@@ -89,9 +77,7 @@ export class KommentarService {
this.kommentarList$.next({ ...this.kommentarList$.value, reload: true });
}
public getKommentareByVorgang(
vorgang: VorgangResource,
): Observable<StateResource<KommentarListResource>> {
public getKommentareByVorgang(vorgang: VorgangResource): Observable<StateResource<KommentarListResource>> {
doIfLoadingRequired(this.kommentarList$.value, () => this.loadKommentare(vorgang));
return this.kommentarList$.asObservable();
}
......@@ -99,9 +85,7 @@ export class KommentarService {
private loadKommentare(vorgang: VorgangResource): void {
this.setListLoadingTrue();
const sub: Subscription = this.repository
.findKommentare(vorgang)
.subscribe((kommentarList: KommentarListResource) => {
const sub: Subscription = this.repository.findKommentare(vorgang).subscribe((kommentarList: KommentarListResource) => {
this.setKommentarList(kommentarList);
sub.unsubscribe();
});
......@@ -125,11 +109,7 @@ export class KommentarService {
public canCreateNewKommentar(kommentareListResource: KommentarListResource): Observable<boolean> {
return this.formularVisibility$.pipe(
map(
(formularVisibility) =>
!formularVisibility &&
hasLink(kommentareListResource, KommentarListLinkRel.CREATE_KOMMENTAR),
),
map((formularVisibility) => !formularVisibility && hasLink(kommentareListResource, KommentarListLinkRel.CREATE_KOMMENTAR)),
);
}
......@@ -153,15 +133,8 @@ export class KommentarService {
return { order: CommandOrder.CREATE_KOMMENTAR, body: kommentar };
}
public editKommentar(
kommentar: KommentarResource,
toPatch: Kommentar,
): Observable<StateResource<CommandResource>> {
return this.createKommentarCommand(
kommentar,
KommentarLinkRel.EDIT,
this.createEditKommentarCommand(toPatch),
);
public editKommentar(kommentar: KommentarResource, toPatch: Kommentar): Observable<StateResource<CommandResource>> {
return this.createKommentarCommand(kommentar, KommentarLinkRel.EDIT, this.createEditKommentarCommand(toPatch));
}
createEditKommentarCommand(kommentar: Kommentar): CreateCommand {
......@@ -174,9 +147,7 @@ export class KommentarService {
command: CreateCommand,
): Observable<StateResource<CommandResource>> {
return this.commandService.createCommand(resource, linkRel, command).pipe(
tap((createdCommand: StateResource<CommandResource>) =>
this.afterCreateOrEditKommentar(createdCommand),
),
tap((createdCommand: StateResource<CommandResource>) => this.afterCreateOrEditKommentar(createdCommand)),
startWith(createEmptyStateResource<CommandResource>(true)),
);
}
......@@ -195,4 +166,8 @@ export class KommentarService {
}
return of(createEmptyStateResource<BinaryFileListResource>());
}
public clearUploadedFiles(type: FileUploadType): void {
this.binaryFileService.clearUploadedFiles(type);
}
}
......@@ -30,11 +30,13 @@
<ods-multi-file-upload-list-container
[parentFormArrayName]="formServiceClass.FIELD_ATTACHMENTS"
[fileUploadType]="KOMMENTAR_UPLOADED_ATTACHMENTS"
data-test-id="kommentar-multi-file-upload-list"
></ods-multi-file-upload-list-container>
<ods-multi-file-upload-editor
[fileUploadType]="KOMMENTAR_UPLOADED_ATTACHMENTS"
[uploadResource]="kommentarListStateResource.resource"
[uploadLinkRelation]="kommentarListLinkRel.UPLOAD_FILE"
data-test-id="kommentar-multi-file-upload-editor"
></ods-multi-file-upload-editor>
<div class="buttons">
......@@ -54,7 +56,7 @@
icon="clear"
color=""
class="cancel-button"
(clickEmitter)="cancel.emit()"
(clickEmitter)="onCancel()"
>
</ozgcloud-stroked-button-with-spinner>
</div>
......
......@@ -21,21 +21,26 @@
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BinaryFileAttachmentContainerComponent } from '@alfa-client/binary-file';
import { CommandResource } from '@alfa-client/command-shared';
import { KOMMENTAR_UPLOADED_ATTACHMENTS, KommentarLinkRel, KommentarListLinkRel, KommentarListResource, KommentarService, } from '@alfa-client/kommentar-shared';
import { createEmptyStateResource, createErrorStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared';
import { existsAsHtmlElement, getElementComponentFromFixtureByCss, Mock, mock, triggerEvent, useFromMock, } from '@alfa-client/test-utils';
import { OzgcloudStrokedButtonWithSpinnerComponent, TextAreaEditorComponent } from '@alfa-client/ui';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { BinaryFileAttachmentContainerComponent } from '@alfa-client/binary-file';
import { KommentarLinkRel, KommentarService } from '@alfa-client/kommentar-shared';
import { createStateResource } from '@alfa-client/tech-shared';
import { mock } from '@alfa-client/test-utils';
import {
OzgcloudStrokedButtonWithSpinnerComponent,
TextAreaEditorComponent,
} from '@alfa-client/ui';
import { expect } from '@jest/globals';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
import { EMPTY, of } from 'rxjs';
import { createBinaryFileListResource } from '../../../../../binary-file-shared/test/binary-file';
import { createKommentarResource } from '../../../../../kommentar-shared/test/kommentar';
import { createSuccessfullyDoneCommandStateResource } from '../../../../../command-shared/test/command';
import { MultiFileUploadEditorComponent } from '../../../../../design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component';
import { MultiFileUploadListContainerComponent } from '../../../../../design-component/src/lib/form/multi-file-upload-list-container/multi-file-upload-list-container.component';
import { createKommentarListResource, createKommentarResource } from '../../../../../kommentar-shared/test/kommentar';
import { getDataTestIdOf } from '../../../../../tech-shared/test/data-test';
import { createProblemDetail } from '../../../../../tech-shared/test/error';
import { singleColdCompleted } from '../../../../../tech-shared/test/marbles';
import { KommentarFormComponent } from './kommentar-form.component';
import { KommentarFormService } from './kommentar.formservice';
......@@ -43,23 +48,43 @@ describe('KommentarFormComponent', () => {
let component: KommentarFormComponent;
let fixture: ComponentFixture<KommentarFormComponent>;
const formService = mock(KommentarFormService);
const kommentarService = mock(KommentarService);
const cancelButtonTestId: string = getDataTestIdOf('cancel-button');
const fileUploadListTestId: string = getDataTestIdOf('kommentar-multi-file-upload-list');
const fileUploadEditorTestId: string = getDataTestIdOf('kommentar-multi-file-upload-editor');
const kommentarListStateResource: StateResource<KommentarListResource> = createStateResource(createKommentarListResource());
let kommentarService: Mock<KommentarService>;
let formService: KommentarFormService;
beforeEach(() => {
formService = new KommentarFormService(new FormBuilder(), useFromMock(kommentarService));
kommentarService = mock(KommentarService);
});
beforeEach(async () => {
TestBed.overrideComponent(KommentarFormComponent, {
set: {
providers: [
{
provide: KommentarFormService,
useValue: formService,
},
],
},
});
await TestBed.configureTestingModule({
declarations: [
KommentarFormComponent,
MockComponent(TextAreaEditorComponent),
MockComponent(OzgcloudStrokedButtonWithSpinnerComponent),
MockComponent(BinaryFileAttachmentContainerComponent),
MockComponent(MultiFileUploadListContainerComponent),
MockComponent(MultiFileUploadEditorComponent),
],
imports: [MatFormFieldModule, ReactiveFormsModule],
providers: [
{
provide: KommentarFormService,
useValue: formService,
},
{
provide: KommentarService,
useValue: kommentarService,
......@@ -71,6 +96,7 @@ describe('KommentarFormComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(KommentarFormComponent);
component = fixture.componentInstance;
component.kommentarListStateResource = kommentarListStateResource;
fixture.detectChanges();
});
......@@ -82,9 +108,7 @@ describe('KommentarFormComponent', () => {
const patchSpy = jest.spyOn(KommentarFormService.prototype, 'patch').mockImplementation();
const kommentarResource = createKommentarResource([KommentarLinkRel.ATTACHMENTS]);
component.kommentar = kommentarResource;
kommentarService.getAttachments.mockReturnValue(
of(createStateResource(createBinaryFileListResource())),
);
kommentarService.getAttachments.mockReturnValue(of(createStateResource(createBinaryFileListResource())));
component.ngOnChanges();
......@@ -93,9 +117,7 @@ describe('KommentarFormComponent', () => {
it('should load attachments', (done) => {
component.kommentar = createKommentarResource([KommentarLinkRel.ATTACHMENTS]);
kommentarService.getAttachments.mockReturnValue(
of(createStateResource(createBinaryFileListResource())),
);
kommentarService.getAttachments.mockReturnValue(of(createStateResource(createBinaryFileListResource())));
component.ngOnChanges();
......@@ -108,12 +130,131 @@ describe('KommentarFormComponent', () => {
it('should call kommentarService', () => {
const kommentarResource = createKommentarResource([KommentarLinkRel.ATTACHMENTS]);
component.kommentar = kommentarResource;
kommentarService.getAttachments.mockReturnValue(
of(createStateResource(createBinaryFileListResource())),
);
kommentarService.getAttachments.mockReturnValue(of(createStateResource(createBinaryFileListResource())));
component.ngOnChanges();
expect(kommentarService.getAttachments).toHaveBeenCalledWith(kommentarResource);
});
describe('submit', () => {
it('should submit form', () => {
formService.submit = jest.fn().mockReturnValue(EMPTY);
component.submit();
expect(formService.submit).toHaveBeenCalled();
});
it('should set submit in progress', () => {
const command: StateResource<CommandResource> = createSuccessfullyDoneCommandStateResource();
formService.submit = jest.fn().mockReturnValue(of(command));
component.submit();
expect(component.submitInProgress$).toBeObservable(singleColdCompleted(command));
});
it('should clear uploaded attachments', () => {
formService.submit = jest.fn().mockReturnValue(of(createSuccessfullyDoneCommandStateResource()));
component.submit();
component.submitInProgress$.subscribe();
expect(kommentarService.clearUploadedFiles).toHaveBeenCalledWith(KOMMENTAR_UPLOADED_ATTACHMENTS);
});
it('should NOT clear uploaded attachments on loading', () => {
formService.submit = jest.fn().mockReturnValue(of(createEmptyStateResource(true)));
component.submit();
component.submitInProgress$.subscribe();
expect(kommentarService.clearUploadedFiles).not.toHaveBeenCalled();
});
it('should NOT clear uploaded attachments on error', () => {
formService.submit = jest.fn().mockReturnValue(of(createErrorStateResource(createProblemDetail())));
component.submit();
component.submitInProgress$.subscribe();
expect(kommentarService.clearUploadedFiles).not.toHaveBeenCalled();
});
});
describe('onCancel', () => {
beforeEach(() => {
component.cancel.emit = jest.fn();
});
it('should clear uploaded attachments', () => {
component.onCancel();
expect(kommentarService.clearUploadedFiles).toHaveBeenCalledWith(KOMMENTAR_UPLOADED_ATTACHMENTS);
});
it('should emit', () => {
component.onCancel();
expect(component.cancel.emit).toHaveBeenCalled();
});
});
describe('template', () => {
describe('file upload list', () => {
it('should exists', () => {
existsAsHtmlElement(fixture, fileUploadListTestId);
});
it('should have inputs', () => {
fixture.detectChanges();
const fileUploadListComponent: MultiFileUploadListContainerComponent = getElementComponentFromFixtureByCss(
fixture,
fileUploadListTestId,
);
expect(fileUploadListComponent.parentFormArrayName).toEqual(KommentarFormService.FIELD_ATTACHMENTS);
expect(fileUploadListComponent.fileUploadType).toEqual(KOMMENTAR_UPLOADED_ATTACHMENTS);
});
});
describe('file upload editor', () => {
it('should exists', () => {
existsAsHtmlElement(fixture, fileUploadEditorTestId);
});
it('should have inputs', () => {
fixture.detectChanges();
const fileUploadEditorComponent: MultiFileUploadEditorComponent = getElementComponentFromFixtureByCss(
fixture,
fileUploadEditorTestId,
);
expect(fileUploadEditorComponent.fileUploadType).toEqual(KOMMENTAR_UPLOADED_ATTACHMENTS);
expect(fileUploadEditorComponent.uploadResource).toEqual(component.kommentarListStateResource.resource);
expect(fileUploadEditorComponent.uploadLinkRelation).toEqual(KommentarListLinkRel.UPLOAD_FILE);
});
});
describe('cancel button', () => {
it('should exists', () => {
existsAsHtmlElement(fixture, cancelButtonTestId);
});
describe('output', () => {
describe('clickEmitter', () => {
it('should call handler', () => {
component.onCancel = jest.fn();
triggerEvent({ fixture, elementSelector: cancelButtonTestId, name: 'clickEmitter' });
expect(component.onCancel).toHaveBeenCalled();
});
});
});
});
});
});
......@@ -22,14 +22,13 @@
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BinaryFileListLinkRel, BinaryFileResource } from '@alfa-client/binary-file-shared';
import { CommandResource } from '@alfa-client/command-shared';
import { CommandResource, tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared';
import { KOMMENTAR_UPLOADED_ATTACHMENTS, KommentarListLinkRel, KommentarListResource, KommentarResource, KommentarService, } from '@alfa-client/kommentar-shared';
import { createEmptyStateResource, getEmbeddedResources, StateResource } from '@alfa-client/tech-shared';
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { isNil } from 'lodash-es';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { MultiFileUploadListOrientation } from '../../../../../design-component/src/lib/form/multi-file-upload-list-container/multi-file-upload-list-container.component';
import { KommentarFormService } from './kommentar.formservice';
@Component({
......@@ -74,9 +73,16 @@ export class KommentarFormComponent implements OnChanges {
this.formService.patch(this.kommentar);
}
submit(): void {
this.submitInProgress$ = <Observable<StateResource<CommandResource>>>this.formService.submit();
public submit(): void {
this.submitInProgress$ = <Observable<StateResource<CommandResource>>>(
this.formService
.submit()
.pipe(tapOnCommandSuccessfullyDone(() => this.kommentarService.clearUploadedFiles(KOMMENTAR_UPLOADED_ATTACHMENTS)))
);
}
protected readonly ListOrientation = MultiFileUploadListOrientation;
public onCancel(): void {
this.kommentarService.clearUploadedFiles(KOMMENTAR_UPLOADED_ATTACHMENTS);
this.cancel.emit();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment