Skip to content

Commit

Permalink
Edit and Close buttons now tab indexed properly so that Space/Enter w…
Browse files Browse the repository at this point in the history
…orks on them (#14865)

* fix(query-builder): fix edit/close buttons now tabindexed properly
  • Loading branch information
ivanvpetrov authored Oct 3, 2024
1 parent bf391c7 commit d9090f0
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2150,7 +2150,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
tick(50);
fix.detectChanges();
expect(editIcon.getAttribute('aria-hidden')).toBe('true');
expect(editIcon.getAttribute('tabIndex')).toBeFalsy();
expect(editIcon.getAttribute('tabIndex')).toBe('0');
}));

describe('Context Menu - ', () => {
Expand Down Expand Up @@ -3001,6 +3001,40 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
verifyContextMenuVisibility(fix, false);
}));

it('Should navigate with Tab/Shift+Tab through chips" "edit", "cancel" and "adding" buttons, fields of a condition in edit mode.', fakeAsync(() => {
// Apply advanced filter through API.
const tree = new FilteringExpressionsTree(FilteringLogic.Or);
tree.filteringOperands.push({
fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'),
ignoreCase: true
});
tree.filteringOperands.push({
fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'),
ignoreCase: true
});
grid.advancedFilteringExpressionsTree = tree;
fix.detectChanges();

// Open Advanced Filtering dialog.
grid.openAdvancedFilteringDialog();
fix.detectChanges();

// Press 'Enter' on the second chip and verify it is selected.
UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [0]));
tick(200);
fix.detectChanges();

let chipActions = fix.debugElement.query(By.css('.igx-filter-tree'));
GridFunctions.verifyTabbableElements(chipActions);

// Press 'Enter' on the edit button.
UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionEditIcon(fix, [0]));
fix.detectChanges();

chipActions = fix.debugElement.query(By.css('.igx-filter-tree'));
GridFunctions.verifyInEditTabbableElements(chipActions);
}));

});

});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,18 @@ <h6 class="igx-filter-empty__title">
expressionItem.hovered
"
>
<span tabindex="0">
<span tabindex="-1">
<igx-icon
tabindex="0"
(keydown)="invokeClick($event)"
(click)="enterExpressionEdit(expressionItem)"
>
edit
</igx-icon>
</span>
<span tabindex="0">
<span tabindex="-1">
<igx-icon
tabindex="0"
(keydown)="invokeClick($event)"
(click)="enterExpressionAdd(expressionItem)"
*ngIf="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2082,6 +2082,75 @@ export class GridFunctions {
expect(cell.isInvalid).toEqual(!valid);
expect(cell.nativeElement.classList.contains(CELL_INVALID_CSS_CLASS)).not.toEqual(valid);
}

public static verifyTabbableElements = (chipActions: DebugElement) => {
const tabElements = this.getTabbableElements(chipActions.nativeElement);

let i = 0;
tabElements.forEach((element: HTMLElement) => {
switch (i) {
case 0: expect(element).toHaveClass('igx-filter-tree__line--or'); break;
case 1: expect(element).toHaveClass('igx-chip'); break;
case 2: expect(element).toHaveClass('igx-chip__remove'); break;
case 3: expect(element).toHaveClass('igx-icon');
expect(element.innerText).toContain('edit');
break;
case 4: expect(element).toHaveClass('igx-icon');
expect(element.innerText).toContain('add');
break;
case 5: expect(element).toHaveClass('igx-chip'); break;
case 6: expect(element).toHaveClass('igx-chip__remove'); break;
case 7: expect(element).toHaveClass('igx-button');
expect(element.innerText).toContain('Condition');
break;
case 8: expect(element).toHaveClass('igx-button');
expect(element.innerText).toContain('"And" Group');
break;
case 9: expect(element).toHaveClass('igx-button');
expect(element.innerText).toContain('"Or" Group');
break;
}
i++;
});
};

public static verifyInEditTabbableElements = (chipActions: DebugElement) => {
const tabElements = this.getTabbableElements(chipActions.nativeElement);

let i = 0;
tabElements.forEach((element: HTMLElement) => {
switch (i) {
case 0: expect(element).toHaveClass('igx-filter-tree__line--or'); break;
case 1: expect(element).toHaveClass('igx-input-group__input'); break;
case 2: expect(element).toHaveClass('igx-input-group__input'); break;
case 3: expect(element).toHaveClass('igx-input-group__input'); break;
case 4: expect(element).toHaveClass('igx-icon-button');
expect(element.innerText).toContain('check');
break;
case 5: expect(element).toHaveClass('igx-icon-button');
expect(element.innerText).toContain('close');
break;
case 6: expect(element).toHaveClass('igx-chip'); break;
case 7: expect(element).toHaveClass('igx-chip__remove'); break;
}
i++;
});
};

/*
* Get tabbable elements in a container element. Result is returned as node elements ordered the way they will be tabbed
*/
public static getTabbableElements(inElement: HTMLElement) {
const focusableElements =
'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';

return Array.prototype.filter.call(
inElement.querySelectorAll(focusableElements),
element => {
return (element.offsetWidth > 0 || element.offsetHeight > 0);
}
);
}
}
export class GridSummaryFunctions {
public static getRootSummaryRow(fix): DebugElement {
Expand Down

0 comments on commit d9090f0

Please sign in to comment.