diff --git a/demo-shell/src/app.config.json b/demo-shell/src/app.config.json index ca29655209..70b878bd22 100644 --- a/demo-shell/src/app.config.json +++ b/demo-shell/src/app.config.json @@ -459,7 +459,8 @@ "settings": { "pattern": "cm:name:'(.*?)'", "field": "cm:name", - "placeholder": "Enter the name" + "placeholder": "Enter the name", + "searchSuffix" : "*" } } }, diff --git a/docs/content-services/components/search-text.component.md b/docs/content-services/components/search-text.component.md index c94d6d65be..aa8e26489a 100644 --- a/docs/content-services/components/search-text.component.md +++ b/docs/content-services/components/search-text.component.md @@ -25,6 +25,8 @@ Implements a text input [widget](../../../lib/testing/src/lib/core/pages/form/wi "component": { "selector": "text", "settings": { + "searchPrefix": "", + "searchSuffix": "", "pattern": "cm:name:'(.*?)'", "field": "cm:name", "placeholder": "Enter the name" @@ -43,6 +45,8 @@ Implements a text input [widget](../../../lib/testing/src/lib/core/pages/form/wi | field | string | Field to apply the query fragment to. Required value | | pattern | string | Regular expression pattern to restrict the format of the input text | | placeholder | string | Text displayed in the [widget](../../../lib/testing/src/lib/core/pages/form/widgets/widget.ts) when the input string is empty | +| searchSuffix | string | Text to append always in the search of a string| +| searchPrefix | string | Text to prepend always in the search of a string| ## Details diff --git a/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts b/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts index c9c887e8c0..6c14ebfa33 100644 --- a/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts +++ b/lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts @@ -43,6 +43,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit { options: SearchFilterList; operator: string = 'OR'; pageSize = 5; + isActive = false; constructor() { this.options = new SearchFilterList(); @@ -60,6 +61,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit { } reset() { + this.isActive = false; this.options.items.forEach((opt) => { opt.checked = false; }); @@ -80,6 +82,8 @@ export class SearchCheckListComponent implements SearchWidget, OnInit { .filter((option) => option.checked) .map((option) => option.value); + this.isActive = !!checkedValues.length; + const query = checkedValues.join(` ${this.operator} `); if (this.id && this.context) { diff --git a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts index 7e40f4a6dd..797291cc30 100644 --- a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts +++ b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts @@ -56,6 +56,8 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy context?: SearchQueryBuilderService; datePickerDateFormat = DEFAULT_FORMAT_DATE; maxDate: any; + isActive = false; + private onDestroy$ = new Subject(); constructor(private dateAdapter: DateAdapter, @@ -117,6 +119,8 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy apply(model: { from: string, to: string }, isValid: boolean) { if (isValid && this.id && this.context && this.settings && this.settings.field) { + this.isActive = true; + const start = moment(model.from).startOf('day').format(); const end = moment(model.to).endOf('day').format(); @@ -130,6 +134,7 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy } reset() { + this.isActive = false; this.form.reset({ from: '', to: '' diff --git a/lib/content-services/src/lib/search/components/search-header/search-header.component.html b/lib/content-services/src/lib/search/components/search-header/search-header.component.html index b744cb56d6..04fbae7c92 100644 --- a/lib/content-services/src/lib/search/components/search-header/search-header.component.html +++ b/lib/content-services/src/lib/search/components/search-header/search-header.component.html @@ -7,10 +7,10 @@ class="adf-filter-button" [matTooltip]="getTooltipTranslation(col?.title)"> + [matBadgeHidden]="!isActive()"> diff --git a/lib/content-services/src/lib/search/components/search-header/search-header.component.spec.ts b/lib/content-services/src/lib/search/components/search-header/search-header.component.spec.ts index 9b814a42f3..4b1791b578 100644 --- a/lib/content-services/src/lib/search/components/search-header/search-header.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-header/search-header.component.spec.ts @@ -95,6 +95,7 @@ describe('SearchHeaderComponent', () => { menuButton.click(); fixture.detectChanges(); await fixture.whenStable(); + component.widgetContainer.componentRef.instance.value = 'searchText'; const applyButton = fixture.debugElement.query(By.css('#apply-filter-button')); applyButton.triggerEventHandler('click', {}); fixture.detectChanges(); diff --git a/lib/content-services/src/lib/search/components/search-header/search-header.component.ts b/lib/content-services/src/lib/search/components/search-header/search-header.component.ts index 3d6920d412..7af2865cea 100644 --- a/lib/content-services/src/lib/search/components/search-header/search-header.component.ts +++ b/lib/content-services/src/lib/search/components/search-header/search-header.component.ts @@ -15,7 +15,19 @@ * limitations under the License. */ -import { Component, Input, Output, OnInit, OnChanges, EventEmitter, SimpleChanges, ViewEncapsulation, ViewChild, Inject, OnDestroy } from '@angular/core'; +import { + Component, + Input, + Output, + OnInit, + OnChanges, + EventEmitter, + SimpleChanges, + ViewEncapsulation, + ViewChild, + Inject, + OnDestroy +} from '@angular/core'; import { DataColumn, TranslationService } from '@alfresco/adf-core'; import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component'; import { SearchHeaderQueryBuilderService } from '../../search-header-query-builder.service'; @@ -60,8 +72,6 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy { @ViewChild(SearchWidgetContainerComponent) widgetContainer: SearchWidgetContainerComponent; - public isActive: boolean; - category: SearchCategory; isFilterServiceActive: boolean; @@ -81,19 +91,13 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy { .pipe(takeUntil(this.onDestroy$)) .subscribe((newNodePaging: NodePaging) => { this.update.emit(newNodePaging); - }); + }); } ngOnChanges(changes: SimpleChanges) { - if (changes['currentFolderNodeId'] && changes['currentFolderNodeId'].currentValue) { - const currentIdValue = changes['currentFolderNodeId'].currentValue; - const previousIdValue = changes['currentFolderNodeId'].previousValue; - this.searchHeaderQueryBuilder.setCurrentRootFolderId( - currentIdValue, - previousIdValue - ); - - this.isActive = false; + if (changes['currentFolderNodeId'] && changes['currentFolderNodeId'].currentValue !== changes['currentFolderNodeId'].previousValue) { + this.searchHeaderQueryBuilder.setCurrentRootFolderId(changes['currentFolderNodeId'].currentValue); + this.clearHeader(); } if (changes['maxItems'] || changes['skipCount']) { @@ -125,7 +129,12 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy { } onApplyButtonClick() { - this.isActive = true; + // TODO Move this piece of code in the search text widget + if (this.widgetContainer.selector === 'text' && this.widgetContainer.componentRef.instance.value === '') { + this.clearHeader(); + return; + } + this.widgetContainer.applyInnerWidget(); this.searchHeaderQueryBuilder.setActiveFilter(this.category.columnKey); this.searchHeaderQueryBuilder.execute(); @@ -133,13 +142,14 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy { onClearButtonClick(event: Event) { event.stopPropagation(); - this.widgetContainer.resetInnerWidget(); - this.isActive = false; - this.searchHeaderQueryBuilder.removeActiveFilter(this.category.columnKey); - if (this.searchHeaderQueryBuilder.isNoFilterActive()) { + this.clearHeader(); + } + + clearHeader() { + if (this.widgetContainer) { + this.widgetContainer.resetInnerWidget(); + this.searchHeaderQueryBuilder.removeActiveFilter(this.category.columnKey); this.clear.emit(); - } else { - this.searchHeaderQueryBuilder.execute(); } } @@ -147,6 +157,10 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy { if (!columnTitle) { columnTitle = 'SEARCH.SEARCH_HEADER.TYPE'; } - return this.translationService.instant('SEARCH.SEARCH_HEADER.FILTER_BY', {category: this.translationService.instant(columnTitle)}); + return this.translationService.instant('SEARCH.SEARCH_HEADER.FILTER_BY', { category: this.translationService.instant(columnTitle) }); + } + + isActive(): boolean { + return this.widgetContainer && this.widgetContainer.componentRef && this.widgetContainer.componentRef.instance.isActive; } } diff --git a/lib/content-services/src/lib/search/components/search-number-range/search-number-range.component.ts b/lib/content-services/src/lib/search/components/search-number-range/search-number-range.component.ts index 3ec87642f5..ccc626fd0c 100644 --- a/lib/content-services/src/lib/search/components/search-number-range/search-number-range.component.ts +++ b/lib/content-services/src/lib/search/components/search-number-range/search-number-range.component.ts @@ -44,6 +44,8 @@ export class SearchNumberRangeComponent implements SearchWidget, OnInit { field: string; format = '[{FROM} TO {TO}]'; + isActive = false; + validators: Validators; ngOnInit(): void { @@ -74,6 +76,8 @@ export class SearchNumberRangeComponent implements SearchWidget, OnInit { apply(model: { from: string, to: string }, isValid: boolean) { if (isValid && this.id && this.context && this.field) { + this.isActive = true; + const map = new Map(); map.set('FROM', model.from); map.set('TO', model.to); @@ -97,6 +101,8 @@ export class SearchNumberRangeComponent implements SearchWidget, OnInit { } reset() { + this.isActive = false; + this.form.reset({ from: '', to: '' diff --git a/lib/content-services/src/lib/search/components/search-radio/search-radio.component.ts b/lib/content-services/src/lib/search/components/search-radio/search-radio.component.ts index b143546388..de3f821a5b 100644 --- a/lib/content-services/src/lib/search/components/search-radio/search-radio.component.ts +++ b/lib/content-services/src/lib/search/components/search-radio/search-radio.component.ts @@ -46,6 +46,7 @@ export class SearchRadioComponent implements SearchWidget, OnInit { context: SearchQueryBuilderService; options: SearchFilterList; pageSize = 5; + isActive = false; constructor() { this.options = new SearchFilterList(); @@ -71,6 +72,8 @@ export class SearchRadioComponent implements SearchWidget, OnInit { private getSelectedValue(): string { const options: any[] = this.settings['options'] || []; if (options && options.length > 0) { + this.isActive = true; + let selected = options.find((opt) => opt.default); if (!selected) { selected = options[0]; @@ -91,6 +94,8 @@ export class SearchRadioComponent implements SearchWidget, OnInit { } reset() { + this.isActive = false; + const initialValue = this.getSelectedValue(); if (initialValue !== null) { this.setValue(initialValue); diff --git a/lib/content-services/src/lib/search/components/search-text/search-text.component.ts b/lib/content-services/src/lib/search/components/search-text/search-text.component.ts index a8e360e87a..27b6a1e642 100644 --- a/lib/content-services/src/lib/search/components/search-text/search-text.component.ts +++ b/lib/content-services/src/lib/search/components/search-text/search-text.component.ts @@ -36,6 +36,7 @@ export class SearchTextComponent implements SearchWidget, OnInit { id: string; settings: SearchWidgetSettings; context: SearchQueryBuilderService; + isActive = false; ngOnInit() { if (this.context && this.settings && this.settings.pattern) { @@ -49,6 +50,8 @@ export class SearchTextComponent implements SearchWidget, OnInit { } reset() { + this.isActive = false; + this.value = ''; this.updateQuery(null); } @@ -59,10 +62,21 @@ export class SearchTextComponent implements SearchWidget, OnInit { } private updateQuery(value: string) { + this.isActive = !!value; + if (this.context && this.settings && this.settings.field) { - this.context.queryFragments[this.id] = value ? `${this.settings.field}:'${value}'` : ''; + this.context.queryFragments[this.id] = value ? `${this.settings.field}:'${this.getSearchPrefix()}${value}${this.getSearchSuffix()}'` : ''; this.context.update(); } + + } + + private getSearchPrefix(): string { + return this.settings.searchPrefix ? this.settings.searchPrefix : ''; + } + + private getSearchSuffix(): string { + return this.settings.searchSuffix ? this.settings.searchSuffix : ''; } } diff --git a/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts b/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts index d9ebd80f19..148077d2fc 100644 --- a/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts +++ b/lib/content-services/src/lib/search/components/search-widget-container/search-widget-container.component.ts @@ -41,7 +41,7 @@ export class SearchWidgetContainerComponent implements OnInit, OnDestroy { @Input() config: any; - private componentRef: ComponentRef; + componentRef: ComponentRef; constructor( private searchFilterService: SearchFilterService, diff --git a/lib/content-services/src/lib/search/search-header-query-builder.service.spec.ts b/lib/content-services/src/lib/search/search-header-query-builder.service.spec.ts index 1075de48f4..662affa94e 100644 --- a/lib/content-services/src/lib/search/search-header-query-builder.service.spec.ts +++ b/lib/content-services/src/lib/search/search-header-query-builder.service.spec.ts @@ -94,8 +94,6 @@ describe('SearchHeaderQueryBuilder', () => { }; const expectedResult = [ - { query: 'query1' }, - { query: 'query2' }, { query: 'PARENT:"workspace://SpacesStore/fake-node-id"' } ]; @@ -105,7 +103,7 @@ describe('SearchHeaderQueryBuilder', () => { null ); - searchHeaderService.setCurrentRootFolderId('fake-node-id', undefined); + searchHeaderService.setCurrentRootFolderId('fake-node-id'); expect(searchHeaderService.filterQueries).toEqual(expectedResult, 'Filters are not as expected'); }); @@ -113,9 +111,7 @@ describe('SearchHeaderQueryBuilder', () => { it('should not add again the parent filter if that node is already added', () => { const expectedResult = [ - { query: 'query1' }, - { query: 'query2' }, - { query: 'PARENT:"workspace://SpacesStore/fake-node-id' } + { query: 'PARENT:"workspace://SpacesStore/fake-node-id"' } ]; const config: SearchConfiguration = { @@ -132,7 +128,7 @@ describe('SearchHeaderQueryBuilder', () => { null ); - searchHeaderService.setCurrentRootFolderId('fake-node-id', undefined); + searchHeaderService.setCurrentRootFolderId('fake-node-id'); expect(searchHeaderService.filterQueries).toEqual( expectedResult, @@ -142,8 +138,6 @@ describe('SearchHeaderQueryBuilder', () => { it('should replace the new query filter for the old parent node with the new one', () => { const expectedResult = [ - { query: 'query1' }, - { query: 'query2' }, { query: 'PARENT:"workspace://SpacesStore/fake-next-node-id"' } ]; @@ -153,8 +147,6 @@ describe('SearchHeaderQueryBuilder', () => { { id: 'cat2', enabled: true } ], filterQueries: [ - { query: 'query1' }, - { query: 'query2' }, { query: 'PARENT:"workspace://SpacesStore/fake-node-id' } ] }; @@ -165,9 +157,10 @@ describe('SearchHeaderQueryBuilder', () => { null ); + searchHeaderService.currentParentFolderId = 'fake-node-id'; + searchHeaderService.setCurrentRootFolderId( - 'fake-next-node-id', - 'fake-node-id' + 'fake-next-node-id' ); expect(searchHeaderService.filterQueries).toEqual( diff --git a/lib/content-services/src/lib/search/search-header-query-builder.service.ts b/lib/content-services/src/lib/search/search-header-query-builder.service.ts index 4430267c99..2553454ef6 100644 --- a/lib/content-services/src/lib/search/search-header-query-builder.service.ts +++ b/lib/content-services/src/lib/search/search-header-query-builder.service.ts @@ -30,7 +30,7 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService { private customSources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-', '-my-']; activeFilters: string[] = []; - currentParentFolderID: string; + currentParentFolderId: string; constructor(appConfig: AppConfigService, alfrescoApiService: AlfrescoApiService, private nodeApiService: NodesApiService) { super(appConfig, alfrescoApiService); @@ -78,41 +78,31 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService { return foundCategory; } - setCurrentRootFolderId(currentFolderId: string, previousFolderId: string) { - if (this.customSources.includes(currentFolderId)) { - if (currentFolderId !== this.currentParentFolderID) { + setCurrentRootFolderId(currentFolderId: string) { + if (currentFolderId !== this.currentParentFolderId) { + if (this.customSources.includes(currentFolderId)) { this.nodeApiService.getNode(currentFolderId).subscribe((nodeEntity: MinimalNode) => { - this.updateCurrentParentFilter(nodeEntity.id, previousFolderId); + this.updateCurrentParentFilter(nodeEntity.id); }); + } else { + this.currentParentFolderId = currentFolderId; + this.updateCurrentParentFilter(currentFolderId); } - } else { - this.updateCurrentParentFilter(currentFolderId, previousFolderId); } } - private updateCurrentParentFilter(currentFolderId: string, previousFolderId: string) { + private updateCurrentParentFilter(currentFolderId: string) { const alreadyAddedFilter = this.filterQueries.find(filterQueries => filterQueries.query.includes(currentFolderId) ); - if (!alreadyAddedFilter) { - this.removeOldFolderFiltering(previousFolderId, this.currentParentFolderID); - this.currentParentFolderID = currentFolderId; - this.filterQueries.push({ - query: `PARENT:"workspace://SpacesStore/${currentFolderId}"` - }); + if (alreadyAddedFilter !== undefined) { + this.filterQueries = []; } + + this.filterQueries = [{ + query: `PARENT:"workspace://SpacesStore/${currentFolderId}"` + }]; } - private removeOldFolderFiltering(previousFolderId: string, actualSetFolderId: string) { - if (previousFolderId || actualSetFolderId) { - const folderIdToRetrieve = previousFolderId ? previousFolderId : actualSetFolderId; - const oldFilterIndex = this.filterQueries.findIndex(filterQueries => - filterQueries.query.includes(folderIdToRetrieve) - ); - if (oldFilterIndex) { - this.filterQueries.splice(oldFilterIndex, 1); - } - } - } } diff --git a/lib/content-services/src/lib/search/search-widget.interface.ts b/lib/content-services/src/lib/search/search-widget.interface.ts index 883a4249e1..fe06b5a842 100644 --- a/lib/content-services/src/lib/search/search-widget.interface.ts +++ b/lib/content-services/src/lib/search/search-widget.interface.ts @@ -22,5 +22,6 @@ export interface SearchWidget { id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; + isActive?: boolean; reset(); }