[ADF-5419][aca] support multiple search form (#2173)

* [ADF-5419][aca] support multiple search form

* * revert app config and tests added

* * removed unused key

* * resolved conflicts and upgrade adf

* * fix e2e

* * update defalut name
This commit is contained in:
Dharan
2021-06-24 13:28:47 +05:30
committed by GitHub
parent 3d14a28e84
commit c87dad1ade
7 changed files with 72 additions and 85 deletions

View File

@@ -150,13 +150,13 @@ describe('Empty list views', () => {
expect(await pagination.isNextButtonPresent()).toBe(false, 'Next button is present'); expect(await pagination.isNextButtonPresent()).toBe(false, 'Next button is present');
}); });
it('[C279189] Search filters panel is not displayed on empty Search Results page', async () => { it('[C279189] Search filters panel is displayed on empty Search Results page', async () => {
await searchInput.clickSearchButton(); await searchInput.clickSearchButton();
/* cspell:disable-next-line */ /* cspell:disable-next-line */
await searchInput.searchFor('qwertyuiop'); await searchInput.searchFor('qwertyuiop');
await dataTable.waitForBody(); await dataTable.waitForBody();
expect(await searchResultsPage.filters.isSearchFiltersPanelDisplayed()).toBe(false, 'Search filters panel is present'); expect(await searchResultsPage.filters.isSearchFiltersPanelDisplayed()).toBe(true, 'Search filters panel is not present');
}); });
it('[C290020] Empty Search results - Libraries', async () => { it('[C290020] Empty Search results - Libraries', async () => {

View File

@@ -189,7 +189,7 @@
"multi-value-pipe-separator": ", ", "multi-value-pipe-separator": ", ",
"multi-value-chips": true "multi-value-chips": true
}, },
"search": { "search": [{
"filterWithContains": true, "filterWithContains": true,
"aca:fields": ["cm:name", "cm:title", "cm:description", "TEXT", "TAG"], "aca:fields": ["cm:name", "cm:title", "cm:description", "TEXT", "TAG"],
"include": ["path", "allowableOperations", "properties"], "include": ["path", "allowableOperations", "properties"],
@@ -372,8 +372,10 @@
} }
} }
} }
] ],
}, "name": "APP.BROWSE.SEARCH.DEFAULT_SEARCH",
"default": true
}],
"search-headers": { "search-headers": {
"filterWithContains": true, "filterWithContains": true,
"app:fields": [ "app:fields": [

View File

@@ -15,7 +15,7 @@
#searchFilter #searchFilter
[ngClass]="{ [ngClass]="{
'adf-search-filter--hidden': 'adf-search-filter--hidden':
hideSearchFilter() || !(showFacetFilter$ | async) !(showFacetFilter$ | async)
}" }"
></adf-search-filter> ></adf-search-filter>
<div class="adf-search-results__content"> <div class="adf-search-results__content">
@@ -25,10 +25,8 @@
mode="indeterminate" mode="indeterminate"
> >
</mat-progress-bar> </mat-progress-bar>
<div <div class="adf-search-results__content-header content">
class="adf-search-results__content-header content" <adf-search-form (formChange)="onFormChange($event)"></adf-search-form>
*ngIf="data?.list.entries.length"
>
<div class="content__side--left"> <div class="content__side--left">
<div <div
class="adf-search-results--info-text" class="adf-search-results--info-text"

View File

@@ -81,6 +81,8 @@
&__side--left { &__side--left {
@include flex-column; @include flex-column;
height: unset; height: unset;
padding-right: 15px;
padding-left: 15px;
} }
} }

View File

@@ -23,17 +23,18 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { SearchResultsComponent } from './search-results.component'; import { SearchResultsComponent } from './search-results.component';
import { AppTestingModule } from '../../../testing/app-testing.module'; import { AppTestingModule } from '../../../testing/app-testing.module';
import { AppSearchResultsModule } from '../search-results.module'; import { AppSearchResultsModule } from '../search-results.module';
import { CoreModule, AppConfigService, AlfrescoApiService, TranslationService } from '@alfresco/adf-core'; import { AlfrescoApiService, AppConfigService, CoreModule, TranslationService } from '@alfresco/adf-core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NavigateToFolder, SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { NavigateToFolder, SnackbarErrorAction } from '@alfresco/aca-shared/store';
import { Pagination, SearchRequest } from '@alfresco/js-api'; import { Pagination, SearchRequest } from '@alfresco/js-api';
import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs';
describe('SearchComponent', () => { describe('SearchComponent', () => {
let component: SearchResultsComponent; let component: SearchResultsComponent;
@@ -45,8 +46,10 @@ describe('SearchComponent', () => {
let translate: TranslationService; let translate: TranslationService;
let router: Router; let router: Router;
const searchRequest = {} as SearchRequest; const searchRequest = {} as SearchRequest;
let params: BehaviorSubject<any>;
beforeEach(() => { beforeEach(() => {
params = new BehaviorSubject({ q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), CoreModule.forRoot(), AppTestingModule, AppSearchResultsModule], imports: [TranslateModule.forRoot(), CoreModule.forRoot(), AppTestingModule, AppSearchResultsModule],
providers: [ providers: [
@@ -58,11 +61,7 @@ describe('SearchComponent', () => {
sortingPreferenceKey: '' sortingPreferenceKey: ''
} }
}, },
params: [ params: params.asObservable()
{
q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29'
}
]
} }
} }
] ]
@@ -75,6 +74,10 @@ describe('SearchComponent', () => {
translate = TestBed.inject(TranslationService); translate = TestBed.inject(TranslationService);
router = TestBed.inject(Router); router = TestBed.inject(Router);
config.config = {
search: {}
};
fixture = TestBed.createComponent(SearchResultsComponent); fixture = TestBed.createComponent(SearchResultsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@@ -83,6 +86,10 @@ describe('SearchComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
afterEach(() => {
params.complete();
});
it('should raise an error if search fails', fakeAsync(() => { it('should raise an error if search fails', fakeAsync(() => {
spyOn(alfrescoApi.searchApi, 'search').and.returnValue( spyOn(alfrescoApi.searchApi, 'search').and.returnValue(
Promise.reject({ Promise.reject({
@@ -165,47 +172,23 @@ describe('SearchComponent', () => {
}); });
it('should format user input according to the configuration fields', () => { it('should format user input according to the configuration fields', () => {
config.config = { const query = component.formatSearchQuery('hello', ['cm:name', 'cm:title']);
search: {
'aca:fields': ['cm:name', 'cm:title']
}
};
const query = component.formatSearchQuery('hello');
expect(query).toBe(`(cm:name:"hello*" OR cm:title:"hello*")`); expect(query).toBe(`(cm:name:"hello*" OR cm:title:"hello*")`);
}); });
it('should format user input as cm:name if configuration not provided', () => { it('should format user input as cm:name if configuration not provided', () => {
config.config = { const query = component.formatSearchQuery('hello', undefined);
search: {
'aca:fields': undefined
}
};
const query = component.formatSearchQuery('hello');
expect(query).toBe(`(cm:name:"hello*")`); expect(query).toBe(`(cm:name:"hello*")`);
}); });
it('should use AND operator when conjunction has no operators', () => { it('should use AND operator when conjunction has no operators', () => {
config.config = { const query = component.formatSearchQuery('big yellow banana', ['cm:name']);
search: {
'aca:fields': ['cm:name']
}
};
const query = component.formatSearchQuery('big yellow banana');
expect(query).toBe(`(cm:name:"big*") AND (cm:name:"yellow*") AND (cm:name:"banana*")`); expect(query).toBe(`(cm:name:"big*") AND (cm:name:"yellow*") AND (cm:name:"banana*")`);
}); });
it('should support conjunctions with AND operator', () => { it('should support conjunctions with AND operator', () => {
config.config = { const query = component.formatSearchQuery('big AND yellow AND banana', ['cm:name', 'cm:title']);
search: {
'aca:fields': ['cm:name', 'cm:title']
}
};
const query = component.formatSearchQuery('big AND yellow AND banana');
expect(query).toBe( expect(query).toBe(
`(cm:name:"big*" OR cm:title:"big*") AND (cm:name:"yellow*" OR cm:title:"yellow*") AND (cm:name:"banana*" OR cm:title:"banana*")` `(cm:name:"big*" OR cm:title:"big*") AND (cm:name:"yellow*" OR cm:title:"yellow*") AND (cm:name:"banana*" OR cm:title:"banana*")`
@@ -213,13 +196,7 @@ describe('SearchComponent', () => {
}); });
it('should support conjunctions with OR operator', () => { it('should support conjunctions with OR operator', () => {
config.config = { const query = component.formatSearchQuery('big OR yellow OR banana', ['cm:name', 'cm:title']);
search: {
'aca:fields': ['cm:name', 'cm:title']
}
};
const query = component.formatSearchQuery('big OR yellow OR banana');
expect(query).toBe( expect(query).toBe(
`(cm:name:"big*" OR cm:title:"big*") OR (cm:name:"yellow*" OR cm:title:"yellow*") OR (cm:name:"banana*" OR cm:title:"banana*")` `(cm:name:"big*" OR cm:title:"big*") OR (cm:name:"yellow*" OR cm:title:"yellow*") OR (cm:name:"banana*" OR cm:title:"banana*")`
@@ -227,25 +204,13 @@ describe('SearchComponent', () => {
}); });
it('should support exact term matching with default fields', () => { it('should support exact term matching with default fields', () => {
config.config = { const query = component.formatSearchQuery('=orange', ['cm:name', 'cm:title']);
search: {
'aca:fields': ['cm:name', 'cm:title']
}
};
const query = component.formatSearchQuery('=orange');
expect(query).toBe(`(=cm:name:"orange" OR =cm:title:"orange")`); expect(query).toBe(`(=cm:name:"orange" OR =cm:title:"orange")`);
}); });
it('should support exact term matching with operators', () => { it('should support exact term matching with operators', () => {
config.config = { const query = component.formatSearchQuery('=test1.pdf or =test2.pdf', ['cm:name', 'cm:title']);
search: {
'aca:fields': ['cm:name', 'cm:title']
}
};
const query = component.formatSearchQuery('=test1.pdf or =test2.pdf');
expect(query).toBe(`(=cm:name:"test1.pdf" OR =cm:title:"test1.pdf") or (=cm:name:"test2.pdf" OR =cm:title:"test2.pdf")`); expect(query).toBe(`(=cm:name:"test1.pdf" OR =cm:title:"test1.pdf") or (=cm:name:"test2.pdf" OR =cm:title:"test2.pdf")`);
}); });
@@ -294,4 +259,17 @@ describe('SearchComponent', () => {
}); });
expect(queryBuilder.update).toHaveBeenCalled(); expect(queryBuilder.update).toHaveBeenCalled();
}); });
it('should update the user query whenever param changed', () => {
params.next({ q: '=orange' });
expect(queryBuilder.userQuery).toBe(`((=cm:name:"orange"))`);
expect(queryBuilder.update).toHaveBeenCalled();
});
it('should update the user query whenever configuration changed', () => {
params.next({ q: '=orange' });
queryBuilder.configUpdated.next({ 'aca:fields': ['cm:tag'] } as any);
expect(queryBuilder.userQuery).toBe(`((=cm:tag:"orange"))`);
expect(queryBuilder.update).toHaveBeenCalled();
});
}); });

View File

@@ -24,25 +24,26 @@
*/ */
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { Pagination, MinimalNodeEntity, ResultSetPaging } from '@alfresco/js-api'; import { MinimalNodeEntity, Pagination, ResultSetPaging } from '@alfresco/js-api';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { SearchQueryBuilderService, SearchFilterComponent } from '@alfresco/adf-content-services'; import { SearchFilterComponent, SearchForm, SearchQueryBuilderService } from '@alfresco/adf-content-services';
import { PageComponent } from '../../page.component'; import { PageComponent } from '../../page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { import {
AppStore, AppStore,
NavigateToFolder,
SnackbarErrorAction,
showFacetFilter,
infoDrawerPreview, infoDrawerPreview,
ShowInfoDrawerPreviewAction, NavigateToFolder,
SetInfoDrawerPreviewStateAction,
SetInfoDrawerStateAction, SetInfoDrawerStateAction,
SetInfoDrawerPreviewStateAction showFacetFilter,
ShowInfoDrawerPreviewAction,
SnackbarErrorAction
} from '@alfresco/aca-shared/store'; } from '@alfresco/aca-shared/store';
import { ContentManagementService } from '../../../services/content-management.service'; import { ContentManagementService } from '../../../services/content-management.service';
import { AppConfigService, TranslationService } from '@alfresco/adf-core'; import { TranslationService } from '@alfresco/adf-core';
import { Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { AppExtensionService } from '@alfresco/aca-shared'; import { AppExtensionService } from '@alfresco/aca-shared';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'aca-search-results', selector: 'aca-search-results',
@@ -68,7 +69,6 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
constructor( constructor(
private queryBuilder: SearchQueryBuilderService, private queryBuilder: SearchQueryBuilderService,
private route: ActivatedRoute, private route: ActivatedRoute,
private config: AppConfigService,
store: Store<AppStore>, store: Store<AppStore>,
extensions: AppExtensionService, extensions: AppExtensionService,
content: ContentManagementService, content: ContentManagementService,
@@ -84,11 +84,21 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
this.showFacetFilter$ = store.select(showFacetFilter); this.showFacetFilter$ = store.select(showFacetFilter);
this.infoDrawerPreview$ = store.select(infoDrawerPreview); this.infoDrawerPreview$ = store.select(infoDrawerPreview);
combineLatest([this.route.params, this.queryBuilder.configUpdated])
.pipe(takeUntil(this.onDestroy$))
.subscribe(([params, searchConfig]) => {
this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
const query = this.formatSearchQuery(this.searchedWord, searchConfig['aca:fields']);
if (query) {
this.queryBuilder.userQuery = decodeURIComponent(query);
}
});
} }
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
this.queryBuilder.resetToDefaults();
this.sorting = this.getSorting(); this.sorting = this.getSorting();
this.subscriptions.push( this.subscriptions.push(
@@ -114,10 +124,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
if (this.route) { if (this.route) {
this.route.params.forEach((params: Params) => { this.route.params.forEach((params: Params) => {
this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null; this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
const query = this.formatSearchQuery(this.searchedWord); if (this.searchedWord) {
if (query) {
this.queryBuilder.userQuery = decodeURIComponent(query);
this.queryBuilder.update(); this.queryBuilder.update();
} else { } else {
this.queryBuilder.userQuery = null; this.queryBuilder.userQuery = null;
@@ -165,7 +172,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
return '(' + fields.map((field) => `${prefix}${field}:"${term}${suffix}"`).join(' OR ') + ')'; return '(' + fields.map((field) => `${prefix}${field}:"${term}${suffix}"`).join(' OR ') + ')';
} }
formatSearchQuery(userInput: string) { formatSearchQuery(userInput: string, fields = ['cm:name']) {
if (!userInput) { if (!userInput) {
return null; return null;
} }
@@ -176,7 +183,6 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
return userInput; return userInput;
} }
const fields = this.config.get<string[]>('search.aca:fields', ['cm:name']);
const words = userInput.split(' '); const words = userInput.split(' ');
if (words.length > 1) { if (words.length > 1) {
@@ -247,8 +253,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
} }
} }
hideSearchFilter() { onFormChange(form: SearchForm) {
return !this.totalResults && !this.hasSelectedFilters; this.queryBuilder.updateSelectedConfiguration(form.index);
} }
onPreviewClosed() { onPreviewClosed() {

View File

@@ -167,7 +167,8 @@
"TITLE": "About" "TITLE": "About"
}, },
"SEARCH": { "SEARCH": {
"TITLE": "Search Results", "DEFAULT_SEARCH": "Default",
"TITLE": "Search Results",
"FOUND_RESULTS": "{{ number }} results found", "FOUND_RESULTS": "{{ number }} results found",
"FOUND_ONE_RESULT": "{{ number }} result found", "FOUND_ONE_RESULT": "{{ number }} result found",
"CUSTOM_ROW": { "CUSTOM_ROW": {