diff --git a/e2e/suites/list-views/empty-list.test.ts b/e2e/suites/list-views/empty-list.test.ts index 28906af9f..71bf510d3 100755 --- a/e2e/suites/list-views/empty-list.test.ts +++ b/e2e/suites/list-views/empty-list.test.ts @@ -150,13 +150,13 @@ describe('Empty list views', () => { 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(); /* cspell:disable-next-line */ await searchInput.searchFor('qwertyuiop'); 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 () => { diff --git a/src/app.config.json b/src/app.config.json index a80eb1592..8b923c3b4 100644 --- a/src/app.config.json +++ b/src/app.config.json @@ -189,7 +189,7 @@ "multi-value-pipe-separator": ", ", "multi-value-chips": true }, - "search": { + "search": [{ "filterWithContains": true, "aca:fields": ["cm:name", "cm:title", "cm:description", "TEXT", "TAG"], "include": ["path", "allowableOperations", "properties"], @@ -372,8 +372,10 @@ } } } - ] - }, + ], + "name": "APP.BROWSE.SEARCH.DEFAULT_SEARCH", + "default": true + }], "search-headers": { "filterWithContains": true, "app:fields": [ 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 04350b64d..32efe71dc 100644 --- a/src/app/components/search/search-results/search-results.component.html +++ b/src/app/components/search/search-results/search-results.component.html @@ -15,7 +15,7 @@ #searchFilter [ngClass]="{ 'adf-search-filter--hidden': - hideSearchFilter() || !(showFacetFilter$ | async) + !(showFacetFilter$ | async) }" >
@@ -25,10 +25,8 @@ mode="indeterminate" > -
+
+
. */ -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 { AppTestingModule } from '../../../testing/app-testing.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 { NavigateToFolder, SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { Pagination, SearchRequest } from '@alfresco/js-api'; import { SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { BehaviorSubject } from 'rxjs'; describe('SearchComponent', () => { let component: SearchResultsComponent; @@ -45,8 +46,10 @@ describe('SearchComponent', () => { let translate: TranslationService; let router: Router; const searchRequest = {} as SearchRequest; + let params: BehaviorSubject; beforeEach(() => { + params = new BehaviorSubject({ q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' }); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), CoreModule.forRoot(), AppTestingModule, AppSearchResultsModule], providers: [ @@ -58,11 +61,7 @@ describe('SearchComponent', () => { sortingPreferenceKey: '' } }, - params: [ - { - q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' - } - ] + params: params.asObservable() } } ] @@ -75,6 +74,10 @@ describe('SearchComponent', () => { translate = TestBed.inject(TranslationService); router = TestBed.inject(Router); + config.config = { + search: {} + }; + fixture = TestBed.createComponent(SearchResultsComponent); component = fixture.componentInstance; @@ -83,6 +86,10 @@ describe('SearchComponent', () => { fixture.detectChanges(); }); + afterEach(() => { + params.complete(); + }); + it('should raise an error if search fails', fakeAsync(() => { spyOn(alfrescoApi.searchApi, 'search').and.returnValue( Promise.reject({ @@ -165,47 +172,23 @@ describe('SearchComponent', () => { }); it('should format user input according to the configuration fields', () => { - config.config = { - search: { - 'aca:fields': ['cm:name', 'cm:title'] - } - }; - - const query = component.formatSearchQuery('hello'); + const query = component.formatSearchQuery('hello', ['cm:name', 'cm:title']); expect(query).toBe(`(cm:name:"hello*" OR cm:title:"hello*")`); }); it('should format user input as cm:name if configuration not provided', () => { - config.config = { - search: { - 'aca:fields': undefined - } - }; - - const query = component.formatSearchQuery('hello'); + const query = component.formatSearchQuery('hello', undefined); expect(query).toBe(`(cm:name:"hello*")`); }); it('should use AND operator when conjunction has no operators', () => { - config.config = { - search: { - 'aca:fields': ['cm:name'] - } - }; - - const query = component.formatSearchQuery('big yellow banana'); + const query = component.formatSearchQuery('big yellow banana', ['cm:name']); expect(query).toBe(`(cm:name:"big*") AND (cm:name:"yellow*") AND (cm:name:"banana*")`); }); it('should support conjunctions with AND operator', () => { - config.config = { - search: { - 'aca:fields': ['cm:name', 'cm:title'] - } - }; - - const query = component.formatSearchQuery('big AND yellow AND banana'); + const query = component.formatSearchQuery('big AND yellow AND banana', ['cm:name', 'cm:title']); 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*")` @@ -213,13 +196,7 @@ describe('SearchComponent', () => { }); it('should support conjunctions with OR operator', () => { - config.config = { - search: { - 'aca:fields': ['cm:name', 'cm:title'] - } - }; - - const query = component.formatSearchQuery('big OR yellow OR banana'); + const query = component.formatSearchQuery('big OR yellow OR banana', ['cm:name', 'cm:title']); 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*")` @@ -227,25 +204,13 @@ describe('SearchComponent', () => { }); it('should support exact term matching with default fields', () => { - config.config = { - search: { - 'aca:fields': ['cm:name', 'cm:title'] - } - }; - - const query = component.formatSearchQuery('=orange'); + const query = component.formatSearchQuery('=orange', ['cm:name', 'cm:title']); expect(query).toBe(`(=cm:name:"orange" OR =cm:title:"orange")`); }); it('should support exact term matching with operators', () => { - config.config = { - search: { - 'aca:fields': ['cm:name', 'cm:title'] - } - }; - - const query = component.formatSearchQuery('=test1.pdf or =test2.pdf'); + const query = component.formatSearchQuery('=test1.pdf or =test2.pdf', ['cm:name', 'cm:title']); 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(); }); + + 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(); + }); }); 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 c71dac3bd..7e8f23b7b 100644 --- a/src/app/components/search/search-results/search-results.component.ts +++ b/src/app/components/search/search-results/search-results.component.ts @@ -24,25 +24,26 @@ */ 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 { SearchQueryBuilderService, SearchFilterComponent } from '@alfresco/adf-content-services'; +import { SearchFilterComponent, SearchForm, SearchQueryBuilderService } from '@alfresco/adf-content-services'; import { PageComponent } from '../../page.component'; import { Store } from '@ngrx/store'; import { AppStore, - NavigateToFolder, - SnackbarErrorAction, - showFacetFilter, infoDrawerPreview, - ShowInfoDrawerPreviewAction, + NavigateToFolder, + SetInfoDrawerPreviewStateAction, SetInfoDrawerStateAction, - SetInfoDrawerPreviewStateAction + showFacetFilter, + ShowInfoDrawerPreviewAction, + SnackbarErrorAction } from '@alfresco/aca-shared/store'; import { ContentManagementService } from '../../../services/content-management.service'; -import { AppConfigService, TranslationService } from '@alfresco/adf-core'; -import { Observable } from 'rxjs'; +import { TranslationService } from '@alfresco/adf-core'; +import { combineLatest, Observable } from 'rxjs'; import { AppExtensionService } from '@alfresco/aca-shared'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'aca-search-results', @@ -68,7 +69,6 @@ export class SearchResultsComponent extends PageComponent implements OnInit { constructor( private queryBuilder: SearchQueryBuilderService, private route: ActivatedRoute, - private config: AppConfigService, store: Store, extensions: AppExtensionService, content: ContentManagementService, @@ -84,11 +84,21 @@ export class SearchResultsComponent extends PageComponent implements OnInit { this.showFacetFilter$ = store.select(showFacetFilter); 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() { super.ngOnInit(); + this.queryBuilder.resetToDefaults(); this.sorting = this.getSorting(); this.subscriptions.push( @@ -114,10 +124,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit { if (this.route) { this.route.params.forEach((params: Params) => { this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null; - const query = this.formatSearchQuery(this.searchedWord); - - if (query) { - this.queryBuilder.userQuery = decodeURIComponent(query); + if (this.searchedWord) { this.queryBuilder.update(); } else { 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 ') + ')'; } - formatSearchQuery(userInput: string) { + formatSearchQuery(userInput: string, fields = ['cm:name']) { if (!userInput) { return null; } @@ -176,7 +183,6 @@ export class SearchResultsComponent extends PageComponent implements OnInit { return userInput; } - const fields = this.config.get('search.aca:fields', ['cm:name']); const words = userInput.split(' '); if (words.length > 1) { @@ -247,8 +253,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit { } } - hideSearchFilter() { - return !this.totalResults && !this.hasSelectedFilters; + onFormChange(form: SearchForm) { + this.queryBuilder.updateSelectedConfiguration(form.index); } onPreviewClosed() { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index cccf994fa..e0c1bf278 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -167,7 +167,8 @@ "TITLE": "About" }, "SEARCH": { - "TITLE": "Search Results", + "DEFAULT_SEARCH": "Default", + "TITLE": "Search Results", "FOUND_RESULTS": "{{ number }} results found", "FOUND_ONE_RESULT": "{{ number }} result found", "CUSTOM_ROW": {