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

Refactoring filter cell navigation so that it is handled in the navigation service. Handling special scenarios for hierarchical grid in the hierarchical navigation service. #4267

Merged
merged 22 commits into from
Apr 1, 2019
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e6ed866
fix(HierarchicalGrid): Refactoring filter cell navigation so that it …
Mar 8, 2019
2069c97
fix(HierarchicalGrid): Moving handling for sceanrios when child has f…
Mar 11, 2019
801cf17
chore(*): Storing and reusing cols collection.
Mar 11, 2019
232f288
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 12, 2019
9b2f44d
chore(*): Applying code review comments.
Mar 12, 2019
819893e
Merge branch 'mkirova/fix-4217' of https://github.com/IgniteUI/ignite…
Mar 12, 2019
63850ee
Merge from 7.2.x
Mar 12, 2019
87ba131
chore(*): Moving checks whether next/prev filter cell exists and maki…
Mar 14, 2019
920a087
chore(*): Using visible index for isColumnLeftFullyVisible/isColumnFu…
Mar 14, 2019
9f41f58
Merge from 7.2.x
Mar 14, 2019
14f8255
Merge from 7.2.x
Mar 21, 2019
ea54720
Merge from 7.2.x
Mar 25, 2019
c9a6930
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 26, 2019
0a49c18
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 26, 2019
3b0b1ec
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 27, 2019
8e0de99
fix(HierarchicalGrid): Additional fix in case next child grid row is …
Mar 27, 2019
02e234b
Merge branch 'mkirova/fix-4217' of https://github.com/IgniteUI/ignite…
Mar 27, 2019
b939adb
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 27, 2019
ef225f2
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Mar 28, 2019
be06b88
Merge branch '7.2.x' into mkirova/fix-4217
kdinev Mar 29, 2019
120014e
Merge branch '7.2.x' into mkirova/fix-4217
ChronosSF Apr 1, 2019
22d1407
Merge branch '7.2.x' into mkirova/fix-4217
mpavlinov Apr 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,46 +82,17 @@ export class IgxGridFilteringCellComponent implements AfterViewInit, OnInit, DoC

@HostListener('keydown.tab', ['$event'])
public onTabKeyDown(eventArgs) {
const nextIndex = this.filteringService.unpinnedFilterableColumns.indexOf(this.column) + 1;

if (this.isLastElementFocused()) {
if (this.column === this.getLastPinnedFilterableColumn() &&
(!this.isColumnLeftVisible(nextIndex) || !this.isColumnRightVisible(nextIndex))) {
this.filteringService.scrollToFilterCell(this.filteringService.unpinnedFilterableColumns[nextIndex], false);
eventArgs.stopPropagation();
return;
}

if (nextIndex >= this.filteringService.unpinnedFilterableColumns.length) {
if (!this.filteringService.grid.filteredData || this.filteringService.grid.filteredData.length > 0) {
if (this.filteringService.grid.rowList.filter(row => row instanceof IgxGridGroupByRowComponent).length > 0) {
eventArgs.stopPropagation();
return;
}
this.navService.goToFirstCell();
}
eventArgs.preventDefault();
} else if (!this.column.pinned && !this.isColumnRightVisible(nextIndex)) {
eventArgs.preventDefault();
this.filteringService.scrollToFilterCell(this.filteringService.unpinnedFilterableColumns[nextIndex], true);
}
this.filteringService.grid.navigation.navigateNextFilterCell(this.column, eventArgs);
}
eventArgs.stopPropagation();
}

@HostListener('keydown.shift.tab', ['$event'])
public onShiftTabKeyDown(eventArgs) {
if (this.isFirstElementFocused()) {
const prevIndex = this.filteringService.unpinnedFilterableColumns.indexOf(this.column) - 1;

if (prevIndex >= 0 && this.column.visibleIndex > 0 && !this.isColumnLeftVisible(prevIndex) && !this.column.pinned) {
eventArgs.preventDefault();
this.filteringService.scrollToFilterCell(this.filteringService.unpinnedFilterableColumns[prevIndex], false);
} else if (this.column.visibleIndex === 0 ||
(prevIndex < 0 && !this.getFirstPinnedFilterableColumn()) ||
this.column === this.getFirstPinnedFilterableColumn()) {
eventArgs.preventDefault();
}
this.filteringService.grid.navigation.navigatePrevFilterCell(this.column, eventArgs);
}
eventArgs.stopPropagation();
}
Expand Down Expand Up @@ -360,16 +331,6 @@ export class IgxGridFilteringCellComponent implements AfterViewInit, OnInit, DoC
}
}

private getLastPinnedFilterableColumn(): IgxColumnComponent {
const pinnedFilterableColums =
this.filteringService.grid.pinnedColumns.filter(col => !(col instanceof IgxColumnGroupComponent) && col.filterable);
return pinnedFilterableColums[pinnedFilterableColums.length - 1];
}

private getFirstPinnedFilterableColumn(): IgxColumnComponent {
return this.filteringService.grid.pinnedColumns.filter(col => !(col instanceof IgxColumnGroupComponent) && col.filterable)[0];
}

private isColumnRightVisible(columnIndex: number): boolean {
if (this.filteringService.areAllColumnsInView) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2459,7 +2459,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
protected resolver: ComponentFactoryResolver,
protected differs: IterableDiffers,
protected viewRef: ViewContainerRef,
private navigation: IgxGridNavigationService,
public navigation: IgxGridNavigationService,
public filteringService: IgxFilteringService,
@Inject(IgxOverlayService) protected overlayService: IgxOverlayService,
public summaryService: IgxGridSummaryService,
Expand Down
69 changes: 65 additions & 4 deletions projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { IgxGridBaseComponent, FilterMode } from './grid-base.component';
import { first } from 'rxjs/operators';
import { IgxColumnComponent } from './column.component';
import { IgxGridGroupByRowComponent } from './grid/groupby-row.component';

enum MoveDirection {
LEFT = 'left',
Expand Down Expand Up @@ -461,15 +462,75 @@ export class IgxGridNavigationService {
}
}

public moveFocusToFilterCell() {
public moveFocusToFilterCell(toStart?: boolean) {
const columns = this.grid.filteringService.unpinnedFilterableColumns;
if (this.isColumnFullyVisible(columns.length - 1)) {
this.grid.filteringService.focusFilterCellChip(columns[columns.length - 1], false);
const targetIndex = toStart ? 0 : columns.length - 1;
const visibleIndex = columns[targetIndex].visibleIndex;
const isVisible = toStart ? this.isColumnLeftFullyVisible(visibleIndex) : this.isColumnFullyVisible(visibleIndex);
if (isVisible) {
this.grid.filteringService.focusFilterCellChip(columns[targetIndex], false);
} else {
this.grid.filteringService.scrollToFilterCell(columns[columns.length - 1], false);
this.grid.filteringService.scrollToFilterCell(columns[targetIndex], false);
}
}

public navigatePrevFilterCell(column: IgxColumnComponent, eventArgs) {
const cols = this.grid.filteringService.unpinnedFilterableColumns;
const prevFilterableIndex = cols.indexOf(column) - 1;
const visibleIndex = column.visibleIndex;
if (visibleIndex === 0 || prevFilterableIndex < 0) {
// prev is not filter cell
const firstFiltarableCol = this.getFirstPinnedFilterableColumn();
if (!firstFiltarableCol || column === firstFiltarableCol) {
eventArgs.preventDefault();
}
return;
}
const prevColumn = cols[prevFilterableIndex];
const prevVisibleIndex = prevColumn.visibleIndex;

if (prevFilterableIndex >= 0 && visibleIndex > 0 && !this.isColumnLeftFullyVisible(prevVisibleIndex) && !column.pinned) {
eventArgs.preventDefault();
this.grid.filteringService.scrollToFilterCell(prevColumn, false);
}
}

public navigateNextFilterCell(column: IgxColumnComponent, eventArgs) {
skrustev marked this conversation as resolved.
Show resolved Hide resolved
const cols = this.grid.filteringService.unpinnedFilterableColumns;
const nextFilterableIndex = cols.indexOf(column) + 1;
if (nextFilterableIndex >= this.grid.filteringService.unpinnedFilterableColumns.length) {
// next is not filter cell
if (!this.grid.filteringService.grid.filteredData || this.grid.filteringService.grid.filteredData.length > 0) {
if (this.grid.filteringService.grid.rowList.filter(row => row instanceof IgxGridGroupByRowComponent).length > 0) {
eventArgs.stopPropagation();
return;
}
this.goToFirstCell();
}
eventArgs.preventDefault();
return;
}
const nextColumn = cols[nextFilterableIndex];
const nextVisibleIndex = nextColumn.visibleIndex;
if (!column.pinned && !this.isColumnFullyVisible(nextVisibleIndex)) {
eventArgs.preventDefault();
this.grid.filteringService.scrollToFilterCell(nextColumn, true);
} else if (column === this.getLastPinnedFilterableColumn() && !this.isColumnFullyVisible(nextVisibleIndex)) {
this.grid.filteringService.scrollToFilterCell(nextColumn, false);
eventArgs.stopPropagation();
}
}

private getLastPinnedFilterableColumn(): IgxColumnComponent {
const pinnedFilterableColums =
this.grid.pinnedColumns.filter(col => !(col.columnGroup) && col.filterable);
return pinnedFilterableColums[pinnedFilterableColums.length - 1];
}

private getFirstPinnedFilterableColumn(): IgxColumnComponent {
return this.grid.pinnedColumns.filter(col => !(col.columnGroup) && col.filterable)[0];
}

public performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex, isSummary = false) {
if (isSummary && rowIndex === 0 && visibleColumnIndex === 0 && this.grid.rowList.length) {
this.goToLastBodyElement();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { IgxGridNavigationService } from '../grid-navigation.service';
import { IgxHierarchicalGridComponent } from './hierarchical-grid.component';
import { first } from 'rxjs/operators';
import { FilterMode } from '../grid-base.component';
import { IgxColumnComponent } from '../../grids/column.component';

export class IgxHierarchicalGridNavigationService extends IgxGridNavigationService {
public grid: IgxHierarchicalGridComponent;
Expand Down Expand Up @@ -249,9 +251,28 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
const nextIsDataRow = this.grid.dataRowList.find(row => row.index === rowIndex + 1) ;
const isLastColumn = this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === visibleColumnIndex;
const isLastSummaryRow = hasSummaries && isSummaryRow;
const nextIndex = rowIndex + 1;
const virt = this.grid.verticalScrollContainer;
const isNextChild = nextIndex <= virt.igxForOf.length - 1 &&
this.grid.isChildGridRecord(virt.igxForOf[nextIndex]);
if (!nextIsDataRow && !(isLastDataRow && hasSummaries) && isLastColumn && !isSummaryRow) {
// navigating in child, next is not summary
this.navigateDown(currentRowEl, rowIndex, 0);
const childContainer = this.getChildGridRowContainer();
const nextIsSiblingChild = this.grid.parent ? !!childContainer.nextElementSibling : false;
if (nextIsSiblingChild) {
this.focusNextChildDOMElem(childContainer, this.grid.parent);
} else if (isNextChild) {
const isInView = virt.state.startIndex + virt.state.chunkSize > nextIndex;
if (!isInView) {
this.scrollGrid(this.grid, 'next', () => {
this.focusNextChildDOMElem(currentRowEl, this.grid);
});
} else {
this.focusNextChildDOMElem(currentRowEl, this.grid);
}
} else {
this.navigateDown(currentRowEl, rowIndex, 0);
}
} else if (isLastSummaryRow && isLastColumn && this.grid.parent) {
// navigating in child summary, next is parent summary or next parent row
const parent = this.grid.parent;
Expand All @@ -274,13 +295,61 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
// navigating in child rows, next is child grid's summary row
this.focusNextRow(summaryRows[0].nativeElement, 0, this.grid.parent, true);
} else {
// navigating in normal cells
super.performTab(currentRowEl, rowIndex, visibleColumnIndex, isSummaryRow);
}
}

private focusNextChildDOMElem(currentRowEl, grid) {
const gridElem = currentRowEl.nextElementSibling.querySelector('igx-hierarchical-grid');
const childGridID = gridElem.getAttribute('id');
const childGrid = this.getChildGrid(childGridID, grid);
if (childGrid.allowFiltering && childGrid.filterMode === FilterMode.quickFilter) {
childGrid.navigation.moveFocusToFilterCell(true);
return;
}
this.focusNextChild(currentRowEl.nextElementSibling, 0, grid);
}

public navigatePrevFilterCell(column: IgxColumnComponent, eventArgs) {
if (column.visibleIndex === 0 && this.grid.parent) {
eventArgs.preventDefault();
let targetGrid = this.grid.parent;
const prevSiblingChild = this.getChildGridRowContainer().previousElementSibling;
if (prevSiblingChild) {
const gridElem = prevSiblingChild.querySelectorAll('igx-hierarchical-grid')[0];
targetGrid = this.getChildGrid(gridElem.getAttribute('id'), this.grid.parent);
}
this.focusPrev(targetGrid.unpinnedColumns[targetGrid.unpinnedColumns.length - 1].visibleIndex);
} else {
super.navigatePrevFilterCell(column, eventArgs);
}
}

public navigateNextFilterCell(column: IgxColumnComponent, eventArgs) {
const cols = this.grid.filteringService.unpinnedFilterableColumns;
const nextFilterableIndex = cols.indexOf(column) + 1;
if (nextFilterableIndex >= this.grid.filteringService.unpinnedFilterableColumns.length) {
// next is not filter cell
const dataRows = this.grid.rowList.toArray();
const hasRows = dataRows.length !== 0;
const summaryRows = this.grid.summariesRowList.toArray();
const hasSummaries = summaryRows.length > 0 && summaryRows[0].summaryCells.length > 0;
if (hasRows) {
this.focusNextRow(dataRows[0].nativeElement, 0, this.grid, false);
} else if (hasSummaries) {
this.focusNextRow(summaryRows[0].nativeElement, 0, this.grid, true);
} else {
this.focusNext(0);
}
eventArgs.preventDefault();
} else {
super.navigateNextFilterCell(column, eventArgs);
}
}

public performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex, isSummary = false) {
if (visibleColumnIndex === 0 && rowIndex === 0 && this.grid.parent && !isSummary) {
if (this.grid.allowFiltering) {
if (this.grid.allowFiltering && this.grid.filterMode === FilterMode.quickFilter) {
this.moveFocusToFilterCell();
} else {
const prevSiblingChild = this.getChildGridRowContainer().previousElementSibling;
Expand All @@ -298,7 +367,15 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
this.performShiftTabIntoChild(gridElem, currentRowEl, rowIndex);
} else if (visibleColumnIndex === 0 && isSummary) {
const lastRowIndex = this.grid.verticalScrollContainer.igxForOf.length - 1;
if (!this.getIsChildAtIndex(lastRowIndex)) {
if (lastRowIndex === -1) {
// no child data
if (this.grid.allowFiltering && this.grid.filterMode === FilterMode.quickFilter) {
this.moveFocusToFilterCell();
} else {
this.navigateUp(currentRowEl, rowIndex,
this.grid.parent.unpinnedColumns[this.grid.parent.unpinnedColumns.length - 1].visibleIndex);
}
} else if (!this.getIsChildAtIndex(lastRowIndex)) {
super.goToLastCell();
} else {
const scrTopPosition = this.grid.verticalScrollContainer.getScrollForIndex(lastRowIndex, true);
Expand Down Expand Up @@ -330,13 +407,18 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
const childGrid = this.getChildGrid(childGridID, this.grid) || this.getChildGrid(childGridID, this.grid.parent);
const lastIndex = childGrid.unpinnedColumns[childGrid.unpinnedColumns.length - 1].visibleIndex;
const summaryRows = childGrid.summariesRowList.toArray();
if (summaryRows.length > 0) {
if (summaryRows.length > 0 && summaryRows[0].summaryCells.length > 0) {
// move focus to last summary row cell
const summaryRow = summaryRows[0].nativeElement;
this.focusPrevRow(summaryRow, lastIndex, childGrid, true, true);
return;
} else if (childGrid.rowList.toArray().length === 0 &&
childGrid.allowFiltering && childGrid.filterMode === FilterMode.quickFilter) {
// move to filter cell
childGrid.navigation.moveFocusToFilterCell();
} else {
// move to next cell
this.navigateUp(currentRowEl, rowIndex, lastIndex);
}
this.navigateUp(currentRowEl, rowIndex, lastIndex);
}

private _focusScrollCellInView(visibleColumnIndex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,36 @@ describe('IgxHierarchicalGrid Basic Navigation', () => {

}));

it('should move focus to/from filter chip when navigat with Tab/Shift+Tab from parent to child that has filtering. ', (async () => {
const child1 = hierarchicalGrid.hgridAPI.getChildGrids(false)[0];
child1.allowFiltering = true;
fixture.detectChanges();

const horizontalScrDir = hierarchicalGrid.dataRowList.toArray()[0].virtDirRow;
horizontalScrDir.scrollTo(6);
await wait(100);
fixture.detectChanges();

const lastParentCell = hierarchicalGrid.getCellByColumn(0, 'childData2');
lastParentCell.nativeElement.focus();
fixture.detectChanges();

lastParentCell.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
await wait(100);
fixture.detectChanges();

const filterItems = fixture.debugElement.queryAll(By.css('.igx-chip__item'));
const firstFilterItem = filterItems[0].nativeElement;
expect(document.activeElement === firstFilterItem).toBeTruthy();

firstFilterItem.closest('.igx-grid__filtering-cell').dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true }));
await wait(100);
fixture.detectChanges();

expect(lastParentCell.selected).toBeTruthy();
expect(lastParentCell.focused).toBeTruthy();
}));

it('should navigate inside summary row with Ctrl + Arrow Right/ Ctrl + Arrow Left', (async () => {
const col = hierarchicalGrid.getColumnByName('ID');
col.hasSummary = true;
Expand Down