Skip to content
Snippets Groups Projects
Commit 41d0df88 authored by OZGCloud's avatar OZGCloud
Browse files

Merge remote-tracking branch 'origin/master' into OZG-6499-Signatur-in-Nachrichten-anzeigen

parents 2b67e562 56cfb68c
Branches
Tags
No related merge requests found
Showing
with 465 additions and 88 deletions
<ng-container *ngIf="(apiRootStateResource$ | async)?.resource as apiRoot">
<header class="flex items-center justify-between bg-white p-6" data-test-id="admin-header">
<div class="font-extrabold text-ozgblue">OZG-Cloud Administration</div>
<div>
<header
class="flex h-16 items-center justify-between border-b border-b-ozggray-300 bg-white px-9 py-2"
data-test-id="admin-header"
>
<a
class="rounded border-2 border-transparent p-1 outline-2 outline-offset-2 hover:border-primary focus-visible:border-gray-200 focus-visible:outline-focus"
aria-label="OZG-Cloud Administration"
routerLink="/"
data-test-id="logo-link"
>
<ods-admin-logo-icon />
</a>
<user-profile-button-container
data-test-id="user-profile-button"
></user-profile-button-container>
</div>
</header>
<div class="flex w-full flex-auto justify-center overflow-y-auto">
<div class="w-72 bg-slate-100 p-6">
<nav>
<admin-navigation
*ngIf="apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION"
data-test-id="navigation"
></admin-navigation>
</nav>
</div>
<main class="flex-auto overflow-y-auto bg-slate-200 p-6">
<div class="flex h-screen w-full justify-center overflow-y-auto">
<ods-navbar data-test-id="navigation">
<ng-container *ngIf="apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION">
<ods-nav-item caption="Organisationseinheiten" to="/organisationseinheiten">
<ods-orga-unit-icon icon />
</ods-nav-item>
<ods-nav-item caption="Postfach" to="/postfach">
<ods-mailbox-icon icon />
</ods-nav-item>
</ng-container>
</ods-navbar>
<main class="flex-1 overflow-y-auto bg-white px-6 py-4">
<router-outlet
*ngIf="
apiRoot | hasLink: ApiRootLinkRel.CONFIGURATION;
......@@ -29,5 +39,5 @@
</ng-template>
</main>
</div>
<span data-test-id="build-version">Version: {{ apiRoot.version }}</span>
<footer data-test-id="build-version">Version: {{ apiRoot.version }}</footer>
</ng-container>
......@@ -6,19 +6,26 @@ import {
} from '@alfa-client/tech-shared';
import {
Mock,
dispatchEventFromFixture,
existsAsHtmlElement,
getElementFromFixture,
mock,
notExistsAsHtmlElement,
} from '@alfa-client/test-utils';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { Router, RouterOutlet } from '@angular/router';
import {
AdminLogoIconComponent,
MailboxIconComponent,
NavItemComponent,
NavbarComponent,
OrgaUnitIconComponent,
} from '@ods/system';
import { AuthenticationService } from 'authentication';
import { NavigationComponent } from 'libs/admin-settings/src/lib/navigation/navigation.component';
import { createApiRootResource } from 'libs/api-root-shared/test/api-root';
import { getDataTestIdOf } from 'libs/tech-shared/test/data-test';
import { MockComponent } from 'ng-mocks';
import { MockComponent, MockDirective } from 'ng-mocks';
import { of } from 'rxjs';
import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component';
import { UnavailablePageComponent } from '../pages/unavailable/unavailable-page/unavailable-page.component';
......@@ -32,6 +39,7 @@ describe('AppComponent', () => {
const buildVersionSelector: string = getDataTestIdOf('build-version');
const userProfileButtonSelector: string = getDataTestIdOf('user-profile-button');
const navigationSelector: string = getDataTestIdOf('navigation');
const logoLink: string = getDataTestIdOf('logo-link');
const routerOutletSelector: string = getDataTestIdOf('router-outlet');
const authenticationService: Mock<AuthenticationService> = {
......@@ -47,11 +55,16 @@ describe('AppComponent', () => {
declarations: [
AppComponent,
MockComponent(NavigationComponent),
MockComponent(AdminLogoIconComponent),
MockComponent(OrgaUnitIconComponent),
MockComponent(MailboxIconComponent),
MockComponent(UserProfileButtonContainerComponent),
MockComponent(UnavailablePageComponent),
MockComponent(NavbarComponent),
MockComponent(NavItemComponent),
HasLinkPipe,
MockDirective(RouterOutlet),
],
imports: [RouterTestingModule],
providers: [
{
provide: AuthenticationService,
......@@ -129,22 +142,41 @@ describe('AppComponent', () => {
});
});
describe('administration logo', () => {
const apiResource: ApiRootResource = createApiRootResource();
beforeEach(() => {
component.apiRootStateResource$ = of(createStateResource(apiResource));
fixture.detectChanges();
});
it('should navigate to start page on click', () => {
dispatchEventFromFixture(fixture, logoLink, 'click');
expect(router.navigate).toHaveBeenCalledWith(['/']);
});
});
describe('navigation', () => {
beforeEach(() => {});
it('should exist if configuration link exists', () => {
it('should show links if configuration link exists', () => {
component.apiRootStateResource$ = of(
createStateResource(createApiRootResource([ApiRootLinkRel.CONFIGURATION])),
);
fixture.detectChanges();
existsAsHtmlElement(fixture, navigationSelector);
const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector);
expect(navbarElement.children.length).toBeGreaterThan(0);
});
it('should not exist if configuration resource not available', () => {
it('should not not show links if configuration resource not available', () => {
component.apiRootStateResource$ = of(createStateResource(createApiRootResource([])));
fixture.detectChanges();
notExistsAsHtmlElement(fixture, navigationSelector);
const navbarElement: HTMLElement = getElementFromFixture(fixture, navigationSelector);
expect(navbarElement.children.length).toBe(0);
});
});
......
......@@ -13,7 +13,16 @@ import { EffectsModule } from '@ngrx/effects';
import { StoreRouterConnectingModule } from '@ngrx/router-store';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { TestbtnComponent } from '@ods/system';
import {
AdminLogoIconComponent,
LogoutIconComponent,
MailboxIconComponent,
NavItemComponent,
NavbarComponent,
OrgaUnitIconComponent,
PopupComponent,
PopupListItemComponent,
} from '@ods/system';
import { OAuthModule } from 'angular-oauth2-oidc';
import { HttpUnauthorizedInterceptor } from 'libs/authentication/src/lib/http-unauthorized.interceptor';
import { UserProfileButtonContainerComponent } from '../common/user-profile-button-container/user-profile.button-container.component';
......@@ -34,7 +43,14 @@ import { appRoutes } from './app.routes';
],
imports: [
CommonModule,
TestbtnComponent,
AdminLogoIconComponent,
PopupComponent,
PopupListItemComponent,
NavItemComponent,
NavbarComponent,
OrgaUnitIconComponent,
LogoutIconComponent,
MailboxIconComponent,
RouterModule.forRoot(appRoutes),
BrowserModule,
BrowserAnimationsModule,
......
<div class="dropdown">
<button (click)="showDropDown()" class="dropbtn" data-test-id="drop-down-button">
{{ currentUserInitials }}
</button>
<div id="myDropdown" class="dropdown-content">
<span style="cursor: pointer" (click)="authenticationService.logout()" data-test-id="logout"
>Abmelden</span
<ods-popup buttonClass="rounded-full">
<div
button-content
role="img"
class="flex size-9 items-center justify-center rounded-full border-2 border-transparent bg-ozggray-900 hover:border-primary"
>
<p class="font-semibold text-whitetext" data-test-id="popup-button-content">
{{ currentUserInitials }}
</p>
</div>
</div>
<ods-popup-list-item
caption="Abmelden"
(itemClicked)="authenticationService.logout()"
data-test-id="popup-logout-button"
>
<ods-logout-icon icon />
</ods-popup-list-item>
</ods-popup>
.dropbtn {
background-color: #666666;
color: white;
padding: 16px;
cursor: pointer;
border-radius: 30px;
}
.dropbtn:hover,
.dropbtn:focus {
background-color: #666666;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
z-index: 1;
}
.show {
display: block;
}
......@@ -6,8 +6,10 @@ import {
} from '@alfa-client/test-utils';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { LogoutIconComponent, PopupComponent, PopupListItemComponent } from '@ods/system';
import { AuthenticationService } from 'authentication';
import { getDataTestIdOf } from 'libs/tech-shared/test/data-test';
import { MockComponent } from 'ng-mocks';
import { UserProfileButtonContainerComponent } from './user-profile.button-container.component';
describe('UserProfileButtonContainerComponent', () => {
......@@ -16,13 +18,18 @@ describe('UserProfileButtonContainerComponent', () => {
const authenticationService: Mock<AuthenticationService> = mock(AuthenticationService);
const dropDownButton: string = getDataTestIdOf('drop-down-button');
const logout: string = getDataTestIdOf('logout');
const popupButtonContent: string = getDataTestIdOf('popup-button-content');
const popupLogoutButton: string = getDataTestIdOf('popup-logout-button');
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UserProfileButtonContainerComponent],
imports: [RouterTestingModule],
imports: [
RouterTestingModule,
MockComponent(PopupComponent),
MockComponent(PopupListItemComponent),
MockComponent(LogoutIconComponent),
],
providers: [
{
provide: AuthenticationService,
......@@ -50,29 +57,23 @@ describe('UserProfileButtonContainerComponent', () => {
});
});
describe('button', () => {
it('should call showDropDown on click', () => {
component.showDropDown = jest.fn();
dispatchEventFromFixture(fixture, dropDownButton, 'click');
expect(component.showDropDown).toHaveBeenCalled();
});
describe('popup button', () => {
it('should show initials', () => {
const userInitials: string = 'AV';
component.currentUserInitials = userInitials;
component.currentUserInitials = 'AV';
fixture.detectChanges();
const buttonElement: HTMLElement = getElementFromFixture(fixture, dropDownButton);
expect(buttonElement.textContent.trim()).toEqual('AV');
const popupButtonContentElement: HTMLElement = getElementFromFixture(
fixture,
popupButtonContent,
);
expect(popupButtonContentElement.textContent.trim()).toEqual('AV');
});
});
describe('abmelden', () => {
describe('logout', () => {
it('should call authService logout', () => {
dispatchEventFromFixture(fixture, logout, 'click');
dispatchEventFromFixture(fixture, popupLogoutButton, 'itemClicked');
expect(authenticationService.logout).toHaveBeenCalled();
});
......
......@@ -4,7 +4,6 @@ import { AuthenticationService } from 'libs/authentication/src/lib/authenticatio
@Component({
selector: 'user-profile-button-container',
templateUrl: './user-profile-button-container.component.html',
styleUrls: ['./user-profile-button-container.component.scss'],
})
export class UserProfileButtonContainerComponent implements OnInit {
public currentUserInitials: string;
......@@ -14,8 +13,4 @@ export class UserProfileButtonContainerComponent implements OnInit {
ngOnInit(): void {
this.currentUserInitials = this.authenticationService.getCurrentUserInitials();
}
public showDropDown(): void {
document.getElementById('myDropdown').classList.toggle('show');
}
}
......@@ -2,4 +2,4 @@
@tailwind components;
@tailwind utilities;
/* You can add global styles to this file, and also import other style files */
@import 'libs/design-system/src/lib/tailwind-preset/root.css';
......@@ -14,6 +14,9 @@ alfa:
ingress:
use_staging_cert: true
ozgcloud:
user_assistance:
documentation:
url: /assets/benutzerleitfaden/benutzerleitfaden.pdf
vorgang:
bescheid:
- formEngineName: FormSolutions
......
......@@ -67,6 +67,13 @@ module.exports = (on: any, config: any) => {
},
});
on('after:spec', (spec: Cypress.Spec, results: CypressCommandLine.RunResult) => {
if (results && results.video && results.stats.failures === 0) {
console.log(`Delete recorded video because spec passed: ${results.video}`);
fs.unlinkSync(results.video);
}
});
// Workaround für Angular 13 und Cypress mit Webpack 4,
// Siehe https://github.com/cypress-io/cypress/issues/19066#issuecomment-1012055705
// Entfernen, sobald Cypress Webpack 5 nutzt - https://github.com/cypress-io/cypress/issues/19555
......
<div class="my-32 flex h-screen flex-col gap-2">
<div class="flex gap-48 py-6 lg:gap-96">
<h1 class="text-xl font-bold text-primary">Zuständige Stelle auswählen</h1>
<ods-button variant="icon" size="fit" (clickEmitter)="closeDialog()">
<ods-button
variant="icon"
size="fit"
(clickEmitter)="closeDialog()"
dataTestId="close-search-dialog"
>
<ods-close-icon class="fill-primary" icon />
</ods-button>
</div>
......
......@@ -10,6 +10,7 @@ export * from './lib/form/file-upload-button/file-upload-button.component';
export * from './lib/form/radio-button-card/radio-button-card.component';
export * from './lib/form/text-input/text-input.component';
export * from './lib/form/textarea/textarea.component';
export * from './lib/icons/admin-logo-icon/admin-logo-icon.component';
export * from './lib/icons/attachment-icon/attachment-icon.component';
export * from './lib/icons/bescheid-generate-icon/bescheid-generate-icon.component';
export * from './lib/icons/bescheid-upload-icon/bescheid-upload-icon.component';
......@@ -18,7 +19,10 @@ export * from './lib/icons/collaboration-icon/collaboration-icon.component';
export * from './lib/icons/exclamation-icon/exclamation-icon.component';
export * from './lib/icons/file-icon/file-icon.component';
export * from './lib/icons/iconVariants';
export * from './lib/icons/logout-icon/logout-icon.component';
export * from './lib/icons/mailbox-icon/mailbox-icon.component';
export * from './lib/icons/office-icon/office-icon.component';
export * from './lib/icons/orga-unit-icon/orga-unit-icon.component';
export * from './lib/icons/save-icon/save-icon.component';
export * from './lib/icons/search-icon/search-icon.component';
export * from './lib/icons/send-icon/send-icon.component';
......@@ -26,6 +30,8 @@ export * from './lib/icons/spinner-icon/spinner-icon.component';
export * from './lib/icons/stamp-icon/stamp-icon.component';
export * from './lib/instant-search/instant-search/instant-search.component';
export * from './lib/instant-search/instant-search/instant-search.model';
export * from './lib/navbar/nav-item/nav-item.component';
export * from './lib/navbar/navbar/navbar.component';
export * from './lib/popup/popup-list-item/popup-list-item.component';
export * from './lib/popup/popup/popup.component';
export * from './lib/testbtn/testbtn.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminLogoIconComponent } from './admin-logo-icon.component';
describe('AdminLogoIconComponent', () => {
let component: AdminLogoIconComponent;
let fixture: ComponentFixture<AdminLogoIconComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AdminLogoIconComponent],
}).compileComponents();
fixture = TestBed.createComponent(AdminLogoIconComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
@Component({
selector: 'ods-admin-logo-icon',
standalone: true,
imports: [CommonModule],
templateUrl: `./admin-logo-icon.component.html`,
})
export class AdminLogoIconComponent {}
import type { Meta, StoryObj } from '@storybook/angular';
import { AdminLogoIconComponent } from './admin-logo-icon.component';
const meta: Meta<AdminLogoIconComponent> = {
title: 'Icons/Admin logo icon',
component: AdminLogoIconComponent,
excludeStories: /.*Data$/,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<AdminLogoIconComponent>;
export const Default: Story = {};
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LogoutIconComponent } from './logout-icon.component';
describe('LogoutIconComponent', () => {
let component: LogoutIconComponent;
let fixture: ComponentFixture<LogoutIconComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LogoutIconComponent],
}).compileComponents();
fixture = TestBed.createComponent(LogoutIconComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { NgClass } from '@angular/common';
import { Component, Input } from '@angular/core';
import { twMerge } from 'tailwind-merge';
import { IconVariants, iconVariants } from '../iconVariants';
@Component({
selector: 'ods-logout-icon',
standalone: true,
imports: [NgClass],
template: `<svg
[ngClass]="twMerge(iconVariants({ size }), 'fill-text', class)"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H12V5H5V19H12V21H5ZM16 17L14.625 15.55L17.175 13H9V11H17.175L14.625 8.45L16 7L21 12L16 17Z"
/>
</svg>`,
})
export class LogoutIconComponent {
@Input() size: IconVariants['size'] = 'medium';
@Input() class: string = '';
readonly iconVariants = iconVariants;
readonly twMerge = twMerge;
}
import type { Meta, StoryObj } from '@storybook/angular';
import { LogoutIconComponent } from './logout-icon.component';
const meta: Meta<LogoutIconComponent> = {
title: 'Icons/Logout icon',
component: LogoutIconComponent,
excludeStories: /.*Data$/,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<LogoutIconComponent>;
export const Default: Story = {
args: { size: 'medium' },
argTypes: {
size: {
control: 'select',
options: ['small', 'medium', 'large', 'extra-large', 'full'],
description: 'Size of icon. Property "full" means 100%',
table: {
defaultValue: { summary: 'medium' },
},
},
},
};
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MailboxIconComponent } from './mailbox-icon.component';
describe('MailboxIconComponent', () => {
let component: MailboxIconComponent;
let fixture: ComponentFixture<MailboxIconComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MailboxIconComponent],
}).compileComponents();
fixture = TestBed.createComponent(MailboxIconComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment