Skip to content

Commit af19050

Browse files
committed
Merge branch '8.2.x' of https://github.com/IgniteUI/igniteui-angular into carousel-ssr-fix-82x
2 parents 3622588 + 0faffd9 commit af19050

File tree

13 files changed

+360
-66
lines changed

13 files changed

+360
-66
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/filtering/advanced-filtering/advanced-filtering-dialog.component.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,6 @@ export class IgxAdvancedFilteringDialogComponent implements AfterViewInit, OnDes
208208
constructor(private element: ElementRef, public cdr: ChangeDetectorRef) { }
209209

210210
public ngAfterViewInit(): void {
211-
if (this.addRootAndGroupButton) {
212-
this.addRootAndGroupButton.nativeElement.focus();
213-
} else if (this.addConditionButton) {
214-
this.addConditionButton.nativeElement.focus();
215-
}
216-
217211
this._overlaySettings.outlet = this.overlayOutlet;
218212
this.columnSelectOverlaySettings.outlet = this.overlayOutlet;
219213
this.conditionSelectOverlaySettings.outlet = this.overlayOutlet;
@@ -791,6 +785,17 @@ export class IgxAdvancedFilteringDialogComponent implements AfterViewInit, OnDes
791785
}
792786
}
793787

788+
/**
789+
* @hidden @internal
790+
*/
791+
public setAddButtonFocus() {
792+
if (this.addRootAndGroupButton) {
793+
this.addRootAndGroupButton.nativeElement.focus();
794+
} else if (this.addConditionButton) {
795+
this.addConditionButton.nativeElement.focus();
796+
}
797+
}
798+
794799
public context(expression: ExpressionItem, afterExpression?: ExpressionItem) {
795800
return {
796801
$implicit: expression,

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

Lines changed: 5 additions & 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
/**
@@ -2991,6 +2991,10 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
29912991
this.overlayService.onOpened.pipe(destructor).subscribe((event) => {
29922992
// do not hide the advanced filtering overlay on scroll
29932993
if (this._advancedFilteringOverlayId === event.id) {
2994+
const instance = event.componentRef.instance as IgxAdvancedFilteringDialogComponent;
2995+
if (instance) {
2996+
instance.setAddButtonFocus();
2997+
}
29942998
return;
29952999
}
29963000

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ private resolver;
185185
outputs.forEach(output => {
186186
if (this.hGrid[output.propName]) {
187187
this.hGrid[output.propName].pipe(destructor).subscribe((args) => {
188+
if (!args) {
189+
args = {};
190+
}
188191
args.owner = this.hGrid;
189192
this.layout[output.propName].emit(args);
190193
});

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
*/

0 commit comments

Comments
 (0)