mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +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
|
||||
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';
|
||||
@@ -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({
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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]
|
||||
});
|
||||
@@ -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);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
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 {
|
||||
@@ -7,6 +7,11 @@ import {
|
||||
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 = '-';
|
||||
|
||||
@@ -50,8 +55,12 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement
|
||||
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);
|
||||
}
|
||||
@@ -166,6 +175,67 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement
|
||||
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) {
|
||||
this.focus.emit(event);
|
||||
}
|
||||
|
Reference in New Issue
Block a user