diff --git a/e2e/pages/adf/demo-shell/dataTablePage.ts b/e2e/pages/adf/demo-shell/dataTablePage.ts index 4ca5b454ba..cc50832e61 100644 --- a/e2e/pages/adf/demo-shell/dataTablePage.ts +++ b/e2e/pages/adf/demo-shell/dataTablePage.ts @@ -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 { 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 { 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 { diff --git a/e2e/pages/adf/process-services/processFiltersPage.ts b/e2e/pages/adf/process-services/processFiltersPage.ts index cbbe2a17e0..626b39f0a4 100644 --- a/e2e/pages/adf/process-services/processFiltersPage.ts +++ b/e2e/pages/adf/process-services/processFiltersPage.ts @@ -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 { diff --git a/e2e/pages/adf/process-services/taskDetailsPage.ts b/e2e/pages/adf/process-services/taskDetailsPage.ts index a263d2ae17..8d1894584d 100644 --- a/e2e/pages/adf/process-services/taskDetailsPage.ts +++ b/e2e/pages/adf/process-services/taskDetailsPage.ts @@ -263,7 +263,7 @@ export class TaskDetailsPage { } async removeInvolvedUser(user): Promise { - 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); diff --git a/e2e/pages/adf/process-services/tasksPage.ts b/e2e/pages/adf/process-services/tasksPage.ts index 9d2aae0e0d..6404f965b4 100644 --- a/e2e/pages/adf/process-services/tasksPage.ts +++ b/e2e/pages/adf/process-services/tasksPage.ts @@ -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')); diff --git a/e2e/pages/adf/trashcanPage.ts b/e2e/pages/adf/trashcanPage.ts index 9c1a183e26..1d57bbba98 100644 --- a/e2e/pages/adf/trashcanPage.ts +++ b/e2e/pages/adf/trashcanPage.ts @@ -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')); diff --git a/lib/content-services/src/lib/directives/node-lock.directive.spec.ts b/lib/content-services/src/lib/directives/node-lock.directive.spec.ts index 53353d6030..71f4d58b94 100644 --- a/lib/content-services/src/lib/directives/node-lock.directive.spec.ts +++ b/lib/content-services/src/lib/directives/node-lock.directive.spec.ts @@ -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); }); diff --git a/lib/content-services/src/lib/directives/node-lock.directive.ts b/lib/content-services/src/lib/directives/node-lock.directive.ts index d0a7ad72ba..0934e1d36d 100644 --- a/lib/content-services/src/lib/directives/node-lock.directive.ts +++ b/lib/content-services/src/lib/directives/node-lock.directive.ts @@ -33,7 +33,7 @@ export class NodeLockDirective implements AfterViewInit { @HostListener('click', [ '$event' ]) onClick(event) { - event.preventDefault(); + event.stopPropagation(); this.contentNodeDialogService.openLockNodeDialog(this.node); } diff --git a/lib/core/datatable/components/datatable/datatable-row.component.spec.ts b/lib/core/datatable/components/datatable/datatable-row.component.spec.ts new file mode 100644 index 0000000000..74a3744c7b --- /dev/null +++ b/lib/core/datatable/components/datatable/datatable-row.component.spec.ts @@ -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; + 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); + }); +}); diff --git a/lib/core/datatable/components/datatable/datatable-row.component.ts b/lib/core/datatable/components/datatable/datatable-row.component.ts new file mode 100644 index 0000000000..f99aa5d7c3 --- /dev/null +++ b/lib/core/datatable/components/datatable/datatable-row.component.ts @@ -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: ``, + encapsulation: ViewEncapsulation.None, + host: { + class: 'adf-datatable-row', + tabindex: '0', + role: 'row' + } +}) +export class DataTableRowComponent implements FocusableOption { + @Input() row: DataRow; + + @Output() + select: EventEmitter = new EventEmitter(); + + @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(); + } +} diff --git a/lib/core/datatable/components/datatable/datatable.component.html b/lib/core/datatable/components/datatable/datatable.component.html index 399e5a994e..eed8833758 100644 --- a/lib/core/datatable/components/datatable/datatable.component.html +++ b/lib/core/datatable/components/datatable/datatable.component.html @@ -51,14 +51,15 @@
-
+
- -
+
{ 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 = {}; + 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 = {}; + 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 = {}; + dataTable.data = new ObjectDataTableAdapter([], []); dataTable.ngOnChanges({}); + fixture.detectChanges(); dataTable.rowDblClick.subscribe(() => { done(); @@ -635,7 +646,9 @@ describe('DataTable', () => { const row = {}; 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 = {}; + 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 = {}; + 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'); + }); }); diff --git a/lib/core/datatable/components/datatable/datatable.component.ts b/lib/core/datatable/components/datatable/datatable.component.ts index cbb7ffff13..1882b89252 100644 --- a/lib/core/datatable/components/datatable/datatable.component.ts +++ b/lib/core/datatable/components/datatable/datatable.component.ts @@ -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; + @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; private clickObserver: Observer; private click$: Observable; @@ -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); } diff --git a/lib/core/datatable/components/datatable/filesize-cell.component.ts b/lib/core/datatable/components/datatable/filesize-cell.component.ts index f263b750ea..a2328886c4 100644 --- a/lib/core/datatable/components/datatable/filesize-cell.component.ts +++ b/lib/core/datatable/components/datatable/filesize-cell.component.ts @@ -22,11 +22,11 @@ import { AlfrescoApiService } from '../../../services/alfresco-api.service'; @Component({ selector: 'adf-filesize-cell', template: ` - + {{ value$ | async | adfFileSize }}{{ fileSize }} `, diff --git a/lib/core/datatable/datatable.module.ts b/lib/core/datatable/datatable.module.ts index 23611bc2e1..e3b943750a 100644 --- a/lib/core/datatable/datatable.module.ts +++ b/lib/core/datatable/datatable.module.ts @@ -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, diff --git a/lib/core/datatable/public-api.ts b/lib/core/datatable/public-api.ts index 35551ba887..40a6d393ce 100644 --- a/lib/core/datatable/public-api.ts +++ b/lib/core/datatable/public-api.ts @@ -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'; diff --git a/lib/testing/src/lib/core/pages/data-table-component.page.ts b/lib/testing/src/lib/core/pages/data-table-component.page.ts index 9da827aba2..7aff53df03 100644 --- a/lib/testing/src/lib/core/pages/data-table-component.page.ts +++ b/lib/testing/src/lib/core/pages/data-table-component.page.ts @@ -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 { - 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 { - 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 { @@ -282,21 +282,21 @@ export class DataTableComponentPage { } async checkRowByContentIsSelected(folderName): Promise { - 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 { - 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 { - 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); }