Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/536 teamfilter bug #664

Merged
merged 7 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion frontend/cypress/e2e/tab.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ describe('Tab workflow tests', () => {
}

function openCreateObjective() {
cy.get('.objective').first().focus();
cy.contains('Puzzle ITC');
cy.contains('Teamverwaltung').focus();
cy.tabForward();
cy.tabForwardUntil('[data-testId="add-objective"]');
cy.focused().contains('Objective hinzufügen');
cy.realPress('Enter');
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"watch": "ng build --watch --configuration development",
"test": "jest --silent",
"cypress:open": "cypress open",
"cypress:run": "cypress run --browser chrome",
"cypress:run": "cypress run --browser chrome --headed",
"cypress:open-test": "concurrently \"npm start\" \"cypress open\"",
"cypress:run-test": "npm run build && concurrently \"npm run serve:dist\" \"cypress run\"",
"format": "prettier --write \"./**/*.{js,ts,json,css,scss,html,md,yaml}\"",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<pzsh-banner id="okrBanner">
<pzsh-banner id="okrBanner" #okrBanner>
<img alt="okr-tangram" height="140" ngSrc="assets/images/triangles-okr-header.svg" slot="tangram" width="274" />
<div class="h-100 m-0 d-flex" slot="content">
<section class="d-none d-md-flex">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin

import { ApplicationBannerComponent } from './application-banner.component';
import { By } from '@angular/platform-browser';
import { RefreshDataService } from '../shared/services/refresh-data.service';
import { PUZZLE_TOP_BAR_HEIGHT } from '../shared/constantLibary';

class ResizeObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}

const refreshDataServiceMock = {
okrBannerHeightSubject: {
next: jest.fn(),
},
};

describe('ApplicationBannerComponent', () => {
//@ts-ignore
global.ResizeObserver = ResizeObserverMock;
Expand All @@ -18,7 +26,9 @@ describe('ApplicationBannerComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ApplicationBannerComponent],
providers: [{ provide: RefreshDataService, useValue: refreshDataServiceMock }],
});

fixture = TestBed.createComponent(ApplicationBannerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
Expand All @@ -35,58 +45,53 @@ describe('ApplicationBannerComponent', () => {
let scrollTop: number = 180;
//Set lastScrollPosition to smaller than scrollTop => user scrolls down
component.lastScrollPosition = 160;
component.bannerHeight = bannerHeight;

//Set banner style
component.setOKRBannerStyle(bannerHeight, scrollTop);
component.refreshBanner(scrollTop);
tick(600);

//Assert that banner is hidden was changed
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('#okrBanner')).attributes['style']).toContain(
'top: -' + (component.PUZZLE_TOP_BAR_HEIGHT + bannerHeight),
'top: -' + (PUZZLE_TOP_BAR_HEIGHT + bannerHeight),
);
}));

it('should show banner if scrolled up', fakeAsync(() => {
//Set bannerHeight to default
let bannerHeight: number = 160;
//Scroll more than the height of the banner
let scrollTop: number = 180;
//Set lastScrollPosition to bigger than scrollTop => user scrolls up
component.lastScrollPosition = 200;

//Set banner style
component.setOKRBannerStyle(bannerHeight, scrollTop);
component.refreshBanner(scrollTop);
tick(600);

//Assert that banner is visible
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('#okrBanner')).attributes['style']).toContain(
'top: ' + component.PUZZLE_TOP_BAR_HEIGHT,
'top: ' + PUZZLE_TOP_BAR_HEIGHT,
);
}));

it('should call setOKRBannerStyle() when changing header appearance', () => {
jest.spyOn(component, 'setOKRBannerStyle').mockReturnValue();
jest.spyOn(component, 'refreshBanner').mockReturnValue();

//Set bannerHeight to default and execute header appearance change
let bannerHeight: number = 160;
component.changeHeaderAppearance(bannerHeight);
component.bannerHeight = 160;
component.changeHeaderAppearance();

//Assert that banner is visible
fixture.detectChanges();
expect(component.setOKRBannerStyle).toHaveBeenCalled();
expect(component.refreshBanner).toHaveBeenCalled();
});

it('should call removeScrollEventListener() when updating scroll event listeners', () => {
jest.spyOn(component, 'removeScrollEventListener').mockReturnValue();
it('should call correct method after call scroll()', () => {
jest.spyOn(component, 'changeHeaderAppearance');

//Set bannerHeight to default and execute updating of scroll event listeners
let bannerHeight: number = 160;
component.updateScrollEventListeners(bannerHeight);
component.scroll();

//Assert that banner is visible
fixture.detectChanges();
expect(component.removeScrollEventListener).toHaveBeenCalled();
expect(component.changeHeaderAppearance).toHaveBeenCalled();
});
});
75 changes: 46 additions & 29 deletions frontend/src/app/application-banner/application-banner.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
ElementRef,
HostListener,
OnDestroy,
ViewChild,
} from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { RefreshDataService } from '../shared/services/refresh-data.service';
import { DEFAULT_HEADER_HEIGHT_PX, PUZZLE_TOP_BAR_HEIGHT } from '../shared/constantLibary';

@Component({
selector: 'app-application-banner',
Expand All @@ -8,50 +18,57 @@ import { BehaviorSubject, Subject } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ApplicationBannerComponent implements AfterViewInit, OnDestroy {
lastScrollPosition: number = 0;
PUZZLE_TOP_BAR_HEIGHT: number = 48;
okrBanner: HTMLElement | null = null;
eventListener: EventListener | null = null;
@ViewChild('okrBanner') okrBanner!: ElementRef;
quarterLabel$: BehaviorSubject<string> = new BehaviorSubject<string>('');
panelOpenState = false;
resizeObserver: ResizeObserver;
bannerHeight: number = DEFAULT_HEADER_HEIGHT_PX;
lastScrollPosition: number = 0;

resizeObserver: ResizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
this.updateScrollEventListeners(entries[0].contentRect.height);
});

ngAfterViewInit(): void {
this.okrBanner = document.getElementById('okrBanner')!;
this.resizeObserver.observe(this.okrBanner);
constructor(private refreshDataService: RefreshDataService) {
this.resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
const newBannerHeight = entries[0].contentRect.height;
if (newBannerHeight != this.bannerHeight) {
this.bannerHeight = newBannerHeight;
this.refreshDataService.okrBannerHeightSubject.next(this.bannerHeight);
}
});
}

ngOnDestroy(): void {
this.removeScrollEventListener();
this.resizeObserver.disconnect();
ngAfterViewInit(): void {
this.resizeObserver.observe(this.okrBanner.nativeElement);
}

changeHeaderAppearance(bannerHeight: number) {
changeHeaderAppearance() {
let scrollTop: number = window.scrollY || document.documentElement.scrollTop;
this.setOKRBannerStyle(bannerHeight, scrollTop);
this.refreshBanner(scrollTop);
this.lastScrollPosition = scrollTop;
}

setOKRBannerStyle(bannerHeight: number, scrollTop: number) {
this.okrBanner!.style.top = this.showOrHideBanner(scrollTop, bannerHeight);
refreshBanner(scrollTop: number) {
const newBannerPadding = this.getBannerTopPadding(scrollTop);
this.okrBanner.nativeElement.style.top = newBannerPadding + 'px';

const overviewPadding = this.getOverviewPadding(newBannerPadding, this.bannerHeight);
this.refreshDataService.okrBannerHeightSubject.next(overviewPadding);
}

showOrHideBanner(scrollTop: number, bannerHeight: number) {
getBannerTopPadding(scrollTop: number) {
return scrollTop > this.lastScrollPosition
? '-' + (this.PUZZLE_TOP_BAR_HEIGHT + bannerHeight) + 'px'
: this.PUZZLE_TOP_BAR_HEIGHT + 'px';
? 0 - (PUZZLE_TOP_BAR_HEIGHT + this.bannerHeight)
: PUZZLE_TOP_BAR_HEIGHT;
}

getOverviewPadding(newBannerPadding: number, paddingAmount: number): number {
return newBannerPadding < 0 ? PUZZLE_TOP_BAR_HEIGHT * 2 : paddingAmount;
}

updateScrollEventListeners(bannerHeight: number) {
this.removeScrollEventListener();
this.eventListener = () => this.changeHeaderAppearance(bannerHeight);
window.addEventListener('scroll', this.eventListener);
@HostListener('window:scroll')
scroll() {
this.changeHeaderAppearance();
}

removeScrollEventListener() {
window.removeEventListener('scroll', this.eventListener!);
ngOnDestroy(): void {
this.resizeObserver.disconnect();
}
}
2 changes: 1 addition & 1 deletion frontend/src/app/overview/overview.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<app-application-top-bar [hasAdminAccess]="hasAdminAccess"></app-application-top-bar>
<app-application-banner id="bannerComponent"></app-application-banner>
<div class="overviewContainer">
<div class="overviewContainer px-sm-4" [ngStyle]="{ 'padding-top.px': overviewPadding | async }">
<app-team
[hasAdminAccess]="hasAdminAccess"
*ngFor="let overviewEntity of overviewEntities$ | async; trackBy: trackByFn"
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/app/overview/overview.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import "variables";

.overviewContainer {
overflow-x: hidden;
background-color: $overview-bg;
transition: 0.5s;
}
32 changes: 29 additions & 3 deletions frontend/src/app/overview/overview.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverviewComponent } from './overview.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { overViewEntity1 } from '../shared/testData';
import { of, Subject } from 'rxjs';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { OverviewService } from '../shared/services/overview.service';
import { AppRoutingModule } from '../app-routing.module';
import { RouterTestingHarness } from '@angular/router/testing';
import { RefreshDataService } from '../shared/services/refresh-data.service';
import { authGuard } from '../shared/guards/auth.guard';
import { ApplicationBannerComponent } from '../application-banner/application-banner.component';
import { ApplicationTopBarComponent } from '../application-top-bar/application-top-bar.component';
import { DateTimeProvider, OAuthLogger, OAuthService, UrlHelperService } from 'angular-oauth2-oidc';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

const overviewService = {
getOverview: jest.fn(),
Expand All @@ -22,15 +29,25 @@ const refreshDataServiceMock = {
teamFilterReady: new Subject(),
quarterFilterReady: new Subject(),
reloadOverviewSubject: new Subject(),
okrBannerHeightSubject: new BehaviorSubject(5),
};

class ResizeObserverMock {
observe() {}
unobserve() {}
disconnect() {}
}

describe('OverviewComponent', () => {
//@ts-ignore
global.ResizeObserver = ResizeObserverMock;

let component: OverviewComponent;
let fixture: ComponentFixture<OverviewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HttpClientTestingModule, AppRoutingModule],
declarations: [OverviewComponent],
imports: [HttpClientTestingModule, AppRoutingModule, MatDialogModule, MatIconModule, MatMenuModule],
declarations: [OverviewComponent, ApplicationBannerComponent, ApplicationTopBarComponent],
providers: [
{
provide: OverviewService,
Expand All @@ -44,7 +61,16 @@ describe('OverviewComponent', () => {
provide: RefreshDataService,
useValue: refreshDataServiceMock,
},
{
provide: MatDialogRef,
useValue: {},
},
OAuthService,
UrlHelperService,
OAuthLogger,
DateTimeProvider,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();

fixture = TestBed.createComponent(OverviewComponent);
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/app/overview/overview.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { OverviewEntity } from '../shared/types/model/OverviewEntity';
import { catchError, combineLatest, EMPTY, Observable, ReplaySubject, Subject, take, takeUntil } from 'rxjs';
import { catchError, combineLatest, EMPTY, ReplaySubject, Subject, take, takeUntil } from 'rxjs';
import { OverviewService } from '../shared/services/overview.service';
import { ActivatedRoute } from '@angular/router';
import { RefreshDataService } from '../shared/services/refresh-data.service';
Expand All @@ -12,16 +12,18 @@ import { getQueryString, getValueFromQuery, trackByFn } from '../shared/common';
styleUrls: ['./overview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class OverviewComponent implements OnDestroy {
export class OverviewComponent implements OnInit, OnDestroy {
overviewEntities$: Subject<OverviewEntity[]> = new Subject<OverviewEntity[]>();
protected readonly trackByFn = trackByFn;
private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);
hasAdminAccess: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);
overviewPadding: Subject<number> = new Subject();

constructor(
private overviewService: OverviewService,
private refreshDataService: RefreshDataService,
private activatedRoute: ActivatedRoute,
private changeDetector: ChangeDetectorRef,
) {
this.refreshDataService.reloadOverviewSubject
.pipe(takeUntil(this.destroyed$))
Expand All @@ -39,6 +41,13 @@ export class OverviewComponent implements OnDestroy {
});
}

ngOnInit(): void {
this.refreshDataService.okrBannerHeightSubject.subscribe((e) => {
this.overviewPadding.next(e);
this.changeDetector.detectChanges();
});
}

loadOverviewWithParams() {
const quarterQuery = this.activatedRoute.snapshot.queryParams['quarter'];
const teamQuery = this.activatedRoute.snapshot.queryParams['teams'];
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/shared/constantLibary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ interface MessageKeyMap {
};
}

export const PUZZLE_TOP_BAR_HEIGHT: number = 48;
export const DEFAULT_HEADER_HEIGHT_PX = 140;

export const SUCCESS_MESSAGE_KEY_PREFIX = 'SUCCESS.';
export const ERROR_MESSAGE_KEY_PREFIX = 'ERROR.';

export const DATE_FORMAT = 'dd.MM.yyyy';
export const CONFIRM_DIALOG_WIDTH: string = '450px';
export const DRAWER_ROUTES = ['objective', 'keyresult'];
export const BLACKLIST_TOASTER_ROUTES_ERROR = ['/token'];

export const SUCCESS_MESSAGE_MAP: MessageKeyMap = {
teams: {
KEY: 'TEAM',
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/shared/services/refresh-data.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { BehaviorSubject, Subject } from 'rxjs';
import { DEFAULT_HEADER_HEIGHT_PX } from '../constantLibary';

@Injectable({
providedIn: 'root',
Expand All @@ -10,6 +11,8 @@ export class RefreshDataService {
public quarterFilterReady: Subject<void> = new Subject<void>();
public teamFilterReady: Subject<void> = new Subject<void>();

public okrBannerHeightSubject: BehaviorSubject<number> = new BehaviorSubject<number>(DEFAULT_HEADER_HEIGHT_PX);

markDataRefresh() {
this.reloadOverviewSubject.next();
}
Expand Down
Loading