mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
#806 Prevent the filter apply button from jumping on open & close
This commit is contained in:
@@ -38,7 +38,12 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="apply-filter-wrapper">
|
||||
<div
|
||||
class="apply-filter-wrapper"
|
||||
centerBottomDuringTransition
|
||||
[inTransition]="inTransition"
|
||||
className="in-transition"
|
||||
>
|
||||
<button
|
||||
class="isa-btn isa-btn-primary isa-btn-pill btn-apply-filters text-nowrap"
|
||||
(click)="applyFilters()"
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
.apply-filter-wrapper {
|
||||
text-align: center;
|
||||
padding-bottom: 5px;
|
||||
|
||||
&.in-transition {
|
||||
position: absolute;
|
||||
transform: translate(-50%);
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-apply-filters {
|
||||
|
||||
@@ -1,15 +1,46 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RemissionListFilterComponent } from './remission-list-filter.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { IsaOverlayRef } from 'apps/sales/src/app/core/overlay';
|
||||
import { RemissionFilterService } from '../../services/remission-filter.service';
|
||||
import { RemissionService } from '@isa/remission';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgxsModule } from '@ngxs/store';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
describe('RemissionListFilterComponent', () => {
|
||||
fdescribe('RemissionListFilterComponent', () => {
|
||||
let fixture: ComponentFixture<RemissionListFilterComponent>;
|
||||
let component: RemissionListFilterComponent;
|
||||
let filterService: RemissionFilterService;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, NgxsModule.forRoot([])],
|
||||
providers: [
|
||||
{
|
||||
provide: IsaOverlayRef,
|
||||
useValue: jasmine.createSpyObj('isaOverlayRef', ['close']),
|
||||
},
|
||||
{
|
||||
provide: RemissionFilterService,
|
||||
useValue: jasmine.createSpy('remissionFilterService'),
|
||||
},
|
||||
{
|
||||
provide: RemissionService,
|
||||
useValue: jasmine.createSpy('remissionService'),
|
||||
},
|
||||
],
|
||||
declarations: [RemissionListFilterComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RemissionListFilterComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RemissionListFilterComponent);
|
||||
component = fixture.componentInstance;
|
||||
filterService = TestBed.get(RemissionFilterService);
|
||||
|
||||
spyOn(component, 'ngOnInit').and.callFake(() => {});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -17,4 +48,47 @@ describe('RemissionListFilterComponent', () => {
|
||||
it('should create the component', () => {
|
||||
expect(fixture).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('close', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'close').and.callThrough();
|
||||
filterService.resetFilters$ = new BehaviorSubject(null);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should set inTransition to true', () => {
|
||||
component.close();
|
||||
fixture.detectChanges();
|
||||
|
||||
const result = component.inTransition;
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('closeAnimationDone', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'closeAnimationDone').and.callThrough();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should set inTransition to false if animation is complete (in)', () => {
|
||||
component.animate = 'in';
|
||||
component.inTransition = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.closeAnimationDone();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.inTransition).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not set in transition if animation out is already complete', () => {
|
||||
component.animate = 'out';
|
||||
component.inTransition = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.closeAnimationDone();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.inTransition).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,8 @@ import { slideIn } from 'apps/sales/src/app/core/overlay';
|
||||
animations: [slideIn],
|
||||
})
|
||||
export class RemissionListFilterComponent implements OnInit, OnDestroy {
|
||||
animate = 'in';
|
||||
animate: 'in' | 'out' = 'in';
|
||||
inTransition = true;
|
||||
|
||||
@ViewChild('selectionContainer', { static: false })
|
||||
selectionContainer: ElementRef;
|
||||
@@ -211,11 +212,21 @@ export class RemissionListFilterComponent implements OnInit, OnDestroy {
|
||||
close() {
|
||||
this.animate = 'out';
|
||||
this.filterService.resetFilters$.next();
|
||||
|
||||
this.setTransition(true);
|
||||
}
|
||||
|
||||
closeAnimationDone() {
|
||||
if (this.animate === 'in') {
|
||||
this.setTransition(false);
|
||||
}
|
||||
|
||||
if (this.animate === 'out') {
|
||||
this.overlayRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
private setTransition(isTransitioning: boolean) {
|
||||
this.inTransition = isTransitioning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { CenterBottomDuringTransitionDirective } from './center-bottom-transition.directive';
|
||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
import {
|
||||
ElementRef,
|
||||
Renderer2,
|
||||
Component,
|
||||
ViewChild,
|
||||
DebugElement,
|
||||
Type,
|
||||
} from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
template: `<div
|
||||
centerBottomDuringTransition
|
||||
className="test-class"
|
||||
[inTransition]="inTransition"
|
||||
>
|
||||
Mock Component
|
||||
</div>`,
|
||||
})
|
||||
class TestHostComponent {
|
||||
@ViewChild(CenterBottomDuringTransitionDirective, { static: true })
|
||||
directive: CenterBottomDuringTransitionDirective;
|
||||
inTransition = false;
|
||||
|
||||
setTransition(isTransitioning: boolean) {
|
||||
this.inTransition = isTransitioning;
|
||||
}
|
||||
}
|
||||
|
||||
class MockElementRef {
|
||||
get nativeElement() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
fdescribe('Directive: CenterBottomTransition', () => {
|
||||
let fixture: ComponentFixture<TestHostComponent>;
|
||||
let component: TestHostComponent;
|
||||
let directive: CenterBottomDuringTransitionDirective;
|
||||
let elementRef: ElementRef;
|
||||
let renderer: Renderer2;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [Renderer2, { provide: ElementRef, useClass: MockElementRef }],
|
||||
declarations: [TestHostComponent, CenterBottomDuringTransitionDirective],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestHostComponent);
|
||||
component = fixture.componentInstance;
|
||||
directive = component.directive;
|
||||
elementRef = TestBed.get(ElementRef);
|
||||
renderer = fixture.componentRef.injector.get<Renderer2>(
|
||||
Renderer2 as Type<Renderer2>
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(
|
||||
directive instanceof CenterBottomDuringTransitionDirective
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('applyPosition', () => {
|
||||
let mockElement: DebugElement;
|
||||
beforeEach(() => {
|
||||
spyOn(directive, 'applyPosition').and.callThrough();
|
||||
spyOn(renderer, 'addClass').and.callFake(() => {});
|
||||
spyOn(renderer, 'removeClass').and.callFake(() => {});
|
||||
|
||||
mockElement = fixture.debugElement.query(By.css('div'));
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be called when inTransition Input changes', () => {
|
||||
component.inTransition = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(directive.applyPosition).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should return if nativeElement is undefined', () => {
|
||||
spyOn(elementRef, 'nativeElement').and.returnValue(undefined);
|
||||
|
||||
directive.applyPosition(true);
|
||||
});
|
||||
|
||||
it('should add the provided class to the native element if inTransition is true', () => {
|
||||
spyOn(elementRef, 'nativeElement').and.returnValue(
|
||||
mockElement.nativeElement
|
||||
);
|
||||
|
||||
directive.applyPosition(true);
|
||||
|
||||
expect(renderer.addClass).toHaveBeenCalledWith(
|
||||
mockElement.nativeElement,
|
||||
'test-class'
|
||||
);
|
||||
expect(renderer.removeClass).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove the provided class from the native element if inTransition is false', () => {
|
||||
spyOn(elementRef, 'nativeElement').and.returnValue(
|
||||
mockElement.nativeElement
|
||||
);
|
||||
|
||||
directive.applyPosition(false);
|
||||
|
||||
expect(renderer.removeClass).toHaveBeenCalledWith(
|
||||
mockElement.nativeElement,
|
||||
'test-class'
|
||||
);
|
||||
expect(renderer.addClass).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Directive, Input, ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
// tslint:disable-next-line: directive-selector
|
||||
@Directive({ selector: '[centerBottomDuringTransition]' })
|
||||
export class CenterBottomDuringTransitionDirective {
|
||||
@Input() set inTransition(isTransitioning: boolean) {
|
||||
this.applyPosition(isTransitioning);
|
||||
}
|
||||
|
||||
@Input() className = 'in-transition';
|
||||
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {}
|
||||
|
||||
applyPosition(inTransition: boolean) {
|
||||
const nativeElement = this.elementRef.nativeElement;
|
||||
|
||||
if (!nativeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inTransition) {
|
||||
this.renderer.addClass(nativeElement, this.className);
|
||||
}
|
||||
|
||||
if (!inTransition) {
|
||||
this.renderer.removeClass(nativeElement, this.className);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,5 @@ export * from './click-outside.directive';
|
||||
export * from './header-clicked.directive';
|
||||
export * from './tooltip.directive';
|
||||
export * from './var.directive';
|
||||
export * from './center-bottom-transition.directive';
|
||||
// end:ng42.barrel
|
||||
|
||||
|
||||
@@ -15,10 +15,21 @@ import { PackageNumberParserPipe } from '../pipes/package-number-parser.pipe';
|
||||
import { AddClassDirective } from './directives/add-class.directive';
|
||||
import { ShippingDocumentNumberFormatterPipe } from '../pipes/shipping-document-number-formatter.pipe';
|
||||
import { RemissionSourcesDisplayPipe } from '../pipes/remission-sources-display.pipe';
|
||||
import { ClickOutsideDirective, HeaderClickedDirective } from './directives';
|
||||
import {
|
||||
ClickOutsideDirective,
|
||||
HeaderClickedDirective,
|
||||
CenterBottomDuringTransitionDirective,
|
||||
} from './directives';
|
||||
|
||||
const components = [BackArrowComponent, ModalDialogComponent];
|
||||
const directives = [VarDirective, TooltipDirective, AddClassDirective, ClickOutsideDirective, HeaderClickedDirective];
|
||||
const directives = [
|
||||
VarDirective,
|
||||
TooltipDirective,
|
||||
AddClassDirective,
|
||||
ClickOutsideDirective,
|
||||
HeaderClickedDirective,
|
||||
CenterBottomDuringTransitionDirective,
|
||||
];
|
||||
const pipes = [
|
||||
SafeHtmlPipe,
|
||||
BookPricePipe,
|
||||
@@ -28,12 +39,12 @@ const pipes = [
|
||||
TrimPipe,
|
||||
PackageNumberParserPipe,
|
||||
ShippingDocumentNumberFormatterPipe,
|
||||
RemissionSourcesDisplayPipe
|
||||
RemissionSourcesDisplayPipe,
|
||||
];
|
||||
@NgModule({
|
||||
imports: [CommonModule, IconModule, ButtonModule],
|
||||
exports: [...components, ...directives, ...pipes],
|
||||
declarations: [...components, ...directives, ...pipes],
|
||||
providers: []
|
||||
providers: [],
|
||||
})
|
||||
export class SharedModule { }
|
||||
export class SharedModule {}
|
||||
|
||||
Reference in New Issue
Block a user