mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 18:14:17 +00:00
Merged in DSC-106 (pull request #643)
[DSC-106] Date input usable via keyboard using tab Approved-by: Vincenzo Mecca
This commit is contained in:

committed by
Alisa Ismailati

parent
e1e2941983
commit
543b4ad576
@@ -1,7 +1,7 @@
|
|||||||
// Load the implementations that should be tested
|
// 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 { 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 { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
|
import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core';
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
mockDynamicFormLayoutService,
|
mockDynamicFormLayoutService,
|
||||||
mockDynamicFormValidationService
|
mockDynamicFormValidationService
|
||||||
} from '../../../../../testing/dynamic-form-mock-services';
|
} from '../../../../../testing/dynamic-form-mock-services';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
|
||||||
export const DATE_TEST_GROUP = new UntypedFormGroup({
|
export const DATE_TEST_GROUP = new UntypedFormGroup({
|
||||||
@@ -39,6 +40,11 @@ describe('DsDatePickerComponent test suite', () => {
|
|||||||
let dateFixture: ComponentFixture<DsDatePickerComponent>;
|
let dateFixture: ComponentFixture<DsDatePickerComponent>;
|
||||||
let html;
|
let html;
|
||||||
|
|
||||||
|
const renderer2: Renderer2 = {
|
||||||
|
selectRootElement: jasmine.createSpy('selectRootElement'),
|
||||||
|
querySelector: jasmine.createSpy('querySelector'),
|
||||||
|
} as unknown as Renderer2;
|
||||||
|
|
||||||
// waitForAsync beforeEach
|
// waitForAsync beforeEach
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
|
|
||||||
@@ -54,7 +60,8 @@ describe('DsDatePickerComponent test suite', () => {
|
|||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
DsDatePickerComponent,
|
DsDatePickerComponent,
|
||||||
{ provide: DynamicFormLayoutService, useValue: mockDynamicFormLayoutService },
|
{ provide: DynamicFormLayoutService, useValue: mockDynamicFormLayoutService },
|
||||||
{ provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService }
|
{ provide: DynamicFormValidationService, useValue: mockDynamicFormValidationService },
|
||||||
|
{ provide: Renderer2, useValue: renderer2 },
|
||||||
],
|
],
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||||
});
|
});
|
||||||
@@ -233,6 +240,102 @@ describe('DsDatePickerComponent test suite', () => {
|
|||||||
expect(dateComp.disabledMonth).toBeFalsy();
|
expect(dateComp.disabledMonth).toBeFalsy();
|
||||||
expect(dateComp.disabledDay).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);
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
|
import { Component, EventEmitter, HostListener, Inject, Input, OnInit, Output, Renderer2 } from '@angular/core';
|
||||||
import { DynamicDsDatePickerModel } from './date-picker.model';
|
import { DynamicDsDatePickerModel } from './date-picker.model';
|
||||||
import { hasValue } from '../../../../../empty.util';
|
import { hasValue } from '../../../../../empty.util';
|
||||||
import {
|
import {
|
||||||
@@ -7,6 +7,11 @@ import {
|
|||||||
DynamicFormLayoutService,
|
DynamicFormLayoutService,
|
||||||
DynamicFormValidationService
|
DynamicFormValidationService
|
||||||
} from '@ng-dynamic-forms/core';
|
} 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 = '-';
|
export const DS_DATE_PICKER_SEPARATOR = '-';
|
||||||
|
|
||||||
@@ -50,8 +55,12 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement
|
|||||||
disabledMonth = true;
|
disabledMonth = true;
|
||||||
disabledDay = true;
|
disabledDay = true;
|
||||||
|
|
||||||
|
private readonly fields: DatePickerFieldType[] = ['_year', '_month', '_day'];
|
||||||
|
|
||||||
constructor(protected layoutService: DynamicFormLayoutService,
|
constructor(protected layoutService: DynamicFormLayoutService,
|
||||||
protected validationService: DynamicFormValidationService
|
protected validationService: DynamicFormValidationService,
|
||||||
|
private renderer: Renderer2,
|
||||||
|
@Inject(DOCUMENT) private _document: Document
|
||||||
) {
|
) {
|
||||||
super(layoutService, validationService);
|
super(layoutService, validationService);
|
||||||
}
|
}
|
||||||
@@ -166,6 +175,67 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement
|
|||||||
this.change.emit(value);
|
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;
|
||||||
|
}
|
||||||
|
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) {
|
onFocus(event) {
|
||||||
this.focus.emit(event);
|
this.focus.emit(event);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user