[ACA-4511] search layout update (#2272)

* [ACA-4511] search layout update

* * separate description column

* * enable path and fix e2e

* * revert lock  changes
This commit is contained in:
Dharan 2021-08-26 15:04:26 +05:30 committed by GitHub
parent 63dc24494b
commit 5212a40278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 180 additions and 147 deletions

View File

@ -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`
);

View File

@ -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 () => {

View File

@ -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<string> {
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<number> {
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<string> {
return this.getSearchResultNthLine(name, location, 0).getText();
}
async getSearchResultDescription(name: string, location: string = ''): Promise<string> {
return this.getSearchResultNthLine(name, location, 1).getText();
}
async getSearchResultModified(name: string, location: string = ''): Promise<string> {
return this.getSearchResultNthLine(name, location, 2).getText();
}
async getSearchResultLocation(name: string, location: string = ''): Promise<string> {
return this.getSearchResultNthLine(name, location, 3).getText();
}
private getSearchResultNameLink(itemName: string, location: string = ''): ElementFinder {
return this.getSearchResultsRowByName(itemName, location).$('.link');
}

View File

@ -45,6 +45,30 @@ export class SearchResultsPage extends BrowsingPage {
return this.infoText.getText();
}
async getName(name: string): Promise<string> {
return this.dataTable.getRowByName(name).element(by.css('[title="Name"] div.search-file-name')).getText();
}
async getDescription(name: string): Promise<string> {
return this.dataTable.getRowByName(name).element(by.css('[title="Description"]')).getText();
}
async getModified(name: string): Promise<string> {
return BrowserActions.getAttribute(this.dataTable.getRowByName(name).element(by.css('[title="Modified"] span')), 'title');
}
async getSize(name: string): Promise<string> {
return this.dataTable.getRowByName(name).element(by.css('[title="Size"]')).getText();
}
async getModifiedBy(name: string): Promise<string> {
return this.dataTable.getRowByName(name).element(by.css('[title="Modified by"]')).getText();
}
async getLocation(name: string): Promise<string> {
return this.dataTable.getRowByName(name).element(by.css('[title="Name"] a')).getText();
}
async getResultsChipsValues(): Promise<string[]> {
const chips = this.chipList.all(by.css('.mat-chip'));
return chips.map(async (elem) => {

View File

@ -35,8 +35,13 @@ import { TranslationService } from '@alfresco/adf-core';
@Component({
selector: 'aca-location-link',
template: `
<a href="" [title]="nodeLocation$ | async" (click)="goToLocation()" class="adf-datatable-cell-value">
{{ displayText | async | translate }}
<a
href=""
[title]="nodeLocation$ | async"
(click)="goToLocation()"
class="adf-datatable-cell-value"
[innerHTML]="displayText | async | translate"
>
</a>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@ -49,18 +54,13 @@ export class LocationLinkComponent implements OnInit {
private _path: PathInfo;
nodeLocation$ = new BehaviorSubject('');
displayText: Observable<string>;
@Input()
context: any;
@Input()
link: any[];
@Input()
displayText: Observable<string>;
@Input()
tooltip: Observable<string>;
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, ' &#8250; '));
} else {
this.displayText = this.getDisplayText(path);
}
this._path = path;
} else {
this.displayText = of('APP.BROWSE.SEARCH.UNKNOWN_LOCATION');

View File

@ -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 {}

View File

@ -0,0 +1,7 @@
<mat-icon class="adf-datatable-selected" *ngIf="context.row.isSelected" svgIcon="selected"></mat-icon>
<img *ngIf="!context.row.isSelected"
class="adf-datatable-center-img-ie"
src="{{ getThumbnail(context) }}"
[alt]="getToolTip(context)"
[matTooltip]="getToolTip(context)">

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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}` : '';
}
}

View File

@ -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()

View File

@ -1,4 +1,4 @@
<div class="line">
<div class="search-file-name">
<span tabindex="0" role="link" *ngIf="isFile" (click)="showPreview($event)" (keyup.enter)="showPreview($event)" class="link">
{{ name$ | async }}
</span>
@ -7,25 +7,6 @@
</span>
<span>{{ title$ | async }}</span>
</div>
<div *ngIf="description" class="line">{{ description }}</div>
<div class="line">
{{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.MODIFIED' | translate }}:
{{ modifiedAt | date: 'medium' }}
{{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.BY_USER' | translate: { user: user } }}
<span *ngIf="size"
>| {{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.SIZE' | translate }}:
{{ size | adfFileSize }}
</span>
</div>
<div class="line" *ngIf="isFile && isFileWriteLocked">
<aca-locked-by [node]="context.row.node"></aca-locked-by>
</div>
<div class="line">
{{ 'APP.BROWSE.SEARCH.CUSTOM_ROW.LOCATION' | translate }}:
<aca-location-link [context]="context"></aca-location-link>
<div class="result-location">
<aca-location-link [context]="context" [showLocation]="true"></aca-location-link>
</div>

View File

@ -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;

View File

@ -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 }));

View File

@ -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();
});
});

View File

@ -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]

View File

@ -22,15 +22,11 @@
<button mat-button adf-reset-search class="content__reset-action"><mat-icon> refresh </mat-icon></button>
</div>
<div>
<aca-search-action-menu (sortingSelected)="onSearchSortingUpdate($event)"></aca-search-action-menu>
</div>
<adf-document-list
#documentList
acaDocumentList
acaContextActions
[showHeader]="showHeader"
[selectionMode]="'multiple'"
[sortingMode]="'server'"
[sorting]="sorting"
@ -39,13 +35,35 @@
(node-dblclick)="handleNodeClick($event)"
>
<data-columns>
<data-column [key]="'$thumbnail'" [type]="'image'" [sr-title]="'ADF-DOCUMENT-LIST.LAYOUT.THUMBNAIL'" [sortable]="false"> </data-column>
<data-column key="$thumbnail" type="image" [sr-title]="'ADF-DOCUMENT-LIST.LAYOUT.THUMBNAIL'" [sortable]="false">
<ng-template let-context>
<aca-custom-thumbnail-column [context]="context"></aca-custom-thumbnail-column>
</ng-template>
<data-column key type="text">
<adf-data-column-header>
<ng-template>
<aca-search-action-menu (sortingSelected)="onSearchSortingUpdate($event)"></aca-search-action-menu>
</ng-template>
</adf-data-column-header>
</data-column>
<data-column key type="text" class="adf-ellipsis-cell adf-expand-cell-5" title="APP.DOCUMENT_LIST.COLUMNS.NAME" [sortable]="false">
<ng-template let-context>
<aca-search-results-row [context]="context"></aca-search-results-row>
</ng-template>
</data-column>
<data-column key="properties" title="Description" class="adf-expand-cell-3" [sortable]="false" *ngIf="!isSmallScreen">
<ng-template let-context>
<span class="adf-datatable-cell-value adf-ellipsis-cell">
{{context.row?.node?.entry?.properties && context.row?.node?.entry?.properties['cm:description']}}
</span>
</ng-template>
</data-column>
<data-column key="content.sizeInBytes" type="fileSize" title="APP.DOCUMENT_LIST.COLUMNS.SIZE" class="adf-no-grow-cell adf-ellipsis-cell" [sortable]="false" *ngIf="!isSmallScreen"></data-column>
<data-column key="modifiedAt" type="date" title="APP.DOCUMENT_LIST.COLUMNS.MODIFIED_ON" class="adf-no-grow-cell adf-ellipsis-cell" format="timeAgo" [sortable]="false" *ngIf="!isSmallScreen"></data-column>
<data-column key="modifiedByUser.displayName" title="APP.DOCUMENT_LIST.COLUMNS.MODIFIED_BY" class="adf-no-grow-cell adf-ellipsis-cell" [sortable]="false" *ngIf="!isSmallScreen"></data-column>
</data-columns>
<adf-custom-empty-content-template>

View File

@ -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;
}
}

View File

@ -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() {