Skip to content

Commit 11b2f5d

Browse files
authored
Merge branch '7.2.x' into SKrastev/fix-4209
2 parents a60e56a + 671b982 commit 11b2f5d

File tree

5 files changed

+151
-35
lines changed

5 files changed

+151
-35
lines changed

projects/igniteui-angular/src/lib/grids/grid-navigation.service.ts

+9
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,11 @@ export class IgxGridNavigationService {
429429
}
430430

431431
public performTab(currentRowEl, rowIndex, visibleColumnIndex, isSummaryRow = false) {
432+
if (isSummaryRow && rowIndex === 0 &&
433+
this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === visibleColumnIndex) {
434+
return;
435+
436+
}
432437
if (this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === visibleColumnIndex) {
433438
if (this.isRowInEditMode(rowIndex)) {
434439
this.grid.rowEditTabs.first.element.nativeElement.focus();
@@ -466,6 +471,10 @@ export class IgxGridNavigationService {
466471
}
467472

468473
public performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex, isSummary = false) {
474+
if (isSummary && rowIndex === 0 && visibleColumnIndex === 0 && this.grid.rowList.length) {
475+
this.goToLastBodyElement();
476+
return;
477+
}
469478
if (visibleColumnIndex === 0) {
470479
if (this.isRowInEditMode(rowIndex)) {
471480
this.grid.rowEditTabs.last.element.nativeElement.focus();

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

+108-25
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
5151
return this.grid.isChildGridRecord(this.grid.verticalScrollContainer.igxForOf[index]);
5252
}
5353

54-
public getCellElementByVisibleIndex(rowIndex, visibleColumnIndex) {
55-
const cellSelector = this.getCellSelector(visibleColumnIndex);
54+
public getCellElementByVisibleIndex(rowIndex, visibleColumnIndex, isSummary = false) {
55+
const cellSelector = this.getCellSelector(visibleColumnIndex, isSummary);
56+
if (isSummary) {
57+
const summaryRow = this.grid.summariesRowList.toArray()[0].nativeElement;
58+
return summaryRow.querySelector(
59+
`${cellSelector}[data-visibleIndex="${visibleColumnIndex}"]`);
60+
}
5661
const row = this.getRowByIndex(rowIndex);
5762
return row.querySelector(
5863
`${cellSelector}[data-rowindex="${rowIndex}"][data-visibleIndex="${visibleColumnIndex}"]`);
@@ -192,11 +197,22 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
192197
// handle scenario where last child row might not be in view
193198
// parent should scroll to child grid end
194199
const childContainer = this.grid.nativeElement.parentNode.parentNode;
195-
const diff =
200+
const diffBottom =
196201
childContainer.getBoundingClientRect().bottom - this.grid.rootGrid.nativeElement.getBoundingClientRect().bottom;
197-
const endIsVisible = diff <= 0;
202+
const row = this.grid.getRowByIndex(rowIndex).element.nativeElement;
203+
const gridTop = this._getMaxTop(this.grid);
204+
const diffTop = row.getBoundingClientRect().bottom -
205+
row.offsetHeight - gridTop;
206+
const endIsVisible = diffBottom <= 0;
207+
const topVisible = diffTop >= 0;
198208
if (!endIsVisible) {
199-
this.scrollGrid(this.grid.parent, diff, () => super.onKeydownEnd(rowIndex));
209+
this.scrollGrid(this.grid.parent, diffBottom, () => super.onKeydownEnd(rowIndex));
210+
} else if (!topVisible) {
211+
const scrGrid = this.grid.verticalScrollContainer.getVerticalScroll().scrollTop !== 0 ? this.grid :
212+
this.getNextScrollable(this.grid).grid;
213+
const topGrid = scrGrid.tbody.nativeElement.getBoundingClientRect().top >
214+
this.grid.rootGrid.tbody.nativeElement.getBoundingClientRect().top ? scrGrid : this.grid.rootGrid;
215+
this.scrollGrid(topGrid, diffTop, () => super.onKeydownEnd(rowIndex));
200216
} else {
201217
super.onKeydownEnd(rowIndex, isSummary);
202218
}
@@ -226,40 +242,103 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
226242
}
227243
}
228244

229-
public performTab(currentRowEl, rowIndex, visibleColumnIndex) {
230-
if (!this.grid.rowList.find(row => row.index === rowIndex + 1) && this.grid.parent &&
231-
this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === visibleColumnIndex) {
245+
public performTab(currentRowEl, rowIndex, visibleColumnIndex, isSummaryRow = false) {
246+
const summaryRows = this.grid.summariesRowList.toArray();
247+
const hasSummaries = summaryRows.length > 0;
248+
const isLastDataRow = rowIndex === this.grid.verticalScrollContainer.igxForOf.length - 1;
249+
const nextIsDataRow = this.grid.dataRowList.find(row => row.index === rowIndex + 1) ;
250+
const isLastColumn = this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === visibleColumnIndex;
251+
const isLastSummaryRow = hasSummaries && isSummaryRow;
252+
if (!nextIsDataRow && !(isLastDataRow && hasSummaries) && isLastColumn && !isSummaryRow) {
253+
// navigating in child, next is not summary
232254
this.navigateDown(currentRowEl, rowIndex, 0);
255+
} else if (isLastSummaryRow && isLastColumn && this.grid.parent) {
256+
// navigating in child summary, next is parent summary or next parent row
257+
const parent = this.grid.parent;
258+
const parentHasSummary = parent.summariesRowList.toArray().length > 0;
259+
const parentRowIndex = parseInt(
260+
this.getClosestElemByTag(currentRowEl, 'igx-child-grid-row').parentNode.getAttribute('data-rowindex'), 10);
261+
const isLastRowInParent = parent.verticalScrollContainer.igxForOf.length - 1 === parentRowIndex;
262+
// check if next is sibling
263+
const childRowContainer = this.getChildGridRowContainer(this.grid);
264+
const nextIsSiblingChild = !!childRowContainer.nextElementSibling;
265+
if (isLastRowInParent && parentHasSummary && !nextIsSiblingChild) {
266+
// next is parent summary
267+
const parentSummary = parent.summariesRowList.toArray()[0].nativeElement;
268+
parent.navigation.focusNextRow(parentSummary, 0, this.grid.rootGrid, true);
269+
} else {
270+
// next is sibling or parent
271+
this.focusNext(0);
272+
}
273+
} else if (isLastDataRow && hasSummaries && isLastColumn && this.grid.parent) {
274+
// navigating in child rows, next is child grid's summary row
275+
this.focusNextRow(summaryRows[0].nativeElement, 0, this.grid.parent, true);
233276
} else {
234-
super.performTab(currentRowEl, rowIndex, visibleColumnIndex);
277+
// navigating in normal cells
278+
super.performTab(currentRowEl, rowIndex, visibleColumnIndex, isSummaryRow);
235279
}
236280
}
237-
public performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex) {
238-
if (visibleColumnIndex === 0 && rowIndex === 0 && this.grid.parent) {
281+
public performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex, isSummary = false) {
282+
if (visibleColumnIndex === 0 && rowIndex === 0 && this.grid.parent && !isSummary) {
239283
if (this.grid.allowFiltering) {
240284
this.moveFocusToFilterCell();
241285
} else {
242286
const prevSiblingChild = this.getChildGridRowContainer().previousElementSibling;
243287
if (prevSiblingChild) {
244288
const gridElem = prevSiblingChild.querySelectorAll('igx-hierarchical-grid')[0];
245-
const prevGridCols = this.getChildGrid(gridElem.getAttribute('id'), this.grid.parent).unpinnedColumns;
246-
this.navigateUp(currentRowEl, rowIndex, prevGridCols[prevGridCols.length - 1].visibleIndex);
289+
this.performShiftTabIntoChild(gridElem, currentRowEl, rowIndex);
247290
} else {
248291
this.navigateUp(currentRowEl, rowIndex,
249292
this.grid.parent.unpinnedColumns[this.grid.parent.unpinnedColumns.length - 1].visibleIndex);
250293
}
251294
}
252295
} else if (visibleColumnIndex === 0 && currentRowEl.previousElementSibling &&
253296
currentRowEl.previousElementSibling.children[0].tagName.toLowerCase() === 'igx-child-grid-row') {
254-
const gridElem = currentRowEl.previousElementSibling.querySelector('igx-hierarchical-grid');
255-
const childGridID = gridElem.getAttribute('id');
256-
const childGrid = this.getChildGrid(childGridID, this.grid);
257-
this.navigateUp(currentRowEl, rowIndex, childGrid.unpinnedColumns[childGrid.unpinnedColumns.length - 1].visibleIndex);
297+
const gridElem = this.getLastGridElem(currentRowEl.previousElementSibling);
298+
this.performShiftTabIntoChild(gridElem, currentRowEl, rowIndex);
299+
} else if (visibleColumnIndex === 0 && isSummary) {
300+
const lastRowIndex = this.grid.verticalScrollContainer.igxForOf.length - 1;
301+
if (!this.getIsChildAtIndex(lastRowIndex)) {
302+
super.goToLastCell();
303+
} else {
304+
const scrTopPosition = this.grid.verticalScrollContainer.getScrollForIndex(lastRowIndex, true);
305+
const verticalScroll = this.grid.verticalScrollContainer.getVerticalScroll();
306+
if (verticalScroll.scrollTop === scrTopPosition || isNaN(scrTopPosition)) {
307+
const closestChild = this.getLastGridElem(this.grid.getRowByIndex(lastRowIndex).nativeElement.parentElement);
308+
this.performShiftTabIntoChild(closestChild, currentRowEl, rowIndex);
309+
} else {
310+
this.scrollGrid(this.grid, scrTopPosition - verticalScroll.scrollTop,
311+
() => {
312+
const closestChild = this.getLastGridElem(this.grid.getRowByIndex(lastRowIndex).nativeElement.parentElement);
313+
this.performShiftTabIntoChild(closestChild, currentRowEl, rowIndex);
314+
});
315+
}
316+
}
258317
} else {
259-
super.performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex);
318+
super.performShiftTabKey(currentRowEl, rowIndex, visibleColumnIndex, isSummary);
260319
}
261320
}
262321

322+
private getLastGridElem(trContainer) {
323+
const children = trContainer.children;
324+
const closestChild = children[children.length - 1].children[0].children[0];
325+
return closestChild;
326+
}
327+
328+
private performShiftTabIntoChild(gridElem, currentRowEl, rowIndex) {
329+
const childGridID = gridElem.getAttribute('id');
330+
const childGrid = this.getChildGrid(childGridID, this.grid) || this.getChildGrid(childGridID, this.grid.parent);
331+
const lastIndex = childGrid.unpinnedColumns[childGrid.unpinnedColumns.length - 1].visibleIndex;
332+
const summaryRows = childGrid.summariesRowList.toArray();
333+
if (summaryRows.length > 0) {
334+
// move focus to last summary row cell
335+
const summaryRow = summaryRows[0].nativeElement;
336+
this.focusPrevRow(summaryRow, lastIndex, childGrid, true, true);
337+
return;
338+
}
339+
this.navigateUp(currentRowEl, rowIndex, lastIndex);
340+
}
341+
263342
private _focusScrollCellInView(visibleColumnIndex) {
264343
const cellSelector = this.getCellSelector(visibleColumnIndex);
265344
const cells = this.grid.nativeElement.querySelectorAll(
@@ -461,8 +540,8 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
461540
return top;
462541
}
463542

464-
private focusNextRow(elem, visibleColumnIndex, grid) {
465-
const cellSelector = this.getCellSelector(visibleColumnIndex);
543+
private focusNextRow(elem, visibleColumnIndex, grid, isSummary?) {
544+
const cellSelector = this.getCellSelector(visibleColumnIndex, isSummary);
466545
if (grid.navigation.isColumnFullyVisible(visibleColumnIndex) && grid.navigation.isColumnLeftFullyVisible(visibleColumnIndex)) {
467546
const cell =
468547
elem.querySelector(`${cellSelector}[data-visibleIndex="${visibleColumnIndex}"]`);
@@ -471,7 +550,11 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
471550
const gridBottom = this._getMinBottom(grid);
472551
const diff = cell.getBoundingClientRect().bottom - gridBottom;
473552
const inView = diff <= 0;
474-
if (!inView) {
553+
const scrollTop = closestScrollableGrid.verticalScrollContainer.getVerticalScroll().scrollTop;
554+
const scrollHeight = closestScrollableGrid.verticalScrollContainer.getVerticalScroll().scrollHeight;
555+
const canScroll = !(scrollHeight === 0 ||
556+
Math.round(scrollTop + closestScrollableGrid.verticalScrollContainer.igxForContainerSize) === scrollHeight);
557+
if (!inView && canScroll) {
475558
this.scrollGrid(closestScrollableGrid, diff, () => cell.focus({ preventScroll: true }));
476559
} else {
477560
cell.focus({ preventScroll: true });
@@ -483,9 +566,9 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
483566
}
484567
}
485568

486-
private focusPrevRow(elem, visibleColumnIndex, grid, inChild?) {
569+
private focusPrevRow(elem, visibleColumnIndex, grid, inChild?, isSummary?) {
487570
if (grid.navigation.isColumnFullyVisible(visibleColumnIndex) && grid.navigation.isColumnLeftFullyVisible(visibleColumnIndex)) {
488-
const cellSelector = this.getCellSelector(visibleColumnIndex);
571+
const cellSelector = this.getCellSelector(visibleColumnIndex, isSummary);
489572
const cells = elem.querySelectorAll(`${cellSelector}[data-visibleIndex="${visibleColumnIndex}"]`);
490573
let cell = cells[cells.length - 1];
491574
const rIndex = parseInt(elem.getAttribute('data-rowindex'), 10);
@@ -499,7 +582,7 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
499582
cell.offsetHeight - gridTop;
500583
if (scrTop !== 0 && diff < 0 && !inChild) {
501584
this.scrollGrid(scrGrid, diff, () => {
502-
const el = grid.navigation.getRowByIndex(rIndex);
585+
const el = !isSummary ? grid.navigation.getRowByIndex(rIndex) : elem;
503586
cell = el.querySelectorAll(`${cellSelector}[data-visibleIndex="${visibleColumnIndex}"]`)[0];
504587
cell.focus({ preventScroll: true });
505588
});
@@ -512,7 +595,7 @@ export class IgxHierarchicalGridNavigationService extends IgxGridNavigationServi
512595
}
513596
} else {
514597
this.horizontalScrollGridToIndex(grid, visibleColumnIndex, () => {
515-
this.focusPrevRow(elem, visibleColumnIndex, grid, inChild);
598+
this.focusPrevRow(elem, visibleColumnIndex, grid, inChild, isSummary);
516599
});
517600
}
518601
}

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

+28
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,34 @@ describe('IgxHierarchicalGrid Basic Navigation', () => {
195195
expect(childLastCell.focused).toBe(true);
196196
}));
197197

198+
it('should include summary rows in tab sequence.', (async () => {
199+
const childGrid = hierarchicalGrid.hgridAPI.getChildGrids(false)[0];
200+
childGrid.getColumnByName('ID').hasSummary = true;
201+
fixture.detectChanges();
202+
childGrid.cdr.detectChanges();
203+
hierarchicalGrid.verticalScrollContainer.scrollTo(2);
204+
await wait(100);
205+
fixture.detectChanges();
206+
207+
const parentCell = hierarchicalGrid.getCellByKey(1, 'ID');
208+
parentCell.nativeElement.focus();
209+
210+
parentCell.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', shiftKey: true }));
211+
await wait(100);
212+
fixture.detectChanges();
213+
214+
const summaryCells = fixture.debugElement.queryAll(By.css('igx-grid-summary-cell'));
215+
const lastSummaryCell = summaryCells[summaryCells.length - 1].nativeElement;
216+
expect(document.activeElement).toBe(lastSummaryCell);
217+
218+
lastSummaryCell.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
219+
await wait(100);
220+
fixture.detectChanges();
221+
222+
expect(parentCell.selected).toBe(true);
223+
expect(parentCell.focused).toBe(true);
224+
}));
225+
198226
it('should allow navigating to end in child grid when child grid target row moves outside the parent view port.', (async () => {
199227
const childGrid = hierarchicalGrid.hgridAPI.getChildGrids(false)[0];
200228
const childCell = childGrid.dataRowList.toArray()[0].cells.toArray()[0];

projects/igniteui-angular/src/lib/grids/summaries/summary-cell.component.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,9 @@ export class IgxSummaryCellComponent {
9595
switch (key) {
9696
case 'tab':
9797
if (shift) {
98-
if (this.rowIndex === 0 && this.visibleColumnIndex === 0 && this.grid.data && this.grid.data.length) {
99-
this.grid.navigation.goToLastBodyElement();
100-
return;
101-
}
10298
this.grid.navigation.performShiftTabKey(row, this.rowIndex, this.visibleColumnIndex, true);
10399
break;
104100
}
105-
if (this.rowIndex === 0 &&
106-
this.grid.unpinnedColumns[this.grid.unpinnedColumns.length - 1].visibleIndex === this.visibleColumnIndex) {
107-
return;
108-
109-
}
110101
this.grid.navigation.performTab(row, this.rowIndex, this.visibleColumnIndex, true);
111102
break;
112103
case 'arrowleft':
@@ -174,7 +165,8 @@ export class IgxSummaryCellComponent {
174165
}
175166

176167
private getRowElementByIndex(rowIndex) {
177-
return this.grid.nativeElement.querySelector(`igx-grid-summary-row[data-rowindex="${rowIndex}"]`);
168+
const summaryRows = this.grid.summariesRowList.toArray();
169+
return summaryRows.find((sr) => sr.dataRowIndex === rowIndex).nativeElement;
178170
}
179171

180172
private isKeySupportedInCell(key) {

src/app/hierarchical-grid/hierarchical-grid.sample.html

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ <h4 class="sample-title">Sample One</h4>
3131
<igx-column field="ProductName"></igx-column>
3232
</igx-column-group>
3333
</igx-row-island>
34+
<igx-row-island [key]="'childData2'" [autoGenerate]="false" [rowSelectable]='isRowSelectable' >
35+
<igx-column field="ChildLevels" [groupable]='true' [hasSummary]='true'></igx-column>
36+
<igx-column field="ProductName" [groupable]='true' [hasSummary]='true'></igx-column>
37+
</igx-row-island>
3438

3539
</igx-row-island>
3640
</igx-row-island>

0 commit comments

Comments
 (0)