diff --git a/goofy-client/apps/goofy-e2e/src/components/vorgang/vorgang-search.e2e.component.ts b/goofy-client/apps/goofy-e2e/src/components/vorgang/vorgang-search.e2e.component.ts index acb6a8b3347a9063df5c6e7daf97f02cb023b92b..46102ab7dfbfeeb69004e0776270ff7ab13abd11 100644 --- a/goofy-client/apps/goofy-e2e/src/components/vorgang/vorgang-search.e2e.component.ts +++ b/goofy-client/apps/goofy-e2e/src/components/vorgang/vorgang-search.e2e.component.ts @@ -25,7 +25,7 @@ import { convertToDataTestId } from "../../support/tech.util"; export class VorgangSearchE2EComponent { - //private readonly locatorButton: string = 'search-button'; + private readonly locatorButton: string = 'search-button'; private readonly locatorInput: string = 'search-input'; private readonly locatorForm: string = 'search-form'; private readonly locatorSearchPreviewList: string = 'search-preview-list'; @@ -41,10 +41,9 @@ export class VorgangSearchE2EComponent { return cy.getTestElement(this.locatorInput); } - //FIXME klaeren wie mit dem ausgebauten Button umgegangen werden soll - // public search(): void { - // cy.getTestElement(this.locatorButton).click(); - // } + public getSearchButton() { + return cy.getTestElement(this.locatorButton); + } public getForm() { return cy.getTestElement(this.locatorForm); diff --git a/goofy-client/apps/goofy-e2e/src/integration/main-tests/vorgang-list/vorgang-list.search.e2e-spec.ts b/goofy-client/apps/goofy-e2e/src/integration/main-tests/vorgang-list/vorgang-list.search.e2e-spec.ts index 958e022ca5cb5e97bbacc4d48178657b8953f32e..2f6bb9973b4d8cb99fd0e79922aec8a589d70d1d 100644 --- a/goofy-client/apps/goofy-e2e/src/integration/main-tests/vorgang-list/vorgang-list.search.e2e-spec.ts +++ b/goofy-client/apps/goofy-e2e/src/integration/main-tests/vorgang-list/vorgang-list.search.e2e-spec.ts @@ -273,7 +273,7 @@ describe('VorgangList Suche', () => { initVorgaenge([vorgangStayByVorgangName, vorgangStayByAktenzeichen, vorgangStayByAntragstellerVorname, vorgangStayByAntragstellerNachname, vorgangStayByRequestId]) initSearchIndex([vorgangStayByVorgangName, vorgangStayByAktenzeichen, vorgangStayByAntragstellerVorname, vorgangStayByAntragstellerNachname, vorgangStayByRequestId]) - loginAsSabine();; + loginAsSabine(); waitForSpinnerToDisappear(); exist(vorgangList.getRoot()); @@ -355,6 +355,17 @@ describe('VorgangList Suche', () => { }) }) + describe('leave search field', () => { + it('without submit should set previously entered text', () => { + doSearchWith('Gewerbe'); + + mainPage.getVorgangSearch().getInput().clear().type('Gewe'); + mainPage.getHeader().getNavigationToggle().click(); + + haveValue(mainPage.getVorgangSearch().getInput(), 'Gewerbe'); + }) + }) + function doSearchWith(searchBy: string): void { mainPage.getVorgangSearch().getInput().clear().type(searchBy + CypressKeyboardActions.ENTER); } diff --git a/goofy-client/apps/goofy-e2e/src/page-objects/header.po.ts b/goofy-client/apps/goofy-e2e/src/page-objects/header.po.ts index 69276520aaa1634e2f3851e448876cf70b36754d..497a51e0e0e2b0ea82aa67144eb7bdf47f9c79e7 100644 --- a/goofy-client/apps/goofy-e2e/src/page-objects/header.po.ts +++ b/goofy-client/apps/goofy-e2e/src/page-objects/header.po.ts @@ -28,6 +28,7 @@ export class HeaderE2EComponent { private readonly locatorTitle: string = 'title'; private readonly locatorRoot: string = 'header'; + private readonly locatorNavigationToggle: string = 'navigation-toggle'; private readonly userSettings: UserSettingsE2EComponent = new UserSettingsE2EComponent(); private readonly currentUserProfile: CurrentUserProfileE2EComponent = new CurrentUserProfileE2EComponent(); @@ -47,4 +48,8 @@ export class HeaderE2EComponent { public getCurrentUserProfile(): CurrentUserProfileE2EComponent { return this.currentUserProfile; } + + public getNavigationToggle() { + return cy.getTestElement(this.locatorNavigationToggle); + } } \ No newline at end of file diff --git a/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-form/kommentar-form.component.spec.ts b/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-form/kommentar-form.component.spec.ts index 110928737df9c6f08d1e7f8aaa3698df988fcbff..b769f92459fadea42984de5cc1230c73950ae41c 100644 --- a/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-form/kommentar-form.component.spec.ts +++ b/goofy-client/libs/kommentar/src/lib/kommentar-list-in-vorgang-container/kommentar-form/kommentar-form.component.spec.ts @@ -41,7 +41,6 @@ describe('KommentarFormComponent', () => { let fixture: ComponentFixture<KommentarFormComponent>; const formService = mock(KommentarFormService); - const service = mock(KommentarService); const kommentarService = mock(KommentarService); beforeEach(async () => { @@ -61,10 +60,6 @@ describe('KommentarFormComponent', () => { provide: KommentarFormService, useValue: formService }, - { - provide: KommentarService, - useValue: service - }, { provide: KommentarService, useValue: kommentarService diff --git a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html index 0fcca3bec30131bde81a015002b2f10255ab928c..30f11b01a16f436b5666ae02460bdd496c0a86b5 100644 --- a/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html +++ b/goofy-client/libs/navigation/src/lib/header-container/header/header.component.html @@ -26,6 +26,7 @@ <header data-test-id="header"> <div class="left"> <goofy-client-icon-button-with-spinner + data-test-id="navigation-toggle" icon="menu" toolTip="Hauptmenü umschalten" (clickEmitter)="toggleMenuEvent.emit(!this.navigationCollapse)"> </goofy-client-icon-button-with-spinner> diff --git a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html index eb6f5e1f72f9581fa5d3cd62d3f6b15915423522..e4daf9170f2c631a806ee859586b794027aa420b 100644 --- a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html +++ b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.html @@ -23,20 +23,28 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<form (ngSubmit)="formService.submit()" [formGroup]="formService.form" [class.search-field--open]="searchAutoComplete.isOpen" class="search-field" data-test-id="search-form"> - <button type="submit" data-test-id="search-button" mat-icon-button aria-label="Vorgang suchen"> +<form (ngSubmit)="submit()" [formGroup]="formService.form" + [class.search-field--open]="searchAutoComplete.isOpen" class="search-field" + data-test-id="search-form"> + <button + #searchSubmitButton + type="submit" + data-test-id="search-button" + mat-icon-button aria-label="Vorgang suchen"> <mat-icon matPrefix class="search-icon">search</mat-icon> </button> <mat-form-field floatLabel="never"> <input matInput data-test-id="search-input" - #searchInput - placeholder="Vorgang suchen" - aria-label="Vorgang suchen" - type="text" - maxlength="50" - [matAutocomplete]="searchAutoComplete" - [formControlName]="formService.SEARCH_FIELD" - name="searchString" /> + #searchInput + placeholder="Vorgang suchen" + aria-label="Vorgang suchen" + type="text" + maxlength="50" + [matAutocomplete]="searchAutoComplete" + [formControlName]="formService.SEARCH_FIELD" + name="searchString" + (focusout)="focusOut($event)" + (focusin)="focusIn()"/> <mat-autocomplete #searchAutoComplete="matAutocomplete" class="vorgang-search" (optionSelected)="formService.submitByPreviewList($event.option.value)"> <goofy-client-spinner [stateResource]="vorgangSearchPreviewList" [class.autocomplete-spinner]="vorgangSearchPreviewList.loading"></goofy-client-spinner> diff --git a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts index 818a68c21c3b7107d994efb14a25e297420ec40e..c5c506cde9f3afd3146937fea13589d975ed2784 100644 --- a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts +++ b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.spec.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms'; +import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIcon } from '@angular/material/icon'; @@ -36,11 +36,12 @@ import { SearchInfo, VorgangHeaderLinkRel, VorgangListService } from '@goofy-cli import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { createVorgangListResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; -import { BehaviorSubject, Subject } from 'rxjs'; import { VorgangSearchAutocompleteOptionsContentComponent } from './vorgang-search-autocomplete-options-content/vorgang-search-autocomplete-options-content.component'; import { VorgangSearchClearButtonComponent } from './vorgang-search-clear-button/vorgang-search-clear-button.component'; import { VorgangSearchComponent } from './vorgang-search.component'; import { VorgangSearchFormService } from './vorgang-search.formservice'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { MatButton } from '@angular/material/button'; describe('VorgangSearchComponent', () => { let component: VorgangSearchComponent; @@ -49,7 +50,10 @@ describe('VorgangSearchComponent', () => { const searchFormService = mock(VorgangSearchFormService); const vorgangListService = { ...mock(VorgangListService), getSearchInfo: () => searchInfoSubj }; - const searchInfoSubj: Subject<SearchInfo> = new BehaviorSubject({ searchString: EMPTY_STRING, changedAfterSearchDone: false }); + const searchInfoSubj: Subject<SearchInfo> = new BehaviorSubject({ + searchString: EMPTY_STRING, + changedAfterSearchDone: false + }); const searchPreviewOption: string = getDataTestClassOf('search-preview-option'); const searchClearButton: string = getDataTestIdOf('vorgang-search-clear-button'); @@ -137,5 +141,76 @@ describe('VorgangSearchComponent', () => { expect(component.searchInput.nativeElement.focus).toHaveBeenCalled(); }) - }) + }); + + describe('submit', () => { + + it('should store entered search value', () => { + jest.spyOn(VorgangSearchFormService.prototype, 'getValue').mockReturnValue('Gewerbe'); + + component.submit(); + + expect(component.previouslyEnteredSearchValue).toEqual('Gewerbe'); + }); + + it('should submit form', () => { + const submitSpy = jest.spyOn(VorgangSearchFormService.prototype, 'submit'); + + component.submit(); + + expect(submitSpy).toHaveBeenCalled(); + }); + }); + + describe('focusIn', () => { + + it('should store current form value', () => { + jest.spyOn(VorgangSearchFormService.prototype, 'getValue').mockReturnValue('Gewerbe'); + + component.focusIn(); + + expect(component.previouslyEnteredSearchValue).toEqual('Gewerbe'); + }); + }); + + describe('focusOut', () => { + + function createSearchSubmitMockButton(nativeElement: any): MatButton { + return { + _elementRef: { + nativeElement: nativeElement + } + } as MatButton; + } + + function createMockFormGroup(searchFieldFormControl: UntypedFormControl): UntypedFormGroup { + return new UntypedFormGroup({ + search: searchFieldFormControl + }); + } + + it('should set new form value when focus out to search button (Lupe)', () => { + const lupeElement = {}; + component.previouslyEnteredSearchValue = 'Gewerbe'; + component.searchSubmitButton = createSearchSubmitMockButton(lupeElement); + const searchFieldFormControl = new UntypedFormControl('Gewe'); + component.formService.form = createMockFormGroup(searchFieldFormControl); + + component.focusOut({ relatedTarget: lupeElement } as FocusEvent); + + expect(searchFieldFormControl.value).toEqual('Gewe'); + }); + + + it('should set previous value', () => { + component.previouslyEnteredSearchValue = 'Gewe'; + component.searchSubmitButton = createSearchSubmitMockButton(null); + const searchFieldFormControl = new UntypedFormControl('Gewerbe'); + component.formService.form = createMockFormGroup(searchFieldFormControl); + + component.focusOut({ relatedTarget: {} } as FocusEvent); + + expect(searchFieldFormControl.value).toEqual('Gewe'); + }); + }); }); \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts index 47b8c58e8e441a66a19aef7c8c7a47df5ff3bbf8..d687df545461c8e78b2dcc5f934ca0c741ba1000 100644 --- a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts +++ b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.component.ts @@ -25,6 +25,7 @@ import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@ import { StateResource } from '@goofy-client/tech-shared'; import { VorgangHeaderLinkRel, VorgangListLinkRel, VorgangListResource } from '@goofy-client/vorgang-shared'; import { VorgangSearchFormService } from './vorgang-search.formservice'; +import { MatButton } from '@angular/material/button'; @Component({ selector: 'goofy-client-vorgang-search', @@ -39,13 +40,36 @@ export class VorgangSearchComponent { @Output() public clearVorgangSearchPreviewList: EventEmitter<void> = new EventEmitter<void>(); @ViewChild('searchInput') searchInput: ElementRef; + @ViewChild('searchSubmitButton') searchSubmitButton: MatButton; + + previouslyEnteredSearchValue: string; readonly vorgangHeaderLinkRel = VorgangHeaderLinkRel; readonly vorgangListLinkRel = VorgangListLinkRel; - constructor(public formService: VorgangSearchFormService) { } + constructor(public formService: VorgangSearchFormService) { + } focus(): void { this.searchInput.nativeElement.focus(); } + + submit(): void { + this.previouslyEnteredSearchValue = this.formService.getValue(); + this.formService.submit(); + } + + focusIn(): void { + this.previouslyEnteredSearchValue = this.formService.getValue(); + } + + focusOut(event: FocusEvent): void { + if (!this.isRelatedTargetSearchButton(event)) { + this.formService.setSearchValue(this.previouslyEnteredSearchValue); + } + } + + private isRelatedTargetSearchButton(event: FocusEvent): boolean { + return event.relatedTarget === this.searchSubmitButton._elementRef.nativeElement; + } } \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts index d9905f2c403b19fded90412c4cf4569d262a4627..fffd5bfb872865d0b496c1d675ca24d4df8e5815 100644 --- a/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts +++ b/goofy-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts @@ -149,6 +149,10 @@ export class VorgangSearchFormService implements OnDestroy { return isEmpty(value) ? null : value; } + public setSearchValue(value: string): void { + this.form.controls[this.SEARCH_FIELD].setValue(value); + } + ngOnDestroy(): void { if (isNotNil(this.subscription)) this.subscription.unsubscribe(); if (isNotNil(this.previewSubscription)) this.previewSubscription.unsubscribe();