diff --git a/alfa-client/.prettierignore b/alfa-client/.prettierignore index ee7824bd9c6d18d685c77479a451c9554c111e9d..0eb1899d00dd4018fa9605a6b746a08a07174333 100644 --- a/alfa-client/.prettierignore +++ b/alfa-client/.prettierignore @@ -6,4 +6,5 @@ packages/workspace/src/generators/**/files/**/*.json /.nx/cache .angular -/.nx/workspace-data \ No newline at end of file +/.nx/workspace-data +*.md \ No newline at end of file diff --git a/alfa-client/apps/admin/src/main.ts b/alfa-client/apps/admin/src/main.ts index 7f198e2f5459037b727eb464f2259321948eeaed..de7bed8ea66ac6e222311d9d8c185b4cad477b85 100644 --- a/alfa-client/apps/admin/src/main.ts +++ b/alfa-client/apps/admin/src/main.ts @@ -21,15 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { EnvironmentModule, loadEnvironment } from '@alfa-client/environment-shared'; -import { enableProdMode, importProvidersFrom, Injectable } from '@angular/core'; -import { isNil } from 'lodash-es'; - +import { AggregationMappingProvider } from '@admin-client/reporting-shared'; import { ApiRootModule } from '@alfa-client/api-root-shared'; +import { EnvironmentModule, loadEnvironment } from '@alfa-client/environment-shared'; import { NavigationSharedModule } from '@alfa-client/navigation-shared'; import { registerLocaleData } from '@angular/common'; import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import localeDe from '@angular/common/locales/de'; +import { enableProdMode, importProvidersFrom, Injectable } from '@angular/core'; import { bootstrapApplication, BrowserModule } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; import { @@ -49,6 +48,7 @@ import { ConfigurationsProviders } from 'libs/admin/configuration-shared/src/lib import { PostfachProviders } from 'libs/admin/postfach-shared/src/lib/postfach.providers'; import { SettingsProviders } from 'libs/admin/settings-shared/src/lib/settings.providers'; import { UserProviders } from 'libs/admin/user/src/lib/user.providers'; +import { isNil } from 'lodash-es'; import { HttpBinaryFileInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/http-binary-file.interceptor'; import { HttpXsrfInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/http-xsrf.interceptor'; import { XhrInterceptor } from '../../../libs/tech-shared/src/lib/interceptor/xhr.interceptor'; @@ -97,6 +97,7 @@ loadEnvironment(environment.environmentUrl).then((env) => { PostfachProviders, SettingsProviders, UserProviders, + AggregationMappingProvider, importProvidersFrom( NavigationSharedModule, BrowserModule, diff --git a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts index a861afd0728827a45330d8d5d2d78f63e6dfc5d1..938cc1b0061d2194105629ca70c1e5f8619d881c 100644 --- a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts +++ b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.spec.ts @@ -1,7 +1,7 @@ +import { AdminStatistikFieldsFormComponent } from '@admin-client/statistik'; import { existsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockComponent } from 'ng-mocks'; -import { AdminStatistikFieldsFormComponent } from '../../../../../../libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component'; import { getDataTestIdOf } from '../../../../../../libs/tech-shared/test/data-test'; import { StatistikFieldsFormPageComponent } from './statistik-fields-form-page.component'; diff --git a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts index e249c034417fc7133d8cc2c5db690ddcb94ea50f..c4122b72d148865b4219d5446a3c2060574b23e7 100644 --- a/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts +++ b/alfa-client/apps/admin/src/pages/statistik/statistik-fields-form-page/statistik-fields-form-page.component.ts @@ -1,6 +1,6 @@ +import { AdminStatistikFieldsFormComponent } from '@admin-client/statistik'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { AdminStatistikFieldsFormComponent } from '../../../../../../libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component'; @Component({ selector: 'statistik-fields-form-page', diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-historie.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-historie.cy.ts index 0507e30112e6638ca225665737c64002a6ec99f0..e6a92b958deb0a0f2947b27bbb14283d2bee379d 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-historie.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-historie.cy.ts @@ -111,6 +111,8 @@ describe('Bescheid History', () => { uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); notExist(bescheidWizard.getAttachmentUploadSpinner()); bescheidWizard.getWeiterButton().click(); + waitForSpinnerToDisappear(); + bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); waitForSpinnerToDisappear(); @@ -144,6 +146,8 @@ describe('Bescheid History', () => { uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); notExist(bescheidWizard.getBescheidUploadSpinner()); bescheidWizard.getWeiterButton().click(); + waitForSpinnerToDisappear(); + bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); waitForSpinnerToDisappear(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts index 6e602187c7712d336ea0cc1532cab9cab587bd52..a6d7e8cc4795a1794745211f20c99a6f4a7e213e 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts @@ -28,36 +28,15 @@ import { VorgangBescheidWizardE2EComponent } from 'apps/alfa-e2e/src/components/ import { VorgangBescheideE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-bescheide.e2e.component'; import { VorgangFormularButtonsE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular-buttons.e2e.components'; import { VorgangSubnavigationE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-subnavigation'; -import { - VorgangE2E, - VorgangStatusE2E, - vorgangStatusLabelE2E, -} from 'apps/alfa-e2e/src/model/vorgang'; -import { - TEST_FILE_BESCHEID_ANHANG_VALID, - TEST_FILE_BESCHEID_VALID, -} from 'apps/alfa-e2e/src/support/data.util'; +import { VorgangE2E, VorgangStatusE2E, vorgangStatusLabelE2E } from 'apps/alfa-e2e/src/model/vorgang'; +import { TEST_FILE_BESCHEID_ANHANG_VALID, TEST_FILE_BESCHEID_VALID } from 'apps/alfa-e2e/src/support/data.util'; import { getAdjustedDateGerman } from 'apps/alfa-e2e/src/support/tech.util'; import 'cypress-real-events/support'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { - countDownloadFiles, - deleteDownloadFolder, - dropCollections, - readFileFromDownloads, -} from '../../../support/cypress-helper'; -import { - contains, - enterWith, - exist, - haveLength, - haveText, - notBeVisible, - notExist, - visible, -} from '../../../support/cypress.util'; +import { countDownloadFiles, deleteDownloadFolder, dropCollections, readFileFromDownloads, } from '../../../support/cypress-helper'; +import { contains, enterWith, exist, haveLength, haveText, notBeVisible, notExist, visible } from '../../../support/cypress.util'; import { initUsermanagerUsers, loginAsSabine } from '../../../support/user-util'; import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang-util'; @@ -79,11 +58,9 @@ describe('Bescheid Info anzeigen', () => { status: VorgangStatusE2E.IN_BEARBEITUNG, }; - const vorgangFormularButtons: VorgangFormularButtonsE2EComponent = - vorgangPage.getFormularButtons(); + const vorgangFormularButtons: VorgangFormularButtonsE2EComponent = vorgangPage.getFormularButtons(); - const vorgangSubnavigationButtons: VorgangSubnavigationE2EComponent = - vorgangPage.getSubnavigation(); + const vorgangSubnavigationButtons: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); const bescheide: VorgangBescheideE2EComponent = vorgangPage.getBescheide(); @@ -129,10 +106,7 @@ describe('Bescheid Info anzeigen', () => { }); it('should show status In Bearbeitung', () => { - haveText( - vorgangPage.getVorgangDetailHeader().getStatus(), - vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], - ); + haveText(vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG]); }); it('should show Bescheid as Entwurf with abgelehnt status and date yesterday', () => { @@ -149,6 +123,8 @@ describe('Bescheid Info anzeigen', () => { describe('should show complete info after saving Bescheid', () => { it('should set date to two days before and continue to step 2', () => { vorgangFormularButtons.getBescheidenButton().click(); + waitForSpinnerToDisappear(); + enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-2)); bescheidWizard.getWeiterButton().click(); bescheidWizard.isBescheidDocumentsStep(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-nur-speichern.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-nur-speichern.cy.ts index d2e2df9695234a26ff907b7cdde54563df9ca977..e6c7a90ef2a6e1f91f94d7ef31b83235732aca8c 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-nur-speichern.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-nur-speichern.cy.ts @@ -86,6 +86,8 @@ describe('Bescheid speichern', () => { uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); notExist(bescheidWizard.getAttachmentUploadSpinner()); bescheidWizard.getWeiterButton().click(); + waitForSpinnerToDisappear(); + bescheidWizard.getSaveButton().click(); contains(bescheidWizard.getStatusText(), abgelehntText + ' ' + getAdjustedDateGerman(-1)); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-abschliessen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-abschliessen.cy.ts index 81bbd0cdfbe44f1480fcd852caa3f0ae3657e701..e7cbc9d44df2099c5443729d0bb2b1187dd4e5f9 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-abschliessen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-abschliessen.cy.ts @@ -27,7 +27,7 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { VorgangE2E, VorgangMessagesE2E, VorgangStatusE2E, vorgangStatusLabelE2E } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections, wait } from '../../../support/cypress-helper'; +import { dropCollections } from '../../../support/cypress-helper'; import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { loginAsSabine } from '../../../support/user-util'; import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang-util'; @@ -132,15 +132,12 @@ describe('Vorgang abschliessen', () => { it('should close snackbar on revoke', () => { snackBar.getRevokeButton().click(); - wait(500); waitForSpinnerToDisappear(); - notExist(snackBar.getMessage()); }); - it.skip('UNSTABLE: should show status Beschieden', () => { + it('should show status Beschieden', () => { notExist(vorgangPage.getProgressBar()); - haveText(vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.BESCHIEDEN]); }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-wiedereroeffnen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-wiedereroeffnen.cy.ts index 246013c7b169d348bd6882b3cd20f1d211f1b6fa..a589537f1b6c1ebc75b77173ca51aa829824bc8a 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-wiedereroeffnen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-wiedereroeffnen.cy.ts @@ -27,7 +27,7 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { VorgangE2E, VorgangMessagesE2E, VorgangStatusE2E, vorgangStatusLabelE2E } from '../../../model/vorgang'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections, wait } from '../../../support/cypress-helper'; +import { dropCollections } from '../../../support/cypress-helper'; import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { loginAsSabine } from '../../../support/user-util'; import { buildVorgang, createVorgang, initVorgaenge, objectIds } from '../../../support/vorgang-util'; @@ -137,8 +137,6 @@ describe('Vorgang wiedereroeffnen', () => { describe('by icon-button', () => { it('should have status Beschieden', () => { - wait(500); - haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[vorgangBeschiedenWiedereroeffnenRevoke.status], @@ -155,7 +153,6 @@ describe('Vorgang wiedereroeffnen', () => { it('should click on snackbar revoke', () => { snackbar.getRevokeButton().click(); - wait(500); waitForSpinnerToDisappear(); notExist(snackbar.getMessage()); @@ -261,7 +258,6 @@ describe('Vorgang wiedereroeffnen', () => { it('should click snackbar revoke', () => { snackbar.getRevokeButton().click(); - wait(500); waitForSpinnerToDisappear(); notExist(snackbar.getMessage()); diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts index 69e7bccdfc44a17993598e6c49a8e81bd066db33..3dbd6155939b778a411f9b2fd18822286feaf958 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts @@ -46,10 +46,9 @@ export function notExist(element: any): void { } export function haveText(element: any, text: string): void { - element - .invoke('text') - .then((elementText) => elementText.trim()) - .should('equal', text); + element.invoke('text').should(($text) => { + expect($text.trim()).to.equal(text); + }); } export function haveTextWithoutChildren(element: any, text: string): void { diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts index 043e5c2e056bb11eb29983c5eef09ce0fa17a74e..43bc3d38565e870585b2ba846b8c2d681d977014 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.spec.ts @@ -39,6 +39,7 @@ describe('AdminOrganisationsEinheitRepository', () => { (group: GroupRepresentation): AdminOrganisationsEinheit => ({ name: group.name, id: group.id, + attributes: group.attributes, }), ); @@ -63,6 +64,7 @@ describe('AdminOrganisationsEinheitRepository', () => { const organisationsEinheit: AdminOrganisationsEinheit = { name: groupRep.name, id: groupRep.id, + attributes: groupRep.attributes, }; beforeEach(() => { diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts index 5dc1b0ee3bf1ea39b1668edf2fa7379a6836f81f..c2d454434e07562492f0c929bc710d7793a70403 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/organisations-einheit.repository.ts @@ -40,6 +40,7 @@ export class AdminOrganisationsEinheitRepository { return { id: group.id, name: group.name, + attributes: group.attributes, }; } } diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts index 8a4b105a948caf4f288a8639468d888d2fb3c255..3a93c42e6b0042f02dd9f509969444431ce6c374 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/lib/organisations-einheit.model.ts @@ -24,4 +24,5 @@ export interface AdminOrganisationsEinheit { id?: string; name: string; + attributes: { [key: string]: string[] }; } diff --git a/alfa-client/libs/admin/organisations-einheit-shared/src/test/organisations-einheit.ts b/alfa-client/libs/admin/organisations-einheit-shared/src/test/organisations-einheit.ts index 6bb1baad301e83997665d96347628cf092f1d5ea..aac833df1f46a18a007d4c780e5673dbe5a7ec19 100644 --- a/alfa-client/libs/admin/organisations-einheit-shared/src/test/organisations-einheit.ts +++ b/alfa-client/libs/admin/organisations-einheit-shared/src/test/organisations-einheit.ts @@ -29,6 +29,9 @@ export function createAdminOrganisationsEinheit(): AdminOrganisationsEinheit { return { id: faker.string.uuid(), name: faker.lorem.word(), + attributes: { + organisationseinheitId: [faker.string.uuid()], + }, }; } diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts index 562b00dafa62d2291eae204de2e79ed2d5b8b98f..2f207e5573f8398234d5d01e201430a4c4e34303 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.spec.ts @@ -152,7 +152,10 @@ describe('OrganisationsEinheitContainerComponent', () => { it('should call organisationsEinheitService create', () => { component.handleDialogResponse(organisationsEinheitResource); - expect(organisationsEinheitService.create).toHaveBeenCalledWith({ name: organisationsEinheitResource.name }); + expect(organisationsEinheitService.create).toHaveBeenCalledWith({ + name: organisationsEinheitResource.name, + attributes: { organisationseinheitId: [organisationsEinheitResource.organisationEinheitId] }, + }); }); it('should set createOrganisationsEinheitProgress$', () => { diff --git a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts index e12a8a85f72d90a48141f6794852f69ebf607085..31e3a25fa7f482c8b85cf9de2a97e8623596d555 100644 --- a/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts +++ b/alfa-client/libs/admin/organisations-einheit/src/lib/organisations-einheit-container/organisations-einheit-container.component.ts @@ -80,6 +80,9 @@ export class OrganisationsEinheitContainerComponent implements OnInit, OnDestroy mapToAdminOrganisationsEinheit(organisationsEinheitResource: OrganisationsEinheitResource): AdminOrganisationsEinheit { return { name: organisationsEinheitResource.name, + attributes: { + organisationseinheitId: [organisationsEinheitResource.organisationEinheitId], + }, }; } } diff --git a/alfa-client/libs/admin/postfach-shared/project.json b/alfa-client/libs/admin/postfach-shared/project.json index 6489dc23d02f10a0660d03ccd39ccebae8dadc5d..e02554623fc02a4f2929efea43c8669d24ecdf3a 100644 --- a/alfa-client/libs/admin/postfach-shared/project.json +++ b/alfa-client/libs/admin/postfach-shared/project.json @@ -10,7 +10,7 @@ "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { - "tsConfig": "libs/admin//postfach-shared/tsconfig.spec.json", + "tsConfig": "libs/admin/postfach-shared/tsconfig.spec.json", "jestConfig": "libs/admin/postfach-shared/jest.config.ts" } }, diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts index 48043163dea114eba85fa881d9047ec90f8ef24a..a617ed3abf3f31d102889877f0d10a8117bb23b2 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach-resource.service.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { SettingsService } from '@admin-client/settings-shared'; +import { SettingName, SettingsService } from '@admin-client/settings-shared'; import { ApiResourceService, ResourceRepository, ResourceServiceConfig } from '@alfa-client/tech-shared'; import { PostfachLinkRel } from './postfach.linkrel'; import { PostfachResource } from './postfach.model'; @@ -34,7 +34,7 @@ export function createPostfachResourceService(repository: ResourceRepository, se function buildConfig(settingService: SettingsService): ResourceServiceConfig<PostfachResource> { return { - resource: settingService.getPostfach(), + resource: settingService.get(SettingName.POSTFACH), getLinkRel: PostfachLinkRel.SELF, edit: { linkRel: PostfachLinkRel.SELF }, }; diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts index c592dec3e6621543ca69e9ee23abdeea1ec6f4ec..e3ce2fb8bd6153796bea9bf82422718d2324721e 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.model.ts @@ -21,8 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Resource } from '@ngxp/rest'; -import { SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; +import { SettingItemResource } from 'libs/admin/settings-shared/src/lib/settings.model'; export interface Absender { name: string; @@ -37,9 +36,4 @@ export interface Postfach { signatur: string; } -export declare type PostfachSettingsItem = { - name: SettingName.POSTFACH; - settingBody: Postfach; -}; - -export declare type PostfachResource = Resource & PostfachSettingsItem; +export declare type PostfachResource = SettingItemResource<Postfach>; diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts index d779cb50dc2173e7dee4dc929a7e11f5d259724d..2441dc06db2b787fe3f33389d1fb2daae412e3bf 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.spec.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { SettingItem } from '@admin-client/settings-shared'; import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; @@ -31,7 +32,7 @@ import { singleCold, singleHot } from 'libs/tech-shared/test/marbles'; import { Observable, of } from 'rxjs'; import { createPostfachResource, createPostfachSettingItem } from '../test/postfach'; import { PostfachResourceService } from './postfach-resource.service'; -import { PostfachResource, PostfachSettingsItem } from './postfach.model'; +import { Postfach, PostfachResource } from './postfach.model'; import { PostfachService } from './postfach.service'; describe('PostfachService', () => { @@ -78,7 +79,7 @@ describe('PostfachService', () => { }); describe('save', () => { - const postfachSettingsItem: PostfachSettingsItem = createPostfachSettingItem(); + const postfachSettingsItem: SettingItem<Postfach> = createPostfachSettingItem(); const postfachResource: PostfachResource = createPostfachResource(); const postfachStateResource: StateResource<PostfachResource> = createStateResource(postfachResource); diff --git a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts index 19cf4ef6cf561586b44f3afee70e4c716ec54052..3321cfc1690bd504a1e0f94ac6eda627170a29c9 100644 --- a/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts +++ b/alfa-client/libs/admin/postfach-shared/src/lib/postfach.service.ts @@ -21,13 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { SettingName } from '@admin-client/settings-shared'; +import { SettingItem, SettingName } from '@admin-client/settings-shared'; import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { SnackBarService } from '@alfa-client/ui'; import { inject, Injectable } from '@angular/core'; import { Observable, startWith, tap } from 'rxjs'; import { PostfachResourceService } from './postfach-resource.service'; -import { Postfach, PostfachResource, PostfachSettingsItem } from './postfach.model'; +import { Postfach, PostfachResource } from './postfach.model'; @Injectable() export class PostfachService { @@ -51,7 +51,7 @@ export class PostfachService { } } - private buildPostfachSettingItem(postfach: Postfach): PostfachSettingsItem { + private buildPostfachSettingItem(postfach: Postfach): SettingItem<Postfach> { return { name: SettingName.POSTFACH, settingBody: postfach, diff --git a/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts b/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts index 93d7492c32b0e0b595a447757952ee358b0e1345..88132bc3d88c73d130f50caed0da9aa15ae68d5b 100644 --- a/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts +++ b/alfa-client/libs/admin/postfach-shared/src/test/postfach.ts @@ -22,9 +22,9 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { faker } from '@faker-js/faker'; -import { SettingItemResource, SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; +import { SettingItem, SettingName } from 'libs/admin/settings-shared/src/lib/settings.model'; import { toResource } from 'libs/tech-shared/test/resource'; -import { Postfach, PostfachResource, PostfachSettingsItem } from '../lib/postfach.model'; +import { Postfach, PostfachResource } from '../lib/postfach.model'; export function createPostfach(): Postfach { return { @@ -39,7 +39,7 @@ export function createPostfach(): Postfach { }; } -export function createPostfachSettingItem(): PostfachSettingsItem { +export function createPostfachSettingItem(): SettingItem<Postfach> { return { name: SettingName.POSTFACH, settingBody: createPostfach(), @@ -49,10 +49,3 @@ export function createPostfachSettingItem(): PostfachSettingsItem { export function createPostfachResource(): PostfachResource { return toResource(createPostfachSettingItem()); } - -export function createSettingItemResource(): SettingItemResource { - return toResource({ - name: faker.word.sample(), - settingBody: {}, - }); -} diff --git a/alfa-client/libs/admin/reporting-shared/.eslintrc.json b/alfa-client/libs/admin/reporting-shared/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..7474579d583c598ae092a906b3e6cf1ad3c08a50 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "admin", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "admin", + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"], + "rules": {} + } + ] +} diff --git a/alfa-client/libs/admin/reporting-shared/README.md b/alfa-client/libs/admin/reporting-shared/README.md new file mode 100644 index 0000000000000000000000000000000000000000..be5bb22469ec116a5b8b3d498db669555c92da4e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/README.md @@ -0,0 +1,7 @@ +# admin-reporting-shared + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test admin-reporting-shared` to execute the unit tests. diff --git a/alfa-client/libs/admin/reporting-shared/jest.config.ts b/alfa-client/libs/admin/reporting-shared/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4f392a061baff74cb98d86a02f0100960384315 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/jest.config.ts @@ -0,0 +1,21 @@ +export default { + displayName: 'admin-reporting-shared', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], + coverageDirectory: '../../../coverage/libs/admin/reporting-shared', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '<rootDir>/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/alfa-client/libs/admin/reporting-shared/project.json b/alfa-client/libs/admin/reporting-shared/project.json new file mode 100644 index 0000000000000000000000000000000000000000..f7a760900530aff273dc117494292f1e2b028824 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/project.json @@ -0,0 +1,22 @@ +{ + "name": "admin-reporting-shared", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/admin/reporting-shared/src", + "prefix": "admin", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "tsConfig": "libs/admin/reporting-shared/tsconfig.spec.json", + "jestConfig": "libs/admin/reporting-shared/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] + } + } +} diff --git a/alfa-client/libs/admin/reporting-shared/src/index.ts b/alfa-client/libs/admin/reporting-shared/src/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..44ab78233d97deaf497e327e28f71c431ae6960e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/index.ts @@ -0,0 +1,4 @@ +export * from './lib/aggregation-mapping-resource.service'; +export * from './lib/aggregation-mapping.model'; +export * from './lib/aggregation-mapping.provider'; +export * from './lib/aggregation-mapping.service'; diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..65eb7a8e1680b97f12cdba2556ae8d9d9041883c --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts @@ -0,0 +1,26 @@ +import { ConfigurationLinkRel, ConfigurationResource, ConfigurationService } from '@admin-client/configuration-shared'; +import { ListResourceServiceConfig, ResourceListService, ResourceRepository } from '@alfa-client/tech-shared'; +import { AggregationMappingListLinkRel } from './aggregation-mapping.linkrel'; +import { AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; + +export class AggregationMappingListResourceService extends ResourceListService< + ConfigurationResource, + AggregationMappingListResource, + AggregationMappingResource +> {} + +export function createAggregationMappingResourceService( + repository: ResourceRepository, + configurationService: ConfigurationService, +) { + return new ResourceListService(buildConfig(configurationService), repository); +} + +function buildConfig(configurationService: ConfigurationService): ListResourceServiceConfig<ConfigurationResource> { + return { + baseResource: configurationService.get(), + listLinkRel: ConfigurationLinkRel.AGGREGATION_MAPPINGS, + listResourceListLinkRel: AggregationMappingListLinkRel.LIST, + createLinkRel: AggregationMappingListLinkRel.SELF, + }; +} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4a02bfd7087b105a9d0d2dd816aa2b180a4db9c --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.linkrel.ts @@ -0,0 +1,4 @@ +export enum AggregationMappingListLinkRel { + LIST = 'aggregationMappings', + SELF = 'self', +} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..0db6cb4e397768b10781b3c34c9379fc866cec2b --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.model.ts @@ -0,0 +1,20 @@ +import { ListResource } from '@alfa-client/tech-shared'; +import { Resource } from '@ngxp/rest'; + +export interface AggregationMapping { + formIdentifier: FormIdentifier; + mappings: FieldMapping[]; +} + +export interface FormIdentifier { + formEngineName: string; + formId: string; +} + +export interface FieldMapping { + sourcePath: string; + targetPath: string; +} + +export interface AggregationMappingResource extends AggregationMapping, Resource {} +export interface AggregationMappingListResource extends ListResource {} diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..888d7154e81dbfe53fb7476e4de8835463de6d25 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.provider.ts @@ -0,0 +1,17 @@ +import { ResourceRepository } from '@alfa-client/tech-shared'; +import { Provider } from '@angular/core'; +import { ConfigurationService } from 'libs/admin/configuration-shared/src/lib/configuration.service'; +import { + AggregationMappingListResourceService, + createAggregationMappingResourceService, +} from './aggregation-mapping-resource.service'; +import { AggregationMappingService } from './aggregation-mapping.service'; + +export const AggregationMappingProvider: Provider[] = [ + { + provide: AggregationMappingListResourceService, + useFactory: createAggregationMappingResourceService, + deps: [ResourceRepository, ConfigurationService], + }, + AggregationMappingService, +]; diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ecf4387ceee6a3dbdd6775f8ef4fa3a306144002 --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.spec.ts @@ -0,0 +1,79 @@ +import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { TestBed } from '@angular/core/testing'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { Observable } from 'rxjs'; +import { + createAggregationMapping, + createAggregationMappingListResource, + createAggregationMappingResource, +} from '../../test/aggregation-mapping'; +import { AggregationMappingListResourceService } from './aggregation-mapping-resource.service'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; +import { AggregationMappingService } from './aggregation-mapping.service'; + +describe('AggregationMappingService', () => { + let service: AggregationMappingService; + let listResourceService: Mock<AggregationMappingListResourceService>; + + beforeEach(() => { + listResourceService = mock(AggregationMappingListResourceService); + + TestBed.configureTestingModule({ + providers: [AggregationMappingService, { provide: AggregationMappingListResourceService, useValue: listResourceService }], + }); + + service = TestBed.inject(AggregationMappingService); + }); + + it('should create', () => { + expect(service).toBeTruthy(); + }); + + describe('get list', () => { + const aggregationMappingListResource: AggregationMappingListResource = createAggregationMappingListResource(); + const aggregationMappingListStateResource: StateResource<AggregationMappingListResource> = + createStateResource(aggregationMappingListResource); + + beforeEach(() => { + listResourceService.getList = jest.fn().mockReturnValue(singleCold(aggregationMappingListStateResource)); + }); + + it('should call listResourceService', () => { + service.getList(); + + expect(listResourceService.getList).toHaveBeenCalled(); + }); + + it('should return value', () => { + const loadedAggregationMappingListResource: Observable<StateResource<AggregationMappingListResource>> = service.getList(); + + expect(loadedAggregationMappingListResource).toBeObservable(singleCold(aggregationMappingListStateResource)); + }); + }); + + describe('create', () => { + const aggregationMappingResource: AggregationMappingResource = createAggregationMappingResource(); + const aggregationMappingStateResource: StateResource<AggregationMappingResource> = + createStateResource(aggregationMappingResource); + + const aggregationMapping: AggregationMapping = createAggregationMapping(); + + beforeEach(() => { + listResourceService.create = jest.fn().mockReturnValue(singleCold(aggregationMappingStateResource)); + }); + + it('should call resourceService', () => { + service.create(aggregationMapping); + + expect(listResourceService.create).toHaveBeenCalledWith(aggregationMapping); + }); + + it('should return value', () => { + const loadedAggregationMappingResource: Observable<StateResource<AggregationMappingResource>> = + service.create(aggregationMapping); + + expect(loadedAggregationMappingResource).toBeObservable(singleCold(aggregationMappingStateResource)); + }); + }); +}); diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..20a8ce6c1ccda133323bac99a671d887e77afb8e --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping.service.ts @@ -0,0 +1,18 @@ +import { StateResource } from '@alfa-client/tech-shared'; +import { Injectable, inject } from '@angular/core'; +import { Observable } from 'rxjs'; +import { AggregationMappingListResourceService } from './aggregation-mapping-resource.service'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; + +@Injectable() +export class AggregationMappingService { + readonly listService = inject(AggregationMappingListResourceService); + + public getList(): Observable<StateResource<AggregationMappingListResource>> { + return this.listService.getList(); + } + + public create(toCreate: AggregationMapping): Observable<StateResource<AggregationMappingResource>> { + return this.listService.create(toCreate); + } +} diff --git a/alfa-client/libs/admin/reporting-shared/src/test-setup.ts b/alfa-client/libs/admin/reporting-shared/src/test-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..c408668266d2fec3a9803c0ec044bc163fb987fe --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/src/test-setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom'; +import 'jest-preset-angular/setup-jest'; + +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +getTestBed().resetTestEnvironment(); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { + teardown: { destroyAfterEach: false }, + errorOnUnknownProperties: true, + errorOnUnknownElements: true, +}); diff --git a/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts b/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts new file mode 100644 index 0000000000000000000000000000000000000000..acbcc9fa75722396e4a410ea77e9157a2a8ce8fc --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/test/aggregation-mapping.ts @@ -0,0 +1,35 @@ +import { faker } from '@faker-js/faker'; +import { times } from 'lodash-es'; +import { LinkRelationName } from '../../../tech-shared/src'; +import { toResource } from '../../../tech-shared/test/resource'; +import { AggregationMapping, AggregationMappingListResource, AggregationMappingResource } from '../src'; +import { AggregationMappingListLinkRel } from '../src/lib/aggregation-mapping.linkrel'; + +export function createAggregationMapping(): AggregationMapping { + return { + formIdentifier: { + formEngineName: faker.lorem.word(), + formId: faker.string.uuid(), + }, + mappings: [ + { + sourcePath: faker.lorem.word(), + targetPath: faker.lorem.word(), + }, + ], + }; +} + +export function createAggregationMappingResource(linkRelations: LinkRelationName[] = []): AggregationMappingResource { + return toResource(createAggregationMapping(), linkRelations); +} + +export function createAggregationMappingResources(linkRelations: LinkRelationName[] = []): AggregationMappingResource[] { + return times(10, () => createAggregationMappingResource(linkRelations)); +} + +export function createAggregationMappingListResource(linkRelations: LinkRelationName[] = []): AggregationMappingListResource { + return toResource({}, linkRelations, { + [AggregationMappingListLinkRel.LIST]: createAggregationMappingResources(), + }); +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.json b/alfa-client/libs/admin/reporting-shared/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..8ca9ad312c2bd4dc364383853ddd91a2ed8f86fd --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "target": "es2022" + } +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json b/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json new file mode 100644 index 0000000000000000000000000000000000000000..8441346f6e5858b2ef4235cb3c3160eda256f94a --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/**/*.spec.ts", "src/test-setup.ts", "jest.config.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json b/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..723782fbd367969806c5992aea882773ab65af8b --- /dev/null +++ b/alfa-client/libs/admin/reporting-shared/tsconfig.spec.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "target": "ES2022", + "useDefineForClassFields": false + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] +} diff --git a/alfa-client/libs/admin/settings-shared/jest.config.ts b/alfa-client/libs/admin/settings-shared/jest.config.ts index e9e4c0e81848959b2203b3551b8a0e999c127500..9c940034939a689a0f138e47d5c3a5c34d2bb92e 100644 --- a/alfa-client/libs/admin/settings-shared/jest.config.ts +++ b/alfa-client/libs/admin/settings-shared/jest.config.ts @@ -1,9 +1,8 @@ export default { - displayName: 'admin-settings', + displayName: 'admin-settings-shared', preset: '../../../jest.preset.js', setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], - globals: {}, - coverageDirectory: '../../coverage/libs/admin/settings-shared', + coverageDirectory: '../../../coverage/libs/admin/settings-shared', transform: { '^.+\\.(ts|mjs|js|html)$': [ 'jest-preset-angular', @@ -13,7 +12,7 @@ export default { }, ], }, - transformIgnorePatterns: ['node_modules/(?!.pnpm|.*\\.mjs$)'], + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], snapshotSerializers: [ 'jest-preset-angular/build/serializers/no-ng-attributes', 'jest-preset-angular/build/serializers/ng-snapshot', diff --git a/alfa-client/libs/admin/settings-shared/project.json b/alfa-client/libs/admin/settings-shared/project.json index 702a27446f83db42dc97ec9409c8f3d323dcbb65..02e461c6816868190ac3b45dea543a934a1d6d1d 100644 --- a/alfa-client/libs/admin/settings-shared/project.json +++ b/alfa-client/libs/admin/settings-shared/project.json @@ -1,15 +1,11 @@ { - "name": "admin-settings", + "name": "admin-settings-shared", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/admin/settings-shared/src", - "projectType": "library", "prefix": "admin", + "projectType": "library", "tags": [], "targets": { - "lint": { - "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] - }, "test": { "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], @@ -17,6 +13,10 @@ "tsConfig": "libs/admin/settings-shared/tsconfig.spec.json", "jestConfig": "libs/admin/settings-shared/jest.config.ts" } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"] } } } diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts index a75f91afec901a8c969c00ede9a8631766421f1f..7e9df14e6ec13929f720978ada7ebcb3e74c5ef9 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings-resource.service.ts @@ -4,7 +4,11 @@ import { Resource } from '@ngxp/rest'; import { SettingListLinkRel } from './settings.linkrel'; import { SettingItemResource, SettingListResource } from './settings.model'; -export class SettingListResourceService extends ResourceListService<Resource, SettingListResource, SettingItemResource> {} +export class SettingListResourceService extends ResourceListService< + Resource, + SettingListResource, + SettingItemResource<unknown> +> {} export function createSettingListResourceService(repository: ResourceRepository, configurationService: ConfigurationService) { return new ResourceListService(buildConfig(configurationService), repository); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts index 263fca126d53eb889e1701e931a8b32a38899145..2e97584b7934ea105c828cbb2523c0d16ce431a1 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.model.ts @@ -25,16 +25,16 @@ import { ListResource } from '@alfa-client/tech-shared'; import { Resource } from '@ngxp/rest'; export interface SettingListResource extends ListResource { - _embedded: { settings: SettingItemResource[] }; + _embedded: { settings: SettingItemResource<unknown>[] }; } export enum SettingName { POSTFACH = 'Postfach', } -export interface SettingItem { +export interface SettingItem<T> { name: SettingName; - settingBody: unknown; + settingBody: T; } -export declare type SettingItemResource = Resource & SettingItem; +export interface SettingItemResource<T> extends SettingItem<T>, Resource {} diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts index 9b24d56767a9f5de93f738638d976333cfa93af7..1cf23c357186c0480ce12a349497c2c1f66139e5 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.spec.ts @@ -21,16 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; import { StateResource, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; import { Mock, mock } from '@alfa-client/test-utils'; import { TestBed } from '@angular/core/testing'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { Observable, of } from 'rxjs'; -import { createPostfachResource, createSettingItemResource } from '../../../postfach-shared/src/test/postfach'; -import { createSettingsListResource } from '../../test/settings'; +import { createSettingItemResource, createSettingsListResource } from '../../test/settings'; import { SettingListResourceService } from './settings-resource.service'; -import { SettingListResource } from './settings.model'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; import { SettingsService } from './settings.service'; describe('SettingsService', () => { @@ -51,11 +49,12 @@ describe('SettingsService', () => { expect(service).toBeTruthy(); }); - describe('get Postfach', () => { - const postfachResource = createPostfachResource(); - const postfachStateResource: StateResource<PostfachResource> = createStateResource(postfachResource); + describe('get by setting name', () => { + const settingItemName: SettingName = <SettingName>'DUMMY'; + const settingItemResource: SettingItemResource<unknown> = { ...createSettingItemResource(), name: settingItemName }; + const settingItemStateResource: StateResource<SettingItemResource<unknown>> = createStateResource(settingItemResource); const settingsListResource: StateResource<SettingListResource> = createStateResource( - createSettingsListResource([postfachResource]), + createSettingsListResource([settingItemResource]), ); beforeEach(() => { @@ -63,28 +62,28 @@ describe('SettingsService', () => { }); it('should call resource service', () => { - service.getPostfach(); + service.get(settingItemName); expect(settingListResourceService.getList).toHaveBeenCalled(); }); - it('should return null for non postfach resource', () => { + it('should return null for non existing setting item resource', () => { const emptySettingsListResource: StateResource<SettingListResource> = createStateResource( createSettingsListResource([createSettingItemResource()]), ); - settingListResourceService.getList = jest.fn().mockReturnValue(singleCold(emptySettingsListResource)); - const postfach: Observable<StateResource<PostfachResource>> = service.getPostfach(); - expect(postfach).toBeObservable(singleCold(createEmptyStateResource())); + const settingItemStateResource$: Observable<StateResource<SettingItemResource<unknown>>> = service.get(settingItemName); + + expect(settingItemStateResource$).toBeObservable(singleCold(createEmptyStateResource())); }); - it('should return item resource as postfach resource', () => { + it('should return item resource as setting item resource', () => { settingListResourceService.getList = jest.fn().mockReturnValue(singleCold(settingsListResource)); - const postfach: Observable<StateResource<PostfachResource>> = service.getPostfach(); + const settingItemStateResource$: Observable<StateResource<SettingItemResource<unknown>>> = service.get(settingItemName); - expect(postfach).toBeObservable(singleCold(postfachStateResource)); + expect(settingItemStateResource$).toBeObservable(singleCold(settingItemStateResource)); }); }); }); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts index 51faa2be7ea9aa2a9f878de233ad56d32ec0f81c..770c9d7da6230f67e7d92c553e97e062277b5994 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.service.ts @@ -1,15 +1,17 @@ -import { PostfachResource } from '@admin-client/postfach-shared'; import { StateResource } from '@alfa-client/tech-shared'; import { inject, Injectable } from '@angular/core'; import { map, Observable } from 'rxjs'; import { SettingListResourceService } from './settings-resource.service'; -import { getPostfachResource } from './settings.util'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; +import { getSettingItemResource } from './settings.util'; @Injectable() export class SettingsService { private settingListResourceService = inject(SettingListResourceService); - public getPostfach(): Observable<StateResource<PostfachResource>> { - return this.settingListResourceService.getList().pipe(map(getPostfachResource)); + public get<T>(name: SettingName): Observable<StateResource<SettingItemResource<T>>> { + return this.settingListResourceService + .getList() + .pipe(map((list: StateResource<SettingListResource>) => getSettingItemResource<T>(list, name))); } } diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts index 5b78df34aa506c8180878416054cd11bb4164d14..fe8480f147ba265070096e70d6fe54f817324397 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.spec.ts @@ -21,30 +21,31 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; -import { SettingListResource } from '@admin-client/settings-shared'; import { createEmptyStateResource, createStateResource, StateResource } from '@alfa-client/tech-shared'; -import { createPostfachResource } from 'libs/admin/postfach-shared/src/test/postfach'; -import { createFilledSettingsListResource } from '../../test/settings'; -import { getPostfachResource } from './settings.util'; +import { Resource } from '@ngxp/rest'; +import { createFilledSettingsListResource, createSettingItemResource } from '../../test/settings'; +import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; +import { getSettingItemResource } from './settings.util'; -describe('get postfach resource', () => { - it('should return state resource with postfach resource if exists', () => { - const postfachResource: PostfachResource = createPostfachResource(); +describe('get setting item resource', () => { + const settingName: SettingName = <SettingName>'DUMMY'; + + it('should return matching setting resource as state resource if exists', () => { + const settingItemResource: SettingItemResource<unknown> = { ...createSettingItemResource(), name: settingName }; const settingsListResource: StateResource<SettingListResource> = createStateResource( - createFilledSettingsListResource([postfachResource]), + createFilledSettingsListResource([settingItemResource]), ); - const postfachStateResource: StateResource<PostfachResource> = getPostfachResource(settingsListResource); + const stateResource: StateResource<Resource> = getSettingItemResource(settingsListResource, settingName); - expect(postfachStateResource.resource).toEqual(postfachResource); + expect(stateResource.resource).toEqual(settingItemResource); }); - it('should return empty state resource if postfach resource NOT exists', () => { + it('should return empty state resource if no matching resource exists', () => { const settingsListResource: StateResource<SettingListResource> = createStateResource(createFilledSettingsListResource([])); - const postfachStateResource: StateResource<PostfachResource> = getPostfachResource(settingsListResource); + const stateResource: StateResource<Resource> = getSettingItemResource(settingsListResource, settingName); - expect(postfachStateResource).toEqual(createEmptyStateResource()); + expect(stateResource).toEqual(createEmptyStateResource()); }); }); diff --git a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts index d5cf7605cada028e253a26d1e3641eee5856e2c7..2a00f86b2a84fef05b45ccfde06c3ff8a6321745 100644 --- a/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts +++ b/alfa-client/libs/admin/settings-shared/src/lib/settings.util.ts @@ -21,7 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { PostfachResource } from '@admin-client/postfach-shared'; import { createEmptyStateResource, createStateResource, @@ -32,14 +31,11 @@ import { import { SettingListLinkRel } from './settings.linkrel'; import { SettingItemResource, SettingListResource, SettingName } from './settings.model'; -export function getPostfachResource(settingsListResource: StateResource<SettingListResource>): StateResource<PostfachResource> { - const entries: SettingItemResource[] = getEmbeddedResources(settingsListResource, SettingListLinkRel.LIST); - const postfachSettingItemResource: SettingItemResource = entries.find(isPostfachSettingItem); - return isNotNil(postfachSettingItemResource) ? - createStateResource(postfachSettingItemResource as PostfachResource) - : createEmptyStateResource(); -} - -function isPostfachSettingItem(item: SettingItemResource): boolean { - return item.name === SettingName.POSTFACH; +export function getSettingItemResource<T>( + settingsListResource: StateResource<SettingListResource>, + itemName: SettingName, +): StateResource<SettingItemResource<T>> { + const entries: SettingItemResource<T>[] = getEmbeddedResources(settingsListResource, SettingListLinkRel.LIST); + const entry: SettingItemResource<T> = entries.find((item: SettingItemResource<T>) => item.name === itemName); + return isNotNil(entry) ? createStateResource(entry) : createEmptyStateResource<SettingItemResource<T>>(); } diff --git a/alfa-client/libs/admin/settings-shared/test/settings.ts b/alfa-client/libs/admin/settings-shared/test/settings.ts index c3b9478ea57d373b18ad6315c0e5161fd7d9b008..a7bf1ded5bac477efcce579bd74f1646d8664a2d 100644 --- a/alfa-client/libs/admin/settings-shared/test/settings.ts +++ b/alfa-client/libs/admin/settings-shared/test/settings.ts @@ -1,9 +1,10 @@ +import { faker } from '@faker-js/faker/.'; import { Resource } from '@ngxp/rest'; import { toResource } from 'libs/tech-shared/test/resource'; import { SettingListLinkRel } from '../src/lib/settings.linkrel'; import { SettingItemResource, SettingListResource } from '../src/lib/settings.model'; -export function createSettingsListResource(settingsItems: SettingItemResource[]): SettingListResource { +export function createSettingsListResource(settingsItems: SettingItemResource<unknown>[]): SettingListResource { return toResource({}, [], { settings: settingsItems, }); @@ -14,3 +15,10 @@ export function createFilledSettingsListResource(resources: Resource[], linkRela [SettingListLinkRel.LIST]: resources, }); } + +export function createSettingItemResource(): SettingItemResource<unknown> { + return toResource({ + name: faker.word.sample(), + settingBody: {}, + }); +} diff --git a/alfa-client/libs/admin/shared/src/index.ts b/alfa-client/libs/admin/shared/src/index.ts index ece2c025dc3e05d4b26ad131fc7e419331e301d2..8f0992f8376dfb02cbd0096e33f516ac819f3d97 100644 --- a/alfa-client/libs/admin/shared/src/index.ts +++ b/alfa-client/libs/admin/shared/src/index.ts @@ -1 +1,4 @@ +export * from './lib/admin-cancel-button/admin-cancel-button.component'; +export * from './lib/admin-save-button/admin-save-button.component'; export * from './lib/routes'; +export * from './lib/token'; diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3125fbdfd9242e48e3a30a5891e445bb6d8b8498 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.html @@ -0,0 +1,3 @@ +<ods-routing-button [linkPath]="linkPath" text="Abbrechen" variant="outline" dataTestId="cancel-button"> + <ods-close-icon icon class="fill-primary" /> +</ods-routing-button> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..253650b230fb8be30ddb854fe3ad62351b058e5b --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.spec.ts @@ -0,0 +1,36 @@ +import { AdminCancelButtonComponent } from '@admin-client/shared'; +import { getMockComponent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RoutingButtonComponent } from '@ods/component'; +import { CloseIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; + +describe('AdminCancelButtonComponent', () => { + let component: AdminCancelButtonComponent; + let fixture: ComponentFixture<AdminCancelButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AdminCancelButtonComponent], + declarations: [MockComponent(RoutingButtonComponent), MockComponent(CloseIconComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminCancelButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('button', () => { + it('should be called with linkPath', () => { + component.linkPath = 'dummyLinkPath'; + + fixture.detectChanges(); + + expect(getMockComponent(fixture, RoutingButtonComponent).linkPath).toBe('dummyLinkPath'); + }); + }); +}); diff --git a/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c284f05c61e3d545025c8447d4d302a0fc3db85 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-cancel-button/admin-cancel-button.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { RoutingButtonComponent } from '@ods/component'; +import { CloseIconComponent } from '@ods/system'; + +@Component({ + selector: 'admin-cancel-button', + templateUrl: './admin-cancel-button.component.html', + standalone: true, + imports: [CommonModule, RoutingButtonComponent, CloseIconComponent], +}) +export class AdminCancelButtonComponent { + @Input() linkPath: string; +} diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8bd6a5f92932e22006b3b8e2cb353a9c3fa93eb1 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.html @@ -0,0 +1,6 @@ +<ods-button-with-spinner + (clickEmitter)="submit()" + text="Speichern" + dataTestId="save-button" + [stateResource]="stateResource$ | async" +/> diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..52a303f378e824677dd6518126ae838ebd416e3a --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.spec.ts @@ -0,0 +1,71 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { AbstractFormService, createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, getMockComponent, Mock, MockEvent } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Resource } from '@ngxp/rest'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { AdminSaveButtonComponent } from './admin-save-button.component'; + +describe('AdminSaveButtonComponent', () => { + let component: AdminSaveButtonComponent; + let fixture: ComponentFixture<AdminSaveButtonComponent>; + + let formService: Mock<AbstractFormService<Resource>>; + + const saveButton: string = getDataTestIdAttributeOf('save-button'); + + const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); + + beforeEach(async () => { + formService = <any>{ submit: jest.fn().mockReturnValue(singleCold(stateResource)) }; + + await TestBed.configureTestingModule({ + imports: [AdminSaveButtonComponent], + declarations: [MockComponent(ButtonWithSpinnerComponent)], + providers: [ + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminSaveButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('on submit', () => { + it('should call formService', () => { + dispatchEventFromFixture(fixture, saveButton, MockEvent.CLICK); + + expect(formService.submit).toHaveBeenCalled(); + }); + + it('should assign state resource', () => { + dispatchEventFromFixture(fixture, saveButton, MockEvent.CLICK); + + expect(component.stateResource$).toBeObservable(singleCold(stateResource)); + }); + }); + + describe('button', () => { + it('should call with stateResource', () => { + component.stateResource$ = of(stateResource); + + fixture.detectChanges(); + + const comp: ButtonWithSpinnerComponent = getMockComponent(fixture, ButtonWithSpinnerComponent); + expect(comp.stateResource).toBe(stateResource); + }); + }); +}); diff --git a/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..60abd08c7f311ecabe6d56ecc47fbfcd16412877 --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/admin-save-button/admin-save-button.component.ts @@ -0,0 +1,23 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { AbstractFormService, createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'admin-save-button', + standalone: true, + imports: [CommonModule, ButtonWithSpinnerComponent], + templateUrl: './admin-save-button.component.html', +}) +export class AdminSaveButtonComponent { + private formService: AbstractFormService<Resource> = inject(ADMIN_FORMSERVICE); + + public stateResource$: Observable<StateResource<Resource>> = of(createEmptyStateResource<Resource>()); + + public submit(): void { + this.stateResource$ = this.formService.submit(); + } +} diff --git a/alfa-client/libs/admin/shared/src/lib/token.ts b/alfa-client/libs/admin/shared/src/lib/token.ts new file mode 100644 index 0000000000000000000000000000000000000000..f19e2c875946591ba08470c654f91c80664176aa --- /dev/null +++ b/alfa-client/libs/admin/shared/src/lib/token.ts @@ -0,0 +1,5 @@ +import { AbstractFormService } from '@alfa-client/tech-shared'; +import { InjectionToken } from '@angular/core'; +import { Resource } from '@ngxp/rest'; + +export const ADMIN_FORMSERVICE = new InjectionToken<AbstractFormService<Resource>>('adminFormService'); diff --git a/alfa-client/libs/admin/statistik/src/index.ts b/alfa-client/libs/admin/statistik/src/index.ts index cdc5d7b64f24d50920b721f3ab01c5b69cb41b99..157271feb3bf8625864f12faec9623307c3b7690 100644 --- a/alfa-client/libs/admin/statistik/src/index.ts +++ b/alfa-client/libs/admin/statistik/src/index.ts @@ -1 +1,2 @@ export * from './lib/statistik-container/statistik-container.component'; +export * from './lib/statistik-fields-form/admin-statistik-fields-form.component'; diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html index 77c10ae229f5d8c6430d82349d6f2d213fa72054..1fd0441a39d71372d0c65fd84d0ce5386ac52fd2 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.html @@ -24,10 +24,12 @@ --> <h1 class="heading-1" data-test-id="statistik-header-text">Statistik</h1> -<div class="mt-4"> - <ods-button +<div class="mt-4 w-fit"> + <ods-routing-button + [linkPath]="ROUTES.STATISTIK_NEU" text="Weitere Felder auswerten" - (clickEmitter)="navigateToStatistikFieldsForm()" dataTestId="weitere-felder-auswerten-button" - ></ods-button> + ></ods-routing-button> </div> + +<ng-container *ngIf="listStateResource$ | async"></ng-container> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts index b4390c03face2557262e05f6964e02dc40e4cd7a..a2bc7e4bfe5efdec1af21b9ad1da7deaccf1a21f 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.spec.ts @@ -21,11 +21,15 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { existsAsHtmlElement, mock, Mock, triggerEvent } from '@alfa-client/test-utils'; +import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; +import { createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, mock, Mock } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RoutingButtonComponent } from '@ods/component'; +import { singleCold } from 'libs/tech-shared/test/marbles'; +import { MockComponent } from 'ng-mocks'; import { getDataTestIdAttributeOf } from '../../../../../tech-shared/test/data-test'; +import { createAggregationMappingListResource } from '../../../../reporting-shared/test/aggregation-mapping'; import { StatistikContainerComponent } from './statistik-container.component'; describe('StatistikContainerComponent', () => { @@ -34,43 +38,34 @@ describe('StatistikContainerComponent', () => { const evaluateAdditionalFieldsTestId: string = getDataTestIdAttributeOf('weitere-felder-auswerten-button'); - let navigationService: Mock<NavigationService>; - - beforeEach(() => { - navigationService = mock(NavigationService); - }); + let aggregationMappingService: Mock<AggregationMappingService>; beforeEach(async () => { + aggregationMappingService = mock(AggregationMappingService); + await TestBed.configureTestingModule({ imports: [StatistikContainerComponent], - providers: [ - { - provide: NavigationService, - useValue: navigationService, + declarations: [MockComponent(RoutingButtonComponent)], + }) + .overrideComponent(StatistikContainerComponent, { + set: { + providers: [ + { + provide: AggregationMappingService, + useValue: aggregationMappingService, + }, + ], }, - ], - }); - }); + }) + .compileComponents(); - beforeEach(() => { fixture = TestBed.createComponent(StatistikContainerComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); - describe('component', () => { - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateToStatistikFieldsForm', () => { - it('should call navigation service', () => { - component.navigateToStatistikFieldsForm(); - - expect(navigationService.navigate).toHaveBeenCalledWith(ROUTES.STATISTIK_NEU); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); }); describe('template', () => { @@ -80,23 +75,28 @@ describe('StatistikContainerComponent', () => { existsAsHtmlElement(fixture, evaluateAdditionalFieldsTestId); }); + }); + }); - describe('output', () => { - describe('clickEmitter', () => { - it('should call handler', () => { - component.navigateToStatistikFieldsForm = jest.fn(); - fixture.detectChanges(); + describe('on init', () => { + const stateResource: StateResource<AggregationMappingListResource> = createStateResource( + createAggregationMappingListResource(), + ); - triggerEvent({ - fixture, - elementSelector: evaluateAdditionalFieldsTestId, - name: 'clickEmitter', - }); + beforeEach(() => { + aggregationMappingService.getList.mockReturnValue(singleCold(stateResource)); + }); - expect(component.navigateToStatistikFieldsForm).toHaveBeenCalled(); - }); - }); - }); + it('should call service to get list', () => { + component.ngOnInit(); + + expect(aggregationMappingService.getList).toHaveBeenCalled(); + }); + + it('should assign stateResource', () => { + component.ngOnInit(); + + expect(component.listStateResource$).toBeObservable(singleCold(stateResource)); }); }); }); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts index a00051de29e86232f267891b6f6c8324a726993a..90c3515896348b1c1d6ee77ba4f247428e79c4bf 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-container/statistik-container.component.ts @@ -21,22 +21,29 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { AggregationMappingListResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; +import { StateResource } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; -import { ButtonComponent } from '@ods/system'; +import { Component, inject, OnInit } from '@angular/core'; +import { RoutingButtonComponent } from '@ods/component'; +import { Observable } from 'rxjs'; @Component({ selector: 'admin-statistik-container', templateUrl: './statistik-container.component.html', standalone: true, - imports: [CommonModule, ButtonComponent], + imports: [CommonModule, RoutingButtonComponent], + providers: [AggregationMappingService], }) -export class StatistikContainerComponent { - private readonly navigationService = inject(NavigationService); +export class StatistikContainerComponent implements OnInit { + private service = inject(AggregationMappingService); - public navigateToStatistikFieldsForm(): void { - this.navigationService.navigate(ROUTES.STATISTIK_NEU); + public listStateResource$: Observable<StateResource<AggregationMappingListResource>>; + + public readonly ROUTES = ROUTES; + + ngOnInit(): void { + this.listStateResource$ = this.service.getList(); } } diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html index cef302fc15f148000041c0c968f1ccbefc446e83..27805c87fe074bae41311083bc1aed20d5aaeaef 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.html @@ -1,37 +1,29 @@ <h2 class="heading-2" data-test-id="statistik-fields-form-header-text">Felder zur Auswertung hinzufügen</h2> <div class="flex max-w-4xl flex-col gap-4"> - <ods-text-input - [fieldControl]="formEngineFormControl" - label="Formengine" - placeholder="Tragen Sie hier die Formengine des Formulars ein." - data-test-id="form-engine-input" - ></ods-text-input> - - <ods-text-input - [fieldControl]="formIdFormControl" - label="FormID" - placeholder="Tragen Sie hier die FormID des Formulars ein." - data-test-id="form-id-input" - ></ods-text-input> - - @for (dataFieldControl of dataFieldsFormControls; track $index) { - <ods-text-input - [fieldControl]="dataFieldControl" - label="Pfad des Datenfeldes" - placeholder="Tragen Sie hier den gesamten Pfad des Datenfeldes ein, das Sie auswerten möchten." - [attr.data-test-id]="'data-statistik-field-' + $index" - ></ods-text-input> - } - - <ods-button text="Datenfeld hinzufügen" (clickEmitter)="addDataField()" dataTestId="add-data-field-button"> - <ods-plus-icon icon class="fill-whitetext"/> + <form class="form flex-col" [formGroup]="formService.form" class="flex flex-col gap-4"> + <div [formGroupName]="StatistikFieldsFormService.FIELD_FORM_IDENTIFIER" class="flex flex-col gap-4"> + <ods-text-editor + [formControlName]="StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME" + label="Formengine" + placeholder="Tragen Sie hier die Formengine des Formulars ein." + data-test-id="form-engine-name-input" + ></ods-text-editor> + <ods-text-editor + [formControlName]="StatistikFieldsFormService.FIELD_FORM_ID" + label="FormID" + placeholder="Tragen Sie hier die FormID des Formulars ein." + data-test-id="form-id-input" + ></ods-text-editor> + </div> + <statistik-fields-form-mapping /> + </form> + <ods-button text="Datenfeld hinzufügen" dataTestId="add-mapping-button" (clickEmitter)="formService.addMapping()"> + <ods-plus-icon icon class="fill-whitetext" /> </ods-button> <div class="mt-4 flex gap-4"> - <ods-button text="Speichern" dataTestId="save-statistik-fields-button"></ods-button> - <ods-button text="Abbrechen" variant="outline" (clickEmitter)="onCancel()" dataTestId="cancel-statistik-fields-button"> - <ods-close-icon icon class="fill-primary"/> - </ods-button> + <admin-save-button /> + <admin-cancel-button [linkPath]="Routes.STATISTIK" /> </div> </div> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts index 54502497cd68edcdec0876e2aad378126c2c28e3..a0154fd3137315cfbfd12187d70f0ffb43635df5 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.spec.ts @@ -1,86 +1,75 @@ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { existsAsHtmlElement, getElementComponentFromFixtureByCss, mock, Mock, triggerEvent } from '@alfa-client/test-utils'; +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { EMPTY_STRING } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, existsAsHtmlElement, mock, Mock, MockEvent } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormBuilder } from '@angular/forms'; -import { TextInputComponent } from '@ods/system'; +import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, PlusIconComponent } from '@ods/system'; +import { MockComponent } from 'ng-mocks'; import { getDataTestIdAttributeOf, getDataTestIdOf } from '../../../../../tech-shared/test/data-test'; +import { AdminCancelButtonComponent } from '../../../../shared/src/lib/admin-cancel-button/admin-cancel-button.component'; +import { AdminSaveButtonComponent } from '../../../../shared/src/lib/admin-save-button/admin-save-button.component'; import { AdminStatistikFieldsFormComponent } from './admin-statistik-fields-form.component'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping/statistik-fields-form-mapping.component'; import { StatistikFieldsFormService } from './statistik-fields.formservice'; describe('AdminStatistikFieldsFormComponent', () => { let component: AdminStatistikFieldsFormComponent; let fixture: ComponentFixture<AdminStatistikFieldsFormComponent>; - const formEngineInputTestId: string = getDataTestIdOf('form-engine-input'); + const formEngineNameInputTestId: string = getDataTestIdOf('form-engine-name-input'); const formIdInputTestId: string = getDataTestIdOf('form-id-input'); - const addDataFieldButtonTestId: string = getDataTestIdAttributeOf('add-data-field-button'); - const saveButtonTestId: string = getDataTestIdAttributeOf('save-statistik-fields-button'); - const cancelButtonTestId: string = getDataTestIdAttributeOf('cancel-statistik-fields-button'); - const dataField1TestId: string = getDataTestIdOf('data-statistik-field-0'); + const addMappingButton: string = getDataTestIdAttributeOf('add-mapping-button'); - let formService: StatistikFieldsFormService; - let navigationService: Mock<NavigationService>; + const formBuilder: FormBuilder = new FormBuilder(); - beforeEach(() => { - formService = new StatistikFieldsFormService(new FormBuilder()); - navigationService = mock(NavigationService); - }); + let formService: Mock<StatistikFieldsFormService>; beforeEach(async () => { - TestBed.overrideComponent(AdminStatistikFieldsFormComponent, { - set: { - providers: [ - { - provide: StatistikFieldsFormService, - useValue: formService, - }, - { - provide: NavigationService, - useValue: navigationService, - }, - ], - }, + const form: FormGroup = formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_IDENTIFIER]: formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME]: new FormControl(EMPTY_STRING), + [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), + }), }); + formService = <any>{ ...mock(StatistikFieldsFormService), form }; + await TestBed.configureTestingModule({ - imports: [AdminStatistikFieldsFormComponent], - providers: [{ provide: StatistikFieldsFormService, useValue: formService }], - }).compileComponents(); + declarations: [ + AdminStatistikFieldsFormComponent, + MockComponent(TextEditorComponent), + MockComponent(ButtonComponent), + MockComponent(PlusIconComponent), + MockComponent(AdminSaveButtonComponent), + MockComponent(AdminCancelButtonComponent), + MockComponent(AdminStatistikFieldsFormMappingComponent), + ], + imports: [ReactiveFormsModule], + }) + .overrideComponent(AdminStatistikFieldsFormComponent, { + set: { + providers: [ + { + provide: StatistikFieldsFormService, + useValue: formService, + }, + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }, + }) + .compileComponents(); fixture = TestBed.createComponent(AdminStatistikFieldsFormComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - describe('component', () => { - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should set form controls', () => { - expect(component.formEngineFormControl).toBeDefined(); - expect(component.formIdFormControl).toBeDefined(); - expect(component.dataFieldsFormControls).toBeDefined(); - }); - - describe('addDataField', () => { - it('should call form service', () => { - formService.addDataField = jest.fn(); - - component.addDataField(); - - expect(formService.addDataField).toHaveBeenCalled(); - }); - }); - - describe('onCancel', () => { - it('should call navigation service', () => { - component.onCancel(); - - expect(navigationService.navigate).toHaveBeenCalledWith(ROUTES.STATISTIK); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); }); describe('template', () => { @@ -88,18 +77,7 @@ describe('AdminStatistikFieldsFormComponent', () => { it('should exists', () => { fixture.detectChanges(); - existsAsHtmlElement(fixture, formEngineInputTestId); - }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const formEngineInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - formEngineInputTestId, - ); - - expect(formEngineInput.fieldControl).toEqual(component.formEngineFormControl); + existsAsHtmlElement(fixture, formEngineNameInputTestId); }); }); @@ -109,91 +87,23 @@ describe('AdminStatistikFieldsFormComponent', () => { existsAsHtmlElement(fixture, formIdInputTestId); }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const formIdInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - formIdInputTestId, - ); - - expect(formIdInput.fieldControl).toEqual(component.formIdFormControl); - }); - }); - - describe('data field input', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, dataField1TestId); - }); - - it('should have been called with inputs', () => { - fixture.detectChanges(); - - const dataFieldInput: TextInputComponent = getElementComponentFromFixtureByCss<TextInputComponent>( - fixture, - dataField1TestId, - ); - - expect(dataFieldInput.fieldControl).toEqual(component.dataFieldsFormControls[0]); - }); - }); - - describe('add data field button', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, addDataFieldButtonTestId); - }); - - describe('output', () => { - describe('clickEmitter', () => { - it('should call handler', () => { - fixture.detectChanges(); - component.addDataField = jest.fn(); - - triggerEvent({ - fixture, - elementSelector: addDataFieldButtonTestId, - name: 'clickEmitter', - }); - - expect(component.addDataField).toHaveBeenCalled(); - }); - }); - }); - }); - - describe('save button', () => { - it('should exists', () => { - fixture.detectChanges(); - - existsAsHtmlElement(fixture, saveButtonTestId); - }); }); - describe('cancel button', () => { + describe('add mapping button', () => { it('should exists', () => { fixture.detectChanges(); - existsAsHtmlElement(fixture, cancelButtonTestId); + existsAsHtmlElement(fixture, addMappingButton); }); describe('output', () => { describe('clickEmitter', () => { - it('should call handler', () => { - component.onCancel = jest.fn(); + it('should call formService', () => { fixture.detectChanges(); - triggerEvent({ - fixture, - elementSelector: cancelButtonTestId, - name: 'clickEmitter', - }); + dispatchEventFromFixture(fixture, addMappingButton, MockEvent.CLICK); - expect(component.onCancel).toHaveBeenCalled(); + expect(formService.addMapping).toHaveBeenCalled(); }); }); }); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts index 59d083f34a2f5f727734c29c02f6b8de13c7d2d6..0298196202e623a3d080468c792f3c1c800a8750 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/admin-statistik-fields-form.component.ts @@ -1,37 +1,30 @@ -import { ROUTES } from '@admin-client/shared'; -import { NavigationService } from '@alfa-client/navigation-shared'; -import { CommonModule } from '@angular/common'; +import { ADMIN_FORMSERVICE, AdminCancelButtonComponent, AdminSaveButtonComponent, ROUTES } from '@admin-client/shared'; import { Component, inject } from '@angular/core'; -import { FormArray, FormControl, ReactiveFormsModule } from '@angular/forms'; -import { ButtonComponent, CloseIconComponent, PlusIconComponent, TextInputComponent } from '@ods/system'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent, PlusIconComponent } from '@ods/system'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping/statistik-fields-form-mapping.component'; import { StatistikFieldsFormService } from './statistik-fields.formservice'; @Component({ selector: 'admin-statistik-fields-form', - standalone: true, - imports: [CommonModule, TextInputComponent, ButtonComponent, CloseIconComponent, ReactiveFormsModule, PlusIconComponent], - providers: [StatistikFieldsFormService], templateUrl: './admin-statistik-fields-form.component.html', + standalone: true, + imports: [ + ButtonComponent, + PlusIconComponent, + ReactiveFormsModule, + TextEditorComponent, + DeleteIconComponent, + AdminSaveButtonComponent, + AdminCancelButtonComponent, + AdminStatistikFieldsFormMappingComponent, + ], + providers: [{ provide: ADMIN_FORMSERVICE, useClass: StatistikFieldsFormService }], }) export class AdminStatistikFieldsFormComponent { - private readonly formService = inject(StatistikFieldsFormService); - private readonly navigationService = inject(NavigationService); - - public readonly formEngineFormControl: FormControl = this.formService.form.controls[ - StatistikFieldsFormService.FIELD_FORM_ENGINE - ] as FormControl; - public readonly formIdFormControl: FormControl = this.formService.form.controls[ - StatistikFieldsFormService.FIELD_FORM_ID - ] as FormControl; - public readonly dataFieldsFormControls: FormControl[] = ( - this.formService.form.controls[StatistikFieldsFormService.FIELD_DATA_FIELDS] as FormArray - ).controls as FormControl[]; - - public addDataField(): void { - this.formService.addDataField(); - } + public readonly formService = <StatistikFieldsFormService>inject(ADMIN_FORMSERVICE); - public onCancel(): void { - this.navigationService.navigate(ROUTES.STATISTIK); - } + public readonly StatistikFieldsFormService = StatistikFieldsFormService; + public readonly Routes = ROUTES; } diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html new file mode 100644 index 0000000000000000000000000000000000000000..2ce86b545b1039b795911cf172f154b9e2b97871 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.html @@ -0,0 +1,27 @@ +<form [formGroup]="formService.form"> + <div class="flex flex-col gap-4" [formArrayName]="StatistikFieldsFormService.FIELD_MAPPINGS"> + <div + *ngFor="let mappingControl of formService.mappings.controls; let i = index" + [formGroupName]="i" + class="flex w-full gap-2" + > + <ods-text-editor + class="flex-1" + formControlName="sourcePath" + label="Pfad des Datenfeldes" + placeholder="Tragen Sie hier den gesamten Pfad des Datenfeldes ein, das Sie auswerten möchten." + [attr.data-test-id]="'mapping-field-' + i" + ></ods-text-editor> + <ods-button + class="self-end" + variant="ghost" + size="fit" + destructive="true" + (clickEmitter)="formService.removeMapping(i)" + [dataTestId]="'remove-mapping-button-' + i" + > + <ods-delete-icon icon /> + </ods-button> + </div> + </div> +</form> diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..61cd006e429951cde3657be7688b30063cc2e6eb --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.spec.ts @@ -0,0 +1,90 @@ +import { ADMIN_FORMSERVICE } from '@admin-client/shared'; +import { EMPTY_STRING } from '@alfa-client/tech-shared'; +import { dispatchEventFromFixture, existsAsHtmlElement, mock, Mock, MockEvent, mockGetValue } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent } from '@ods/system'; +import { getDataTestIdOf, getDynamicDataTestIdAttributOf } from 'libs/tech-shared/test/data-test'; +import { MockComponent } from 'ng-mocks'; +import { StatistikFieldsFormService } from '../statistik-fields.formservice'; +import { AdminStatistikFieldsFormMappingComponent } from './statistik-fields-form-mapping.component'; + +describe('AdminStatistikFieldsFormMappingComponent', () => { + let component: AdminStatistikFieldsFormMappingComponent; + let fixture: ComponentFixture<AdminStatistikFieldsFormMappingComponent>; + + const mappingField: string = getDataTestIdOf('mapping-field-0'); + const removeMappingButton: string = getDynamicDataTestIdAttributOf('remove-mapping-button-0'); + + const formBuilder: FormBuilder = new FormBuilder(); + + let formService: Mock<StatistikFieldsFormService>; + + beforeEach(async () => { + const form: FormGroup = formBuilder.group({ + [StatistikFieldsFormService.FIELD_MAPPINGS]: formBuilder.array([ + new FormGroup({ sourcePath: new FormControl(EMPTY_STRING) }), + ]), + }); + + formService = <any>{ + ...mock(StatistikFieldsFormService), + form, + addMapping: jest.fn(), + removeMapping: jest.fn(), + }; + + mockGetValue( + formService, + StatistikFieldsFormService.FIELD_MAPPINGS, + form.controls[StatistikFieldsFormService.FIELD_MAPPINGS], + ); + + await TestBed.configureTestingModule({ + declarations: [ + AdminStatistikFieldsFormMappingComponent, + MockComponent(TextEditorComponent), + MockComponent(ButtonComponent), + MockComponent(DeleteIconComponent), + ], + imports: [ReactiveFormsModule], + providers: [ + { + provide: StatistikFieldsFormService, + useValue: formService, + }, + { + provide: ADMIN_FORMSERVICE, + useValue: formService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AdminStatistikFieldsFormMappingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('template', () => { + describe('mapping input', () => { + it('should exists', () => { + fixture.detectChanges(); + + existsAsHtmlElement(fixture, mappingField); + }); + }); + + describe('remove mapping button', () => { + it('should call formservice', () => { + dispatchEventFromFixture(fixture, removeMappingButton, MockEvent.CLICK); + + expect(formService.removeMapping).toHaveBeenCalledWith(0); + }); + }); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..48bc3eaddd2a2fe51b983939dd8920b02e7f8dae --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields-form-mapping/statistik-fields-form-mapping.component.ts @@ -0,0 +1,29 @@ +import { ADMIN_FORMSERVICE, AdminCancelButtonComponent, AdminSaveButtonComponent, ROUTES } from '@admin-client/shared'; +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TextEditorComponent } from '@ods/component'; +import { ButtonComponent, DeleteIconComponent, PlusIconComponent } from '@ods/system'; +import { StatistikFieldsFormService } from '../statistik-fields.formservice'; + +@Component({ + selector: 'statistik-fields-form-mapping', + templateUrl: './statistik-fields-form-mapping.component.html', + standalone: true, + imports: [ + CommonModule, + ButtonComponent, + PlusIconComponent, + ReactiveFormsModule, + TextEditorComponent, + DeleteIconComponent, + AdminSaveButtonComponent, + AdminCancelButtonComponent, + ], +}) +export class AdminStatistikFieldsFormMappingComponent { + public readonly formService = <StatistikFieldsFormService>inject(ADMIN_FORMSERVICE); + + public readonly StatistikFieldsFormService = StatistikFieldsFormService; + public readonly Routes = ROUTES; +} diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..58a2273cb692762c4f930e583c8c49f210099517 --- /dev/null +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.spec.ts @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { AggregationMappingResource, AggregationMappingService } from '@admin-client/reporting-shared'; +import { EMPTY_STRING, StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { TestBed } from '@angular/core/testing'; +import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { createAggregationMapping, createAggregationMappingResource } from 'libs/admin/reporting-shared/test/aggregation-mapping'; +import { of } from 'rxjs'; +import { StatistikFieldsFormService } from './statistik-fields.formservice'; + +describe('StatistikFieldsFormService', () => { + let formService: StatistikFieldsFormService; + + let service: Mock<AggregationMappingService>; + + beforeEach(() => { + service = mock(AggregationMappingService); + + TestBed.configureTestingModule({ + providers: [StatistikFieldsFormService, { provide: AggregationMappingService, useValue: service }], + }); + + formService = TestBed.inject(StatistikFieldsFormService); + }); + + it('should create', () => { + expect(formService).toBeTruthy(); + }); + + describe('on do submit', () => { + const stateResource: StateResource<AggregationMappingResource> = createStateResource(createAggregationMappingResource()); + + beforeEach(() => { + service.create.mockReturnValue(of(stateResource)); + }); + + it('should call service', () => { + formService.form = <any>createAggregationMapping(); + + formService.submit(); + + expect(service.create).toHaveBeenCalledWith(formService.form.value); + }); + }); + + describe('add mapping', () => { + it('should add mapping control', () => { + formService.addMapping(); + + const mappingFormArray: FormArray = <FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]; + expect(mappingFormArray).toHaveLength(2); + expect(mappingFormArray.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); + + describe('remove mapping', () => { + it('should remove mapping control', () => { + (<FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]).push( + new FormGroup({ sourcePath: new FormControl('controlToRemove') }), + ); + + formService.removeMapping(1); + + const mappingFormArray: FormArray = <FormArray>formService.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS]; + expect(mappingFormArray).toHaveLength(1); + expect(mappingFormArray.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); + + describe('get mappings', () => { + it('should return mappings as array', () => { + const mappings: FormArray = formService.mappings; + + expect(mappings).toHaveLength(1); + expect(mappings.controls[0].value).toEqual({ sourcePath: EMPTY_STRING }); + }); + }); +}); diff --git a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts index 229a7147d1b785c937d46c45c3ae65070a8b2608..1b746e12f42b44cc1261ce6de543380ea61d9ec4 100644 --- a/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts +++ b/alfa-client/libs/admin/statistik/src/lib/statistik-fields-form/statistik-fields.formservice.ts @@ -1,32 +1,50 @@ +import { AggregationMappingResource, AggregationMappingService } from '@admin-client/reporting-shared'; import { AbstractFormService, EMPTY_STRING, StateResource } from '@alfa-client/tech-shared'; -import { Injectable } from '@angular/core'; -import { FormArray, FormControl, UntypedFormGroup } from '@angular/forms'; -import { Resource } from '@ngxp/rest'; -import { EMPTY, Observable } from 'rxjs'; +import { inject, Injectable } from '@angular/core'; +import { FormArray, FormControl, FormGroup, UntypedFormGroup } from '@angular/forms'; +import { Observable } from 'rxjs'; @Injectable() -export class StatistikFieldsFormService extends AbstractFormService<Resource> { - public static readonly FIELD_FORM_ENGINE: string = 'formEngine'; +export class StatistikFieldsFormService extends AbstractFormService<AggregationMappingResource> { + private service = inject(AggregationMappingService); + + public static readonly FIELD_FORM_IDENTIFIER: string = 'formIdentifier'; + public static readonly FIELD_FORM_ENGINE_NAME: string = 'formEngineName'; public static readonly FIELD_FORM_ID: string = 'formId'; - public static readonly FIELD_DATA_FIELDS: string = 'dataFields'; + + public static readonly FIELD_MAPPINGS: string = 'mappings'; protected initForm(): UntypedFormGroup { return this.formBuilder.group({ - [StatistikFieldsFormService.FIELD_FORM_ENGINE]: new FormControl(EMPTY_STRING), - [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), - [StatistikFieldsFormService.FIELD_DATA_FIELDS]: new FormArray([new FormControl(EMPTY_STRING)]), + [StatistikFieldsFormService.FIELD_FORM_IDENTIFIER]: this.formBuilder.group({ + [StatistikFieldsFormService.FIELD_FORM_ENGINE_NAME]: new FormControl(EMPTY_STRING), + [StatistikFieldsFormService.FIELD_FORM_ID]: new FormControl(EMPTY_STRING), + }), + [StatistikFieldsFormService.FIELD_MAPPINGS]: new FormArray([this.createArrayControl()]), }); } - protected doSubmit(): Observable<StateResource<Resource>> { - return EMPTY; + protected doSubmit(): Observable<StateResource<AggregationMappingResource>> { + return this.service.create(this.getFormValue()); } protected getPathPrefix(): string { return 'settingBody'; } - public addDataField(): void { - (this.form.controls[StatistikFieldsFormService.FIELD_DATA_FIELDS] as FormArray).push(new FormControl(EMPTY_STRING)); + public addMapping(): void { + this.mappings.push(this.createArrayControl()); + } + + private createArrayControl(): FormGroup { + return new FormGroup({ sourcePath: new FormControl(EMPTY_STRING) }); + } + + public removeMapping(index: number): void { + this.mappings.removeAt(index); + } + + public get mappings(): FormArray { + return this.form.controls[StatistikFieldsFormService.FIELD_MAPPINGS] as FormArray; } } diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html index 026e846163f1602e26c751e551e0cfa5f016663c..98a2303f351d72dfe65d7710ec73d7566cfdacd7 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.html @@ -3,13 +3,33 @@ <div [formGroupName]="UserFormService.CLIENT_ROLES" class="mb-8 flex gap-56"> <div [formGroupName]="UserFormService.ADMINISTRATION_GROUP" class="flex flex-col gap-2"> <h3 class="text-md block font-medium text-text">Administration</h3> - <ods-checkbox-editor [formControlName]="UserFormService.ADMIN" label="Admin" inputId="admin" /> + <div class="flex items-center gap-2"> + <ods-checkbox-editor [formControlName]="UserFormService.ADMIN" label="Admin" inputId="admin" /> + <ods-info-icon + tooltip='Wird nur in Kombination mit "User" verwendet. Diese Rolle kann Funktionen in Keycloak und der Administration konfigurieren, z.B. Benutzer anlegen, Gruppen erstellen bzw. Organisationseinheiten hinzufügen und Rollen zuweisen.' + /> + </div> </div> <div [formGroupName]="UserFormService.ALFA_GROUP" class="flex flex-col gap-2"> <h3 class="text-md block font-medium text-text">Alfa</h3> - <ods-checkbox-editor [formControlName]="UserFormService.LOESCHEN" label="Löschen" inputId="delete" /> - <ods-checkbox-editor [formControlName]="UserFormService.USER" label="User" inputId="user" /> - <ods-checkbox-editor [formControlName]="UserFormService.POSTSTELLE" label="Poststelle" inputId="post_office" /> + <div class="flex items-center gap-2"> + <ods-checkbox-editor [formControlName]="UserFormService.LOESCHEN" label="Löschen" inputId="delete" /> + <ods-info-icon + tooltip='Diese Rolle hat dieselben Rechte wie die Rolle "User". Zusätzlich kann "Löschen" ausgewählte Vorgänge aus Alfa löschen. Diese Rolle sollten zwei Benutzer haben, da das Löschen einem Vieraugen-Prinzip folgt.' + /> + </div> + <div class="flex items-center gap-2"> + <ods-checkbox-editor [formControlName]="UserFormService.USER" label="User" inputId="user" /> + <ods-info-icon + tooltip='Diese Rolle kann alle Vorgänge sehen und bearbeiten, wenn diese seiner Organisationseinheit zugewiesen sind. Ist kompatibel mit "Löschen" und "Admin".' + /> + </div> + <div class="flex items-center gap-2"> + <ods-checkbox-editor [formControlName]="UserFormService.POSTSTELLE" label="Poststelle" inputId="post_office" /> + <ods-info-icon + tooltip='Diese Rolle kann ausschließlich alle neu eingegangenen Vorgänge sehen und anderen Benutzern zuweisen. Sie sollte nur einem Benutzer zugewiesen sein. Dieser sollte keine weiteren Rollen besitzen. (Sie ist aber kompatibel mit der "Admin")' + /> + </div> </div> </div> </div> diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.spec.ts b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.spec.ts index 386a028e927b68a8277cd8076ee11b357344cb48..b27337b2786891b5738a6d09bae607cecd492cdb 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.spec.ts @@ -1,5 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; +import { InfoIconComponent, TooltipDirective } from '@ods/system'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { createUserFormGroup } from '../../../../test/form'; import { UserFormRolesComponent } from './user-form-roles.component'; @@ -9,7 +11,7 @@ describe('UserFormRolesComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [UserFormRolesComponent, ReactiveFormsModule], + imports: [UserFormRolesComponent, ReactiveFormsModule, MockComponent(InfoIconComponent), MockDirective(TooltipDirective)], }).compileComponents(); fixture = TestBed.createComponent(UserFormRolesComponent); diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.ts b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.ts index d3c25f54bb31a6d18a39ec113a8f166bc14271ef..c7d46e948f2ddc5b4c50fd5880ddf7ff2221030c 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form-roles/user-form-roles.component.ts @@ -1,12 +1,13 @@ import { Component, Input } from '@angular/core'; import { ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { CheckboxEditorComponent } from '@ods/component'; +import { InfoIconComponent, TooltipDirective } from '@ods/system'; import { UserFormService } from '../user.formservice'; @Component({ selector: 'admin-user-form-roles', standalone: true, - imports: [CheckboxEditorComponent, ReactiveFormsModule], + imports: [CheckboxEditorComponent, ReactiveFormsModule, TooltipDirective, InfoIconComponent], templateUrl: './user-form-roles.component.html', }) export class UserFormRolesComponent { diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts index 92031a721d4ebb9e879f75308b003848c82d6cc0..8e823be03c1f064fe2cd152da724bb3e0fcdc001 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.spec.ts @@ -438,7 +438,7 @@ describe('UserFormService', () => { }); it('should return bauamt group when active', () => { - const organisationsEinheit: AdminOrganisationsEinheit = { name: 'bauamt' }; + const organisationsEinheit: AdminOrganisationsEinheit = createAdminOrganisationsEinheit(); organisationsEinheitenGroup.addControl(organisationsEinheit.name, new FormControl(true)); const result: string[] = formService._getActiveOrganisationsEinheiten(); diff --git a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html index bffb14da6bda5e9ae083f25f445a12b3d6854ae5..153e3e19deecd0653b83ae9fc7e516f85b6a8716 100644 --- a/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-list-container/user-list/user-list.component.html @@ -24,9 +24,9 @@ --> <h1 class="heading-1 mb-4">Benutzer & Rollen</h1> -<ods-routing-button [linkPath]="ROUTES.BENUTZER_NEU" text="Benutzer hinzufügen" class="mb-4" dataTestId="add-user-button" /> +<ods-routing-button [linkPath]="ROUTES.BENUTZER_NEU" text="Benutzer hinzufügen" class="mb-4 w-fit" dataTestId="add-user-button" /> <ods-list> @for (user of usersStateResource.resource; track $index) { <admin-user [user]="user" class="block w-full" /> } -</ods-list> \ No newline at end of file +</ods-list> diff --git a/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts b/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts index bf426ac9299c13c5440929e9b54d276e35f6be8e..8e32931929aab5f151b00cc4b14b3aaacc5c9142 100644 --- a/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts +++ b/alfa-client/libs/authentication/src/lib/http-unauthorized.interceptor.spec.ts @@ -22,12 +22,12 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { Mock, mock } from '@alfa-client/test-utils'; +import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { MatDialogModule } from '@angular/material/dialog'; -import { HttpUnauthorizedInterceptor } from './http-unauthorized.interceptor'; -import { HttpErrorResponse, HttpHandler, HttpRequest } from '@angular/common/http'; import { Subject, isEmpty } from 'rxjs'; import { AuthenticationService } from './authentication.service'; +import { HttpUnauthorizedInterceptor } from './http-unauthorized.interceptor'; describe('HttpUnauthorizedInterceptor', () => { let interceptor: HttpUnauthorizedInterceptor; @@ -96,7 +96,7 @@ describe('HttpUnauthorizedInterceptor', () => { it('should rethrow error if not unauthorized status', () => { const anyError: any = new HttpErrorResponse({ status: 500 }); - expect(() => interceptor.handleError(anyError)).toThrowError(); + expect(() => interceptor.handleError(anyError)).toThrow(); }); }); }); diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts index d799453d615d41bd0e21b7940461d59c7131adf0..ab845cf6b4f7e3156ebb7a940e418780e9a410d2 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts @@ -23,6 +23,7 @@ */ import { BinaryFileListResource, BinaryFileResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, CommandResource, CommandService, CreateCommandProps } from '@alfa-client/command-shared'; +import { PostfachService } from '@alfa-client/postfach-shared'; import { ApiError, EMPTY_ARRAY, @@ -80,7 +81,6 @@ import { BescheidService } from './bescheid.service'; import { DocumentLinkRel } from './document.linkrel'; import { DocumentResource } from './document.model'; -import { PostfachService } from '@alfa-client/postfach-shared'; import * as DateUtil from '../../../tech-shared/src/lib/date.util'; import * as BescheidUtil from './bescheid.util'; @@ -89,7 +89,7 @@ describe('BescheidService', () => { let facade: Mock<BescheidFacade>; let vorgangService: Mock<VorgangService>; - let resourceRepository: Mock<ResourceRepository>; + let resourceRepository: Mock<ResourceRepository<BescheidResource>>; let commandService: Mock<CommandService>; let vorgangCommandService: Mock<VorgangCommandService>; let binaryFileService: Mock<BinaryFileService>; diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts index 3426c36b0868b332b4324580b8eadf6fcbab37f0..7b8a2f737d688302a3a9f30e218b126a9fad285e 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -124,7 +124,7 @@ export class BescheidService { private readonly commandService: CommandService, private readonly vorgangCommandService: VorgangCommandService, private readonly binaryFileService: BinaryFileService, - private readonly repository: ResourceRepository, + private readonly repository: ResourceRepository<BescheidResource>, private readonly postfachService: PostfachService, ) { this.bescheidResourceService = new CommandResourceService( @@ -132,7 +132,11 @@ export class BescheidService { repository, this.commandService, ); - this.bescheidListResourceService = new ResourceListService(this.buildBescheidListServiceConfig(), repository); + this.bescheidListResourceService = new ResourceListService< + VorgangWithEingangResource, + BescheidListResource, + BescheidResource + >(this.buildBescheidListServiceConfig(), repository); } public getActiveStep(): Observable<BescheidWizardStep> { diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html index 8861db5df5fd7745a5061fecd7fead4301583384..2843835f2a178e1e40d221a7bc8b4d14bcca81ea 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/antrag-bescheiden/form/bescheid-wizard-antrag-bescheiden-form.component.html @@ -42,7 +42,7 @@ ><ods-close-icon size="large" class="fill-abgelehnt"></ods-close-icon> </ods-radio-button-card> </div> - <div class="flex w-full"> + <div class="flex"> <ozgcloud-date-editor [formControlName]="formServiceClass.FIELD_BESCHIEDEN_AM" label="am" diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.html index 0631bc2d3607a669dc82fd6185fce16cc3593113..456fdbcb23a9cc1bca7ce775ccef70e8a544cf14 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.html @@ -39,7 +39,7 @@ [stateResource]="submitStateResource$ | async" text="Bescheid senden" (clickEmitter)="submit()" - data-test-id="send-button" + dataTestId="send-button" ></ods-button-with-spinner> } </div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.spec.ts index 9f90d5f49aead888a0b1eff751476d748859fe07..e605449d8037e5d69d614a7a796060abb43a5c8f 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/senden/bescheid-wizard-bescheid-versenden-senden.component.spec.ts @@ -26,7 +26,7 @@ import { EMPTY, of } from 'rxjs'; import { BescheidService2 } from '../../../../../../../../bescheid-shared/src/lib/bescheid2.service'; import { createBescheidResource, createWizard } from '../../../../../../../../bescheid-shared/src/test/bescheid'; import { createSuccessfullyDoneCommandStateResource } from '../../../../../../../../command-shared/test/command'; -import { getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test'; +import { getDataTestIdAttributeOf, getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test'; import { createProblemDetail } from '../../../../../../../../tech-shared/test/error'; import { singleColdCompleted } from '../../../../../../../../tech-shared/test/marbles'; import { BescheidFormService } from '../../../../bescheid.formservice'; @@ -38,7 +38,7 @@ describe('BescheidWizardBescheidVersendenSendenComponent', () => { let fixture: ComponentFixture<BescheidWizardBescheidVersendenSendenComponent>; const dokumenteTestsId: string = getDataTestIdOf('bescheid-versenden-dokumente'); - const submitButtonTestId: string = getDataTestIdOf('send-button'); + const submitButtonTestId: string = getDataTestIdAttributeOf('send-button'); const empfaengerTestId: string = getDataTestIdOf('bescheid-nachricht-empfaenger'); let bescheidService: Mock<BescheidService2>; diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.html index a19c2b53db33741056c2cc6844e7b83536512f5d..28a81da1c3c872131b56f1db53765a0608729194 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.html @@ -14,7 +14,7 @@ [stateResource]="submitStateResource$ | async" text="Antrag bescheiden und speichern" (clickEmitter)="submit()" - data-test-id="confirm-and-save-button" + dataTestId="confirm-and-save-button" ></ods-button-with-spinner> } </div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.spec.ts index 4382985e2f96fcff9aee1b1604a7622641202420..40f0c44f14dc4e3e6c458f061f0c549cacd1763d 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-versenden/summary/speichern/bescheid-wizard-bescheid-versenden-speichern.component.spec.ts @@ -23,7 +23,7 @@ import { MockComponent } from 'ng-mocks'; import { EMPTY, of } from 'rxjs'; import { createBescheidResource } from '../../../../../../../../bescheid-shared/src/test/bescheid'; import { createSuccessfullyDoneCommandStateResource } from '../../../../../../../../command-shared/test/command'; -import { getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test'; +import { getDataTestIdAttributeOf, getDataTestIdOf } from '../../../../../../../../tech-shared/test/data-test'; import { createProblemDetail } from '../../../../../../../../tech-shared/test/error'; import { singleColdCompleted } from '../../../../../../../../tech-shared/test/marbles'; import { BescheidFormService } from '../../../../bescheid.formservice'; @@ -36,7 +36,7 @@ describe('BescheidWizardBescheidVersendenSpeichernComponent', () => { let fixture: ComponentFixture<BescheidWizardBescheidVersendenSpeichernComponent>; const dokumenteTestsId: string = getDataTestIdOf('bescheid-versenden-dokumente'); - const submitButtonTestId: string = getDataTestIdOf('confirm-and-save-button'); + const submitButtonTestId: string = getDataTestIdAttributeOf('confirm-and-save-button'); let formService: Mock<BescheidFormService>; let dialogRef: Mock<DialogRef<BescheidWizardDialogResult>>; diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html index 178a0f574555303c350994f4696c6e9e42f1fc52..9a8e66f72ed0f149c6b0be0d67562d5ca86ad5c6 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/bescheid-wizard.component.html @@ -31,7 +31,7 @@ ></ozgcloud-spinner> <form [formGroup]="formService.form" class="h-full"> - <div class="grid h-full"> + <div class="h-full"> @switch (activeStep) { @case (bescheidWizardStep.AntragBescheiden) { <alfa-bescheid-wizard-antrag-bescheiden-container diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html index b9789fd188dee1479e529429092e9fefd574024a..9d885f59c02d12b386c35498c5a74c5a62f5ddea 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.html @@ -23,39 +23,32 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<div - class="relative m-6 max-w-2xl rounded-lg bg-modalBg p-6 shadow-xl" - data-test-id="bescheid-close-dialog" -> +<div class="relative m-6 max-w-2xl rounded-lg bg-modalBg p-6 shadow-xl" data-test-id="bescheid-close-dialog"> <div class="flex flex-col gap-6"> <div> <h4 class="text-lg font-medium text-primary">Bescheiderstellung abbrechen</h4> </div> <div class="grow"> - <p class="text-base"> - Soll der Bescheid-Entwurf zur späteren Bearbeitung gespeichert oder verworfen werden? - </p> + <p class="text-base">Soll der Bescheid-Entwurf zur späteren Bearbeitung gespeichert oder verworfen werden?</p> </div> <div class="flex gap-4"> - <ozgcloud-stroked-button-with-spinner + <ods-button-with-spinner [stateResource]="saveStateResource$ | async" (clickEmitter)="save()" - data-test-id="bescheiderstellung-abbrechen-entwurf-speichern" + dataTestId="bescheiderstellung-abbrechen-entwurf-speichern" text="Entwurf speichern" - type="submit" - icon="check" > - </ozgcloud-stroked-button-with-spinner> - <ozgcloud-stroked-button-with-spinner + <ods-check-icon class="fill-whitetext" icon></ods-check-icon> + </ods-button-with-spinner> + <ods-button-with-spinner [stateResource]="deleteStateResource$ | async" (clickEmitter)="cancel()" - data-test-id="bescheiderstellung-abbrechen-entwurf-verwerfen" + variant="outline" + dataTestId="bescheiderstellung-abbrechen-entwurf-verwerfen" text="Verwerfen" - color="" - icon="clear" - type="submit" > - </ozgcloud-stroked-button-with-spinner> + <ods-close-icon class="fill-primary" icon></ods-close-icon> + </ods-button-with-spinner> </div> </div> </div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts index 5dfde26e4da2a928be6046e7c14d50b60790900a..47aa453fd30472816900af6f9f3c15c74063a05d 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/cancel-dialog-container/bescheid-wizard-cancel-dialog-container.component.spec.ts @@ -23,28 +23,40 @@ */ import { CommandResource } from '@alfa-client/command-shared'; import { createErrorStateResource, StateResource } from '@alfa-client/tech-shared'; -import { createDialogRefMock, DialogRefMock, existsAsHtmlElement, getElementComponentFromFixtureByCss, Mock, mock, triggerEvent, } from '@alfa-client/test-utils'; -import { OzgcloudStrokedButtonWithSpinnerComponent } from '@alfa-client/ui'; +import { + createDialogRefMock, + DialogRefMock, + existsAsHtmlElement, + getElementComponentFromFixtureByCss, + Mock, + mock, + triggerEvent, +} from '@alfa-client/test-utils'; import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { expect } from '@jest/globals'; +import { ButtonWithSpinnerComponent } from '@ods/component'; +import { CheckIconComponent, CloseIconComponent } from '@ods/system'; +import { BescheidService2 } from 'libs/bescheid-shared/src/lib/bescheid2.service'; +import { createBescheidResource } from 'libs/bescheid-shared/src/test/bescheid'; +import { createCommandStateResource, createSuccessfullyDoneCommandStateResource } from 'libs/command-shared/test/command'; +import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; +import { createApiError } from 'libs/tech-shared/test/error'; +import { singleColdCompleted } from 'libs/tech-shared/test/marbles'; import { MockComponent } from 'ng-mocks'; import { EMPTY, of } from 'rxjs'; -import { BescheidService2 } from '../../../../../../bescheid-shared/src/lib/bescheid2.service'; -import { createBescheidResource } from '../../../../../../bescheid-shared/src/test/bescheid'; -import { createCommandStateResource, createSuccessfullyDoneCommandStateResource, } from '../../../../../../command-shared/test/command'; -import { getDataTestIdOf } from '../../../../../../tech-shared/test/data-test'; -import { createApiError } from '../../../../../../tech-shared/test/error'; -import { singleColdCompleted } from '../../../../../../tech-shared/test/marbles'; import { BescheidFormService } from '../../bescheid.formservice'; -import { BescheidWizardCancelDialogContainerComponent, CancelWizardDialogData, } from './bescheid-wizard-cancel-dialog-container.component'; +import { + BescheidWizardCancelDialogContainerComponent, + CancelWizardDialogData, +} from './bescheid-wizard-cancel-dialog-container.component'; describe('BescheidWizardCancelDialogContainerComponent', () => { let component: BescheidWizardCancelDialogContainerComponent; let fixture: ComponentFixture<BescheidWizardCancelDialogContainerComponent>; - const speichernButton: string = getDataTestIdOf('bescheiderstellung-abbrechen-entwurf-speichern'); - const verwerfenButton: string = getDataTestIdOf('bescheiderstellung-abbrechen-entwurf-verwerfen'); + const speichernButton: string = getDataTestIdAttributeOf('bescheiderstellung-abbrechen-entwurf-speichern'); + const verwerfenButton: string = getDataTestIdAttributeOf('bescheiderstellung-abbrechen-entwurf-verwerfen'); const dialogData: CancelWizardDialogData = { bescheidResource: createBescheidResource(), @@ -64,7 +76,12 @@ describe('BescheidWizardCancelDialogContainerComponent', () => { async function configureTestingModule(dialogData: CancelWizardDialogData) { await TestBed.configureTestingModule({ - declarations: [BescheidWizardCancelDialogContainerComponent, MockComponent(OzgcloudStrokedButtonWithSpinnerComponent)], + declarations: [ + BescheidWizardCancelDialogContainerComponent, + MockComponent(ButtonWithSpinnerComponent), + MockComponent(CheckIconComponent), + MockComponent(CloseIconComponent), + ], providers: [ { provide: BescheidService2, @@ -233,10 +250,7 @@ describe('BescheidWizardCancelDialogContainerComponent', () => { const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); component.saveStateResource$ = of(commandStateResource); - const elementComponent: OzgcloudStrokedButtonWithSpinnerComponent = getElementComponentFromFixtureByCss( - fixture, - speichernButton, - ); + const elementComponent: ButtonWithSpinnerComponent = getElementComponentFromFixtureByCss(fixture, speichernButton); fixture.detectChanges(); @@ -263,10 +277,7 @@ describe('BescheidWizardCancelDialogContainerComponent', () => { const commandStateResource: StateResource<CommandResource> = createCommandStateResource(); component.deleteStateResource$ = of(commandStateResource); - const elementComponent: OzgcloudStrokedButtonWithSpinnerComponent = getElementComponentFromFixtureByCss( - fixture, - verwerfenButton, - ); + const elementComponent: ButtonWithSpinnerComponent = getElementComponentFromFixtureByCss(fixture, verwerfenButton); fixture.detectChanges(); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-content-layout/step-content-layout.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-content-layout/step-content-layout.component.html index 43b8946ab2d59886a58ed927028c68158785dedc..5151ccc66814fe01ce9a81566ef11cdb0d4f415b 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-content-layout/step-content-layout.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid-wizard/step-content-layout/step-content-layout.component.html @@ -24,17 +24,17 @@ --> <div class="flex h-full gap-11"> - <div class="flex w-1/2 flex-row gap-7"> + <div class="flex flex-1 flex-row gap-7 overflow-y-auto"> <alfa-bescheid-wizard-stepper [activeStep]="activeStep" (stepChange)="stepChange.emit($event)" data-test-id="wizard-stepper" /> - <div class="mt-2 flex flex-1 flex-col"> + <div class="mr-2 mt-2 flex flex-1 flex-col"> <ng-content select="[stepPanel]" /> </div> </div> - <div class="flex w-1/2"> + <div class="flex flex-1"> <alfa-bescheid-wizard-summary headline="Bescheid" data-test-id="wizard-summary"> <ng-content select="[summary]" /> </alfa-bescheid-wizard-summary> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts index 2125d0b8e171796f264901d4b22e40c269d0cc7d..86c5f0caee0f27416b51343699e465ec2eb4ef47 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.spec.ts @@ -415,41 +415,41 @@ describe('BescheidFormService', () => { }); describe('patchValues', () => { + const sendBy: BescheidSendBy = BescheidSendBy.NACHRICHT; let bescheidResource: BescheidResource; let patch: jest.Mock; beforeEach(() => { bescheidResource = createBescheidResource(); patch = service.patch = jest.fn(); + service._evaluateSendBy = jest.fn().mockReturnValue(sendBy); }); it('should patch', () => { + const sendBy: BescheidSendBy = BescheidSendBy.NACHRICHT; service.patchValues(bescheidResource); expect(patch).toHaveBeenCalledWith({ [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: null, - [BescheidFormService.FIELD_SEND_BY]: String(bescheidResource.sendBy), + [BescheidFormService.FIELD_SEND_BY]: String(sendBy), [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, }); }); - it('should patch sendBy to default', () => { - service.patchValues({ - ...bescheidResource, - sendBy: undefined, - }); + it('should set send by', () => { + const sendBy: BescheidSendBy = BescheidSendBy.NACHRICHT; + service.patchValues(bescheidResource); - expect(patch).toHaveBeenCalledWith({ - [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, - [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), - [BescheidFormService.FIELD_SEND_BY]: BescheidSendBy.NACHRICHT, - [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: null, - [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, - [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, - }); + expect(bescheidService.setSendBy).toHaveBeenCalledWith(sendBy); + }); + + it('should evaluate send by', () => { + service.patchValues(bescheidResource); + + expect(service._evaluateSendBy).toHaveBeenCalledWith(bescheidResource); }); it('should patch bescheid document uri', () => { @@ -465,7 +465,7 @@ describe('BescheidFormService', () => { expect(patch).toHaveBeenCalledWith({ [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), - [BescheidFormService.FIELD_SEND_BY]: BescheidSendBy.NACHRICHT, + [BescheidFormService.FIELD_SEND_BY]: sendBy, [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri, [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, @@ -479,6 +479,44 @@ describe('BescheidFormService', () => { }); }); + describe('_evaluateSendBy', () => { + it('should return MANUAL on MANUAL', () => { + const sendBy: BescheidSendBy = service._evaluateSendBy({ + ...createBescheidResource([BescheidLinkRel.BESCHEIDEN]), + sendBy: BescheidSendBy.MANUAL, + }); + + expect(sendBy).toEqual(BescheidSendBy.MANUAL); + }); + + it('should return MANUAL on NACHRICHT and missing link', () => { + const sendBy: BescheidSendBy = service._evaluateSendBy({ + ...createBescheidResource([BescheidLinkRel.BESCHEIDEN]), + sendBy: BescheidSendBy.NACHRICHT, + }); + + expect(sendBy).toEqual(BescheidSendBy.MANUAL); + }); + + it('should return NACHRICHT', () => { + const sendBy: BescheidSendBy = service._evaluateSendBy({ + ...createBescheidResource([BescheidLinkRel.BESCHEIDEN_UND_SENDEN]), + sendBy: BescheidSendBy.NACHRICHT, + }); + + expect(sendBy).toEqual(BescheidSendBy.NACHRICHT); + }); + + it('should return MANUAL', () => { + const sendBy: BescheidSendBy = service._evaluateSendBy({ + ...createBescheidResource([BescheidLinkRel.BESCHEIDEN_UND_SENDEN]), + sendBy: BescheidSendBy.MANUAL, + }); + + expect(sendBy).toEqual(BescheidSendBy.MANUAL); + }); + }); + describe('getBescheidFormValueChanges', () => { const bescheid: Bescheid = createBescheid(); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts index 4e650ff9f6660e2e987ee53979858474096cafe2..eb96bb9d90af5f6729a3f251ea01bc2e91c58eb7 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-wizard-container/bescheid.formservice.ts @@ -21,21 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { - Bescheid, - BescheidLinkRel, - BescheidResource, - BescheidSendBy, - BescheidWizardStep, - Wizard, -} from '@alfa-client/bescheid-shared'; +import { Bescheid, BescheidLinkRel, BescheidResource, BescheidSendBy, BescheidWizardStep, Wizard, } from '@alfa-client/bescheid-shared'; import { CommandResource, switchMapCommandSuccessfullyDone } from '@alfa-client/command-shared'; import { AbstractFormService, convertToBoolean, formatForDatabase, isNotNil, StateResource } from '@alfa-client/tech-shared'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { Injectable } from '@angular/core'; import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { getUrl, hasLink, ResourceUri } from '@ngxp/rest'; -import { isNil, isUndefined } from 'lodash-es'; +import { isNil } from 'lodash-es'; import { first, Observable, startWith, switchMap } from 'rxjs'; import { BescheidService2 } from '../../../../bescheid-shared/src/lib/bescheid2.service'; @@ -118,12 +111,13 @@ export class BescheidFormService extends AbstractFormService<CommandResource> { public patchValues(bescheidResource: BescheidResource): void { this.bescheidResource = bescheidResource; const bescheidDocumentUri: ResourceUri = this.getBescheidDocumentUri(bescheidResource); + const sendBy: BescheidSendBy = this._evaluateSendBy(bescheidResource); + this.bescheidService.setSendBy(sendBy); this.patch({ [BescheidFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm, [BescheidFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt), [BescheidFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri, - [BescheidFormService.FIELD_SEND_BY]: - isUndefined(bescheidResource.sendBy) ? BescheidSendBy.NACHRICHT : bescheidResource.sendBy, + [BescheidFormService.FIELD_SEND_BY]: sendBy, [BescheidFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject, [BescheidFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText, }); @@ -142,6 +136,16 @@ export class BescheidFormService extends AbstractFormService<CommandResource> { return null; } + _evaluateSendBy(bescheidResource: BescheidResource): BescheidSendBy { + if ( + bescheidResource.sendBy === BescheidSendBy.NACHRICHT && + hasLink(bescheidResource, BescheidLinkRel.BESCHEIDEN_UND_SENDEN) + ) { + return BescheidSendBy.NACHRICHT; + } + return BescheidSendBy.MANUAL; + } + public getBescheidFormValueChanges(): Observable<Bescheid> { return this.form.valueChanges.pipe(startWith(this.getBescheidFormValue())); } diff --git a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts index f6df6b162cf2af8a74cd04ee074f680fce851a20..e4e7092380ee0cdc7f9e1c206af6e03863b13b0c 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts @@ -24,7 +24,13 @@ import { BescheidSharedModule } from '@alfa-client/bescheid-shared'; import { BinaryFileModule } from '@alfa-client/binary-file'; import { CommandSharedModule } from '@alfa-client/command-shared'; -import { GetUrlPipe, HasLinkPipe, ToEmbeddedResourcesPipe } from '@alfa-client/tech-shared'; +import { + ConvertForDataTestPipe, + ConvertProblemDetailToErrorMessagesPipe, + GetUrlPipe, + HasLinkPipe, + ToEmbeddedResourcesPipe, +} from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { @@ -59,6 +65,7 @@ import { BescheidWrapperComponent, ButtonCardComponent, ButtonComponent, + CheckIconComponent, CloseIconComponent, RadioButtonCardComponent, SaveIconComponent, @@ -134,8 +141,11 @@ import { BescheidWizardWeiterButtonComponent } from './bescheid-wizard-container FileUploadEditorComponent, SendIconComponent, SaveIconComponent, + CheckIconComponent, TextEditorComponent, TextareaEditorComponent, + ConvertForDataTestPipe, + ConvertProblemDetailToErrorMessagesPipe, ], declarations: [ BescheidInVorgangContainerComponent, diff --git a/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts b/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts index 9af26540fdf4031d434bcb3d4000e2a3f56d3c96..d5726c77529ec251188d2323565ad95742986a58 100644 --- a/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts +++ b/alfa-client/libs/collaboration/src/lib/collaboration-in-vorgang-container/collaboration-request-form/collaboration.request.formservice.spec.ts @@ -22,11 +22,11 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { CollaborationListResource } from '@alfa-client/collaboration-shared'; -import { SnackBarService } from '@alfa-client/ui'; -import { TestBed } from '@angular/core/testing'; import { CommandLinkRel, CommandResource } from '@alfa-client/command-shared'; import { StateResource, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; -import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; +import { Mock, mock } from '@alfa-client/test-utils'; +import { SnackBarService } from '@alfa-client/ui'; +import { TestBed } from '@angular/core/testing'; import { UntypedFormBuilder } from '@angular/forms'; import { CollaborationService } from 'libs/collaboration-shared/src/lib/collaboration.service'; import { createCollaborationListResource } from 'libs/collaboration-shared/test/collaboration'; @@ -57,7 +57,6 @@ describe('CollaborationRequestFormService', () => { }); formService = TestBed.inject(CollaborationRequestFormService); - TestBed.inject(CollaborationRequestFormService); }); it('should create', () => { diff --git a/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts b/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts index 200b8572a2ed3faccb5d14a2346e085ca8262024..c7640cb221008d78f73dd470ae9d3f10287d4bcb 100644 --- a/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts +++ b/alfa-client/libs/command-shared/src/lib/command-resource.service.spec.ts @@ -74,13 +74,13 @@ describe('CommandResourceService', () => { describe('doSave', () => { it('should throw error', () => { - expect(() => service.doSave(configResource, {})).toThrowError('Method not implemented.'); + expect(() => service.doSave(configResource, {})).toThrow('Method not implemented.'); }); }); describe('doPatch', () => { it('should throw error', () => { - expect(() => service.doPatch(configResource, {})).toThrowError('Method not implemented.'); + expect(() => service.doPatch(configResource, {})).toThrow('Method not implemented.'); }); }); diff --git a/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts b/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts index 2a25cd22e5df3e74e404a3fd986d737b29ab5a2c..98346f3903e1cff4b363c5f4f4dc49243ce84c7d 100644 --- a/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts +++ b/alfa-client/libs/design-component/src/lib/routing-button/routing-button.component.ts @@ -1,22 +1,26 @@ import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { ButtonVariants, buttonVariants } from '@ods/system'; @Component({ selector: 'ods-routing-button', standalone: true, imports: [CommonModule, RouterLink], host: { class: 'block' }, - template: `<a - [routerLink]="'/' + linkPath" - [attr.data-test-id]="dataTestId" - class="block min-h-9 w-fit min-w-32 rounded bg-primary px-4 py-2 text-sm font-medium text-white shadow-sm outline-focus hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" - > - {{ text }} + template: `<a [routerLink]="'/' + linkPath" [attr.data-test-id]="dataTestId" [ngClass]="buttonVariants({ size, variant })"> + <ng-content select="[icon]" /> + @if (text) { + <p class="flex-grow">{{ text }}</p> + } </a>`, }) export class RoutingButtonComponent { @Input() linkPath: string; @Input() text: string; @Input() dataTestId: string; + @Input() variant: ButtonVariants['variant']; + @Input() size: ButtonVariants['size']; + + readonly buttonVariants = buttonVariants; } diff --git a/alfa-client/libs/design-system/src/index.ts b/alfa-client/libs/design-system/src/index.ts index 467032047c06e4ae9c4a6e3948cc13a704b8761d..eda668409767d19830222cb490f11f1608efff9b 100644 --- a/alfa-client/libs/design-system/src/index.ts +++ b/alfa-client/libs/design-system/src/index.ts @@ -52,6 +52,7 @@ export * from './lib/icons/bescheid-upload-icon/bescheid-upload-icon.component'; export * from './lib/icons/check-circle-icon/check-circle-icon.component'; export * from './lib/icons/check-icon/check-icon.component'; export * from './lib/icons/close-icon/close-icon.component'; +export * from './lib/icons/delete-icon/delete-icon.component'; export * from './lib/icons/delete-vorgang-finally-icon/delete-vorgang-finally-icon.component'; export * from './lib/icons/discard-vorgang-icon/discard-vorgang-icon.component'; export * from './lib/icons/edit-icon/edit-icon.component'; @@ -62,6 +63,7 @@ export * from './lib/icons/file-icon/file-icon.component'; export * from './lib/icons/forward-vorgang-icon/forward-vorgang-icon.component'; export * from './lib/icons/help-icon/help-icon.component'; export * from './lib/icons/iconVariants'; +export * from './lib/icons/info-icon/info-icon.component'; export * from './lib/icons/logout-icon/logout-icon.component'; export * from './lib/icons/mailbox-icon/mailbox-icon.component'; export * from './lib/icons/more-icon/more-icon.component'; diff --git a/alfa-client/libs/design-system/src/lib/button/button.component.ts b/alfa-client/libs/design-system/src/lib/button/button.component.ts index 46b0308c205dff5a889142e76693dd27cffc30bb..040648040fa62e418671d2dac380e4ce68994961 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.component.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.component.ts @@ -30,7 +30,7 @@ import { SpinnerIconComponent } from '../icons/spinner-icon/spinner-icon.compone export const buttonVariants = cva( [ - 'flex items-center gap-4 rounded-lg text-sm font-medium box-border', + 'flex items-center gap-3 rounded-lg text-sm font-medium box-border', 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2', ], { @@ -64,22 +64,24 @@ export const buttonVariants = cva( { variant: 'primary', destructive: true, - class: '[&]:hover:enabled:bg-destructive-primary-hover [&]:bg-destructive [&]:outline-destructive', + class: + '[&]:hover:enabled:bg-destructive-primary-hover [&]:bg-destructive [&]:outline-destructive [&]:focus-visible:bg-destructive-primary-hover', }, { variant: 'outline', destructive: true, - class: '[&]:border-destructive [&]:text-destructive [&]:hover:enabled:bg-destructive-hover', + class: + '[&]:border-destructive [&]:text-destructive [&]:hover:enabled:bg-destructive-hover [&]:focus-visible:bg-destructive-hover', }, { variant: 'ghost', destructive: true, - class: '[&]:text-destructive [&]:hover:enabled:bg-destructive-hover', + class: '[&]:text-destructive [&]:hover:enabled:bg-destructive-hover [&]:focus-visible:bg-destructive-hover', }, ], }, ); -type ButtonVariants = VariantProps<typeof buttonVariants>; +export type ButtonVariants = VariantProps<typeof buttonVariants>; @Component({ selector: 'ods-button', diff --git a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts index 5de77014fc3466b2a8a5768e2f003c61347d9dad..ca6a007c19e49ebb481ff0655b407cc3076a9c08 100644 --- a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts @@ -26,6 +26,7 @@ import { CommonModule } from '@angular/common'; import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { cva, VariantProps } from 'class-variance-authority'; +import { uniqueId } from 'lodash-es'; const textInputVariants = cva( [ @@ -86,7 +87,7 @@ export class TextInputComponent { @Input({ required: true }) set label(label: string) { this.inputLabel = label; - this.id = convertForDataTest(label); + this.id = `${convertForDataTest(label)}-${uniqueId()}`; } @Input() placeholder: string = ''; @Input() autocomplete: string = 'off'; diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..354449bdfb0652606ec23dee7b970a5d793fd292 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DeleteIconComponent } from './delete-icon.component'; + +describe('DeleteIconComponent', () => { + let component: DeleteIconComponent; + let fixture: ComponentFixture<DeleteIconComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeleteIconComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DeleteIconComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc06f683c6790f219c7c08fb258764b35cdf2b6c --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.component.ts @@ -0,0 +1,27 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { twMerge } from 'tailwind-merge'; +import { IconVariants, iconVariants } from '../iconVariants'; + +@Component({ + selector: 'ods-delete-icon', + standalone: true, + imports: [CommonModule], + template: `<svg + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + [ngClass]="[twMerge(iconVariants({ size }), 'fill-error', class)]" + aria-hidden="true" + > + <path + d="M7 21C6.45 21 5.97917 20.8042 5.5875 20.4125C5.19583 20.0208 5 19.55 5 19V6H4V4H9V3H15V4H20V6H19V19C19 19.55 18.8042 20.0208 18.4125 20.4125C18.0208 20.8042 17.55 21 17 21H7ZM17 6H7V19H17V6ZM9 17H11V8H9V17ZM13 17H15V8H13V17Z" + /> + </svg> `, +}) +export class DeleteIconComponent { + @Input() size: IconVariants['size'] = 'medium'; + @Input() class: string = ''; + + readonly iconVariants = iconVariants; + readonly twMerge = twMerge; +} diff --git a/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..4602289bc57c1be551e4bf2ba29003ab4bd7de31 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/delete-icon/delete-icon.stories.ts @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from '@storybook/angular'; + +import { DeleteIconComponent } from './delete-icon.component'; + +const meta: Meta<DeleteIconComponent> = { + title: 'Icons/Delete icon', + component: DeleteIconComponent, + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<DeleteIconComponent>; + +export const Default: Story = { + args: { size: 'large' }, + argTypes: { + size: { + control: 'select', + options: ['small', 'medium', 'large', 'extra-large', 'full'], + description: 'Size of icon. Property "full" means 100%', + table: { + defaultValue: { summary: 'medium' }, + }, + }, + }, +}; diff --git a/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.spec.ts b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b0b08534c6c010f7c676d6d1ffcc8c4bbcea98d --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.spec.ts @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { InfoIconComponent } from './info-icon.component'; + +describe('InfoIconComponent', () => { + let component: InfoIconComponent; + let fixture: ComponentFixture<InfoIconComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [InfoIconComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(InfoIconComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fd6e58c3345ecef1e704daf8e423e623d427e62 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.component.ts @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { NgClass } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { twMerge } from 'tailwind-merge'; +import { IconVariants, iconVariants } from '../iconVariants'; + +@Component({ + selector: 'ods-info-icon', + standalone: true, + imports: [NgClass], + template: `<svg + [ngClass]="twMerge(iconVariants({ size }), 'fill-primary', class)" + aria-hidden="true" + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M11.25 16.75H12.75V11H11.25V16.75ZM12 9.2885C12.2288 9.2885 12.4207 9.21108 12.5755 9.05625C12.7303 8.90142 12.8077 8.70958 12.8077 8.48075C12.8077 8.25192 12.7303 8.06008 12.5755 7.90525C12.4207 7.75058 12.2288 7.67325 12 7.67325C11.7712 7.67325 11.5793 7.75058 11.4245 7.90525C11.2697 8.06008 11.1923 8.25192 11.1923 8.48075C11.1923 8.70958 11.2697 8.90142 11.4245 9.05625C11.5793 9.21108 11.7712 9.2885 12 9.2885ZM12.0017 21.5C10.6877 21.5 9.45267 21.2507 8.2965 20.752C7.14033 20.2533 6.13467 19.5766 5.2795 18.7218C4.42433 17.8669 3.74725 16.8617 3.24825 15.706C2.74942 14.5503 2.5 13.3156 2.5 12.0017C2.5 10.6877 2.74933 9.45267 3.248 8.2965C3.74667 7.14033 4.42342 6.13467 5.27825 5.2795C6.13308 4.42433 7.13833 3.74725 8.294 3.24825C9.44967 2.74942 10.6844 2.5 11.9983 2.5C13.3123 2.5 14.5473 2.74933 15.7035 3.248C16.8597 3.74667 17.8653 4.42342 18.7205 5.27825C19.5757 6.13308 20.2528 7.13833 20.7518 8.294C21.2506 9.44967 21.5 10.6844 21.5 11.9983C21.5 13.3123 21.2507 14.5473 20.752 15.7035C20.2533 16.8597 19.5766 17.8653 18.7218 18.7205C17.8669 19.5757 16.8617 20.2528 15.706 20.7518C14.5503 21.2506 13.3156 21.5 12.0017 21.5ZM12 20C14.2333 20 16.125 19.225 17.675 17.675C19.225 16.125 20 14.2333 20 12C20 9.76667 19.225 7.875 17.675 6.325C16.125 4.775 14.2333 4 12 4C9.76667 4 7.875 4.775 6.325 6.325C4.775 7.875 4 9.76667 4 12C4 14.2333 4.775 16.125 6.325 17.675C7.875 19.225 9.76667 20 12 20Z" + /> + </svg>`, +}) +export class InfoIconComponent { + @Input() size: IconVariants['size'] = 'medium'; + @Input() class: string = undefined; + + protected readonly iconVariants = iconVariants; + protected readonly twMerge = twMerge; +} diff --git a/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.stories.ts b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..70a6e7940585b4a5696ea9e53a9043e1c276b6a2 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/icons/info-icon/info-icon.stories.ts @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import type { Meta, StoryObj } from '@storybook/angular'; + +import { InfoIconComponent } from './info-icon.component'; + +const meta: Meta<InfoIconComponent> = { + title: 'Icons/Info icon', + component: InfoIconComponent, + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<InfoIconComponent>; + +export const Default: Story = { + args: { size: 'large' }, + argTypes: { + size: { + control: 'select', + options: ['small', 'medium', 'large', 'extra-large', 'full'], + description: 'Size of icon. Property "full" means 100%', + table: { + defaultValue: { summary: 'medium' }, + }, + }, + }, +}; diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts index f82fe2102cb07bea080f2b61738c783515d58672..671af957965555f680862dad37081cc00c537c9d 100644 --- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts +++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.component.ts @@ -29,12 +29,15 @@ import { TooltipPosition } from './tooltip.directive'; selector: 'ods-tooltip', imports: [NgClass], template: `<span - class="tooltip fixed z-[100] max-w-md animate-fadeIn cursor-default break-words rounded bg-ozggray-900 px-3 py-2 text-sm text-whitetext before:absolute before:border-l-[0.5rem] before:border-r-[0.5rem] before:border-l-transparent before:border-r-transparent dark:bg-white md:max-w-[calc(90vw)]" + class="tooltip fixed z-[100] animate-fadeIn cursor-default break-words rounded bg-ozggray-900 px-3 py-2 text-sm font-normal text-whitetext before:absolute before:border-l-[0.5rem] before:border-r-[0.5rem] before:border-l-transparent before:border-r-transparent dark:bg-white" [ngClass]="class" [class.visible]="show" [class.invisible]="!show" + [class.text-wrap]="width" + [class.text-nowrap]="!width" [style.left.px]="left" [style.top.px]="top" + [style.width.rem]="width" [style.--before-left.px]="leftOffset" [attr.id]="id" role="tooltip" @@ -49,6 +52,7 @@ export class TooltipComponent { text: string = ''; left: number = 0; top: number = 0; + width: number = null; show: boolean = false; id: string; class: string; diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts index e41c410f0efb001d9c7896eaec93fb388fb9d23a..76c569e4245841930a5387dc5c8a2e34e6328c1b 100644 --- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts +++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts @@ -23,10 +23,10 @@ */ import { InteractivityChecker } from '@angular/cdk/a11y'; import { ComponentRef, ElementRef, Renderer2, ViewContainerRef } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { faker } from '@faker-js/faker/.'; import { TooltipComponent } from './tooltip.component'; -import { TooltipDirective, TooltipPosition } from './tooltip.directive'; +import { MAX_WIDTH, REM, TooltipDirective, TooltipPosition } from './tooltip.directive'; class MockElementRef extends ElementRef { nativeElement = { @@ -46,7 +46,17 @@ describe('TooltipDirective', () => { location: null, hostView: null, injector: null, - instance: { id: '', left: 0, top: 0, text: '', show: false, position: TooltipPosition.BELOW, class: '', leftOffset: 0 }, + instance: { + id: '', + left: 0, + top: 0, + text: '', + show: false, + position: TooltipPosition.BELOW, + class: '', + leftOffset: 0, + width: null, + }, }; const parentRect: DOMRect = { bottom: 0, top: 0, height: 0, left: 0, right: 0, toJSON: jest.fn(), width: 0, x: 0, y: 0 }; @@ -152,6 +162,7 @@ describe('TooltipDirective', () => { beforeEach(() => { directive.componentRef = mockComponentRef; directive.setTooltipProperties = jest.fn(); + directive.setWidth = jest.fn(); directive.elementRef.nativeElement.contains = jest.fn().mockReturnValue(true); }); @@ -175,11 +186,19 @@ describe('TooltipDirective', () => { expect(directive.attachedToFocused).toBeTruthy(); }); - it('should set tooltip properties', () => { + it('should set tooltip width', () => { directive.showTooltip(); - expect(directive.setTooltipProperties).toHaveBeenCalled(); + expect(directive.setWidth).toHaveBeenCalled(); }); + + it('should set tooltip properties after tick', fakeAsync(() => { + directive.showTooltip(); + + tick(); + + expect(directive.setTooltipProperties).toHaveBeenCalled(); + })); }); describe('hideTooltip', () => { @@ -253,6 +272,34 @@ describe('TooltipDirective', () => { }); }); + describe('setWidth', () => { + const maxTooltipWidth: number = MAX_WIDTH * REM; + + it('should set width to max width', () => { + setWidthOnComponentRef(maxTooltipWidth + 100); + + directive.setWidth(); + + expect(directive.componentRef.instance.width).toBe(MAX_WIDTH); + }); + + it('should set width to null', () => { + setWidthOnComponentRef(maxTooltipWidth - 100); + + directive.setWidth(); + + expect(directive.componentRef.instance.width).toBe(null); + }); + + function setWidthOnComponentRef(width: number): void { + directive.componentRef = Object.assign(mockComponentRef, { + location: { + nativeElement: { children: [{ getBoundingClientRect: jest.fn().mockReturnValue({ width }) }] }, + }, + }); + } + }); + describe('setAutoPosition', () => { it('should set tooltip position above if it cannot be displayed below', () => { directive.tooltipPosition = TooltipPosition.BELOW; @@ -282,48 +329,40 @@ describe('TooltipDirective', () => { }); }); - describe('setLeftOffset', () => { + describe('calculateLeftOffset', () => { beforeEach(() => { directive.leftOffset = 0; }); - it('should set 0 offset, if parent wider than tooltip', () => { - const rectForStandard: DOMRect = { ...parentRect, left: 50, right: 500, width: 100 }; - - directive.setLeftOffset(36, rectForStandard, 1000); - - expect(directive.leftOffset).toBe(0); - }); - it('should set positive left offset', () => { const rectForLeft: DOMRect = { ...parentRect, left: 10 }; - directive.setLeftOffset(50, rectForLeft, 1000); + const result: number = directive.calculateLeftOffset(50, rectForLeft, 1000); - expect(directive.leftOffset).toBe(15); + expect(result).toBe(15); }); it('should set negative left offset', () => { const rectForRight: DOMRect = { ...parentRect, left: 50, right: 990 }; - directive.setLeftOffset(50, rectForRight, 1000); + const result: number = directive.calculateLeftOffset(50, rectForRight, 1000); - expect(directive.leftOffset).toBe(-15); + expect(result).toBe(-15); }); it('should set 0 offset', () => { const rectForStandard: DOMRect = { ...parentRect, left: 50, right: 500 }; - directive.setLeftOffset(36, rectForStandard, 1000); + const result: number = directive.calculateLeftOffset(36, rectForStandard, 1000); - expect(directive.leftOffset).toBe(0); + expect(result).toBe(0); }); }); describe('setTooltipOffsetAndPosition', () => { beforeEach(() => { directive.setAutoPosition = jest.fn(); - directive.setLeftOffset = jest.fn(); + directive.calculateLeftOffset = jest.fn(); directive.componentRef = Object.assign(mockComponentRef, { location: { nativeElement: { children: [{ getBoundingClientRect: jest.fn().mockReturnValue({ height: 100, width: 200 }) }] }, @@ -340,10 +379,10 @@ describe('TooltipDirective', () => { expect(directive.setAutoPosition).toHaveBeenCalledWith(100, {}, 404); }); - it('should set tooltip left offset', () => { + it('should calculate^ tooltip left offset', () => { directive.setTooltipOffsetAndPosition(); - expect(directive.setLeftOffset).toHaveBeenCalledWith(200, {}, 503); + expect(directive.calculateLeftOffset).toHaveBeenCalledWith(200, {}, 503); }); }); diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts index ad238fc30e5c06357950f574eae44f518582a75a..d8f42d4ab8dd4e8bdab6e49031a00fbbbddbb480 100644 --- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts +++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts @@ -23,7 +23,17 @@ */ import { isEscapeKey, isNotNull } from '@alfa-client/tech-shared'; import { InteractivityChecker } from '@angular/cdk/a11y'; -import { ComponentRef, Directive, ElementRef, HostListener, inject, Input, OnDestroy, Renderer2, ViewContainerRef, } from '@angular/core'; +import { + ComponentRef, + Directive, + ElementRef, + HostListener, + inject, + Input, + OnDestroy, + Renderer2, + ViewContainerRef, +} from '@angular/core'; import { isEmpty, isNull, uniqueId } from 'lodash-es'; import { TooltipComponent } from './tooltip.component'; @@ -32,6 +42,9 @@ export enum TooltipPosition { BELOW = 'below', } +export const REM = 16; +export const MAX_WIDTH = 24; // Max tooltip width in rem + const OUTLINE_INDENT = 4; // Outline offset (2) + outline width (2) type TooltipAriaType = 'aria-describedby' | 'aria-labelledby'; @@ -80,7 +93,10 @@ export class TooltipDirective implements OnDestroy { const nativeElement: HTMLElement = this.elementRef.nativeElement; this.attachedToFocused = nativeElement.contains(document.activeElement); - this.setTooltipProperties(); + this.setWidth(); + setTimeout(() => { + this.setTooltipProperties(); + }); } @HostListener('mouseleave') @@ -126,6 +142,15 @@ export class TooltipDirective implements OnDestroy { this.componentRef.instance.show = true; } + setWidth(): void { + const currentWidth: number = this.componentRef.location.nativeElement.children[0].getBoundingClientRect().width; + if (currentWidth >= MAX_WIDTH * REM) { + this.componentRef.instance.width = MAX_WIDTH; + } else { + this.componentRef.instance.width = null; + } + } + setAutoPosition(tooltipHeight: number, parentRect: DOMRect, windowHeight: number): void { const { top, bottom } = parentRect; @@ -142,32 +167,25 @@ export class TooltipDirective implements OnDestroy { this.position = this.tooltipPosition; } - setLeftOffset(tooltipWidth: number, parentRect: DOMRect, windowWidth: number): void { - const { left, right, width } = parentRect; + calculateLeftOffset(tooltipWidth: number, parentRect: DOMRect, windowWidth: number): number { + const { left, right } = parentRect; const halfTooltipWidth: number = tooltipWidth / 2; - if (tooltipWidth < width) { - this.leftOffset = 0; - return; - } - if (halfTooltipWidth > left) { - this.leftOffset = halfTooltipWidth - left; - return; + return halfTooltipWidth - left; } if (halfTooltipWidth > windowWidth - right) { - this.leftOffset = windowWidth - right - halfTooltipWidth; - return; + return windowWidth - right - halfTooltipWidth; } - this.leftOffset = 0; + return 0; } setTooltipOffsetAndPosition(): void { const { width, height } = this.componentRef.location.nativeElement.children[0].getBoundingClientRect(); const parentRect: DOMRect = this.elementRef.nativeElement.getBoundingClientRect(); - this.setLeftOffset(width, parentRect, window.innerWidth); + this.leftOffset = this.calculateLeftOffset(width, parentRect, window.innerWidth); this.setAutoPosition(height, parentRect, window.innerHeight); } diff --git a/alfa-client/libs/forwarding/src/lib/forward-by-ozgcloud-button-container/forward-by-ozgcloud-button-container.component.html b/alfa-client/libs/forwarding/src/lib/forward-by-ozgcloud-button-container/forward-by-ozgcloud-button-container.component.html index c3a536b79edb8516518911255a4e1d40eeb2448b..6afe4a115c084140ff69e8b5351c1c2fd9804406 100644 --- a/alfa-client/libs/forwarding/src/lib/forward-by-ozgcloud-button-container/forward-by-ozgcloud-button-container.component.html +++ b/alfa-client/libs/forwarding/src/lib/forward-by-ozgcloud-button-container/forward-by-ozgcloud-button-container.component.html @@ -1,7 +1,8 @@ @if (vorgangWithEingang | hasLink: vorgangWithEingangLinkRel.FORWARD_BY_OZGCLOUD) { @if (showAsIconButton) { <ods-button-with-spinner - tooltip="Vorgang weiterleiten" + [tooltip]="'Vorgang weiterleiten'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" dataTestId="forward-by-ozgcloud-icon-button" diff --git a/alfa-client/libs/forwarding/src/lib/vorgang-forwarding-container/vorgang-forward-formular/vorgang-forward-form/vorgang-forward-form.component.html b/alfa-client/libs/forwarding/src/lib/vorgang-forwarding-container/vorgang-forward-formular/vorgang-forward-form/vorgang-forward-form.component.html index 277390cf09c47acbb0411587f3f688ed9160f282..1c778f0931410d2a0f9feedd502031b2093a1a50 100644 --- a/alfa-client/libs/forwarding/src/lib/vorgang-forwarding-container/vorgang-forward-formular/vorgang-forward-form/vorgang-forward-form.component.html +++ b/alfa-client/libs/forwarding/src/lib/vorgang-forwarding-container/vorgang-forward-formular/vorgang-forward-form/vorgang-forward-form.component.html @@ -44,6 +44,7 @@ <ozgcloud-stroked-button-with-spinner dataTestId="forward-button" toolTip="Vorgang weiterleiten" + tooltipAriaType="aria-labelledby" icon="forward" type="submit" text="Weiterleiten" diff --git a/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.html b/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.html index bf476f537b1107285627ef7d450f2a6ad8b3a57f..0043c92cb761914b2f64040ff4fd817e60eb9dfa 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.html +++ b/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.html @@ -26,7 +26,8 @@ @if (showAsIconButton) { <ods-button-with-spinner data-test-id="endgueltig-loeschen-icon-button" - tooltip="Endgültig löschen" + [tooltip]="'Endgültig löschen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="endgueltigLoeschen.emit()" diff --git a/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.spec.ts b/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.spec.ts index 66d46e2f07886affa2ff4603762c800b9f04798c..a2a4b7a68f6cf52c46b9fe237fb0446fe66defc5 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.spec.ts +++ b/alfa-client/libs/loesch-anforderung/src/lib/endgueltig-loeschen-button-container/endgueltig-loeschen-button/endgueltig-loeschen-button.component.spec.ts @@ -24,9 +24,9 @@ import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { DeleteVorgangFinallyIconComponent } from '@ods/system'; +import { DeleteVorgangFinallyIconComponent, TooltipDirective } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { EndgueltigLoeschenButtonComponent } from './endgueltig-loeschen-button.component'; describe('EndgueltigLoeschenButtonComponent', () => { @@ -42,6 +42,7 @@ describe('EndgueltigLoeschenButtonComponent', () => { EndgueltigLoeschenButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(DeleteVorgangFinallyIconComponent), + MockDirective(TooltipDirective), ], }).compileComponents(); diff --git a/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.html b/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.html index 3aee95356bb52e42320e94e2dab3b1ec1c5d9aec..a4536cb3f2ced04e06f416a1fc68e69af784e1fa 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.html +++ b/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.html @@ -27,7 +27,8 @@ <ods-button-with-spinner [stateResource]="loeschAnforderungZuruecknehmenCommand" data-test-id="loesch-anforderung-zuruecknehmen-icon-button" - tooltip="Löschanforderung zurücknehmen" + [tooltip]="'Löschanforderung zurücknehmen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="loeschAnforderungZuruecknehmen.emit()" diff --git a/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.spec.ts b/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.spec.ts index f51d85f10159a0572566a4fd917ebc45f36d66f1..83c5f617a40875c913aa54ca0505c59a9cf691c1 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.spec.ts +++ b/alfa-client/libs/loesch-anforderung/src/lib/loesch-anforderung-zuruecknehmen-button-container/loesch-anforderung-zuruecknehmen-button/loesch-anforderung-zuruecknehmen-button.component.spec.ts @@ -24,9 +24,9 @@ import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { UndoRequestVorgangDeletionIconComponent } from '@ods/system'; +import { TooltipDirective, UndoRequestVorgangDeletionIconComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { LoeschAnforderungZuruecknehmenButtonComponent } from './loesch-anforderung-zuruecknehmen-button.component'; describe('LoeschAnforderungZuruecknehmenButtonComponent', () => { @@ -42,6 +42,7 @@ describe('LoeschAnforderungZuruecknehmenButtonComponent', () => { LoeschAnforderungZuruecknehmenButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(UndoRequestVorgangDeletionIconComponent), + MockDirective(TooltipDirective), ], }).compileComponents(); diff --git a/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.html b/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.html index 4a7cc9cb40b1f9ed650cb7c5e27723ccb30edbef..869474ca105b45602a1d2449b5f160533b759cd5 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.html +++ b/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.html @@ -27,7 +27,8 @@ <ods-button-with-spinner [stateResource]="loeschenAnfordernCommand" data-test-id="loeschen-anfordern-icon-button" - tooltip="Löschen anfordern" + [tooltip]="'Löschen anfordern'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="loeschenAnfordern.emit()" diff --git a/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.spec.ts b/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.spec.ts index f859031bafc62e2db38e95d53cbfd3c6b2a3634a..41d80aba2d58565dd62feb31c0b9cc9cf8aa2fd6 100644 --- a/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.spec.ts +++ b/alfa-client/libs/loesch-anforderung/src/lib/loeschen-anfordern-button-container/loeschen-anfordern-button/loeschen-anfordern-button.component.spec.ts @@ -24,9 +24,9 @@ import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { RequestVorgangDeletionIconComponent } from '@ods/system'; +import { RequestVorgangDeletionIconComponent, TooltipDirective } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { LoeschenAnfordernButtonComponent } from './loeschen-anfordern-button.component'; describe('LoeschenAnfordernButtonComponent', () => { @@ -42,6 +42,7 @@ describe('LoeschenAnfordernButtonComponent', () => { LoeschenAnfordernButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(RequestVorgangDeletionIconComponent), + MockDirective(TooltipDirective), ], }).compileComponents(); diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html index 440eb0b7fee2d2fbf322f3c90295e21e83c7f902..eff1422c21167306dee76fb8185217b4475d9a7a 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-button-container/postfach-mail-button/postfach-mail-button.component.html @@ -29,7 +29,8 @@ <ods-button dataTestId="send-mail-icon-button" [isLoading]="pendingSendPostfachMailCommand.resource | hasLink: commandLinkRel.UPDATE" - tooltip="Neue Nachricht erstellen" + [tooltip]="'Neue Nachricht erstellen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="openPostfachNachrichtenDialog.emit()" diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail-list.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail-list.component.html index 18d2fef09050fcd571653eb9d72411c7fef4d73d..44d553b95fc0e784b4d66bcd22287e9700325f8a 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail-list.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail-list.component.html @@ -28,13 +28,10 @@ <alfa-postfach-mail-pdf-button-container [postfachMailListResource]="postfachMailListStateResource.resource" ></alfa-postfach-mail-pdf-button-container> - <ng-container - *ngIf=" - postfachMailListStateResource.resource | hasLink: postfachMailListLinkRel.SEND_POSTFACH_MAIL - " - > + <ng-container *ngIf="postfachMailListStateResource.resource | hasLink: postfachMailListLinkRel.SEND_POSTFACH_MAIL"> <alfa-postfach-mail-button-container toolTip="Neue Nachricht erstellen" + tooltipAriaType="aria-labelledby" [vorgang]="vorgangStateResource.resource" data-test-id="postfach-mail-button-container-no-label" ></alfa-postfach-mail-button-container> @@ -44,8 +41,7 @@ <ozgcloud-spinner [stateResource]="postfachMailListStateResource"> <alfa-postfach-mail *ngFor=" - let postfachMail of postfachMailListStateResource.resource - | toEmbeddedResources: postfachMailListLinkRel.POSTFACH_MAIL_LIST + let postfachMail of postfachMailListStateResource.resource | toEmbeddedResources: postfachMailListLinkRel.POSTFACH_MAIL_LIST " class="postfach" [vorgangStateResource]="vorgangStateResource" @@ -56,10 +52,7 @@ </ozgcloud-spinner> <ng-container - *ngIf=" - postfachMailListStateResource.resource | hasLink: postfachMailListLinkRel.SEND_POSTFACH_MAIL; - else noPostfach - " + *ngIf="postfachMailListStateResource.resource | hasLink: postfachMailListLinkRel.SEND_POSTFACH_MAIL; else noPostfach" > <alfa-postfach-mail-button-container text="Nachricht" @@ -69,7 +62,5 @@ </ng-container> <ng-template #noPostfach> - <span class="no-postfach-text" data-test-id="no-postfach-text" - >Dieser Vorgang ist nicht mit einem Postfach verknüpft.</span - > + <span class="no-postfach-text" data-test-id="no-postfach-text">Dieser Vorgang ist nicht mit einem Postfach verknüpft.</span> </ng-template> diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html index 0c9088cebcff3a2af6cfb04ffe3273ebed6e39f6..948be283b510f3091ef8044b9509fd6ea936b517 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.html @@ -23,7 +23,7 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<a routerLink="postfach"> +<a [routerLink]="onPage ? null : 'postfach'" data-test-class="incoming-mail-routing"> <div class="mail-head"> <div class="subject" data-test-id="mail-subject"> <mat-icon data-test-id="reply-icon">reply</mat-icon> diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.scss b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.scss index e0ac6f3a748f13674dc88e800b453dcf1316e82e..a7c44b0f07853d161e9e205a2a189f5ffabd67bb 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.scss +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.scss @@ -29,10 +29,6 @@ a { text-decoration: none; color: inherit; - - :host-context(.postfach-links-disabled) { - pointer-events: none; - } } .subject { diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts index e2a9572af4c3a17adcf142028db596f7c960f674..7ef16d31fb1312bf41e337a1c124ad35fdd96fea 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/incomming-mail/incomming-mail.component.spec.ts @@ -23,11 +23,13 @@ */ import { ON_PAGE, PostfachMailLinkRel } from '@alfa-client/postfach-shared'; import { FormatDateWithTimePipe, HasLinkPipe } from '@alfa-client/tech-shared'; -import { getElementFromFixture } from '@alfa-client/test-utils'; +import { getDebugElementFromFixtureByCss, getElementFromFixture } from '@alfa-client/test-utils'; import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; +import { provideRouter, RouterLinkWithHref, RouterModule } from '@angular/router'; +import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; import { createPostfachMailResource } from '../../../../../../../postfach-shared/test/postfach'; import { PostfachMailDateComponent } from '../postfach-mail-date/postfach-mail-date.component'; @@ -39,7 +41,8 @@ describe('IncommingMailComponent', () => { let component: IncommingMailComponent; let fixture: ComponentFixture<IncommingMailComponent>; - const attachmentIcon: string = '[data-test-id="postfach-nachricht-attachment-icon"]'; + const attachmentIcon: string = getDataTestIdOf('postfach-nachricht-attachment-icon'); + const routing: string = getDataTestClassOf('incoming-mail-routing'); beforeEach(async () => { await TestBed.configureTestingModule({ @@ -51,7 +54,9 @@ describe('IncommingMailComponent', () => { HasLinkPipe, MatIcon, ], + imports: [RouterModule], providers: [ + provideRouter([]), { provide: ON_PAGE, useValue: undefined, @@ -82,4 +87,26 @@ describe('IncommingMailComponent', () => { expect(element).toBeInstanceOf(HTMLElement); }); }); + + describe('routing should be', () => { + it('null on page site', () => { + component.onPage = false; + + fixture.detectChanges(); + + const routerElement = getDebugElementFromFixtureByCss(fixture, routing); + const routerLinkInstance: RouterLinkWithHref = (<any>routerElement).injector.get(RouterLinkWithHref); + expect((<any>routerLinkInstance).routerLinkInput[0]).toEqual('postfach'); + }); + + it('"postfach" on detail site', () => { + component.onPage = true; + + fixture.detectChanges(); + + const routerElement = getDebugElementFromFixtureByCss(fixture, routing); + const routerLinkInstance: RouterLinkWithHref = (<any>routerElement).injector.get(RouterLinkWithHref); + expect((<any>routerLinkInstance).routerLinkInput).toBeNull(); + }); + }); }); diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html index c1fcab4f48bdba4b090c1ca3b5e9394efdb1296e..a3d8233fccdc8cd9d12bf0a8bc3537284981e667 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.html @@ -25,7 +25,8 @@ --> <a [ngClass]="{ error: postfachMail | hasLink: postfachNachrichtLinkRel.RESEND_POSTFACH_MAIL }" - routerLink="postfach" + [routerLink]="onPage ? null : 'postfach'" + data-test-class="outgoing-mail-routing" > <div class="mail-head"> <alfa-user-profile-in-postfach-mail-container @@ -53,9 +54,7 @@ > </a> -<alfa-outgoing-mail-error-container - [postfachMailResource]="postfachMail" -></alfa-outgoing-mail-error-container> +<alfa-outgoing-mail-error-container [postfachMailResource]="postfachMail"></alfa-outgoing-mail-error-container> <alfa-postfach-nachricht-edit-button-container *ngIf="(postfachMail | hasLink: postfachNachrichtLinkRel.EDIT) && !onPage" data-test-id="postfach-nachricht-edit-button-container" diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.scss b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.scss index f5ec610968b1d94cd8c7ed35d865c13eba8f88ad..3a27d85601ca856b02e95a6b99715706c2f674e0 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.scss +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.scss @@ -25,10 +25,6 @@ a { text-decoration: none; color: inherit; - :host-context(.postfach-links-disabled) { - pointer-events: none; - } - .message { margin-bottom: 0.25rem; } diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts index bda7d7fa45697697fadf51fc146533788a1c3210..1c5549014cf02065fcac328e8e38dbd3505f9f98 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/outgoing-mail/outgoing-mail.component.spec.ts @@ -21,19 +21,21 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatIcon } from '@angular/material/icon'; import { ON_PAGE, PostfachMailLinkRel, PostfachMailResource } from '@alfa-client/postfach-shared'; -import { HasLinkPipe, StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { createStateResource, HasLinkPipe, StateResource } from '@alfa-client/tech-shared'; import { existsAsHtmlElement, + getDebugElementFromFixtureByCss, getElementFromFixture, getMockComponent, notExistsAsHtmlElement, } from '@alfa-client/test-utils'; import { UserProfileInPostfachMailContainerComponent } from '@alfa-client/user-profile'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatIcon } from '@angular/material/icon'; +import { provideRouter, RouterLinkWithHref, RouterModule } from '@angular/router'; +import { getDataTestClassOf, getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { createPostfachMailResource } from '../../../../../../../postfach-shared/test/postfach'; @@ -50,6 +52,8 @@ describe('OutgoingMailComponent', () => { const postfachEditButton: string = getDataTestIdOf('postfach-nachricht-edit-button-container'); const postfachOutgoingNachrichtDate: string = getDataTestIdOf('postfach-outgoing-nachricht-date'); + const routing: string = getDataTestClassOf('outgoing-mail-routing'); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ @@ -62,7 +66,9 @@ describe('OutgoingMailComponent', () => { MockComponent(PostfachMailDateComponent), MockComponent(PostfachNachrichtEditButtonContainerComponent), ], + imports: [RouterModule], providers: [ + provideRouter([]), { provide: ON_PAGE, useValue: undefined, @@ -95,9 +101,7 @@ describe('OutgoingMailComponent', () => { }); describe('edit button container component', () => { - const postfachNachricht: PostfachMailResource = createPostfachMailResource([ - PostfachMailLinkRel.EDIT, - ]); + const postfachNachricht: PostfachMailResource = createPostfachMailResource([PostfachMailLinkRel.EDIT]); describe('on existing edit link', () => { beforeEach(() => { @@ -151,9 +155,7 @@ describe('OutgoingMailComponent', () => { }); describe('in detail context', () => { - const postfachNachricht: PostfachMailResource = createPostfachMailResource([ - PostfachMailLinkRel.EDIT, - ]); + const postfachNachricht: PostfachMailResource = createPostfachMailResource([PostfachMailLinkRel.EDIT]); beforeEach(() => { component.onPage = true; @@ -185,4 +187,26 @@ describe('OutgoingMailComponent', () => { notExistsAsHtmlElement(fixture, postfachOutgoingNachrichtDate); }); }); + + describe('routing should be', () => { + it('null on page site', () => { + component.onPage = false; + + fixture.detectChanges(); + + const routerElement = getDebugElementFromFixtureByCss(fixture, routing); + const routerLinkInstance: RouterLinkWithHref = routerElement.injector.get(RouterLinkWithHref); + expect((<any>routerLinkInstance).routerLinkInput[0]).toEqual('postfach'); + }); + + it('"postfach" on detail site', () => { + component.onPage = true; + + fixture.detectChanges(); + + const routerElement = getDebugElementFromFixtureByCss(fixture, routing); + const routerLinkInstance: RouterLinkWithHref = routerElement.injector.get(RouterLinkWithHref); + expect((<any>routerLinkInstance).routerLinkInput).toBeNull(); + }); + }); }); diff --git a/alfa-client/libs/postfach/src/lib/postfach-page-container/postfach-page/postfach-page-mail-list/postfach-page-mail-list.component.html b/alfa-client/libs/postfach/src/lib/postfach-page-container/postfach-page/postfach-page-mail-list/postfach-page-mail-list.component.html index 0d0efa20640f413bdc4ab0d7ef083e321f1b0beb..9d74f0c569922238813f128c9def3394b701e647 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-page-container/postfach-page/postfach-page-mail-list/postfach-page-mail-list.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-page-container/postfach-page/postfach-page-mail-list/postfach-page-mail-list.component.html @@ -26,11 +26,10 @@ <ozgcloud-spinner *ngIf="postfachMailListStateResource"> <alfa-postfach-mail *ngFor=" - let postfachMail of postfachMailListStateResource.resource - | toEmbeddedResources: postfachMailListLinkRel.POSTFACH_MAIL_LIST + let postfachMail of postfachMailListStateResource.resource | toEmbeddedResources: postfachMailListLinkRel.POSTFACH_MAIL_LIST " [attr.data-test-id]="(postfachMail.subject | convertForDataTest) + '-item'" - class="postfach postfach-links-disabled w-full lg:w-1/2" + class="postfach w-full lg:w-1/2" [postfachMail]="postfachMail" > </alfa-postfach-mail> diff --git a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts index 02e4ff7ccc5ffa9a28b0295d44991618862758bb..60c4640d92c10a70d0bec7d763d80a8f9618ff33 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.spec.ts @@ -29,7 +29,7 @@ import { cold } from 'jest-marbles'; import { DummyLinkRel, DummyListLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyListResource, createDummyResource, createFilledDummyListResource } from 'libs/tech-shared/test/resource'; import { BehaviorSubject, Observable, of } from 'rxjs'; -import { singleCold, singleHot } from '../../../test/marbles'; +import { multipleCold, singleCold, singleHot } from '../../../test/marbles'; import { EMPTY_ARRAY } from '../tech.util'; import { ResourceListService } from './list-resource.service'; import { CreateResourceData, LinkRelationName, ListItemResource, ListResourceServiceConfig } from './resource.model'; @@ -311,25 +311,27 @@ describe('ListResourceService', () => { describe('create', () => { const toCreate: unknown = {}; + const returnResource: Resource = createDummyResource(); beforeEach(() => { service.listResource.value.resource = listResource; + resourceRepository.createResource.mockReturnValue(of(returnResource)); }); it('should throw error if link is not present', () => { service.listResource.next(createStateResource(createDummyListResource())); - expect(() => service.create(toCreate)).toThrowError('No creation link exists.'); + expect(() => service.create(toCreate)).toThrow('No creation link exists.'); }); it('should throw error if list resource is empty', () => { service.listResource.next(createEmptyStateResource()); - expect(() => service.create(toCreate)).toThrowError('No list resource available.'); + expect(() => service.create(toCreate)).toThrow('No list resource available.'); }); it('should trigger resourceLoader', () => { - service.create(toCreate); + service.create(toCreate).subscribe(); const createResourceData: CreateResourceData<ListResource> = { resource: listResource, @@ -341,11 +343,11 @@ describe('ListResourceService', () => { it('should return value', () => { const returnResource: Resource = createDummyResource(); - resourceRepository.createResource.mockReturnValue(singleHot(returnResource)); + resourceRepository.createResource.mockReturnValue(singleCold(returnResource, '-a')); - const createdResource: Observable<Resource> = service.create(toCreate); + const createdResource: Observable<StateResource<Resource>> = service.create(toCreate); - expect(createdResource).toBeObservable(singleCold(returnResource)); + expect(createdResource).toBeObservable(multipleCold(createEmptyStateResource(true), createStateResource(returnResource))); }); }); @@ -443,7 +445,7 @@ describe('ListResourceService', () => { it('should throw error if prev link is not present', () => { service.listResource.next(createStateResource(listResource)); - expect(() => service.prev()).toThrowError('There is no previous page.'); + expect(() => service.prev()).toThrow('There is no previous page.'); }); it('should call resourceLoader with prev link', () => { @@ -471,7 +473,7 @@ describe('ListResourceService', () => { it('should throw error if next link is not present', () => { service.listResource.next(createStateResource(listResource)); - expect(() => service.next()).toThrowError('There is no next page.'); + expect(() => service.next()).toThrow('There is no next page.'); }); it('should call resourceLoader with next link', () => { diff --git a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts index 13df5d4cf6fa8f06588a096d454e598faa8a4fa3..852a8299c56d256d93bf778e9600ab0717edc00f 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts @@ -58,7 +58,7 @@ export class ResourceListService<B extends Resource, T extends ListResource, I e constructor( private config: ListResourceServiceConfig<B>, - private repository: ResourceRepository, + private repository: ResourceRepository<I>, ) {} public getList(): Observable<StateResource<T>> { @@ -106,9 +106,12 @@ export class ResourceListService<B extends Resource, T extends ListResource, I e this.listResource.next(createEmptyStateResource()); } - public create(toCreate: unknown): Observable<Resource> { + public create(toCreate: unknown): Observable<StateResource<I>> { this.verifyBeforeCreation(); - return this.repository.createResource(this.buildCreateResourceData(toCreate, this.config.createLinkRel)); + return this.repository.createResource(this.buildCreateResourceData(toCreate, this.config.createLinkRel)).pipe( + map((listItemResource: I) => createStateResource(listItemResource)), + startWith(createEmptyStateResource<I>(true)), + ); } private verifyBeforeCreation(): void { diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts index 8a4081f31be113eaf2986e867f294603a9d56a6a..33d37b87af453f7fb777dd2ca69d8cf7a8f394c4 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.repository.ts @@ -28,7 +28,7 @@ import { CreateResourceData, LinkRelationName, SaveResourceData } from './resour import { ListResource } from './resource.util'; @Injectable({ providedIn: 'root' }) -export class ResourceRepository { +export class ResourceRepository<T extends Resource = Resource> { private resourceFactory = inject(ResourceFactory); static SEARCH_PARAM: string = 'searchBy'; @@ -45,7 +45,7 @@ export class ResourceRepository { return uri; } - public createResource(createResourceData: CreateResourceData<Resource>): Observable<Resource> { + public createResource(createResourceData: CreateResourceData<Resource>): Observable<T> { return this.resourceFactory.from(createResourceData.resource).post(createResourceData.linkRel, createResourceData.toCreate); } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts index d7d188b8c526a9d439b934397b54c32e2332227a..5bf83514f4fdbe127b04b8db9caf828408cdfdc1 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts @@ -82,12 +82,6 @@ describe('ResourceService', () => { isInvalidResourceCombinationSpy = jest.spyOn(ResourceUtil, 'isInvalidResourceCombination').mockReturnValue(true); }); - it('should have debounce', () => { - service.get().subscribe(); - - expect(service.handleResourceChanges).not.toHaveBeenCalled(); - }); - it('should handle config resource changed', fakeAsync(() => { service.get().subscribe(); tick(50); @@ -503,7 +497,7 @@ describe('ResourceService', () => { const thrownError$: Observable<StateResource<unknown>> = service.handleError(error); expect.assertions(1); - expect(lastValueFrom(thrownError$)).rejects.toThrowError('Internal Server Error'); + expect(lastValueFrom(thrownError$)).rejects.toThrow('Internal Server Error'); }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts index 1953e85ff4691dec3b95771a73f1bc1c7283efd1..b71624d87bffb0a0169167e60aba0c6be0fce2a1 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts @@ -24,35 +24,14 @@ import { HttpErrorResponse } from '@angular/common/http'; import { getUrl, hasLink, Resource, ResourceUri } from '@ngxp/rest'; import { isEqual, isNull } from 'lodash-es'; -import { - BehaviorSubject, - catchError, - combineLatest, - debounceTime, - filter, - first, - map, - Observable, - of, - startWith, - tap, - throwError, -} from 'rxjs'; +import { BehaviorSubject, catchError, combineLatest, filter, first, map, Observable, of, startWith, tap, throwError } from 'rxjs'; import { isUnprocessableEntity } from '../http.util'; import { HttpError } from '../tech.model'; import { isNotNull } from '../tech.util'; import { ResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; import { mapToFirst, mapToResource } from './resource.rxjs.operator'; -import { - createEmptyStateResource, - createErrorStateResource, - createStateResource, - isInvalidResourceCombination, - isLoadingRequired, - isStateResoureStable, - StateResource, -} from './resource.util'; +import { createEmptyStateResource, createErrorStateResource, createStateResource, isInvalidResourceCombination, isLoadingRequired, isStateResoureStable, StateResource, } from './resource.util'; /** * B = Type of baseresource @@ -70,7 +49,6 @@ export abstract class ResourceService<B extends Resource, T extends Resource> { public get(): Observable<StateResource<T>> { return combineLatest([this.stateResource.asObservable(), this.getConfigResource()]).pipe( - debounceTime(50), tap(([stateResource, configResource]) => this.handleResourceChanges(stateResource, configResource)), filter(([stateResource]) => !isInvalidResourceCombination(stateResource, this.configResource)), mapToFirst<T, B>(), diff --git a/alfa-client/libs/tech-shared/test/data-test.ts b/alfa-client/libs/tech-shared/test/data-test.ts index 98db6b1f6fb58fa31cc9353c8440c442bbfb4112..795b2dee30c083f71da5842c2cc64738c3dd9da4 100644 --- a/alfa-client/libs/tech-shared/test/data-test.ts +++ b/alfa-client/libs/tech-shared/test/data-test.ts @@ -38,3 +38,7 @@ export function getDataTestIdOf(value: string): string { export function getDataTestIdAttributeOf(value: string): string { return `[dataTestId="${value}"]`; } + +export function getDynamicDataTestIdAttributOf(value: string): string { + return `[ng-reflect-data-test-id="${value}"]`; +} diff --git a/alfa-client/libs/tech-shared/test/marbles.ts b/alfa-client/libs/tech-shared/test/marbles.ts index ea8e04237504cc8104ddb569ce574dd66c6ad362..fecda8769f38a80dd34c693c9ca39bf175a69eef 100644 --- a/alfa-client/libs/tech-shared/test/marbles.ts +++ b/alfa-client/libs/tech-shared/test/marbles.ts @@ -34,3 +34,7 @@ export function singleCold(object: any, frame: string = 'a'): ObservableWithSubs export function singleColdCompleted(object: any, frame: string = 'a'): ObservableWithSubscriptions { return cold(`(${frame}|)`, { a: object }); } + +export function multipleCold(first: any, second: any): ObservableWithSubscriptions { + return cold('ab', { a: first, b: second }); +} diff --git a/alfa-client/libs/test-utils/src/lib/mocking.ts b/alfa-client/libs/test-utils/src/lib/mocking.ts index a5e3137a17d3fed7c81cd72e350599195cd079f7..0fb616db60420994880e9acfddec1d0d37e10486 100644 --- a/alfa-client/libs/test-utils/src/lib/mocking.ts +++ b/alfa-client/libs/test-utils/src/lib/mocking.ts @@ -47,3 +47,9 @@ export function mockClass(clazz: any): Mock<any> { export function createSpy(clazz: any, functionName: string) { return jest.spyOn(clazz.prototype, functionName); } + +export function mockGetValue(object: any, name: string, returnValue: any): void { + Object.defineProperty(object, name, { + get: jest.fn(() => returnValue), + }); +} diff --git a/alfa-client/libs/test-utils/src/lib/model.ts b/alfa-client/libs/test-utils/src/lib/model.ts index d89ed415513a8528908c72641e609bc43c86c7c7..1094ac96376b5a8d2d81cf5a7ebf13c69aa2a550 100644 --- a/alfa-client/libs/test-utils/src/lib/model.ts +++ b/alfa-client/libs/test-utils/src/lib/model.ts @@ -29,3 +29,7 @@ export interface EventData<T> { name: string; data?: unknown; } + +export const MockEvent = { + CLICK: 'clickEmitter', +}; diff --git a/alfa-client/libs/ui/src/lib/ui/back-button/back-button.component.html b/alfa-client/libs/ui/src/lib/ui/back-button/back-button.component.html index 24868baff476b7d90f3eb7e44a5133ac4faf59d2..81e091ce81d24d1f5266b23ed9a3d838c6ab0226 100644 --- a/alfa-client/libs/ui/src/lib/ui/back-button/back-button.component.html +++ b/alfa-client/libs/ui/src/lib/ui/back-button/back-button.component.html @@ -23,8 +23,8 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<a [routerLink]="linkTo" data-test-id="back-button" [tooltip]="label" tooltipAriaType="aria-labelledby" tabindex="-1"> - <ods-button variant="ghost" size="fit"> +<a [routerLink]="linkTo" data-test-id="back-button" tabindex="-1"> + <ods-button variant="ghost" size="fit" [tooltip]="label" tooltipAriaType="aria-labelledby"> <ods-arrow-back-icon icon class="fill-text" /> </ods-button> </a> diff --git a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.html b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.html index 33cd2c515616d0786b8c14d6a39948451590524c..3a5337cbab58d332a3c3e4e8d500db3f33bd1d2d 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.html +++ b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.html @@ -23,7 +23,7 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<mat-form-field> +<mat-form-field class="max-w-48"> <mat-label>{{ label }}</mat-label> <input matInput diff --git a/alfa-client/libs/ui/src/lib/ui/progress-bar/progress-bar.component.ts b/alfa-client/libs/ui/src/lib/ui/progress-bar/progress-bar.component.ts index 0ce2009d9f4ac65aa2299249d25930c753fde058..185fa2810d9fcae9d2d9a3e710e140a441fcc617 100644 --- a/alfa-client/libs/ui/src/lib/ui/progress-bar/progress-bar.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/progress-bar/progress-bar.component.ts @@ -23,7 +23,7 @@ */ import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { Component, Input } from '@angular/core'; -import { MatProgressBar } from '@angular/material/progress-bar'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { Resource } from '@ngxp/rest'; @Component({ @@ -31,7 +31,7 @@ import { Resource } from '@ngxp/rest'; templateUrl: './progress-bar.component.html', styleUrls: ['./progress-bar.component.scss'], standalone: true, - imports: [MatProgressBar], + imports: [MatProgressBarModule], }) export class ProgressBarComponent { @Input() stateResource: StateResource<Resource> = createEmptyStateResource<Resource>(); diff --git a/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.html b/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.html index f0e6aa37b1b8932034832aa3beeed0f92b4c4ff4..e1e8b8b763439a3af38eac0f8929c7e06dd44809 100644 --- a/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.html +++ b/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.html @@ -26,7 +26,8 @@ @if (vorgang | hasLink: linkRel.ASSIGN) { <ods-button-with-spinner svgIcon="account_outline" - tooltip="Bearbeiter zuordnen" + [tooltip]="'Bearbeiter zuordnen'" + tooltipAriaType="aria-labelledby" data-test-id="assign-user-profile-icon-button" variant="ghost" size="fit" diff --git a/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.spec.ts b/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.spec.ts index 531805961b6eef4f0453f69ca5ebb481c096b1aa..a99b9f0846590b69dd1337fef67519e677ac48b2 100644 --- a/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.spec.ts +++ b/alfa-client/libs/user-profile/src/lib/assign-user-profile-button-container/assign-user-profile-button-container.component.spec.ts @@ -27,9 +27,9 @@ import { UserProfileService } from '@alfa-client/user-profile-shared'; import { VorgangHeaderLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { AccountCircleIconComponent } from '@ods/system'; +import { AccountCircleIconComponent, TooltipDirective } from '@ods/system'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { AssignUserProfileButtonContainerComponent } from './assign-user-profile-button-container.component'; describe('AssignUserProfileButtonContainerComponent', () => { @@ -47,6 +47,7 @@ describe('AssignUserProfileButtonContainerComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(AccountCircleIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component.ts b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component.ts index 35789e92ba4c39d3dda980b3450d5f27fff0d73c..d4744fd1411a9d59360b7e003d2f01958f2ba7ad 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component.ts @@ -37,6 +37,7 @@ import { AktenzeichenEditDialogMessages } from './aktenzeichen-edit-dialog.messa selector: 'alfa-aktenzeichen-edit-dialog', templateUrl: './aktenzeichen-edit-dialog.component.html', styleUrls: ['./aktenzeichen-edit-dialog.component.scss'], + providers: [AktenzeichenEditDialogFormservice], }) export class AktenzeichenEditDialogComponent implements OnInit { vorgang: VorgangWithEingangResource; diff --git a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.html b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.html index e56be799f110d811aa849ebd4db4709e7c644307..a90a52997e2b9343adcd6918d2df1106ef7f74b8 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.html @@ -30,7 +30,8 @@ class="absolute -top-2" variant="ghost" size="fit" - tooltip="Aktenzeichen bearbeiten" + [tooltip]="'Aktenzeichen bearbeiten'" + tooltipAriaType="aria-labelledby" data-test-id="aktenzeichen-editieren" (clickEmitter)="onEdit()" > diff --git a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.spec.ts index 11982ed18070cf1894cabf09fdf2cb958c9970ff..c930351baf32c30a2f9ee4c40b105e8c53212de0 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/aktenzeichen-editable/aktenzeichen-editable.component.spec.ts @@ -27,10 +27,10 @@ import { DialogService } from '@alfa-client/ui'; import { VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { AktenzeichenComponent } from '@alfa-client/vorgang-shared-ui'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ButtonComponent, EditIconComponent } from '@ods/system'; +import { ButtonComponent, EditIconComponent, TooltipDirective } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { AktenzeichenEditDialogComponent } from '../aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component'; import { AktenzeichenEditableComponent } from './aktenzeichen-editable.component'; @@ -50,6 +50,7 @@ describe('AktenzeichenComponent', () => { MockComponent(ButtonComponent), MockComponent(EditIconComponent), MockComponent(AktenzeichenComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.html index 758515c53318532c5950c7d61b75fa296137cabc..0188a0a906cb073b1e2f488c6fb2dd521413852b 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.html @@ -29,7 +29,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" dataTestId="abschliessen-icon-button" - tooltip="Vorgang abschließen" + [tooltip]="'Vorgang abschließen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="abschliessen()" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.spec.ts index a7334741a042a9d6cbe14d06d190160bd7bda7c5..599cf99c8d98a06cb0ff158ebc92c0735bae820a 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/abschliessen-button/abschliessen-button.component.spec.ts @@ -27,11 +27,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { CheckIconComponent } from '@ods/system'; +import { CheckIconComponent, TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { Observable, of } from 'rxjs'; import { AbschliessenButtonComponent } from './abschliessen-button.component'; @@ -53,6 +53,7 @@ describe('AbschliessenButtonComponent', () => { AbschliessenButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(CheckIconComponent), + MockDirective(TooltipDirective), HasLinkPipe, ], providers: [ diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.html index 7e55b9639e4a09b970c98394ad360cf550f80dcd..f643340a8a87e2f809e814f34ede36f2c7860383 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.html @@ -28,7 +28,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" (clickEmitter)="annehmen()" - tooltip="Vorgang annehmen" + [tooltip]="'Vorgang annehmen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" dataTestId="annehmen-icon-button" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.spec.ts index 0b4059086facce657ae46ba5a1c241afc7eb5122..f9798f26d30ca54c26c2a7c48c7cf013b6e4a5b1 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/annehmen-button/annehmen-button.component.spec.ts @@ -31,7 +31,7 @@ import { CheckCircleIconComponent, TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { AnnehmenButtonComponent } from './annehmen-button.component'; @@ -52,9 +52,9 @@ describe('AnnehmenButtonComponent', () => { declarations: [ AnnehmenButtonComponent, HasLinkPipe, - TooltipDirective, MockComponent(ButtonWithSpinnerComponent), MockComponent(CheckCircleIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.html index 107e3f67c299d7a6199434ebfc4750ae27008242..46880b05312a56ed041fd6c1e3972304f6e10bba 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.html @@ -29,7 +29,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" dataTestId="bearbeiten-icon-button" - tooltip="Vorgang bearbeiten" + [tooltip]="'Vorgang bearbeiten'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="bearbeiten()" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.spec.ts index 9691ba2813cd8669f2e959a8d1f412a8ec435b52..382edfc27fcb85f13e0f5b9b44cdb3bd49224ed0 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bearbeiten-button/bearbeiten-button.component.spec.ts @@ -26,11 +26,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { EditIconComponent } from '@ods/system'; +import { EditIconComponent, TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { BearbeitenButtonComponent } from './bearbeiten-button.component'; @@ -52,6 +52,7 @@ describe('BearbeitenButtonComponent', () => { BearbeitenButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(EditIconComponent), + MockDirective(TooltipDirective), HasLinkPipe, ], providers: [ diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html index 32924b770f3d204f1476521b4fe36050298bdb9f..01e35c10b65415c205ec70225f6df57e54df4575 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.html @@ -29,9 +29,10 @@ [stateResource]="commandStateResource$ | async" (clickEmitter)="bescheiden()" [tooltip]="toolTipText" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" - data-test-id="bescheiden-icon-button" + dataTestId="bescheiden-icon-button" > <ods-stamp-icon icon class="fill-text" size="medium" /> </ods-button-with-spinner> @@ -41,7 +42,7 @@ (clickEmitter)="bescheiden()" [text]="buttonText" variant="outline" - data-test-id="bescheiden-button" + dataTestId="bescheiden-button" > <ods-stamp-icon icon size="medium" class="fill-primary" /> </ods-button-with-spinner> diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts index 66410ba97c5b5c1109c11efefbfe0562dbf2fae3..932363f2b33c3e0ebf50e8082a9389fd83a0a5a7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts @@ -43,7 +43,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; import { StampIconComponent, TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; -import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { Observable, of } from 'rxjs'; @@ -54,8 +54,8 @@ describe('BescheidenButtonComponent', () => { let component: BescheidenButtonComponent; let fixture: ComponentFixture<BescheidenButtonComponent>; - const bescheidenButton: string = getDataTestIdOf('bescheiden-button'); - const bescheidenIconButton: string = getDataTestIdOf('bescheiden-icon-button'); + const bescheidenButton: string = getDataTestIdAttributeOf('bescheiden-button'); + const bescheidenIconButton: string = getDataTestIdAttributeOf('bescheiden-icon-button'); let vorgangCommandService: Mock<VorgangCommandService>; let ozgcloudDialogService: Mock<OzgcloudDialogService>; diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.html index 784ddfbbf353822e8bd5fc423d63a1f86f9f607e..0c3fa80c34898018959613c3aaf227b8281219e8 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.html @@ -28,7 +28,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" dataTestId="verwerfen-icon-button" - tooltip="Vorgang verwerfen" + [tooltip]="'Vorgang verwerfen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="verwerfen()" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.spec.ts index 75b739a2301a0447895e66b8f026c494bd0fb49e..32e09c8c158c73eeed252f39ae3ea824a4b2cfd7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/verwerfen-button/verwerfen-button.component.spec.ts @@ -26,11 +26,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { DiscardVorgangIconComponent } from '@ods/system'; +import { DiscardVorgangIconComponent, TooltipDirective } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { VerwerfenButtonComponent } from './verwerfen-button.component'; @@ -52,6 +52,7 @@ describe('VerwerfenButtonComponent', () => { VerwerfenButtonComponent, MockComponent(ButtonWithSpinnerComponent), MockComponent(DiscardVorgangIconComponent), + MockDirective(TooltipDirective), HasLinkPipe, ], providers: [ diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.html index 0e3bba543bfdd8404c10c1589b32b1a245e746ed..803643eefddd44bce0fa6ba4e43ce9bdd956a8c7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.html @@ -28,7 +28,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" dataTestId="wiedereroeffnen-icon-button" - tooltip="Vorgang wiedereröffnen" + [tooltip]="'Vorgang wiedereröffnen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="wiedereroeffnen()" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.spec.ts index a52280005233c965f956010d03a99c87be471fd3..9ab9c207ac2130f261185304e3cb862dd10794b2 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/wiedereroeffnen-button/wiedereroeffnen-button.component.spec.ts @@ -26,11 +26,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { UndoIconComponent } from '@ods/system'; +import { TooltipDirective, UndoIconComponent } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { WiedereroeffnenButtonComponent } from './wiedereroeffnen-button.component'; @@ -52,6 +52,7 @@ describe('WiedereroeffnenButtonComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(UndoIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.html index d2ac600887aa0ea31937090134e620cdc2c92b0b..8ee807e66333b167a0e6313fba94bfe10cd10265 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.html @@ -30,7 +30,8 @@ dataTestId="zurueckholen-icon-button" variant="ghost" size="fit" - tooltip="Vorgang zurückholen" + [tooltip]="'Vorgang zurückholen'" + tooltipAriaType="aria-labelledby" (clickEmitter)="zurueckholen()" > <ods-undo-icon icon class="fill-text" /> diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.spec.ts index f91b5423f7f409c523592849dacc6c52126e5c92..80f81d1d5d5642d69dd03aaee0ca35aa9b3467c8 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckholen-button/zurueckholen-button.component.spec.ts @@ -26,11 +26,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { UndoIconComponent } from '@ods/system'; +import { TooltipDirective, UndoIconComponent } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { ZurueckholenButtonComponent } from './zurueckholen-button.component'; @@ -53,6 +53,7 @@ describe('ZurueckholenButtonComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(UndoIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.html b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.html index 843b20963ed5baf2cf596165d889f9d4b405e232..d87c88a1a703eef1b5067af8de775f835f4b089b 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.html @@ -29,7 +29,8 @@ <ods-button-with-spinner [stateResource]="commandStateResource$ | async" dataTestId="zurueckstellen-icon-button" - tooltip="Vorgang zurückstellen" + [tooltip]="'Vorgang zurückstellen'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="zurueckstellen()" diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.spec.ts index 4f9a74edc4eff2593b3cea73015e57f11e0f18a9..51f20cf81bc614ac1d8ebbe805042db6709aff56 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/zurueckstellen-button/zurueckstellen-button.component.spec.ts @@ -26,11 +26,11 @@ import { mock } from '@alfa-client/test-utils'; import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { UndoIconComponent } from '@ods/system'; +import { TooltipDirective, UndoIconComponent } from '@ods/system'; import { createCommandResource } from 'libs/command-shared/test/command'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { of } from 'rxjs'; import { ZurueckstellenButtonComponent } from './zurueckstellen-button.component'; @@ -53,6 +53,7 @@ describe('ZurueckstellenButtonComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(UndoIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts index cd9df94996e413452d38811bdde387db2224cf3d..df1ec6563ff01d6130ab2cf54e144c5bfc970465 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts @@ -58,7 +58,7 @@ import { ReactiveFormsModule } from '@angular/forms'; import { MatDialogActions, MatDialogContent } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; import { MatMenuTrigger } from '@angular/material/menu'; -import { MatTab, MatTabGroup } from '@angular/material/tabs'; +import { MatTab, MatTabContent, MatTabGroup } from '@angular/material/tabs'; import { RouterModule, Routes } from '@angular/router'; import { ButtonWithSpinnerComponent, @@ -234,6 +234,7 @@ const routes: Routes = [ TooltipDirective, EditIconComponent, DiscardVorgangIconComponent, + MatTabContent, ], declarations: [ VorgangDetailPageComponent, diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-shared-ui.module.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-shared-ui.module.ts index 1624a507647c7817aece4450f2a4067d9b6b75af..96348a1634f974b37b2626bc204283a10f82bb09 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-shared-ui.module.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-shared-ui.module.ts @@ -34,6 +34,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { MatAutocomplete, MatAutocompleteTrigger, MatOption } from '@angular/material/autocomplete'; +import { MatButtonModule } from '@angular/material/button'; import { MatFormField, MatFormFieldModule } from '@angular/material/form-field'; import { MatIcon, MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; @@ -59,6 +60,7 @@ import { WiedervorlageIconComponent } from './wiedervorlage-icon/wiedervorlage-i MatIconModule, MatInputModule, MatFormField, + MatButtonModule, MatFormFieldModule, ReactiveFormsModule, MatAutocompleteTrigger, diff --git a/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.html b/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.html index 71fb146636883062d7fc3759a4cd120df047aa2e..49f1ca7b954d454668ee3c45f28a2ad9c50e85a4 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.html @@ -27,7 +27,8 @@ <ods-button-with-spinner dataTestId="erledigen-icon-button" [stateResource]="erledigenCommand$ | async" - tooltip="Als erledigt markieren" + [tooltip]="'Als erledigt markieren'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="erledigen()" diff --git a/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.spec.ts b/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.spec.ts index 81eec1106509121cb069b014fe64ac1077d5e96e..f87fc191ba1bce5908e1f9958e3a2060f9343010 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.spec.ts +++ b/alfa-client/libs/wiedervorlage/src/lib/erledigen-button-container/erledigen-button-container.component.spec.ts @@ -26,10 +26,10 @@ import { mock } from '@alfa-client/test-utils'; import { WiedervorlageLinkRel, WiedervorlageService } from '@alfa-client/wiedervorlage-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { CheckCircleIconComponent } from '@ods/system'; +import { CheckCircleIconComponent, TooltipDirective } from '@ods/system'; import { getDataTestIdAttributeOf } from 'libs/tech-shared/test/data-test'; import { createWiedervorlageResource } from 'libs/wiedervorlage-shared/test/wiedervorlage'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { ErledigenButtonContainerComponent } from './erledigen-button-container.component'; describe('ErledigenButtonContainerComponent', () => { @@ -47,6 +47,7 @@ describe('ErledigenButtonContainerComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(CheckCircleIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/wiedervorlage/src/lib/submit-wiedervorlage-button/submit-wiedervorlage-button.component.html b/alfa-client/libs/wiedervorlage/src/lib/submit-wiedervorlage-button/submit-wiedervorlage-button.component.html index df18f4361bbd25449d00c65d6ae30df918cc7617..231640ded0433d236945ca15247c90bdc3273231 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/submit-wiedervorlage-button/submit-wiedervorlage-button.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/submit-wiedervorlage-button/submit-wiedervorlage-button.component.html @@ -27,7 +27,8 @@ <ods-button-with-spinner [stateResource]="submitInProgress$ | async" (clickEmitter)="submit()" - tooltip="Wiedervorlage speichern" + [tooltip]="'Wiedervorlage speichern'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" dataTestId="speichern-icon-button" diff --git a/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.html b/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.html index 12ccae7d16777c95b80eff8e1d266316f462e35f..89e6c8cfe103025f4bf10a587d9f5273e06ffa3f 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.html @@ -27,7 +27,8 @@ <ods-button-with-spinner data-test-id="wiedereroeffnen-icon-button" [stateResource]="wiedereroeffnenCommand$ | async" - tooltip="Als offen markieren" + [tooltip]="'Als offen markieren'" + tooltipAriaType="aria-labelledby" variant="ghost" size="fit" (clickEmitter)="wiedereroeffnen()" diff --git a/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.spec.ts b/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.spec.ts index af8e1281a01dfdefb228d6011700b5cecd64f35b..c80ebee9420238cdf300e9c23e389764c4774b8e 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.spec.ts +++ b/alfa-client/libs/wiedervorlage/src/lib/wiedereroeffnen-button-container/wiedereroeffnen-button-container.component.spec.ts @@ -26,9 +26,9 @@ import { mock } from '@alfa-client/test-utils'; import { WiedervorlageLinkRel, WiedervorlageService } from '@alfa-client/wiedervorlage-shared'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonWithSpinnerComponent } from '@ods/component'; -import { CheckCircleIconComponent } from '@ods/system'; +import { CheckCircleIconComponent, TooltipDirective } from '@ods/system'; import { createWiedervorlageResource } from 'libs/wiedervorlage-shared/test/wiedervorlage'; -import { MockComponent } from 'ng-mocks'; +import { MockComponent, MockDirective } from 'ng-mocks'; import { WiedereroeffnenButtonContainerComponent } from './wiedereroeffnen-button-container.component'; describe('WiedereroeffnenButtonContainerComponent', () => { @@ -46,6 +46,7 @@ describe('WiedereroeffnenButtonContainerComponent', () => { HasLinkPipe, MockComponent(ButtonWithSpinnerComponent), MockComponent(CheckCircleIconComponent), + MockDirective(TooltipDirective), ], providers: [ { diff --git a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-create-button/wiedervorlage-create-button.component.html b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-create-button/wiedervorlage-create-button.component.html index c37de121c882aeb26bbdb25b36ae69bc0ed11c37..6fde7ac98e2140dbdf500d44ae877b8b66a3234a 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-create-button/wiedervorlage-create-button.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-create-button/wiedervorlage-create-button.component.html @@ -32,7 +32,8 @@ data-test-id="create-wiedervorlage-icon-button" variant="ghost" size="fit" - tooltip="Wiedervorlage erstellen" + [tooltip]="'Wiedervorlage erstellen'" + tooltipAriaType="aria-labelledby" > <ods-update-icon icon class="fill-text" /> </ods-button-with-spinner> diff --git a/alfa-client/libs/zustaendige-stelle-shared/src/lib/organisations-einheit/organisations-einheit.model.ts b/alfa-client/libs/zustaendige-stelle-shared/src/lib/organisations-einheit/organisations-einheit.model.ts index a802fe79f851232dcb9c8e27beae5c64f8db2b01..2658c1475c9ca56179e0590d511a948ce3f3adf7 100644 --- a/alfa-client/libs/zustaendige-stelle-shared/src/lib/organisations-einheit/organisations-einheit.model.ts +++ b/alfa-client/libs/zustaendige-stelle-shared/src/lib/organisations-einheit/organisations-einheit.model.ts @@ -27,6 +27,7 @@ import { Resource } from '@ngxp/rest'; export interface OrganisationsEinheit { name: string; anschrift: Anschrift; + organisationEinheitId: string; } export interface Anschrift { diff --git a/alfa-client/libs/zustaendige-stelle-shared/test/organisations-einheit.ts b/alfa-client/libs/zustaendige-stelle-shared/test/organisations-einheit.ts index 2257be08968edef9d5287bf9913a6025fe57dd78..d4aa69a51af006f51b1c1ec551f5d212c1e6e647 100644 --- a/alfa-client/libs/zustaendige-stelle-shared/test/organisations-einheit.ts +++ b/alfa-client/libs/zustaendige-stelle-shared/test/organisations-einheit.ts @@ -45,6 +45,7 @@ export function createOrganisationsEinheit(): OrganisationsEinheit { return { name: faker.word.sample(), anschrift: createAnschrift(), + organisationEinheitId: faker.string.uuid(), }; } diff --git a/alfa-client/tsconfig.base.json b/alfa-client/tsconfig.base.json index 6850f74e4eb7c031308a264a51e8a4782fa9613b..f6bb4c8b2657d8acef026b997d32794971be5656 100644 --- a/alfa-client/tsconfig.base.json +++ b/alfa-client/tsconfig.base.json @@ -22,6 +22,7 @@ "@admin-client/organisations-einheit-shared": ["libs/admin/organisations-einheit-shared/src/index.ts"], "@admin-client/postfach": ["libs/admin/postfach/src/index.ts"], "@admin-client/postfach-shared": ["libs/admin/postfach-shared/src/index.ts"], + "@admin-client/reporting-shared": ["libs/admin/reporting-shared/src/index.ts"], "@admin-client/settings-shared": ["libs/admin/settings-shared/src/index.ts"], "@admin-client/shared": ["libs/admin/shared/src/index.ts"], "@admin-client/statistik": ["libs/admin/statistik/src/index.ts"],