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

Fix "Submission input type date doesn't work properly once a value has been set" #2593

Merged
merged 2 commits into from
Nov 3, 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Load the implementations that should be tested
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, Renderer2 } from '@angular/core';
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing';

import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
Expand All @@ -13,6 +13,7 @@ import {
mockDynamicFormLayoutService,
mockDynamicFormValidationService
} from '../../../../../testing/dynamic-form-mock-services';
import { By } from '@angular/platform-browser';


export const DATE_TEST_GROUP = new UntypedFormGroup({
Expand All @@ -39,6 +40,11 @@ describe('DsDatePickerComponent test suite', () => {
let dateFixture: ComponentFixture<DsDatePickerComponent>;
let html;

const renderer2: Renderer2 = {
selectRootElement: jasmine.createSpy('selectRootElement'),
querySelector: jasmine.createSpy('querySelector'),
} as unknown as Renderer2;

// waitForAsync beforeEach
beforeEach(waitForAsync(() => {

Expand All @@ -54,7 +60,8 @@ describe('DsDatePickerComponent test suite', () => {
ChangeDetectorRef,
DsDatePickerComponent,
{ provide: DynamicFormLayoutService, useValue: mockDynamicFormLayoutService },
{ provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService }
{ provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService },
{ provide: Renderer2, useValue: renderer2 },
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
Expand Down Expand Up @@ -233,6 +240,102 @@ describe('DsDatePickerComponent test suite', () => {
expect(dateComp.disabledMonth).toBeFalsy();
expect(dateComp.disabledDay).toBeFalsy();
});

it('should move focus on month field when on year field and tab pressed', fakeAsync(() => {
const event = {
field: 'day',
value: null
};
const event1 = {
field: 'month',
value: null
};
dateComp.onChange(event);
dateComp.onChange(event1);

const yearElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_year`));
const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`));

yearElement.nativeElement.focus();
dateFixture.detectChanges();

expect(document.activeElement).toBe(yearElement.nativeElement);

dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab' }));
dateFixture.detectChanges();

tick(200);
dateFixture.detectChanges();

expect(document.activeElement).toBe(monthElement.nativeElement);
}));

it('should move focus on day field when on month field and tab pressed', fakeAsync(() => {
const event = {
field: 'day',
value: null
};
dateComp.onChange(event);

const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`));
const dayElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_day`));

monthElement.nativeElement.focus();
dateFixture.detectChanges();

expect(document.activeElement).toBe(monthElement.nativeElement);

dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'tab' }));
dateFixture.detectChanges();

tick(200);
dateFixture.detectChanges();

expect(document.activeElement).toBe(dayElement.nativeElement);
}));

it('should move focus on month field when on day field and shift tab pressed', fakeAsync(() => {
const event = {
field: 'day',
value: null
};
dateComp.onChange(event);

const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`));
const dayElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_day`));

dayElement.nativeElement.focus();
dateFixture.detectChanges();

expect(document.activeElement).toBe(dayElement.nativeElement);

dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'shift.tab' }));
dateFixture.detectChanges();

tick(200);
dateFixture.detectChanges();

expect(document.activeElement).toBe(monthElement.nativeElement);
}));

it('should move focus on year field when on month field and shift tab pressed', fakeAsync(() => {
const yearElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_year`));
const monthElement = dateFixture.debugElement.query(By.css(`#${dateComp.model.id}_month`));

monthElement.nativeElement.focus();
dateFixture.detectChanges();

expect(document.activeElement).toBe(monthElement.nativeElement);

dateFixture.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'shift.tab' }));
dateFixture.detectChanges();

tick(200);
dateFixture.detectChanges();

expect(document.activeElement).toBe(yearElement.nativeElement);
}));

});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { Component, EventEmitter, HostListener, Inject, Input, OnInit, Output, Renderer2 } from '@angular/core';
import { DynamicDsDatePickerModel } from './date-picker.model';
import { hasValue } from '../../../../../empty.util';
import {
DynamicFormControlComponent,
DynamicFormLayoutService,
DynamicFormValidationService
} from '@ng-dynamic-forms/core';
import { DOCUMENT } from '@angular/common';
import isEqual from 'lodash/isEqual';


export type DatePickerFieldType = '_year' | '_month' | '_day';

export const DS_DATE_PICKER_SEPARATOR = '-';

Expand Down Expand Up @@ -50,8 +55,12 @@
disabledMonth = true;
disabledDay = true;

private readonly fields: DatePickerFieldType[] = ['_year', '_month', '_day'];

constructor(protected layoutService: DynamicFormLayoutService,
protected validationService: DynamicFormValidationService
protected validationService: DynamicFormValidationService,
private renderer: Renderer2,
@Inject(DOCUMENT) private _document: Document
) {
super(layoutService, validationService);
}
Expand Down Expand Up @@ -80,9 +89,8 @@
}
}

this.maxYear = this.initialYear + 100;

}
this.maxYear = now.getUTCFullYear() + 100;
}

onBlur(event) {
this.blur.emit();
Expand Down Expand Up @@ -166,6 +174,67 @@
this.change.emit(value);
}

/**
* Listen to keydown Tab event.
* Get the active element and blur it, in order to focus the next input field.
*/
@HostListener('keydown.tab', ['$event'])
onTabKeydown(event: KeyboardEvent) {
event.preventDefault();
const activeElement: Element = this._document.activeElement;
(activeElement as any).blur();
const index = this.selectedFieldIndex(activeElement);
if (index < 0) {
return;

Check warning on line 188 in src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts#L188

Added line #L188 was not covered by tests
}
let fieldToFocusOn = index + 1;
if (fieldToFocusOn < this.fields.length) {
this.focusInput(this.fields[fieldToFocusOn]);
}
}

@HostListener('keydown.shift.tab', ['$event'])
onShiftTabKeyDown(event: KeyboardEvent) {
event.preventDefault();
const activeElement: Element = this._document.activeElement;
(activeElement as any).blur();
const index = this.selectedFieldIndex(activeElement);
let fieldToFocusOn = index - 1;
if (fieldToFocusOn >= 0) {
this.focusInput(this.fields[fieldToFocusOn]);
}
}

private selectedFieldIndex(activeElement: Element): number {
return this.fields.findIndex(field => isEqual(activeElement.id, this.model.id.concat(field)));
}

/**
* Focus the input field for the given type
* based on the model id.
* Used to focus the next input field
* in case of a disabled field.
* @param type DatePickerFieldType
*/
focusInput(type: DatePickerFieldType) {
const field = this._document.getElementById(this.model.id.concat(type));
if (field) {

if (hasValue(this.year) && isEqual(type, '_year')) {
this.disabledMonth = true;
this.disabledDay = true;
}
if (hasValue(this.year) && isEqual(type, '_month')) {
this.disabledMonth = false;
} else if (hasValue(this.month) && isEqual(type, '_day')) {
this.disabledDay = false;
}
setTimeout(() => {
this.renderer.selectRootElement(field).focus();
}, 100);
}
}

onFocus(event) {
this.focus.emit(event);
}
Expand Down
3 changes: 1 addition & 2 deletions src/app/shared/form/number-picker/number-picker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,12 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor {

if (i >= this.min && i <= this.max) {
this.value = i;
this.emitChange();
} else if (event.target.value === null || event.target.value === '') {
this.value = null;
this.emitChange();
} else {
this.value = undefined;
}
this.emitChange();
} catch (e) {
this.value = undefined;
}
Expand Down
Loading