Skip to content

Commit 3cd8602

Browse files
authored
Merge branch '8.2.x' into dmdimitrov/issue6224-8.2.x
2 parents 7823eaa + ffd7b45 commit 3cd8602

File tree

7 files changed

+276
-20
lines changed

7 files changed

+276
-20
lines changed

projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ViewChild } from '@angular/core';
1+
import { Component, ViewChild, ElementRef } from '@angular/core';
22
import { async, fakeAsync, TestBed, tick, flush, ComponentFixture } from '@angular/core/testing';
33
import { FormsModule, FormGroup, FormBuilder, FormControl, ReactiveFormsModule} from '@angular/forms';
44
import { By } from '@angular/platform-browser';
@@ -31,7 +31,8 @@ describe('IgxDatePicker', () => {
3131
IgxDatePickerCustomizedComponent,
3232
IgxDropDownDatePickerRetemplatedComponent,
3333
IgxDatePickerOpeningComponent,
34-
IgxDatePickerReactiveFormComponent
34+
IgxDatePickerReactiveFormComponent,
35+
IgxDatePickerDropdownButtonsComponent
3536
],
3637
imports: [IgxDatePickerModule, FormsModule, ReactiveFormsModule, NoopAnimationsModule, IgxInputGroupModule, IgxCalendarModule,
3738
IgxButtonModule, IgxTextSelectionModule]
@@ -181,7 +182,7 @@ describe('IgxDatePicker', () => {
181182
expect(overlays.length).toEqual(0);
182183
}));
183184

184-
it('When datepicker is closed and the dialog disappear, the focus should remain on the input',
185+
it('When modal datepicker is closed via `Escape` Key and the dialog disappear, the focus should remain on the input',
185186
fakeAsync(() => {
186187
const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker'));
187188
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
@@ -205,6 +206,56 @@ describe('IgxDatePicker', () => {
205206
expect(input).toEqual(document.activeElement);
206207
}));
207208

209+
it('When a modal datepicker is closed via outside click, the focus should remain on the input',
210+
fakeAsync(() => {
211+
const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker'));
212+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
213+
expect(overlayToggle.length).toEqual(0);
214+
215+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
216+
flush();
217+
fixture.detectChanges();
218+
219+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
220+
expect(overlayToggle[0]).not.toBeNull();
221+
expect(overlayToggle[0]).not.toBeUndefined();
222+
223+
UIInteractions.clickElement(overlayToggle[0]);
224+
flush();
225+
fixture.detectChanges();
226+
227+
const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
228+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
229+
expect(overlayToggle[0]).toEqual(undefined);
230+
expect(input).toEqual(document.activeElement);
231+
}));
232+
233+
it('When datepicker is closed upon selecting a date, the focus should remain on the input',
234+
fakeAsync(() => {
235+
const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker'));
236+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
237+
expect(overlayToggle.length).toEqual(0);
238+
239+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
240+
flush();
241+
fixture.detectChanges();
242+
243+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
244+
expect(overlayToggle[0]).not.toBeNull();
245+
expect(overlayToggle[0]).not.toBeUndefined();
246+
247+
// select a date
248+
const dateElemToSelect = document.getElementsByClassName('igx-calendar__date')[10];
249+
UIInteractions.clickElement(dateElemToSelect);
250+
flush();
251+
fixture.detectChanges();
252+
253+
const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
254+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper--modal');
255+
expect(overlayToggle[0]).toEqual(undefined);
256+
expect(input).toEqual(document.activeElement);
257+
}));
258+
208259
});
209260

210261
describe('DatePicker with passed date', () => {
@@ -249,6 +300,99 @@ describe('IgxDatePicker', () => {
249300
});
250301
});
251302

303+
it('When datepicker in "dropdown" mode is closed via outside click, the input should not receive focus',
304+
fakeAsync(() => {
305+
const fixture = TestBed.createComponent(IgxDatePickerDropdownButtonsComponent);
306+
fixture.detectChanges();
307+
308+
const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker'));
309+
const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
310+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
311+
312+
expect(overlayToggle.length).toEqual(0);
313+
314+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
315+
flush();
316+
fixture.detectChanges();
317+
318+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
319+
expect(overlayToggle[0]).not.toBeNull();
320+
expect(overlayToggle[0]).not.toBeUndefined();
321+
322+
const dummyInput = fixture.componentInstance.dummyInput.nativeElement;
323+
dummyInput.focus();
324+
dummyInput.click();
325+
tick();
326+
fixture.detectChanges();
327+
328+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
329+
expect(overlayToggle[0]).toEqual(undefined);
330+
expect(input).not.toEqual(document.activeElement);
331+
expect(dummyInput).toEqual(document.activeElement);
332+
}));
333+
334+
it('When datepicker in "dropdown" mode, should focus input on user interaction with Today btn, Cancel btn, Enter Key, Escape key',
335+
fakeAsync(() => {
336+
const fixture = TestBed.createComponent(IgxDatePickerDropdownButtonsComponent);
337+
fixture.detectChanges();
338+
const datePickerDom = fixture.debugElement.query(By.css('igx-date-picker'));
339+
const input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
340+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
341+
expect(overlayToggle.length).toEqual(0);
342+
343+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
344+
flush();
345+
fixture.detectChanges();
346+
const buttons = document.getElementsByClassName('igx-button--flat');
347+
expect(buttons.length).toEqual(2);
348+
349+
// Today btn
350+
const todayBtn = buttons[1] as HTMLElement;
351+
expect(todayBtn.innerText).toBe('Today');
352+
todayBtn.click();
353+
tick();
354+
fixture.detectChanges();
355+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
356+
expect(overlayToggle[0]).toEqual(undefined);
357+
expect(input).toEqual(document.activeElement);
358+
359+
// Cancel btn
360+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
361+
flush();
362+
fixture.detectChanges();
363+
const cancelBtn = buttons[0] as HTMLElement;
364+
expect(cancelBtn.innerText).toBe('Cancel');
365+
cancelBtn.click();
366+
tick();
367+
fixture.detectChanges();
368+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
369+
expect(overlayToggle[0]).toEqual(undefined);
370+
expect(input).toEqual(document.activeElement);
371+
372+
// Enter key
373+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
374+
flush();
375+
fixture.detectChanges();
376+
document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
377+
tick();
378+
fixture.detectChanges();
379+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
380+
expect(overlayToggle[0]).toEqual(undefined);
381+
expect(input).toEqual(document.activeElement);
382+
383+
// Esc key
384+
UIInteractions.triggerKeyDownEvtUponElem('space', datePickerDom.nativeElement, false);
385+
flush();
386+
fixture.detectChanges();
387+
document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
388+
tick();
389+
fixture.detectChanges();
390+
391+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
392+
expect(overlayToggle[0]).toEqual(undefined);
393+
expect(input).toEqual(document.activeElement);
394+
}));
395+
252396
it('Datepicker week start day (Monday)', () => {
253397
const fixture = TestBed.createComponent(IgxDatePickerWithWeekStartComponent);
254398
fixture.detectChanges();
@@ -1318,3 +1462,17 @@ class IgxDatePickerReactiveFormComponent {
13181462
}
13191463
onSubmitReactive() { }
13201464
}
1465+
1466+
@Component({
1467+
template: `
1468+
<input class="dummyInput" #dummyInput/>
1469+
<igx-date-picker id="dropdownButtonsDatePicker" mode="dropdown" cancelButtonLabel="Cancel" todayButtonLabel="Today" >
1470+
</igx-date-picker>
1471+
`
1472+
})
1473+
class IgxDatePickerDropdownButtonsComponent {
1474+
@ViewChild('dropdownButtonsDatePicker', { read: IgxDatePickerComponent, static: true })
1475+
public datePicker: IgxDatePickerComponent;
1476+
1477+
@ViewChild('dummyInput', {static: true }) public dummyInput: ElementRef;
1478+
}

projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,15 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor
892892
filter(overlay => overlay.id === this._componentID),
893893
takeUntil(this._destroy$)).subscribe((event) => {
894894
this.onClosing.emit(event);
895+
// If canceled in a user onClosing handler
896+
if (event.cancel) {
897+
return;
898+
}
899+
// Do not focus the input if clicking outside in dropdown mode
900+
const input = this.getEditElement();
901+
if (input && !(event.event && this.mode === InteractionMode.DropDown)) {
902+
input.focus();
903+
}
895904
});
896905

897906
if (this.mode === InteractionMode.DropDown) {
@@ -1268,10 +1277,6 @@ export class IgxDatePickerComponent implements IDatePicker, ControlValueAccessor
12681277

12691278
// TODO: remove this line after deprecating 'onClose'
12701279
this.onClose.emit(this);
1271-
1272-
if (this.getEditElement()) {
1273-
this.getEditElement().focus();
1274-
}
12751280
}
12761281

12771282
private _initializeCalendarContainer(componentInstance: IgxCalendarContainerComponent) {

projects/igniteui-angular/src/lib/grids/grid-base.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2055,7 +2055,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
20552055
* @hidden
20562056
*/
20572057
public get parentRowOutletDirective() {
2058-
return null;
2058+
return this.outletDirective;
20592059
}
20602060

20612061
/**

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -443,13 +443,6 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseCompone
443443
return this.rootGrid._outletDirective;
444444
}
445445

446-
/**
447-
* @hidden
448-
*/
449-
public get parentRowOutletDirective() {
450-
return this === this.rootGrid ? null : this.rootGrid.rowEditingOutletDirective;
451-
}
452-
453446
/**
454447
* @hidden
455448
*/

projects/igniteui-angular/src/lib/services/overlay/overlay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ export class IgxOverlayService implements OnDestroy {
652652
if (info.settings.modal) {
653653
fromEvent(info.elementRef.nativeElement.parentElement.parentElement, 'click')
654654
.pipe(takeUntil(this.destroy$))
655-
.subscribe(() => this.hide(info.id));
655+
.subscribe((e: Event) => this._hide(info.id, e));
656656
} else if (
657657
// if all overlays minus closing overlays equals one add the handler
658658
this._overlayInfos.filter(x => x.settings.closeOnOutsideClick && !x.settings.modal).length -

projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ViewChild } from '@angular/core';
1+
import { Component, ViewChild, ElementRef } from '@angular/core';
22
import { async, TestBed, fakeAsync, tick, ComponentFixture } from '@angular/core/testing';
33
import { FormsModule, FormGroup, FormBuilder, FormControl, ReactiveFormsModule } from '@angular/forms';
44
import { By } from '@angular/platform-browser';
@@ -1478,6 +1478,98 @@ describe('IgxTimePicker', () => {
14781478
expect(document.getElementsByClassName('igx-time-picker__buttons').length).toEqual(0);
14791479
}));
14801480

1481+
it('should focus input on user interaction with OK btn, Cancel btn, Enter Key, Escape key', fakeAsync(() => {
1482+
fixture.detectChanges();
1483+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1484+
expect(overlayToggle.length).toEqual(0);
1485+
1486+
const iconTime = dom.queryAll(By.css('.igx-icon'))[0];
1487+
UIInteractions.clickElement(iconTime);
1488+
tick();
1489+
fixture.detectChanges();
1490+
1491+
const buttons = document.getElementsByClassName('igx-time-picker__buttons')[0];
1492+
expect(buttons.children.length).toEqual(2);
1493+
1494+
const okBtn = dom.queryAll(By.css('.igx-button--flat'))[1];
1495+
expect(okBtn.nativeElement.innerText).toBe('OK');
1496+
1497+
// OK btn
1498+
okBtn.triggerEventHandler('click', {});
1499+
tick();
1500+
fixture.detectChanges();
1501+
1502+
input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
1503+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1504+
expect(overlayToggle[0]).toEqual(undefined);
1505+
expect(input).toEqual(document.activeElement);
1506+
1507+
// Cancel btn
1508+
UIInteractions.clickElement(iconTime);
1509+
tick();
1510+
fixture.detectChanges();
1511+
const cancelBtn = dom.queryAll(By.css('.igx-button--flat'))[0];
1512+
expect(cancelBtn.nativeElement.innerText).toBe('Cancel');
1513+
cancelBtn.triggerEventHandler('click', {});
1514+
tick();
1515+
fixture.detectChanges();
1516+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1517+
expect(overlayToggle[0]).toEqual(undefined);
1518+
expect(input).toEqual(document.activeElement);
1519+
1520+
// Enter key
1521+
UIInteractions.clickElement(iconTime);
1522+
tick(100);
1523+
fixture.detectChanges();
1524+
document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
1525+
tick();
1526+
fixture.detectChanges();
1527+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1528+
expect(overlayToggle[0]).toEqual(undefined);
1529+
expect(input).toEqual(document.activeElement);
1530+
1531+
// Esc key
1532+
UIInteractions.clickElement(iconTime);
1533+
tick(100);
1534+
fixture.detectChanges();
1535+
document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
1536+
tick();
1537+
fixture.detectChanges();
1538+
1539+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1540+
expect(overlayToggle[0]).toEqual(undefined);
1541+
expect(input).toEqual(document.activeElement);
1542+
}));
1543+
1544+
it('When timepicker is closed via outside click, the focus should NOT remain on the input',
1545+
fakeAsync(() => {
1546+
fixture.detectChanges();
1547+
input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
1548+
let overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1549+
1550+
expect(overlayToggle.length).toEqual(0);
1551+
1552+
const iconTime = dom.queryAll(By.css('.igx-icon'))[0];
1553+
UIInteractions.clickElement(iconTime);
1554+
tick();
1555+
fixture.detectChanges();
1556+
1557+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1558+
expect(overlayToggle[0]).not.toBeNull();
1559+
expect(overlayToggle[0]).not.toBeUndefined();
1560+
1561+
const dummyInput = fixture.componentInstance.dummyInput.nativeElement;
1562+
dummyInput.focus();
1563+
dummyInput.click();
1564+
tick();
1565+
fixture.detectChanges();
1566+
1567+
overlayToggle = document.getElementsByClassName('igx-overlay__wrapper');
1568+
input = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
1569+
expect(overlayToggle[0]).toEqual(undefined);
1570+
expect(input).not.toEqual(document.activeElement);
1571+
expect(dummyInput).toEqual(document.activeElement);
1572+
}));
14811573
});
14821574

14831575
describe('Timepicker with outlet', () => {
@@ -1948,6 +2040,7 @@ export class IgxTimePickerRetemplatedComponent { }
19482040

19492041
@Component({
19502042
template: `
2043+
<input class="dummyInput" #dummyInput/>
19512044
<igx-time-picker mode="dropdown"
19522045
[isSpinLoop]="isSpinLoop"
19532046
[(ngModel)]="date"
@@ -1964,6 +2057,7 @@ export class IgxTimePickerDropDownComponent {
19642057
date = new Date(2018, 10, 27, 17, 45, 0, 0);
19652058

19662059
@ViewChild(IgxTimePickerComponent, { static: true }) public timePicker: IgxTimePickerComponent;
2060+
@ViewChild('dummyInput', { static: true }) public dummyInput: ElementRef;
19672061
}
19682062
@Component({
19692063
template: `

0 commit comments

Comments
 (0)