diff --git a/e2e/suites/actions/edit/edit-offline.test.ts b/e2e/suites/actions/edit/edit-offline.test.ts index 9367fc118..9eeb33da5 100755 --- a/e2e/suites/actions/edit/edit-offline.test.ts +++ b/e2e/suites/actions/edit/edit-offline.test.ts @@ -326,7 +326,7 @@ describe('Edit offline', () => { it('[C306954] Lock information is displayed', async () => { expect(await dataTable.isItemPresent(fileSearchLocked2, parentSearch)).toBe(true, `${fileSearchLocked2} is not displayed`); expect(await dataTable.hasLockIcon(fileSearchLocked2, parentSearch)).toBe(true, `${fileSearchLocked2} does not have a lock icon`); - expect(await dataTable.getLockOwner(fileSearchLocked2, parentSearch)).toContain( + expect(await dataTable.getLockOwnerToolTip(fileSearchLocked2, parentSearch)).toContain( username, `${fileSearchLocked2} does not have correct lock owner info` ); diff --git a/e2e/suites/search/search-results-files-folders.test.ts b/e2e/suites/search/search-results-files-folders.test.ts index c1b25b8b7..9b690eb37 100644 --- a/e2e/suites/search/search-results-files-folders.test.ts +++ b/e2e/suites/search/search-results-files-folders.test.ts @@ -108,12 +108,13 @@ describe('Search results - files and folders', () => { const size = fileEntry.entry.content.sizeInBytes; expect(await dataTable.isItemPresent(file)).toBe(true, `${file} is not displayed`); - expect(await dataTable.getRowCellsCount(file)).toEqual(2, 'incorrect number of columns'); - expect(await dataTable.getSearchResultLinesCount(file)).toEqual(4, 'incorrect number of lines for search result'); - expect(await dataTable.getSearchResultNameAndTitle(file)).toBe(`${file} ( ${fileTitle} )`); - expect(await dataTable.getSearchResultDescription(file)).toBe(fileDescription); - expect(await dataTable.getSearchResultModified(file)).toBe(`Modified: ${modifiedDate} by ${modifiedBy} | Size: ${size} Bytes`); - expect(await dataTable.getSearchResultLocation(file)).toMatch(/Location:\s+Personal Files/); + expect(await dataTable.getRowCellsCount(file)).toEqual(6, 'incorrect number of columns'); + expect(await page.getName(file)).toBe(`${file} ( ${fileTitle} )`); + expect(await page.getDescription(file)).toBe(fileDescription); + expect(await page.getModified(file)).toBe(modifiedDate); + expect(await page.getModifiedBy(file)).toBe(modifiedBy); + expect(await page.getSize(file)).toBe(`${size} Bytes`); + expect(await page.getLocation(file)).toEqual(`Company Home › User Homes › ${username}`); }); it('[C306867] Folder information', async () => { @@ -127,12 +128,12 @@ describe('Search results - files and folders', () => { const modifiedBy = folderEntry.entry.modifiedByUser.displayName; expect(await dataTable.isItemPresent(folder)).toBe(true, `${folder} is not displayed`); - expect(await dataTable.getRowCellsCount(folder)).toEqual(2, 'incorrect number of columns'); - expect(await dataTable.getSearchResultLinesCount(folder)).toEqual(4, 'incorrect number of lines for search result'); - expect(await dataTable.getSearchResultNameAndTitle(folder)).toBe(`${folder} ( ${folderTitle} )`); - expect(await dataTable.getSearchResultDescription(folder)).toBe(folderDescription); - expect(await dataTable.getSearchResultModified(folder)).toBe(`Modified: ${modifiedDate} by ${modifiedBy}`); - expect(await dataTable.getSearchResultLocation(folder)).toMatch(/Location:\s+Personal Files/); + expect(await dataTable.getRowCellsCount(file)).toEqual(6, 'incorrect number of columns'); + expect(await page.getName(folder)).toBe(`${folder} ( ${folderTitle} )`); + expect(await page.getDescription(folder)).toBe(folderDescription); + expect(await page.getModified(folder)).toBe(modifiedDate); + expect(await page.getModifiedBy(folder)).toBe(modifiedBy); + expect(await page.getLocation(folder)).toEqual(`Company Home › User Homes › ${username}`); }); it('[C290029] Search file with special characters', async () => { diff --git a/projects/aca-testing-shared/src/components/data-table/data-table.ts b/projects/aca-testing-shared/src/components/data-table/data-table.ts index 6fea84a9f..79b39f7d8 100755 --- a/projects/aca-testing-shared/src/components/data-table/data-table.ts +++ b/projects/aca-testing-shared/src/components/data-table/data-table.ts @@ -24,7 +24,7 @@ */ import { browser, by, ElementArrayFinder, ElementFinder, protractor } from 'protractor'; -import { BrowserVisibility, Logger } from '@alfresco/adf-testing'; +import { BrowserActions, BrowserVisibility, Logger } from '@alfresco/adf-testing'; import { BROWSER_WAIT_TIMEOUT } from '../../configs'; import { Component } from '../component'; import { Menu } from '../menu/menu'; @@ -41,7 +41,7 @@ export class DataTable extends Component { cell: '.adf-datatable-cell-container', lockOwner: '.aca-locked-by', searchResultsRow: 'aca-search-results-row', - searchResultsRowLine: '.line' + searchResultsRowLine: 'span' }; head = this.byCss('.adf-datatable-header'); @@ -213,6 +213,14 @@ export class DataTable extends Component { return ''; } + async getLockOwnerToolTip(itemName: string, location: string = ''): Promise { + if (await this.hasLockIcon(itemName, location)) { + const row = this.getRowByName(itemName, location); + return BrowserActions.getAttribute(row.element(by.css('img[src*="lock"]')), 'alt'); + } + return ''; + } + private getNameLink(itemName: string): ElementFinder { return this.getRowNameCell(itemName).$('.adf-datatable-link [role="link"]'); } @@ -419,34 +427,6 @@ export class DataTable extends Component { return this.body.element(by.cssContainingText(DataTable.selectors.searchResultsRow, name)); } - private getSearchResultRowLines(name: string, location: string = ''): ElementArrayFinder { - return this.getSearchResultsRowByName(name, location).all(by.css(DataTable.selectors.searchResultsRowLine)); - } - - async getSearchResultLinesCount(name: string, location: string = ''): Promise { - return this.getSearchResultRowLines(name, location).count(); - } - - private getSearchResultNthLine(name: string, location: string = '', index: number): ElementFinder { - return this.getSearchResultRowLines(name, location).get(index); - } - - async getSearchResultNameAndTitle(name: string, location: string = ''): Promise { - return this.getSearchResultNthLine(name, location, 0).getText(); - } - - async getSearchResultDescription(name: string, location: string = ''): Promise { - return this.getSearchResultNthLine(name, location, 1).getText(); - } - - async getSearchResultModified(name: string, location: string = ''): Promise { - return this.getSearchResultNthLine(name, location, 2).getText(); - } - - async getSearchResultLocation(name: string, location: string = ''): Promise { - return this.getSearchResultNthLine(name, location, 3).getText(); - } - private getSearchResultNameLink(itemName: string, location: string = ''): ElementFinder { return this.getSearchResultsRowByName(itemName, location).$('.link'); } diff --git a/projects/aca-testing-shared/src/pages/search-results-page.ts b/projects/aca-testing-shared/src/pages/search-results-page.ts index 86092cd24..7fbb89ede 100755 --- a/projects/aca-testing-shared/src/pages/search-results-page.ts +++ b/projects/aca-testing-shared/src/pages/search-results-page.ts @@ -45,6 +45,30 @@ export class SearchResultsPage extends BrowsingPage { return this.infoText.getText(); } + async getName(name: string): Promise { + return this.dataTable.getRowByName(name).element(by.css('[title="Name"] div.search-file-name')).getText(); + } + + async getDescription(name: string): Promise { + return this.dataTable.getRowByName(name).element(by.css('[title="Description"]')).getText(); + } + + async getModified(name: string): Promise { + return BrowserActions.getAttribute(this.dataTable.getRowByName(name).element(by.css('[title="Modified"] span')), 'title'); + } + + async getSize(name: string): Promise { + return this.dataTable.getRowByName(name).element(by.css('[title="Size"]')).getText(); + } + + async getModifiedBy(name: string): Promise { + return this.dataTable.getRowByName(name).element(by.css('[title="Modified by"]')).getText(); + } + + async getLocation(name: string): Promise { + return this.dataTable.getRowByName(name).element(by.css('[title="Name"] a')).getText(); + } + async getResultsChipsValues(): Promise { const chips = this.chipList.all(by.css('.mat-chip')); return chips.map(async (elem) => { diff --git a/src/app/components/common/location-link/location-link.component.ts b/src/app/components/common/location-link/location-link.component.ts index e84733897..b8fe70684 100644 --- a/src/app/components/common/location-link/location-link.component.ts +++ b/src/app/components/common/location-link/location-link.component.ts @@ -35,8 +35,13 @@ import { TranslationService } from '@alfresco/adf-core'; @Component({ selector: 'aca-location-link', template: ` - - {{ displayText | async | translate }} + `, changeDetection: ChangeDetectionStrategy.OnPush, @@ -49,18 +54,13 @@ export class LocationLinkComponent implements OnInit { private _path: PathInfo; nodeLocation$ = new BehaviorSubject(''); + displayText: Observable; @Input() context: any; @Input() - link: any[]; - - @Input() - displayText: Observable; - - @Input() - tooltip: Observable; + showLocation = false; @HostListener('mouseenter') onMouseEnter() { @@ -83,7 +83,11 @@ export class LocationLinkComponent implements OnInit { const path = node.entry.path; if (path && path.name && path.elements) { - this.displayText = this.getDisplayText(path); + if (this.showLocation) { + this.displayText = of(path.name.substring(1).replace(/\//g, ' › ')); + } else { + this.displayText = this.getDisplayText(path); + } this._path = path; } else { this.displayText = of('APP.BROWSE.SEARCH.UNKNOWN_LOCATION'); diff --git a/src/app/components/dl-custom-components/document-list-custom-components.module.ts b/src/app/components/dl-custom-components/document-list-custom-components.module.ts index 54f3b0ca3..e35445291 100644 --- a/src/app/components/dl-custom-components/document-list-custom-components.module.ts +++ b/src/app/components/dl-custom-components/document-list-custom-components.module.ts @@ -30,10 +30,11 @@ import { LockedByModule } from '@alfresco/aca-shared'; import { ContentModule } from '@alfresco/adf-content-services'; import { MaterialModule } from '../../material.module'; import { CoreModule } from '@alfresco/adf-core'; +import { ThumbnailColumnComponent } from './thumbnail-column/thumbnail-column.component'; @NgModule({ imports: [BrowserModule, CoreModule.forChild(), ContentModule.forChild(), MaterialModule, LockedByModule], - declarations: [CustomNameColumnComponent], - exports: [CustomNameColumnComponent] + declarations: [CustomNameColumnComponent, ThumbnailColumnComponent], + exports: [CustomNameColumnComponent, ThumbnailColumnComponent] }) export class DocumentListCustomComponentsModule {} diff --git a/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.html b/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.html new file mode 100644 index 000000000..affdfe62f --- /dev/null +++ b/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.html @@ -0,0 +1,7 @@ + + + diff --git a/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.ts b/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.ts new file mode 100644 index 000000000..6cf14a90d --- /dev/null +++ b/src/app/components/dl-custom-components/thumbnail-column/thumbnail-column.component.ts @@ -0,0 +1,48 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2020 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { TranslationService } from '@alfresco/adf-core'; + +@Component({ + selector: 'aca-custom-thumbnail-column', + templateUrl: './thumbnail-column.component.html', + encapsulation: ViewEncapsulation.None +}) +export class ThumbnailColumnComponent { + @Input() + context: any; + + constructor(private translation: TranslationService) {} + + getThumbnail({ data, row, col }): string { + return data.getValue(row, col); + } + + getToolTip({ row }): string { + const user = row.node?.entry?.properties && row.node.entry.properties['cm:lockOwner'] && row.node.entry.properties['cm:lockOwner'].displayName; + return user ? `${this.translation.instant('APP.LOCKED_BY')} ${user}` : ''; + } +} diff --git a/src/app/components/search/search-action-menu/search-action-menu.component.ts b/src/app/components/search/search-action-menu/search-action-menu.component.ts index f1a3a0a0b..2b16c6254 100644 --- a/src/app/components/search/search-action-menu/search-action-menu.component.ts +++ b/src/app/components/search/search-action-menu/search-action-menu.component.ts @@ -30,8 +30,7 @@ import { Component, OnInit, Output, ViewEncapsulation, EventEmitter } from '@ang @Component({ selector: 'aca-search-action-menu', templateUrl: './search-action-menu.component.html', - encapsulation: ViewEncapsulation.None, - host: { class: 'aca-search-input' } + encapsulation: ViewEncapsulation.None }) export class SearchActionMenuComponent implements OnInit { @Output() diff --git a/src/app/components/search/search-results-row/search-results-row.component.html b/src/app/components/search/search-results-row/search-results-row.component.html index a2b2b9d9e..d2ee25c15 100644 --- a/src/app/components/search/search-results-row/search-results-row.component.html +++ b/src/app/components/search/search-results-row/search-results-row.component.html @@ -1,4 +1,4 @@ -
+
{{ name$ | async }} @@ -7,25 +7,6 @@ {{ title$ | async }}
- -
{{ description }}
- -
- {{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.MODIFIED' | translate }}: - {{ modifiedAt | date: 'medium' }} - {{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.BY_USER' | translate: { user: user } }} - - | {{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.SIZE' | translate }}: - {{ size | adfFileSize }} - -
- -
- -
- -
- {{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.LOCATION' | translate }}: - +
+
diff --git a/src/app/components/search/search-results-row/search-results-row.component.scss b/src/app/components/search/search-results-row/search-results-row.component.scss index b686d17df..7f99f36d5 100644 --- a/src/app/components/search/search-results-row/search-results-row.component.scss +++ b/src/app/components/search/search-results-row/search-results-row.component.scss @@ -1,18 +1,8 @@ .aca-search-results-row { - display: flex; - flex-direction: column; - flex: 1; - height: 100%; - overflow: hidden; - min-height: 0; - .line { - margin: 5px; - } - - .bold { - font-weight: 400; - color: var(--theme-text-bold-color); + .result-location { + height: 15px; + padding-top: 3px; } .link { @@ -20,6 +10,11 @@ color: var(--theme-text-bold-color); } + .aca-location-link .adf-datatable-cell-value { + padding: 0; + color: var(--theme-foreground-text-color); + } + .link:hover, .aca-location-link .adf-datatable-cell-value:hover { color: var(--theme-primary-color) !important; diff --git a/src/app/components/search/search-results-row/search-results-row.component.ts b/src/app/components/search/search-results-row/search-results-row.component.ts index c7853048e..a329e3867 100644 --- a/src/app/components/search/search-results-row/search-results-row.component.ts +++ b/src/app/components/search/search-results-row/search-results-row.component.ts @@ -31,7 +31,6 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { AlfrescoApiService } from '@alfresco/adf-core'; import { takeUntil } from 'rxjs/operators'; import { Router } from '@angular/router'; -import { isLocked } from '@alfresco/aca-shared'; @Component({ selector: 'aca-search-results-row', @@ -89,32 +88,10 @@ export class SearchResultsRowComponent implements OnInit, OnDestroy { this.onDestroy$.complete(); } - get description(): string { - const { properties } = this.node.entry; - return properties ? properties['cm:description'] : ''; - } - - get modifiedAt(): Date { - return this.node.entry.modifiedAt; - } - - get size(): number { - const { content } = this.node.entry; - return content ? content.sizeInBytes : null; - } - - get user(): string { - return this.node.entry.modifiedByUser.displayName; - } - get isFile(): boolean { return this.node.entry.isFile; } - get isFileWriteLocked(): boolean { - return isLocked(this.node); - } - showPreview(event: Event) { event.stopPropagation(); this.store.dispatch(new ViewNodeAction(this.node.entry.id, { location: this.router.url })); diff --git a/src/app/components/search/search-results-row/search-results-row.components.spec.ts b/src/app/components/search/search-results-row/search-results-row.components.spec.ts index 9884635ec..2fd554db0 100644 --- a/src/app/components/search/search-results-row/search-results-row.components.spec.ts +++ b/src/app/components/search/search-results-row/search-results-row.components.spec.ts @@ -57,7 +57,7 @@ describe('SearchResultsRowComponent', () => { component.context = { row: { node: nodeEntry } }; fixture.detectChanges(); - const element = fixture.nativeElement.querySelector('.line'); + const element = fixture.nativeElement.querySelector('div'); expect(element).not.toBeNull(); }); }); diff --git a/src/app/components/search/search-results.module.ts b/src/app/components/search/search-results.module.ts index 86afd0412..f5f80a7c7 100644 --- a/src/app/components/search/search-results.module.ts +++ b/src/app/components/search/search-results.module.ts @@ -38,6 +38,7 @@ import { DirectivesModule } from '../../directives/directives.module'; import { AppLayoutModule } from '../layout/layout.module'; import { ContextMenuModule } from '../context-menu/context-menu.module'; import { SearchActionMenuComponent } from './search-action-menu/search-action-menu.component'; +import { DocumentListCustomComponentsModule } from '../dl-custom-components/document-list-custom-components.module'; @NgModule({ imports: [ @@ -50,7 +51,8 @@ import { SearchActionMenuComponent } from './search-action-menu/search-action-me DirectivesModule, AppLayoutModule, ContextMenuModule, - LockedByModule + LockedByModule, + DocumentListCustomComponentsModule ], declarations: [SearchResultsComponent, SearchLibrariesResultsComponent, SearchResultsRowComponent, SearchActionMenuComponent], exports: [SearchResultsComponent, SearchLibrariesResultsComponent, SearchResultsRowComponent, SearchActionMenuComponent] diff --git a/src/app/components/search/search-results/search-results.component.html b/src/app/components/search/search-results/search-results.component.html index d070c8720..f02505e82 100644 --- a/src/app/components/search/search-results/search-results.component.html +++ b/src/app/components/search/search-results/search-results.component.html @@ -22,15 +22,11 @@
-
- -
- + + + + - + + + + + + + + + + + + + {{context.row?.node?.entry?.properties && context.row?.node?.entry?.properties['cm:description']}} + + + + + + + diff --git a/src/app/components/search/search-results/search-results.component.scss b/src/app/components/search/search-results/search-results.component.scss index 8b088ebab..4cd689df9 100644 --- a/src/app/components/search/search-results/search-results.component.scss +++ b/src/app/components/search/search-results/search-results.component.scss @@ -100,29 +100,16 @@ } .adf-datatable { - .adf-datatable-row { - padding: 0 15px; + + aca-search-action-menu button { + width: 0; } - .adf-datatable-cell { - margin: 0 5px; - padding-left: 5px; - padding-top: 9px; - padding-bottom: 9px; - min-height: auto; - - .adf-datatable-cell-value { - display: inline-block; - padding: 0; - } + .aca-location-link a { + font-size: 12px; + max-width: 350px; + text-align: left; + direction: rtl; } } - - .adf-search-date-range .mat-form-field:not(:first-child) { - margin-top: 20px; - } - - .adf-document-list { - overflow-y: auto; - } } diff --git a/src/app/components/search/search-results/search-results.component.ts b/src/app/components/search/search-results/search-results.component.ts index 628269b89..ae317e0d3 100644 --- a/src/app/components/search/search-results/search-results.component.ts +++ b/src/app/components/search/search-results/search-results.component.ts @@ -40,11 +40,12 @@ import { SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { ContentManagementService } from '../../../services/content-management.service'; -import { ShowHeaderMode, TranslationService } from '@alfresco/adf-core'; +import { TranslationService } from '@alfresco/adf-core'; import { combineLatest, Observable } from 'rxjs'; import { AppExtensionService } from '@alfresco/aca-shared'; import { SearchSortingDefinition } from '@alfresco/adf-content-services/lib/search/models/search-sorting-definition.interface'; import { takeUntil } from 'rxjs/operators'; +import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; @Component({ selector: 'aca-search-results', @@ -61,7 +62,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit { data: ResultSetPaging; sorting = ['name', 'asc']; isLoading = false; - showHeader: ShowHeaderMode = ShowHeaderMode.Never; + isSmallScreen = false; constructor( private queryBuilder: SearchQueryBuilderService, @@ -70,7 +71,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit { extensions: AppExtensionService, content: ContentManagementService, private translationService: TranslationService, - private router: Router + private router: Router, + private breakpointObserver: BreakpointObserver ) { super(store, extensions, content); @@ -90,6 +92,13 @@ export class SearchResultsComponent extends PageComponent implements OnInit { this.queryBuilder.userQuery = decodeURIComponent(query); } }); + + this.breakpointObserver + .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) + .pipe(takeUntil(this.onDestroy$)) + .subscribe((result) => { + this.isSmallScreen = result.matches; + }); } ngOnInit() {