[ADF-2990] Datatable - row navigation (#5198)

* datatable row component

* add to module

* implement datatable row component

* use FocusKeyManager for arrows navigation

* tests

* prevent screen reader to announce undefined value

* prevent from bubbling up

* fix unit test

* fix row locator

* fix locator reference

* fix row reference locators

* fix locator xpath
This commit is contained in:
Cilibiu Bogdan
2019-10-30 09:39:43 +02:00
committed by Denys Vuika
parent a150e74366
commit af6bd0892c
17 changed files with 320 additions and 45 deletions

View File

@@ -36,8 +36,8 @@ export class DataTablePage {
dataTable: DataTableComponentPage;
multiSelect: ElementFinder = element(by.css(`div[data-automation-id='multiselect'] label > div[class='mat-checkbox-inner-container']`));
reset: ElementFinder = element(by.xpath(`//span[contains(text(),'Reset to default')]/..`));
allSelectedRows: ElementArrayFinder = element.all(by.css(`div[class*='is-selected']`));
selectedRowNumber: ElementFinder = element(by.css(`div[class*='is-selected'] div[data-automation-id*='text_']`));
allSelectedRows: ElementArrayFinder = element.all(by.css(`adf-datatable-row[class*='is-selected']`));
selectedRowNumber: ElementFinder = element(by.css(`adf-datatable-row[class*='is-selected'] div[data-automation-id*='text_']`));
selectAll: ElementFinder = element(by.css(`div[class*='header'] label`));
addRowElement: ElementFinder = element(by.xpath(`//span[contains(text(),'Add row')]/..`));
replaceRowsElement: ElementFinder = element(by.xpath(`//span[contains(text(),'Replace rows')]/..`));
@@ -85,7 +85,7 @@ export class DataTablePage {
async checkRowIsNotSelected(rowNumber: string): Promise<void> {
const isRowSelected = this.dataTable.getCellElementByValue(this.columns.id, rowNumber)
.element(by.xpath(`ancestor::div[contains(@class, 'adf-datatable-row custom-row-style ng-star-inserted is-selected')]`));
.element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row custom-row-style ng-star-inserted is-selected')]`));
await BrowserVisibility.waitUntilElementIsNotVisible(isRowSelected);
}
@@ -112,7 +112,7 @@ export class DataTablePage {
async clickCheckbox(rowNumber: string): Promise<void> {
await BrowserActions.closeMenuAndDialogs();
const checkbox = this.dataTable.getCellElementByValue(this.columns.id, rowNumber)
.element(by.xpath(`ancestor::div[contains(@class, 'adf-datatable-row')]//mat-checkbox/label`));
.element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row')]//mat-checkbox/label`));
await BrowserActions.click(checkbox);
}
@@ -134,7 +134,7 @@ export class DataTablePage {
}
getRowCheckbox(rowNumber: string): ElementFinder {
return this.dataTable.getCellElementByValue(this.columns.id, rowNumber).element(by.xpath(`ancestor::div/div/mat-checkbox[contains(@class, 'mat-checkbox-checked')]`));
return this.dataTable.getCellElementByValue(this.columns.id, rowNumber).element(by.xpath(`ancestor::adf-datatable-row/div/mat-checkbox[contains(@class, 'mat-checkbox-checked')]`));
}
async getCopyContentTooltip(): Promise<string> {

View File

@@ -32,9 +32,9 @@ export class ProcessFiltersPage {
accordionMenu: ElementFinder = element(by.css('.app-processes-menu mat-accordion'));
buttonWindow: ElementFinder = element(by.css('div > button[data-automation-id="btn-start-process"] > div'));
noContentMessage: ElementFinder = element.all(by.css('div[class="adf-empty-content__title"]')).first();
rows: Locator = by.css('adf-process-instance-list div[class="adf-datatable-body"] div[class*="adf-datatable-row"]');
rows: Locator = by.css('adf-process-instance-list div[class="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"]');
tableBody: ElementFinder = element.all(by.css('adf-datatable div[class="adf-datatable-body"]')).first();
nameColumn: Locator = by.css('div[class*="adf-datatable-body"] div[class*="adf-datatable-row"] div[title="Name"] span');
nameColumn: Locator = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[title="Name"] span');
processIcon: Locator = by.xpath('ancestor::div[@class="mat-list-item-content"]/mat-icon');
async startProcess(): Promise<StartProcessPage> {

View File

@@ -263,7 +263,7 @@ export class TaskDetailsPage {
}
async removeInvolvedUser(user): Promise<void> {
const row = this.getRowsUser(user).element(by.xpath('ancestor::div[contains(@class, "adf-datatable-row")]'));
const row = this.getRowsUser(user).element(by.xpath('ancestor::adf-datatable-row[contains(@class, "adf-datatable-row")]'));
await BrowserActions.click(row.element(by.css('button[data-automation-id="action_menu_0"]')));
await BrowserVisibility.waitUntilElementIsVisible(this.removeInvolvedPeople);
await BrowserActions.click(this.removeInvolvedPeople);

View File

@@ -32,7 +32,7 @@ export class TasksPage {
rowByRowName = by.xpath('ancestor::mat-chip');
checklistContainer = by.css('div[class*="checklist-menu"]');
taskTitle = 'h2[class="adf-activiti-task-details__header"] span';
rows = by.css('div[class*="adf-datatable-body"] div[class*="adf-datatable-row"] div[class*="adf-datatable-cell"]');
rows = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[class*="adf-datatable-cell"]');
completeButtonNoForm: ElementFinder = element(by.id('adf-no-form-complete-button'));
checklistDialog: ElementFinder = element(by.id('checklist-dialog'));
checklistNoMessage: ElementFinder = element(by.id('checklist-none-message'));

View File

@@ -22,7 +22,7 @@ import { element, by, ElementFinder, Locator } from 'protractor';
export class TrashcanPage {
contentList: DocumentListPage = new DocumentListPage(element(by.css('adf-document-list')));
rows: Locator = by.css('adf-document-list div[class*="adf-datatable-body"] div[class*="adf-datatable-row"]');
rows: Locator = by.css('adf-document-list div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"]');
tableBody: ElementFinder = element.all(by.css('adf-document-list div[class="adf-datatable-body"]')).first();
pagination: ElementFinder = element(by.css('adf-pagination'));
emptyTrashcan: ElementFinder = element(by.css('adf-empty-content'));

View File

@@ -73,9 +73,7 @@ describe('NodeLock Directive', () => {
fixture.detectChanges();
element = fixture.debugElement.query(By.directive(NodeLockDirective));
element.triggerEventHandler('click', {
preventDefault: () => {}
});
element.nativeElement.dispatchEvent(new MouseEvent('click'));
expect(contentNodeDialogService.openLockNodeDialog).toHaveBeenCalledWith(fakeNode);
});

View File

@@ -33,7 +33,7 @@ export class NodeLockDirective implements AfterViewInit {
@HostListener('click', [ '$event' ])
onClick(event) {
event.preventDefault();
event.stopPropagation();
this.contentNodeDialogService.openLockNodeDialog(this.node);
}

View File

@@ -0,0 +1,99 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DataTableRowComponent } from './datatable-row.component';
import { DataRow } from '../../data/data-row.model';
import { TestBed, ComponentFixture } from '@angular/core/testing';
describe('DataTableRowComponent', () => {
let fixture: ComponentFixture<DataTableRowComponent>;
let component: DataTableRowComponent;
const row: DataRow = {
isSelected: false,
hasValue: jasmine.createSpy('hasValue'),
getValue: () => {}
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [DataTableRowComponent]
});
fixture = TestBed.createComponent(DataTableRowComponent);
component = fixture.componentInstance;
});
it('should add select class when row is selected', () => {
row.isSelected = true;
component.row = row;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.classList.contains('adf-is-selected')).toBe(true);
});
it('should not have select class when row is not selected', () => {
row.isSelected = false;
component.row = row;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.classList.contains('adf-is-selected'))
.not.toBe(true);
});
it('should set aria selected to true when row is selected', () => {
row.isSelected = true;
component.row = row;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.getAttribute('aria-selected')).toBe('true');
});
it('should set aria selected to false when row is not selected', () => {
row.isSelected = false;
component.row = row;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.getAttribute('aria-selected')).toBe('false');
});
it('should set aria label', () => {
spyOn(row, 'getValue').and.returnValue('some-name');
component.row = row;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.getAttribute('aria-label')).toBe('some-name');
});
it('should focus element', () => {
expect(document.activeElement.classList.contains('adf-datatable-row')).toBe(false);
component.focus();
expect(document.activeElement.classList.contains('adf-datatable-row')).toBe(true);
});
it('should emit keyboard space event', () => {
spyOn(component.select, 'emit');
const event = new KeyboardEvent('keydown', {
key: ' ',
code: 'Space'
});
fixture.debugElement.nativeElement.dispatchEvent(event);
expect(component.select.emit).toHaveBeenCalledWith(event);
});
});

View File

@@ -0,0 +1,75 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Component,
ViewEncapsulation,
ElementRef,
Input,
HostBinding,
HostListener,
Output,
EventEmitter
} from '@angular/core';
import { FocusableOption } from '@angular/cdk/a11y';
import { DataRow } from '../../data/data-row.model';
@Component({
selector: 'adf-datatable-row',
template: `<ng-content></ng-content>`,
encapsulation: ViewEncapsulation.None,
host: {
class: 'adf-datatable-row',
tabindex: '0',
role: 'row'
}
})
export class DataTableRowComponent implements FocusableOption {
@Input() row: DataRow;
@Output()
select: EventEmitter<any> = new EventEmitter<any>();
@HostBinding('class.adf-is-selected')
get isSelected(): boolean {
return this.row.isSelected;
}
@HostBinding('attr.aria-selected')
get isAriaSelected(): boolean {
return this.row.isSelected;
}
@HostBinding('attr.aria-label')
get ariaLabel(): boolean {
return this.row.getValue('name') || '';
}
@HostListener('keydown.space', ['$event'])
onKeyDown(event: KeyboardEvent) {
if ((event.target as Element).tagName === this.element.nativeElement.tagName) {
event.preventDefault();
this.select.emit(event);
}
}
constructor(private element: ElementRef) {}
focus() {
this.element.nativeElement.focus();
}
}

View File

@@ -51,14 +51,15 @@
<div class="adf-datatable-body" role="rowgroup">
<ng-container *ngIf="!loading && !noPermission">
<div *ngFor="let row of data.getRows(); let idx = index"
class="adf-datatable-row"
role="row"
[class.adf-is-selected]="row.isSelected"
[adf-upload]="allowDropFiles && rowAllowsDrop(row)" [adf-upload-data]="row"
<adf-datatable-row *ngFor="let row of data.getRows(); let idx = index"
[row]="row"
(select)="onEnterKeyPressed(row, $event)"
(keyup)="onRowKeyUp(row, $event)"
[adf-upload]="allowDropFiles && rowAllowsDrop(row)"
[adf-upload-data]="row"
[ngStyle]="rowStyle"
[ngClass]="getRowStyle(row)"
(keyup)="onRowKeyUp(row, $event)">
[attr.data-automation-id]="'datatable-row-' + idx">
<!-- Actions (left) -->
<div *ngIf="actions && actionsPosition === 'left'" role="gridcell" class="adf-datatable-cell">
<button mat-icon-button [matMenuTriggerFor]="menu"
@@ -191,7 +192,7 @@
</ng-container>
</div>
<div *ngIf="col.template" class="adf-datatable-cell-container">
<div class="adf-cell-value" tabindex="0">
<div class="adf-cell-value">
<ng-container
[ngTemplateOutlet]="col.template"
[ngTemplateOutletContext]="{ $implicit: { data: data, row: row, col: col }, value: data.getValue(row, col, resolverFn) }">
@@ -222,8 +223,7 @@
</button>
</mat-menu>
</div>
</div>
</adf-datatable-row>
<div *ngIf="isEmpty()"
role="row"
[class.adf-datatable-row]="display === 'list'"

View File

@@ -12,7 +12,7 @@
$data-table-cell-text-color: mat-color($foreground, text) !default;
$data-table-cell-link-color: mat-color($foreground, text) !default;
$data-table-cell-link-hover-color: mat-color($alfresco-ecm-blue, 500) !default;
$data-table-cell-outline: 1px solid mat-color($alfresco-ecm-blue, A200) !default;
$data-table-outline: 1px solid mat-color($alfresco-ecm-blue, A200) !default;
$data-table-divider-color: mat-color($foreground, text, 0.07) !default;
$data-table-hover-color: mat-color($background, 'hover') !default;
$data-table-selection-color: mat-color($background, 'selected-button') !default;
@@ -238,6 +238,11 @@
background-color: $data-table-hover-color;
}
&:focus {
outline-offset: -1px;
outline: $data-table-outline;
}
&.adf-is-selected, &.adf-is-selected:hover {
background-color: $data-table-selection-color;
}
@@ -416,7 +421,7 @@
&:focus {
outline-offset: -1px;
outline: $data-table-cell-outline;
outline: $data-table-outline;
}
}

View File

@@ -373,6 +373,7 @@ describe('DataTable', () => {
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
@@ -395,6 +396,7 @@ describe('DataTable', () => {
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
@@ -462,6 +464,7 @@ describe('DataTable', () => {
);
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeTruthy();
@@ -481,6 +484,7 @@ describe('DataTable', () => {
rows[0].isSelected = true;
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
done();
@@ -509,6 +513,7 @@ describe('DataTable', () => {
});
dataTable.selection.push(rows[0]);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeTruthy();
expect(rows[1].isSelected).toBeTruthy();
@@ -584,6 +589,7 @@ describe('DataTable', () => {
it('should emit row click event', (done) => {
const row = <DataRow> {};
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.rowClick.subscribe((e) => {
expect(e.value).toBe(row);
@@ -591,13 +597,16 @@ describe('DataTable', () => {
});
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.onRowClick(row, null);
});
it('should emit double click if there are two single click in 250ms', (done) => {
const row = <DataRow> {};
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowDblClick.subscribe(() => {
done();
@@ -614,7 +623,9 @@ describe('DataTable', () => {
it('should emit double click if there are more than two single click in 250ms', (done) => {
const row = <DataRow> {};
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowDblClick.subscribe(() => {
done();
@@ -635,7 +646,9 @@ describe('DataTable', () => {
const row = <DataRow> {};
let clickCount = 0;
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
clickCount += 1;
@@ -653,6 +666,7 @@ describe('DataTable', () => {
it('should emit row-click dom event', (done) => {
const row = <DataRow> {};
dataTable.data = new ObjectDataTableAdapter([], []);
fixture.nativeElement.addEventListener('row-click', (e) => {
expect(e.detail.value).toBe(row);
@@ -660,17 +674,20 @@ describe('DataTable', () => {
});
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.onRowClick(row, null);
});
it('should emit row-dblclick dom event', (done) => {
const row = <DataRow> {};
dataTable.data = new ObjectDataTableAdapter([], []);
fixture.nativeElement.addEventListener('row-dblclick', (e) => {
expect(e.detail.value).toBe(row);
done();
});
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.onRowClick(row, null);
dataTable.onRowClick(row, null);
});
@@ -1177,4 +1194,64 @@ describe('Accesibility', () => {
expect(dataTable.getAriaSort(column)).toBe('ADF-DATATABLE.ACCESSIBILITY.SORT_DESCENDING');
});
});
it('should focus next row on ArrowDown event', () => {
const event = new KeyboardEvent('keyup', {
code: 'ArrowDown',
key: 'ArrowDown',
keyCode: 40
} as KeyboardEventInit );
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.ngOnChanges({
rows: new SimpleChange(null, dataRows, false)
});
fixture.detectChanges();
dataTable.ngAfterViewInit();
const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[0];
const rowCellElement = rowElement.querySelector('.adf-datatable-cell');
rowCellElement.dispatchEvent(new MouseEvent('click'));
fixture.debugElement.nativeElement.dispatchEvent(event);
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-1');
});
it('should focus previous row on ArrowUp event', () => {
const event = new KeyboardEvent('keyup', {
code: 'ArrowDown',
key: 'ArrowDown',
keyCode: 38
} as KeyboardEventInit );
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.ngOnChanges({
rows: new SimpleChange(null, dataRows, false)
});
fixture.detectChanges();
dataTable.ngAfterViewInit();
const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[1];
const rowCellElement = rowElement.querySelector('.adf-datatable-cell');
rowCellElement.dispatchEvent(new MouseEvent('click'));
fixture.debugElement.nativeElement.dispatchEvent(event);
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-0');
});
});

View File

@@ -16,9 +16,11 @@
*/
import {
ViewChildren, QueryList, HostListener,
AfterContentInit, Component, ContentChild, DoCheck, ElementRef, EventEmitter, Input,
IterableDiffers, OnChanges, Output, SimpleChange, SimpleChanges, TemplateRef, ViewEncapsulation, OnDestroy
} from '@angular/core';
import { FocusKeyManager } from '@angular/cdk/a11y';
import { MatCheckboxChange } from '@angular/material';
import { Subscription, Observable, Observer } from 'rxjs';
import { DataColumnListComponent } from '../../../data-column/data-column-list.component';
@@ -27,6 +29,7 @@ import { DataRowEvent } from '../../data/data-row-event.model';
import { DataRow } from '../../data/data-row.model';
import { DataSorting } from '../../data/data-sorting.model';
import { DataTableAdapter } from '../../data/datatable-adapter';
import { DataTableRowComponent } from './datatable-row.component';
import { ObjectDataRow } from '../../data/object-datarow.model';
import { ObjectDataTableAdapter } from '../../data/object-datatable-adapter';
@@ -48,6 +51,9 @@ export enum DisplayMode {
})
export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck, OnDestroy {
@ViewChildren(DataTableRowComponent)
rowsList: QueryList<DataTableRowComponent>;
@ContentChild(DataColumnListComponent)
columnList: DataColumnListComponent;
@@ -178,6 +184,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
/** This array of fake rows fix the flex layout for the gallery view */
fakeRows = [];
private keyManager: FocusKeyManager<DataTableRowComponent>;
private clickObserver: Observer<DataRowEvent>;
private click$: Observable<DataRowEvent>;
@@ -189,6 +196,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
private multiClickStreamSub: Subscription;
private dataRowsChanged: Subscription;
@HostListener('keyup', ['$event'])
onKeydown(event: KeyboardEvent): void {
this.keyManager.onKeydown(event);
}
constructor(private elementRef: ElementRef,
differs: IterableDiffers) {
if (differs) {
@@ -210,6 +222,10 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
this.setTableSchema();
}
ngAfterViewInit() {
this.keyManager = new FocusKeyManager(this.rowsList).withWrap();
}
ngOnChanges(changes: SimpleChanges) {
this.initAndSubscribeClickStream();
if (this.isPropertyChanged(changes['data'])) {
@@ -390,6 +406,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges, DoCheck,
}
if (row) {
this.keyManager.setActiveItem(this.data.getRows().indexOf(row));
const dataRowEvent = new DataRowEvent(row, mouseEvent, this);
this.clickObserver.next(dataRowEvent);
}

View File

@@ -22,11 +22,11 @@ import { AlfrescoApiService } from '../../../services/alfresco-api.service';
@Component({
selector: 'adf-filesize-cell',
template: `
<ng-container>
<ng-container *ngIf="(value$ | async | adfFileSize) as fileSize">
<span
[title]="tooltip"
[attr.aria-label]="value$ | async | adfFileSize"
>{{ value$ | async | adfFileSize }}</span
[attr.aria-label]="fileSize"
>{{ fileSize }}</span
>
</ng-container>
`,

View File

@@ -26,6 +26,7 @@ import { PipeModule } from '../pipes/pipe.module';
import { DirectiveModule } from '../directives/directive.module';
import { DataTableCellComponent } from './components/datatable/datatable-cell.component';
import { DataTableRowComponent } from './components/datatable/datatable-row.component';
import { DataTableComponent } from './components/datatable/datatable.component';
import { DateCellComponent } from './components/datatable/date-cell.component';
import { EmptyListBodyDirective,
@@ -62,6 +63,7 @@ import { DropZoneDirective } from './components/datatable/drop-zone.directive';
EmptyListBodyDirective,
EmptyListFooterDirective,
DataTableCellComponent,
DataTableRowComponent,
DateCellComponent,
FileSizeCellComponent,
LocationCellComponent,
@@ -81,6 +83,7 @@ import { DropZoneDirective } from './components/datatable/drop-zone.directive';
EmptyListBodyDirective,
EmptyListFooterDirective,
DataTableCellComponent,
DataTableRowComponent,
DateCellComponent,
FileSizeCellComponent,
LocationCellComponent,

View File

@@ -30,6 +30,7 @@ export * from './components/datatable/data-cell.event';
export * from './components/datatable/data-row-action.event';
export * from './components/datatable/drop-zone.directive';
export * from './components/datatable/datatable-cell.component';
export * from './components/datatable/datatable-row.component';
export * from './components/datatable/datatable.component';
export * from './components/datatable/date-cell.component';
export * from './components/datatable/empty-list.component';

View File

@@ -25,7 +25,7 @@ export class DataTableComponentPage {
list: ElementArrayFinder;
contents: ElementArrayFinder;
tableBody: ElementFinder;
rows: Locator = by.css(`adf-datatable div[class*='adf-datatable-body'] div[class*='adf-datatable-row']`);
rows: Locator = by.css(`adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`);
allColumns: ElementArrayFinder;
selectedRowNumber: ElementFinder;
allSelectedRows: ElementArrayFinder;
@@ -34,12 +34,12 @@ export class DataTableComponentPage {
constructor(rootElement: ElementFinder = element.all(by.css('adf-datatable')).first()) {
this.rootElement = rootElement;
this.list = this.rootElement.all(by.css(`div[class*='adf-datatable-body'] div[class*='adf-datatable-row']`));
this.list = this.rootElement.all(by.css(`div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`));
this.contents = this.rootElement.all(by.css('div[class="adf-datatable-body"] span'));
this.tableBody = this.rootElement.all(by.css(`div[class='adf-datatable-body']`)).first();
this.allColumns = this.rootElement.all(by.css('div[data-automation-id*="auto_id_entry."]'));
this.selectedRowNumber = this.rootElement.element(by.css(`div[class*='is-selected'] div[data-automation-id*='text_']`));
this.allSelectedRows = this.rootElement.all(by.css(`div[class*='is-selected']`));
this.selectedRowNumber = this.rootElement.element(by.css(`adf-datatable-row[class*='is-selected'] div[data-automation-id*='text_']`));
this.allSelectedRows = this.rootElement.all(by.css(`adf-datatable-row[class*='is-selected']`));
this.selectAll = this.rootElement.element(by.css(`div[class*='adf-datatable-header'] mat-checkbox`));
this.copyColumnTooltip = this.rootElement.element(by.css(`adf-copy-content-tooltip span`));
}
@@ -97,12 +97,12 @@ export class DataTableComponentPage {
}
async checkRowIsSelected(columnName, columnValue): Promise<void> {
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`));
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
await BrowserVisibility.waitUntilElementIsVisible(selectedRow);
}
async checkRowIsNotSelected(columnName, columnValue): Promise<void> {
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`));
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
await BrowserVisibility.waitUntilElementIsNotVisible(selectedRow);
}
@@ -164,7 +164,7 @@ export class DataTableComponentPage {
}
async getAllRowsColumnValues(column: string) {
const columnLocator = by.css("adf-datatable div[class*='adf-datatable-body'] div[class*='adf-datatable-row'] div[title='" + column + "'] span");
const columnLocator = by.css("adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row'] div[title='" + column + "'] span");
await BrowserVisibility.waitUntilElementIsPresent(element.all(columnLocator).first());
return await element.all(columnLocator)
.filter(async (el) => await el.isPresent())
@@ -232,11 +232,11 @@ export class DataTableComponentPage {
}
getRow(columnName: string, columnValue: string): ElementFinder {
return this.rootElement.all(by.xpath(`//div[@title="${columnName}"]//div[@data-automation-id="text_${columnValue}"]//ancestor::div[contains(@class, 'adf-datatable-row')]`)).first();
return this.rootElement.all(by.xpath(`//div[@title="${columnName}"]//div[@data-automation-id="text_${columnValue}"]//ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row')]`)).first();
}
getRowByIndex(index: number): ElementFinder {
return this.rootElement.element(by.xpath(`//div[contains(@class,'adf-datatable-body')]//div[contains(@class,'adf-datatable-row')][${index}]`));
return this.rootElement.element(by.xpath(`//div[contains(@class,'adf-datatable-body')]//adf-datatable-row[contains(@class,'adf-datatable-row')][${index}]`));
}
async contentInPosition(position: number): Promise<string> {
@@ -282,21 +282,21 @@ export class DataTableComponentPage {
}
async checkRowByContentIsSelected(folderName): Promise<void> {
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`));
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
await BrowserVisibility.waitUntilElementIsVisible(selectedRow);
}
async checkRowByContentIsNotSelected(folderName): Promise<void> {
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::div[contains(@class, 'is-selected')]`));
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
await BrowserVisibility.waitUntilElementIsNotVisible(selectedRow);
}
getCellByContent(content) {
return this.rootElement.all(by.cssContainingText(`div[class*='adf-datatable-row'] div[class*='adf-datatable-cell']`, content)).first();
return this.rootElement.all(by.cssContainingText(`adf-datatable-row[class*='adf-datatable-row'] div[class*='adf-datatable-cell']`, content)).first();
}
async checkCellByHighlightContent(content) {
const cell = this.rootElement.element(by.cssContainingText(`div[class*='adf-datatable-row'] div[class*='adf-name-location-cell-name'] span.adf-highlight`, content));
const cell = this.rootElement.element(by.cssContainingText(`adf-datatable-row[class*='adf-datatable-row'] div[class*='adf-name-location-cell-name'] span.adf-highlight`, content));
await BrowserVisibility.waitUntilElementIsVisible(cell);
}
@@ -306,7 +306,7 @@ export class DataTableComponentPage {
}
async clickRowByContentCheckbox(name: string): Promise<void> {
const resultElement = this.rootElement.all(by.css(`div[data-automation-id='${name}']`)).first().element(by.xpath(`ancestor::div/div/mat-checkbox`));
const resultElement = this.rootElement.all(by.css(`div[data-automation-id='${name}']`)).first().element(by.xpath(`ancestor::adf-datatable-row/div/mat-checkbox`));
await browser.actions().mouseMove(resultElement);
await BrowserActions.click(resultElement);
}