diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html
index 23592e57e1..aee1d180b2 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.html
@@ -21,7 +21,9 @@
+
+
{
+describe('ContentNodeSelectorPanelComponent', () => {
const debounceSearch = 200;
let component: ContentNodeSelectorPanelComponent;
let fixture: ComponentFixture;
- let contentNodeSelectorService: ContentNodeSelectorService;
- let searchService: SearchService;
let nodeService: NodesApiService;
let sitesService: SitesService;
let searchSpy: jasmine.Spy;
let cnSearchSpy: jasmine.Spy;
const fakeNodeEntry = new Node({ id: 'fakeId' });
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
-
- let _observer: Observer;
+ let searchQueryBuilderService: SearchQueryBuilderService;
+ let contentNodeSelectorService: ContentNodeSelectorService;
function typeToSearchBox(searchTerm = 'string-to-search') {
const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
@@ -71,8 +72,8 @@ describe('ContentNodeSelectorComponent', () => {
fixture.detectChanges();
}
- function respondWithSearchResults(result) {
- _observer.next(result);
+ function triggerSearchResults(searchResults: ResultSetPaging) {
+ component.queryBuilderService.executed.next(searchResults);
}
setupTestBed({
@@ -90,18 +91,14 @@ describe('ContentNodeSelectorComponent', () => {
component = fixture.componentInstance;
component.debounceSearch = 0;
- searchService = TestBed.inject(SearchService);
nodeService = TestBed.inject(NodesApiService);
- contentNodeSelectorService = TestBed.inject(ContentNodeSelectorService);
sitesService = TestBed.inject(SitesService);
+ contentNodeSelectorService = TestBed.inject(ContentNodeSelectorService);
+ searchQueryBuilderService = component.queryBuilderService;
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node', path: { elements: [{ nodeType: 'st:site', name: 'fake-site'}] } }));
- cnSearchSpy = spyOn(contentNodeSelectorService, 'search').and.callThrough();
- searchSpy = spyOn(searchService, 'searchByQueryBody').and.callFake(() => {
- return new Observable((observer: Observer) => {
- _observer = observer;
- });
- });
+ cnSearchSpy = spyOn(contentNodeSelectorService, 'createQuery').and.callThrough();
+ searchSpy = spyOn(searchQueryBuilderService, 'execute');
const fakeSite = new SiteEntry({ entry: { id: 'fake-site', guid: 'fake-site', title: 'fake-site', visibility: 'visible' } });
spyOn(sitesService, 'getSite').and.returnValue(of(fakeSite));
});
@@ -242,73 +239,40 @@ describe('ContentNodeSelectorComponent', () => {
});
});
- it('should not show the breadcrumb if search was performed as last action', fakeAsync(() => {
- typeToSearchBox();
- tick(debounceSearch);
+ it('should not show the breadcrumb if search was performed as last action', async () => {
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
-
- tick(debounceSearch);
-
- fixture.detectChanges();
-
- tick(debounceSearch);
+ await fixture.whenStable();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).toBeNull();
- }));
+ });
- it('should show the breadcrumb again on folder navigation in the results list', fakeAsync(() => {
- typeToSearchBox();
- tick(debounceSearch);
-
- fixture.detectChanges();
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
- tick(debounceSearch);
-
- fixture.detectChanges();
-
- tick(debounceSearch);
+ it('should show the breadcrumb again on folder navigation in the results list', async () => {
+ triggerSearchResults(fakeResultSetPaging);
component.onFolderChange(nodeEntryEvent);
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
- }));
+ });
- it('should show the breadcrumb for the selected node when search results are displayed', fakeAsync(() => {
- typeToSearchBox();
-
- tick(debounceSearch);
-
- fixture.detectChanges();
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
- fixture.detectChanges();
-
- tick(debounceSearch);
+ it('should show the breadcrumb for the selected node when search results are displayed', async () => {
+ triggerSearchResults(fakeResultSetPaging);
const chosenNode = new Node({ path: { elements: ['one'] } });
component.onCurrentSelection([ { entry: chosenNode } ]);
fixture.detectChanges();
- tick(debounceSearch);
-
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode.path).toBe(chosenNode.path);
- }));
+ });
- it('should NOT show the breadcrumb for the selected node when not on search results list', fakeAsync(() => {
- typeToSearchBox();
-
- fixture.detectChanges();
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
+ it('should NOT show the breadcrumb for the selected node when not on search results list', async () => {
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
component.onFolderChange(nodeEntryEvent);
@@ -318,12 +282,10 @@ describe('ContentNodeSelectorComponent', () => {
component.onCurrentSelection([ { entry: chosenNode } ]);
fixture.detectChanges();
- tick(debounceSearch);
-
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode).toEqual(undefined);
- }));
+ });
it('should keep breadcrumb folderNode unchanged if breadcrumbTransform is NOT defined', (done) => {
fixture.detectChanges();
@@ -418,7 +380,7 @@ describe('ContentNodeSelectorComponent', () => {
...parentFiltering
],
scope: {
- locations: ['nodes']
+ locations: 'nodes'
}
};
@@ -484,11 +446,11 @@ describe('ContentNodeSelectorComponent', () => {
expect(component.chosenNode).toBeNull();
}));
- it('should update the breadcrumb when changing to a custom site', fakeAsync(() => {
+ it('should update the breadcrumb when changing to a custom site', async () => {
component.siteChanged( { entry: { guid: '-mysites-', title: 'My Sites' } });
expect(component.breadcrumbFolderTitle).toBe('My Sites');
- }));
+ });
it('should call the search api on changing the site selectBox value', fakeAsync(() => {
typeToSearchBox('vegeta');
@@ -503,7 +465,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(searchSpy.calls.argsFor(1)).toEqual([defaultSearchOptions('vegeta', 'namek')]);
}));
- it('should call the content node selector\'s search with the right parameters on changing the site selectbox\'s value', fakeAsync(() => {
+ it('should create the query with the right parameters on changing the site selectbox\'s value', fakeAsync(() => {
typeToSearchBox('vegeta');
tick(debounceSearch);
@@ -517,7 +479,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(cnSearchSpy).toHaveBeenCalledWith('vegeta', '-sites-', 0, 25, ['123456testId', '09876543testId'], false);
}));
- it('should call the content node selector\'s search with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => {
+ it('should create the query with the right parameters on changing the site selectBox value from a custom dropdown menu', fakeAsync(() => {
component.dropdownSiteList = { list: { entries: [ { entry: { guid: '-sites-' } }, { entry: { guid: 'namek' } }] } };
fixture.detectChanges();
@@ -638,7 +600,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(component.showingSearchResults).toBeFalsy();
});
- it('should restrict the search to the currentFolderId in case is defined', () => {
+ it('should the query restrict the search to the currentFolderId in case is defined', () => {
component.currentFolderId = 'my-root-id';
component.restrictRootToCurrentFolderId = true;
component.ngOnInit();
@@ -647,7 +609,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(cnSearchSpy).toHaveBeenCalledWith('search', 'my-root-id', 0, 25, [], false);
});
- it('should restrict the search to the site and not to the currentFolderId in case is changed', () => {
+ it('should the query restrict the search to the site and not to the currentFolderId in case is changed', () => {
component.currentFolderId = 'my-root-id';
component.restrictRootToCurrentFolderId = true;
component.ngOnInit();
@@ -671,7 +633,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(component.breadcrumbRootId).toBeUndefined();
});
- it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync(() => {
+ it('should clear the search field, nodes and chosenNode when deleting the search input', fakeAsync (() => {
spyOn(component, 'clear').and.callThrough();
typeToSearchBox('a');
@@ -690,26 +652,19 @@ describe('ContentNodeSelectorComponent', () => {
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed');
}));
- it('should clear the search field, nodes and chosenNode on folder navigation in the results list', fakeAsync(() => {
+ it('should clear the search field, nodes and chosenNode on folder navigation in the results list', async () => {
spyOn(component, 'clearSearch').and.callThrough();
- typeToSearchBox('a');
+ triggerSearchResults(fakeResultSetPaging);
- tick(debounceSearch);
- fixture.detectChanges();
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
-
- tick();
fixture.detectChanges();
component.onFolderChange(nodeEntryEvent);
fixture.detectChanges();
expect(component.clearSearch).toHaveBeenCalled();
+ });
- }));
-
- it('should show nodes from the same folder as selected in the dropdown on clearing the search input', fakeAsync(() => {
+ it('should show nodes from the same folder as selected in the dropdown on clearing the search input', fakeAsync (() => {
typeToSearchBox('piccolo');
tick(debounceSearch);
@@ -789,7 +744,7 @@ describe('ContentNodeSelectorComponent', () => {
typeToSearchBox();
setTimeout(() => {
- respondWithSearchResults(ONE_FOLDER_RESULT);
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
@@ -803,7 +758,7 @@ describe('ContentNodeSelectorComponent', () => {
typeToSearchBox('My');
setTimeout(() => {
- respondWithSearchResults(ONE_FOLDER_RESULT);
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -818,7 +773,7 @@ describe('ContentNodeSelectorComponent', () => {
typeToSearchBox();
setTimeout(() => {
- respondWithSearchResults(ONE_FOLDER_RESULT);
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -835,22 +790,21 @@ describe('ContentNodeSelectorComponent', () => {
}, 300);
});
- it('should reload the original documentList when clearing the search input', fakeAsync(() => {
- typeToSearchBox('shenron');
-
- tick(debounceSearch);
-
- respondWithSearchResults(ONE_FOLDER_RESULT);
-
- typeToSearchBox('');
-
- tick(debounceSearch);
+ it('should reload the original folderId when clearing the search input', async() => {
+ component.search('mock-type-search');
+ triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
+ await fixture.whenStable();
- const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
- expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
- }));
+ expect(component.folderIdToShow).toBe(null);
+
+ component.clear();
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku');
+ });
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', (done) => {
component.siteChanged( { entry: { guid: 'Kame-Sennin Muten Roshi' } });
@@ -895,7 +849,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(searchSpy).not.toHaveBeenCalled();
});
- it('should set its loading state to true after search was started', fakeAsync(() => {
+ it('should set its loading state to true after search was started', fakeAsync (() => {
component.showingSearchResults = true;
typeToSearchBox('shenron');
@@ -911,7 +865,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(paginationLoading).not.toBeNull();
}));
- it('Should infinite pagination target be null when we use it for search ', fakeAsync(() => {
+ it('Should infinite pagination target be null when we use it for search ', fakeAsync (() => {
component.showingSearchResults = true;
typeToSearchBox('shenron');
@@ -925,7 +879,7 @@ describe('ContentNodeSelectorComponent', () => {
expect(component.target).toBeNull();
}));
- it('Should infinite pagination target be present when search finish', fakeAsync(() => {
+ it('Should infinite pagination target be present when search finish', fakeAsync (() => {
component.showingSearchResults = true;
typeToSearchBox('shenron');
@@ -971,7 +925,7 @@ describe('ContentNodeSelectorComponent', () => {
component.isSelectionValid = returnHasPermission;
});
- it('should NOT be null after selecting node with the necessary permissions', async(() => {
+ it('should NOT be null after selecting node with the necessary permissions', async () => {
hasAllowableOperations = true;
component.documentList.folderNode = entry;
@@ -982,9 +936,9 @@ describe('ContentNodeSelectorComponent', () => {
});
component.documentList.ready.emit(nodePage);
- }));
+ });
- it('should be null after selecting node without the necessary permissions', async(() => {
+ it('should be null after selecting node without the necessary permissions', async () => {
hasAllowableOperations = false;
component.documentList.folderNode = entry;
@@ -995,9 +949,9 @@ describe('ContentNodeSelectorComponent', () => {
});
component.documentList.ready.emit(nodePage);
- }));
+ });
- it('should NOT be null after clicking on a node (with the right permissions) in the list (onNodeSelect)', async(() => {
+ it('should NOT be null after clicking on a node (with the right permissions) in the list (onNodeSelect)', async() => {
hasAllowableOperations = true;
component.select.subscribe((nodes) => {
@@ -1007,9 +961,9 @@ describe('ContentNodeSelectorComponent', () => {
});
component.onCurrentSelection([ { entry } ]);
- }));
+ });
- it('should remain empty when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', async(() => {
+ it('should remain empty when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', async () => {
hasAllowableOperations = false;
component.select.subscribe((nodes) => {
@@ -1019,9 +973,9 @@ describe('ContentNodeSelectorComponent', () => {
});
component.onCurrentSelection([ { entry } ]);
- }));
+ });
- it('should become empty when clicking on a node (with the WRONG permissions) after previously selecting a right node', async(() => {
+ it('should become empty when clicking on a node (with the WRONG permissions) after previously selecting a right node', async () => {
component.select.subscribe((nodes) => {
if (hasAllowableOperations) {
expect(nodes).toBeDefined();
@@ -1039,10 +993,9 @@ describe('ContentNodeSelectorComponent', () => {
hasAllowableOperations = false;
component.onCurrentSelection([{ entry }]);
+ });
- }));
-
- it('should be empty when the chosenNode is reset', async(() => {
+ it('should be empty when the chosenNode is reset', async () => {
hasAllowableOperations = true;
component.onCurrentSelection([{ entry: {} }]);
@@ -1053,7 +1006,7 @@ describe('ContentNodeSelectorComponent', () => {
});
component.resetChosenNode();
- }));
+ });
});
describe('in the case when isSelectionValid is null', () => {
@@ -1062,7 +1015,7 @@ describe('ContentNodeSelectorComponent', () => {
component.isSelectionValid = null;
});
- it('should NOT be null after selecting node because isSelectionValid would be reset to defaultValidation function', async(() => {
+ it('should NOT be null after selecting node because isSelectionValid would be reset to defaultValidation function', async () => {
component.documentList.folderNode = entry;
fixture.detectChanges();
@@ -1075,9 +1028,9 @@ describe('ContentNodeSelectorComponent', () => {
component.documentList.ready.emit(nodePage);
fixture.detectChanges();
- }));
+ });
- it('should NOT be null after clicking on a node in the list (onNodeSelect)', async(() => {
+ it('should NOT be null after clicking on a node in the list (onNodeSelect)', async () => {
fixture.detectChanges();
component.select.subscribe((nodes) => {
@@ -1089,9 +1042,9 @@ describe('ContentNodeSelectorComponent', () => {
});
component.onCurrentSelection([{ entry }]);
- }));
+ });
- it('should be null when the chosenNode is reset', async(() => {
+ it('should be null when the chosenNode is reset', async () => {
fixture.detectChanges();
component.onCurrentSelection([{ entry: {} }]);
@@ -1104,7 +1057,7 @@ describe('ContentNodeSelectorComponent', () => {
component.resetChosenNode();
fixture.detectChanges();
- }));
+ });
});
describe('in the case when isSelectionValid is not defined', () => {
@@ -1113,7 +1066,7 @@ describe('ContentNodeSelectorComponent', () => {
component.isSelectionValid = undefined;
});
- it('should NOT be null after selecting node because isSelectionValid would be the defaultValidation function', async(() => {
+ it('should NOT be null after selecting node because isSelectionValid would be the defaultValidation function', async () => {
component.documentList.folderNode = entry;
fixture.detectChanges();
@@ -1126,9 +1079,9 @@ describe('ContentNodeSelectorComponent', () => {
component.documentList.ready.emit(nodePage);
fixture.detectChanges();
- }));
+ });
- it('should be null when the chosenNode is reset', async(() => {
+ it('should be null when the chosenNode is reset', async () => {
fixture.detectChanges();
component.onCurrentSelection([{ entry: {} }]);
@@ -1141,7 +1094,7 @@ describe('ContentNodeSelectorComponent', () => {
component.resetChosenNode();
fixture.detectChanges();
- }));
+ });
});
});
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts
index 4719de8554..064138c216 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.component.ts
@@ -15,7 +15,17 @@
* limitations under the License.
*/
-import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core';
+import {
+ Component,
+ EventEmitter,
+ Input,
+ OnInit,
+ Output,
+ ViewChild,
+ ViewEncapsulation,
+ OnDestroy,
+ Inject
+} from '@angular/core';
import {
HighlightDirective,
UserPreferencesService,
@@ -37,6 +47,8 @@ import { debounceTime, takeUntil, scan } from 'rxjs/operators';
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
import { NodeEntryEvent, ShareDataRow } from '../document-list';
import { Subject } from 'rxjs';
+import { SEARCH_QUERY_SERVICE_TOKEN } from '../search/search-query-service.token';
+import { SearchQueryBuilderService } from '../search/search-query-builder.service';
export type ValidationFunction = (entry: Node) => boolean;
@@ -47,7 +59,11 @@ export const defaultValidation = () => true;
styleUrls: ['./content-node-selector-panel.component.scss'],
templateUrl: './content-node-selector-panel.component.html',
encapsulation: ViewEncapsulation.None,
- host: { 'class': 'adf-content-node-selector-panel' }
+ host: { 'class': 'adf-content-node-selector-panel' },
+ providers: [{
+ provide: SEARCH_QUERY_SERVICE_TOKEN,
+ useClass: SearchQueryBuilderService
+ }]
})
export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
@@ -230,6 +246,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
constructor(private contentNodeSelectorService: ContentNodeSelectorService,
private customResourcesService: CustomResourcesService,
+ @Inject(SEARCH_QUERY_SERVICE_TOKEN) public queryBuilderService: SearchQueryBuilderService,
private userPreferencesService: UserPreferencesService,
private nodesApiService: NodesApiService,
private uploadService: UploadService,
@@ -253,6 +270,12 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
)
.subscribe(searchValue => this.search(searchValue));
+ this.queryBuilderService.executed
+ .pipe(takeUntil(this.onDestroy$))
+ .subscribe( (results: NodePaging) => {
+ this.showSearchResults(results);
+ });
+
this.userPreferencesService
.select(UserPreferenceValues.PaginationSize)
.pipe(takeUntil(this.onDestroy$))
@@ -421,15 +444,15 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
if (this.customResourcesService.hasCorrespondingNodeIds(this.siteId)) {
this.customResourcesService.getCorrespondingNodeIds(this.siteId)
.subscribe((nodeIds) => {
- this.contentNodeSelectorService.search(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, nodeIds, this.showFiles)
- .subscribe(this.showSearchResults.bind(this));
- },
+ const query = this.contentNodeSelectorService.createQuery(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, nodeIds, this.showFiles);
+ this.queryBuilderService.execute(query);
+ },
() => {
this.showSearchResults({ list: { entries: [] } });
});
} else {
- this.contentNodeSelectorService.search(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, [], this.showFiles)
- .subscribe(this.showSearchResults.bind(this));
+ const query = this.contentNodeSelectorService.createQuery(this.searchTerm, this.siteId, this.pagination.skipCount, this.pagination.maxItems, [], this.showFiles);
+ this.queryBuilderService.execute(query);
}
}
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.service.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.service.ts
new file mode 100644
index 0000000000..c7e12f1d45
--- /dev/null
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector-panel.service.ts
@@ -0,0 +1,58 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { SearchCategory } from '../search';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ContentNodeSelectorPanelService {
+
+ customModels: any[];
+
+ convertCustomModelPropertiesToSearchCategories(): any[] {
+ const searchConfig = [];
+ this.customModels?.forEach( (propertyModel) => {
+ searchConfig.push(this.convertModelPropertyIntoSearchFilter(propertyModel));
+ });
+
+ return searchConfig;
+ }
+
+ convertModelPropertyIntoSearchFilter(modelProperty: any): SearchCategory {
+ let filterSearch: SearchCategory;
+ if (modelProperty.dataType === 'd:text') {
+ filterSearch = {
+ id : modelProperty.prefixedName,
+ name: modelProperty.prefixedName,
+ expanded: false,
+ enabled: true,
+ component: {
+ selector: 'text',
+ settings: {
+ pattern: `${modelProperty.prefixedName}:'(.*?)'`,
+ field: `${modelProperty.prefixedName}`,
+ placeholder: `Enter the ${modelProperty.name}`
+ }
+ }
+ };
+ }
+ return filterSearch;
+ }
+
+}
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts
index 92f5570498..e7452ee75f 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.component.spec.ts
@@ -30,7 +30,7 @@ import { DocumentListComponent } from '../document-list/components/document-list
import { ShareDataRow } from '../document-list';
import { TranslateModule } from '@ngx-translate/core';
-describe('ContentNodeSelectorDialogComponent', () => {
+describe('ContentNodeSelectorComponent', () => {
let component: ContentNodeSelectorComponent;
let fixture: ComponentFixture;
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.module.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.module.ts
index 21e6baf3eb..af80442ddf 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector.module.ts
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.module.ts
@@ -24,10 +24,12 @@ import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel
import { ContentNodeSelectorComponent } from './content-node-selector.component';
import { SitesDropdownModule } from '../site-dropdown/sites-dropdown.module';
import { BreadcrumbModule } from '../breadcrumb/breadcrumb.module';
+import { SearchModule } from '../search/search.module';
import { CoreModule } from '@alfresco/adf-core';
import { DocumentListModule } from '../document-list/document-list.module';
import { NameLocationCellComponent } from './name-location-cell/name-location-cell.component';
import { UploadModule } from '../upload/upload.module';
+import { SearchQueryBuilderService } from '../search/search-query-builder.service';
@NgModule({
imports: [
@@ -38,6 +40,7 @@ import { UploadModule } from '../upload/upload.module';
MaterialModule,
SitesDropdownModule,
BreadcrumbModule,
+ SearchModule,
DocumentListModule,
UploadModule
],
@@ -50,6 +53,7 @@ import { UploadModule } from '../upload/upload.module';
ContentNodeSelectorPanelComponent,
NameLocationCellComponent,
ContentNodeSelectorComponent
- ]
+ ],
+ providers: [SearchQueryBuilderService]
})
export class ContentNodeSelectorModule {}
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.service.spec.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.service.spec.ts
index 86f1272f4c..5b6b858b19 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector.service.spec.ts
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.service.spec.ts
@@ -16,121 +16,105 @@
*/
import { TestBed } from '@angular/core/testing';
-import { QueryBody, ResultSetPaging } from '@alfresco/js-api';
-import { SearchService, setupTestBed } from '@alfresco/adf-core';
+import { setupTestBed } from '@alfresco/adf-core';
import { ContentNodeSelectorService } from './content-node-selector.service';
import { ContentTestingModule } from '../testing/content.testing.module';
import { TranslateModule } from '@ngx-translate/core';
-import { Observable, of } from 'rxjs';
-
-class SearchServiceMock extends SearchService {
- public query: QueryBody;
-
- searchByQueryBody(queryBody: QueryBody): Observable {
- this.query = queryBody;
- return of({});
- }
-}
describe('ContentNodeSelectorService', () => {
let service: ContentNodeSelectorService;
- let search: SearchServiceMock;
setupTestBed({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
- ],
- providers: [
- { provide: SearchService, useClass: SearchServiceMock }
]
});
beforeEach(() => {
service = TestBed.inject(ContentNodeSelectorService);
- search = TestBed.inject(SearchService) as SearchServiceMock;
});
it('should have the proper main query for search string', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.query).toEqual({
+ expect(queryBody.query).toEqual({
query: 'nuka cola quantum*'
});
});
it('should make it including the path and allowableOperations', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.include).toEqual(['path', 'allowableOperations', 'properties']);
+ expect(queryBody.include).toEqual(['path', 'allowableOperations', 'properties']);
});
it('should make the search restricted to nodes only', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.scope.locations).toEqual(['nodes']);
+ expect(queryBody.scope.locations).toEqual('nodes');
});
it('should set the maxItems and paging properly by parameters', () => {
- service.search('nuka cola quantum', null, 10, 100);
+ const queryBody = service.createQuery('nuka cola quantum', null, 10, 100);
- expect(search.query.paging.maxItems).toEqual(100);
- expect(search.query.paging.skipCount).toEqual(10);
+ expect(queryBody.paging.maxItems).toEqual(100);
+ expect(queryBody.paging.skipCount).toEqual(10);
});
it('should set the maxItems and paging properly by default', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.paging.maxItems).toEqual(25);
- expect(search.query.paging.skipCount).toEqual(0);
+ expect(queryBody.paging.maxItems).toEqual(25);
+ expect(queryBody.paging.skipCount).toEqual(0);
});
it('should filter the search for folders', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.filterQueries).toContain({ query: "TYPE:'cm:folder'" });
+ expect(queryBody.filterQueries).toContain({ query: "TYPE:'cm:folder'" });
});
it('should filter the search for files', () => {
- service.search('nuka cola quantum', null, 0, 25, [], true);
+ const queryBody = service.createQuery('nuka cola quantum', null, 0, 25, [], true);
- expect(search.query.filterQueries).toContain({ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" });
+ expect(queryBody.filterQueries).toContain({ query: "TYPE:'cm:folder' OR TYPE:'cm:content'" });
});
it('should filter out the "system-base" entries', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.filterQueries).toContain({ query: 'NOT cm:creator:System' });
+ expect(queryBody.filterQueries).toContain({ query: 'NOT cm:creator:System' });
});
it('should filter for the provided ancestor if defined', () => {
- service.search('nuka cola quantum', 'diamond-city');
+ const queryBody = service.createQuery('nuka cola quantum', 'diamond-city');
- expect(search.query.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
+ expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
});
it('should NOT filter for the ancestor if NOT defined', () => {
- service.search('nuka cola quantum');
+ const queryBody = service.createQuery('nuka cola quantum');
- expect(search.query.filterQueries).not.toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/null\'' });
+ expect(queryBody.filterQueries).not.toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/null\'' });
});
it('should filter for the extra provided ancestors if defined', () => {
- service.search('nuka cola quantum', 'diamond-city', 0, 25, ['extra-diamond-city']);
+ const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, ['extra-diamond-city']);
- expect(search.query.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\' OR ANCESTOR:\'workspace://SpacesStore/extra-diamond-city\'' });
+ expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\' OR ANCESTOR:\'workspace://SpacesStore/extra-diamond-city\'' });
});
it('should NOT filter for extra ancestors if an empty list of ids is provided', () => {
- service.search('nuka cola quantum', 'diamond-city', 0, 25, []);
+ const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, []);
- expect(search.query.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
+ expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
});
it('should NOT filter for the extra provided ancestor if it\'s the same as the rootNodeId', () => {
- service.search('nuka cola quantum', 'diamond-city', 0, 25, ['diamond-city']);
+ const queryBody = service.createQuery('nuka cola quantum', 'diamond-city', 0, 25, ['diamond-city']);
- expect(search.query.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
+ expect(queryBody.filterQueries).toContain({ query: 'ANCESTOR:\'workspace://SpacesStore/diamond-city\'' });
});
});
diff --git a/lib/content-services/src/lib/content-node-selector/content-node-selector.service.ts b/lib/content-services/src/lib/content-node-selector/content-node-selector.service.ts
index 7fbef32b26..80299f2b36 100644
--- a/lib/content-services/src/lib/content-node-selector/content-node-selector.service.ts
+++ b/lib/content-services/src/lib/content-node-selector/content-node-selector.service.ts
@@ -15,10 +15,8 @@
* limitations under the License.
*/
-import { SearchService } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
-import { ResultSetPaging } from '@alfresco/js-api';
-import { Observable } from 'rxjs';
+import { QueryBody } from '@alfresco/js-api';
/**
* Internal service used by ContentNodeSelector component.
@@ -28,23 +26,7 @@ import { Observable } from 'rxjs';
})
export class ContentNodeSelectorService {
- constructor(private searchService: SearchService) {
- }
-
- /**
- * Performs a search for content node selection
- *
- * @param searchTerm The term to search for
- * @param rootNodeId The root is to start the search from
- * @param skipCount From where to start the loading
- * @param maxItems How many items to load
- * @param [extraNodeIds] List of extra node ids to search from. This last parameter is necessary when
- * the rootNodeId is one of the supported aliases (e.g. '-my-', '-root-', '-mysites-', etc.)
- * and search is not supported for that alias, but can be performed on its corresponding nodes.
- * @param [showFiles] shows the files in the dialog search result
- */
- public search(searchTerm: string, rootNodeId: string = null, skipCount: number = 0, maxItems: number = 25, extraNodeIds?: string[], showFiles?: boolean): Observable {
-
+ createQuery(searchTerm: string, rootNodeId: string = null, skipCount: number = 0, maxItems: number = 25, extraNodeIds?: string[], showFiles?: boolean): QueryBody {
let extraParentFiltering = '';
if (extraNodeIds && extraNodeIds.length) {
@@ -57,7 +39,7 @@ export class ContentNodeSelectorService {
const parentFiltering = rootNodeId ? [{ query: `ANCESTOR:'workspace://SpacesStore/${rootNodeId}'${extraParentFiltering}` }] : [];
- const defaultSearchNode: any = {
+ return {
query: {
query: `${searchTerm}*`
},
@@ -67,15 +49,14 @@ export class ContentNodeSelectorService {
skipCount: skipCount
},
filterQueries: [
- { query: `TYPE:'cm:folder'${ showFiles ? " OR TYPE:'cm:content'" : '' }` },
+ { query: `TYPE:'cm:folder'${showFiles ? " OR TYPE:'cm:content'" : ''}` },
{ query: 'NOT cm:creator:System' },
...parentFiltering
],
scope: {
- locations: ['nodes']
+ locations: 'nodes'
}
};
- return this.searchService.searchByQueryBody(defaultSearchNode);
}
}
diff --git a/lib/content-services/src/lib/content-node-selector/public-api.ts b/lib/content-services/src/lib/content-node-selector/public-api.ts
index a490150b47..7e02ad4b15 100644
--- a/lib/content-services/src/lib/content-node-selector/public-api.ts
+++ b/lib/content-services/src/lib/content-node-selector/public-api.ts
@@ -21,5 +21,6 @@ export * from './content-node-selector-panel.component';
export * from './content-node-selector.component';
export * from './content-node-selector.service';
export * from './content-node-dialog.service';
+export * from './content-node-selector-panel.service';
export * from './content-node-selector.module';
diff --git a/lib/content-services/src/lib/search/base-query-builder.service.ts b/lib/content-services/src/lib/search/base-query-builder.service.ts
index dcdc57dcc3..f8e77bc7f8 100644
--- a/lib/content-services/src/lib/search/base-query-builder.service.ts
+++ b/lib/content-services/src/lib/search/base-query-builder.service.ts
@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
-import { Subject } from 'rxjs';
+import { Subject, Observable, from } from 'rxjs';
import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
import {
QueryBody,
@@ -196,8 +196,8 @@ export abstract class BaseQueryBuilderService {
/**
* Builds the current query and triggers the `updated` event.
*/
- update(): void {
- const query = this.buildQuery();
+ update(queryBody?: QueryBody): void {
+ const query = queryBody ? queryBody : this.buildQuery();
this.updated.next(query);
}
@@ -205,11 +205,11 @@ export abstract class BaseQueryBuilderService {
* Builds and executes the current query.
* @returns Nothing
*/
- async execute() {
+ async execute(queryBody?: QueryBody) {
try {
- const query = this.buildQuery();
+ const query = queryBody ? queryBody : this.buildQuery();
if (query) {
- const resultSetPaging: ResultSetPaging = await this.alfrescoApiService.searchApi.search(query);
+ const resultSetPaging: ResultSetPaging = await this.alfrescoApiService.getInstance().search.searchApi.search(queryBody);
this.executed.next(resultSetPaging);
}
} catch (error) {
@@ -226,6 +226,16 @@ export abstract class BaseQueryBuilderService {
}
}
+ search(queryBody: QueryBody): Observable {
+ const promise = this.alfrescoApiService.searchApi.search(queryBody);
+
+ promise.then((resultSetPaging) => {
+ this.executed.next(resultSetPaging);
+ });
+
+ return from(promise);
+ }
+
/**
* Builds the current query.
* @returns The finished query
diff --git a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.html b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.html
index a3af0a4d6f..bef56b8dc2 100644
--- a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.html
+++ b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.html
@@ -24,7 +24,7 @@
-
+
diff --git a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts
index f39a4a649b..f192ad876a 100644
--- a/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts
+++ b/lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import { Component, ViewEncapsulation, OnInit, OnDestroy, Inject } from '@angular/core';
+import { Component, ViewEncapsulation, OnInit, OnDestroy, Inject, Input } from '@angular/core';
import { MatCheckboxChange } from '@angular/material/checkbox';
-import { SearchService, TranslationService } from '@alfresco/adf-core';
+import { TranslationService, SearchService } from '@alfresco/adf-core';
import { SearchQueryBuilderService } from '../../search-query-builder.service';
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
import { FacetField } from '../../facet-field.interface';
@@ -41,6 +41,9 @@ export interface SelectedBucket {
})
export class SearchFilterComponent implements OnInit, OnDestroy {
+ @Input()
+ showContextFacets: boolean = true;
+
private DEFAULT_PAGE_SIZE = 5;
/** All facet field items to be displayed in the component. These are updated according to the response.
@@ -77,7 +80,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
this.queryBuilder.updated
.pipe(takeUntil(this.onDestroy$))
- .subscribe(() => this.queryBuilder.execute());
+ .subscribe((query) => this.queryBuilder.execute(query));
}
ngOnInit() {
@@ -210,7 +213,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || [];
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
- } else if (responseField) {
+ } else if (responseField && this.showContextFacets) {
const bucketList = new SearchFilterList(responseBuckets, field.pageSize);
bucketList.filter = this.getBucketFilterFunction(bucketList);
@@ -265,7 +268,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
const alreadyExistingBuckets = alreadyExistingField.buckets && alreadyExistingField.buckets.items || [];
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
- } else if (responseField) {
+ } else if (responseField && this.showContextFacets) {
const bucketList = new SearchFilterList(responseBuckets, this.facetQueriesPageSize);
bucketList.filter = this.getBucketFilterFunction(bucketList);
diff --git a/lib/content-services/src/lib/search/components/search-panel/search-panel.component.html b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.html
new file mode 100644
index 0000000000..3a014e0c6d
--- /dev/null
+++ b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.html
@@ -0,0 +1,4 @@
+ 0"
+ class="app-search-settings" #searchFilter
+ [showContextFacets]="false"
+>
diff --git a/lib/content-services/src/lib/search/components/search-panel/search-panel.component.spec.ts b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.spec.ts
new file mode 100644
index 0000000000..0927170e33
--- /dev/null
+++ b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.spec.ts
@@ -0,0 +1,215 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { SearchCheckListComponent, SearchListOption } from '../search-check-list/search-check-list.component';
+import { SearchFilterList } from '../search-filter/models/search-filter-list.model';
+import { setupTestBed } from '@alfresco/adf-core';
+import { ContentTestingModule } from '../../../testing/content.testing.module';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { sizeOptions, stepOne, stepThree } from '../../../mock';
+import { By } from '@angular/platform-browser';
+import { TranslateModule } from '@ngx-translate/core';
+
+describe('SearchCheckListComponent', () => {
+ let fixture: ComponentFixture;
+ let component: SearchCheckListComponent;
+
+ setupTestBed({
+ imports: [
+ TranslateModule.forRoot(),
+ ContentTestingModule
+ ]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SearchCheckListComponent);
+ component = fixture.componentInstance;
+ });
+
+ it('should setup options from settings', () => {
+ const options: any = [
+ { 'name': 'Folder', 'value': "TYPE:'cm:folder'" },
+ { 'name': 'Document', 'value': "TYPE:'cm:content'" }
+ ];
+ component.settings = { options: options };
+ component.ngOnInit();
+
+ expect(component.options.items).toEqual(options);
+ });
+
+ it('should handle enter key as click on checkboxes', () => {
+ component.options = new SearchFilterList([
+ { name: 'Folder', value: "TYPE:'cm:folder'", checked: false },
+ { name: 'Document', value: "TYPE:'cm:content'", checked: false }
+ ]);
+
+ component.ngOnInit();
+ fixture.detectChanges();
+
+ const optionElements = fixture.debugElement.queryAll(By.css('mat-checkbox'));
+
+ optionElements[0].triggerEventHandler('keydown.enter', {});
+ expect(component.options.items[0].checked).toBeTruthy();
+
+ optionElements[0].triggerEventHandler('keydown.enter', {});
+ expect(component.options.items[0].checked).toBeFalsy();
+ });
+
+ it('should setup operator from the settings', () => {
+ component.settings = { operator: 'AND' };
+ component.ngOnInit();
+ expect(component.operator).toBe('AND');
+ });
+
+ it('should use OR operator by default', () => {
+ component.settings = { operator: null };
+ component.ngOnInit();
+ expect(component.operator).toBe('OR');
+ });
+
+ it('should update query builder on checkbox change', () => {
+ component.options = new SearchFilterList([
+ { name: 'Folder', value: "TYPE:'cm:folder'", checked: false },
+ { name: 'Document', value: "TYPE:'cm:content'", checked: false }
+ ]);
+
+ component.id = 'checklist';
+ component.context = {
+ queryFragments: {},
+ update() {}
+ };
+
+ component.ngOnInit();
+
+ spyOn(component.context, 'update').and.stub();
+
+ component.changeHandler(
+ { checked: true },
+ component.options.items[0]
+ );
+
+ expect(component.context.queryFragments[component.id]).toEqual(`TYPE:'cm:folder'`);
+
+ component.changeHandler(
+ { checked: true },
+ component.options.items[1]
+ );
+
+ expect(component.context.queryFragments[component.id]).toEqual(
+ `TYPE:'cm:folder' OR TYPE:'cm:content'`
+ );
+ });
+
+ it('should reset selected boxes', () => {
+ component.options = new SearchFilterList([
+ { name: 'Folder', value: "TYPE:'cm:folder'", checked: true },
+ { name: 'Document', value: "TYPE:'cm:content'", checked: true }
+ ]);
+
+ component.reset();
+
+ expect(component.options.items[0].checked).toBeFalsy();
+ expect(component.options.items[1].checked).toBeFalsy();
+ });
+
+ it('should update query builder on reset', () => {
+ component.id = 'checklist';
+ component.context = {
+ queryFragments: {
+ 'checklist': 'query'
+ },
+ update() {}
+ };
+ spyOn(component.context, 'update').and.stub();
+
+ component.ngOnInit();
+ component.options = new SearchFilterList([
+ { name: 'Folder', value: "TYPE:'cm:folder'", checked: true },
+ { name: 'Document', value: "TYPE:'cm:content'", checked: true }
+ ]);
+
+ component.reset();
+
+ expect(component.context.update).toHaveBeenCalled();
+ expect(component.context.queryFragments[component.id]).toBe('');
+ });
+
+ describe('Pagination', () => {
+ it('should show 5 items when pageSize not defined', () => {
+ component.id = 'checklist';
+ component.context = {
+ queryFragments: {
+ 'checklist': 'query'
+ },
+ update() {}
+ };
+ component.settings = { options: sizeOptions };
+
+ component.ngOnInit();
+ fixture.detectChanges();
+
+ const optionElements = fixture.debugElement.queryAll(By.css('mat-checkbox'));
+ expect(optionElements.length).toEqual(5);
+ const labels = Array.from(optionElements).map(element => element.nativeElement.innerText);
+ expect(labels).toEqual(stepOne);
+ });
+
+ it('should show all items when pageSize is high', () => {
+ component.id = 'checklist';
+ component.context = {
+ queryFragments: {
+ 'checklist': 'query'
+ },
+ update() {}
+ };
+ component.settings = { pageSize: 15, options: sizeOptions };
+ component.ngOnInit();
+ fixture.detectChanges();
+
+ const optionElements = fixture.debugElement.queryAll(By.css('mat-checkbox'));
+ expect(optionElements.length).toEqual(13);
+ const labels = Array.from(optionElements).map(element => element.nativeElement.innerText);
+ expect(labels).toEqual(stepThree);
+ });
+ });
+
+ it('should able to check/reset the checkbox', () => {
+ component.id = 'checklist';
+ component.context = {
+ queryFragments: {
+ 'checklist': 'query'
+ },
+ update: () => {}
+ };
+ component.settings = { options: sizeOptions };
+ spyOn(component, 'submitValues').and.stub();
+ component.ngOnInit();
+ fixture.detectChanges();
+
+ const optionElements = fixture.debugElement.query(By.css('mat-checkbox'));
+ optionElements.triggerEventHandler('change', { checked: true });
+
+ expect(component.submitValues).toHaveBeenCalled();
+
+ const clearAllElement = fixture.debugElement.query(By.css('button[title="SEARCH.FILTER.ACTIONS.CLEAR-ALL"]'));
+ clearAllElement.triggerEventHandler('click', {} );
+ fixture.detectChanges();
+
+ const selectedElements = fixture.debugElement.queryAll(By.css('.mat-checkbox-checked'));
+ expect(selectedElements.length).toBe(0);
+ });
+});
diff --git a/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts
new file mode 100644
index 0000000000..50b1f8b4eb
--- /dev/null
+++ b/lib/content-services/src/lib/search/components/search-panel/search-panel.component.ts
@@ -0,0 +1,39 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
+import { ContentNodeSelectorPanelService } from '../../../content-node-selector/content-node-selector-panel.service';
+import { SearchQueryBuilderService } from '../../search-query-builder.service';
+import { SEARCH_QUERY_SERVICE_TOKEN } from '../../search-query-service.token';
+
+@Component({
+ selector: 'adf-search-panel',
+ templateUrl: './search-panel.component.html',
+ encapsulation: ViewEncapsulation.None,
+ host: { class: 'adf-search-panel' }
+
+})
+export class SearchPanelComponent implements OnInit {
+
+ constructor(public contentNodeSelectorSearchPanelService: ContentNodeSelectorPanelService,
+ @Inject(SEARCH_QUERY_SERVICE_TOKEN) private queryBuilderService: SearchQueryBuilderService) {
+ }
+
+ ngOnInit(): void {
+ this.queryBuilderService.categories = this.contentNodeSelectorSearchPanelService.convertCustomModelPropertiesToSearchCategories();
+ }
+}
diff --git a/lib/content-services/src/lib/search/public-api.ts b/lib/content-services/src/lib/search/public-api.ts
index 48584538b8..137b25e69b 100644
--- a/lib/content-services/src/lib/search/public-api.ts
+++ b/lib/content-services/src/lib/search/public-api.ts
@@ -34,6 +34,7 @@ export * from './components/search-control.component';
export * from './components/empty-search-result.component';
export * from './components/search-control.component';
export * from './components/search.component';
+export * from './components/search-panel/search-panel.component';
export * from './components/search-check-list/search-check-list.component';
export * from './components/search-chip-list/search-chip-list.component';
export * from './components/search-date-range/search-date-range.component';
diff --git a/lib/content-services/src/lib/search/search-query-builder.service.ts b/lib/content-services/src/lib/search/search-query-builder.service.ts
index 640cdc8176..c87f45219a 100644
--- a/lib/content-services/src/lib/search/search-query-builder.service.ts
+++ b/lib/content-services/src/lib/search/search-query-builder.service.ts
@@ -20,9 +20,7 @@ import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
import { SearchConfiguration } from './search-configuration.interface';
import { BaseQueryBuilderService } from './base-query-builder.service';
-@Injectable({
- providedIn: 'root'
-})
+@Injectable()
export class SearchQueryBuilderService extends BaseQueryBuilderService {
public isFilterServiceActive(): boolean {
diff --git a/lib/content-services/src/lib/search/search.module.ts b/lib/content-services/src/lib/search/search.module.ts
index d035731ef1..6b6c41dfd7 100644
--- a/lib/content-services/src/lib/search/search.module.ts
+++ b/lib/content-services/src/lib/search/search.module.ts
@@ -32,6 +32,7 @@ import { SearchTextComponent } from './components/search-text/search-text.compon
import { SearchRadioComponent } from './components/search-radio/search-radio.component';
import { SearchSliderComponent } from './components/search-slider/search-slider.component';
import { SearchNumberRangeComponent } from './components/search-number-range/search-number-range.component';
+import { SearchPanelComponent } from './components/search-panel/search-panel.component';
import { SearchCheckListComponent } from './components/search-check-list/search-check-list.component';
import { SearchDateRangeComponent } from './components/search-date-range/search-date-range.component';
import { SearchSortingPickerComponent } from './components/search-sorting-picker/search-sorting-picker.component';
@@ -58,6 +59,7 @@ import { SearchFilterContainerComponent } from './components/search-filter-conta
SearchRadioComponent,
SearchSliderComponent,
SearchNumberRangeComponent,
+ SearchPanelComponent,
SearchCheckListComponent,
SearchDateRangeComponent,
SearchSortingPickerComponent,
@@ -74,6 +76,7 @@ import { SearchFilterContainerComponent } from './components/search-filter-conta
SearchRadioComponent,
SearchSliderComponent,
SearchNumberRangeComponent,
+ SearchPanelComponent,
SearchCheckListComponent,
SearchDateRangeComponent,
SearchSortingPickerComponent,
diff --git a/lib/core/services/search.service.ts b/lib/core/services/search.service.ts
index bfaa0b49cc..74e557b995 100644
--- a/lib/core/services/search.service.ts
+++ b/lib/core/services/search.service.ts
@@ -29,8 +29,7 @@ export class SearchService {
dataLoaded: Subject = new Subject();
constructor(private apiService: AlfrescoApiService,
- private searchConfigurationService: SearchConfigurationService) {
- }
+ private searchConfigurationService: SearchConfigurationService) {}
/**
* Gets a list of nodes that match the given search criteria.
diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts
index 37ebafbfd1..8abe79283a 100644
--- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts
+++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts
@@ -34,6 +34,7 @@ import { ContentCloudNodeSelectorService } from '../../../services/content-cloud
import { ProcessCloudContentService } from '../../../services/process-cloud-content.service';
import { UploadCloudWidgetComponent } from './upload-cloud.widget';
import { DestinationFolderPathModel } from '../../../models/form-cloud-representation.model';
+import { ContentNodeSelectorPanelService } from '@alfresco/adf-content-services';
@Component({
selector: 'adf-cloud-attach-file-cloud-widget',
@@ -75,7 +76,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
notificationService: NotificationService,
private contentNodeSelectorService: ContentCloudNodeSelectorService,
private appConfigService: AppConfigService,
- private apiService: AlfrescoApiService
+ private apiService: AlfrescoApiService,
+ private contentNodeSelectorPanelService: ContentNodeSelectorPanelService
) {
super(formService, thumbnails, processCloudContentService, notificationService, logger);
}
@@ -122,7 +124,6 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
async openSelectDialog() {
const selectedMode = this.field.params.multiple ? 'multiple' : 'single';
-
if (this.isAlfrescoAndLocal()) {
const destinationFolderPath = this.getAliasAndRelativePathFromDestinationFolderPath(this.field.params.fileSource.destinationFolderPath);
destinationFolderPath.path = this.replaceAppNameAliasWithValue(destinationFolderPath.path);
@@ -130,6 +131,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
this.rootNodeId = nodeId ? nodeId : destinationFolderPath.alias;
}
+ this.contentNodeSelectorPanelService.customModels = this.field.params.customModels;
+
this.contentNodeSelectorService
.openUploadFileDialog(this.rootNodeId, selectedMode, this.isAlfrescoAndLocal())
.subscribe((selections: Node[]) => {