mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
search bug fixes and documentation updates (#3256)
* bug fixes for search * test fixes * bug fixes for search
This commit is contained in:
committed by
Eugenio Romano
parent
a9ab0af640
commit
856c4fd7f5
@@ -65,7 +65,9 @@
|
||||
{ "field": "modifier", "mincount": 1, "label": "Modifier" },
|
||||
{ "field": "created", "mincount": 1, "label": "Created" }
|
||||
],
|
||||
"facetQueries": [
|
||||
"facetQueries": {
|
||||
"label": "My facet queries",
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "Created This Year" },
|
||||
{ "query": "content.mimetype", "label": "Type" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||
@@ -74,7 +76,8 @@
|
||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "Size: large" },
|
||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "Size: xtra large" },
|
||||
{ "query": "content.size:[134217728 TO MAX]", "label": "Size: XX large" }
|
||||
],
|
||||
]
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": "queryName",
|
||||
|
@@ -19,6 +19,12 @@ Represents a main container component for custom search and faceted search setti
|
||||
The component is based on dynamically created widgets to modify the resulting query and options,
|
||||
and the [Search Query Builder service](search-query-builder.service.md)\` to build and execute the search queries.
|
||||
|
||||
Before you begin with customizations, check also the following articles:
|
||||
|
||||
- [Search API](https://docs.alfresco.com/5.2/concepts/search-api.html)
|
||||
- [Alfresco Full Text Search Reference](https://docs.alfresco.com/5.2/concepts/rm-searchsyntax-intro.html)
|
||||
- [ACS API Explorer](https://api-explorer.alfresco.com/api-explorer/#!/search/search)
|
||||
|
||||
### Configuration
|
||||
|
||||
The configuration should be provided via the `search` entry in the `app.config.json` file.
|
||||
@@ -121,6 +127,9 @@ The interface above also describes entries in the `search.query.categories` sect
|
||||
|
||||

|
||||
|
||||
Important note: you need at least one category field to be provided in order to execute the query,
|
||||
so that filters and selected facets are applied.
|
||||
|
||||
### Settings
|
||||
|
||||
Every use case will have a different set of settings.
|
||||
@@ -161,10 +170,16 @@ If there are more than 5 entries, the "Show more" button is displayed to allow d
|
||||
|
||||
### Facet Queries
|
||||
|
||||
Provides a custom category based on admin-defined facet queries.
|
||||
|
||||
```json
|
||||
{
|
||||
"search": {
|
||||
"facetQueries": [
|
||||
"facetQueries": {
|
||||
"label": "Facet queries",
|
||||
"pageSize": 5,
|
||||
"expanded": true,
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "Created This Year" },
|
||||
{ "query": "content.mimetype", "label": "Type" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||
@@ -175,10 +190,15 @@ If there are more than 5 entries, the "Show more" button is displayed to allow d
|
||||
{ "query": "content.size:[134217728 TO MAX]", "label": "Size: XX large" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The queries declared in the `facetQueries` are collected into a single collapsible category.
|
||||
Only the queries that have 1 or more response entries are displayed at runtime.
|
||||
Based on the `pageSize` value, the component provides a `Show more` button to display more items.
|
||||
|
||||
You can also provide a custom `label` (or i18n resource key) for the resulting collapsible category.
|
||||
|
||||

|
||||
|
||||
@@ -350,6 +370,13 @@ Provides ability to select a numeric range based on `min` and `max` values in th
|
||||
|
||||

|
||||
|
||||
### Resetting slider value
|
||||
|
||||
Slider widget comes with a `Clear` button that allows users to reset selected value to the initial state.
|
||||
|
||||
This helps to undo changes for scenarios where minimal value (like 0 or predefined number) still should not be used in a query.
|
||||
Upon clicking the `Clear` button slider will be reset to the `min` value or `0`, and underlying fragment is removed from the resulting query.
|
||||
|
||||
### Text Widget
|
||||
|
||||
```json
|
||||
@@ -377,6 +404,9 @@ Provides ability to select a numeric range based on `min` and `max` values in th
|
||||
|
||||

|
||||
|
||||
Important note: you need at least one category field to be provided in order to execute the query,
|
||||
so that filters and selected facets are applied.
|
||||
|
||||
## Custom Widgets
|
||||
|
||||
### Implementing custom widget
|
||||
|
@@ -174,7 +174,8 @@
|
||||
"ACTIONS": {
|
||||
"CLEAR": "Clear",
|
||||
"APPLY": "Apply",
|
||||
"CLEAR-ALL": "Clear all"
|
||||
"CLEAR-ALL": "Clear all",
|
||||
"SHOW-MORE": "Show more"
|
||||
},
|
||||
"RANGE": {
|
||||
"FROM": "From",
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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 { ResponseFacetQuery } from '../../../facet-query.interface';
|
||||
|
||||
export class ResponseFacetQueryList {
|
||||
|
||||
items: ResponseFacetQuery[] = [];
|
||||
pageSize: number = 5;
|
||||
currentPageSize: number = 5;
|
||||
|
||||
get visibleItems(): ResponseFacetQuery[] {
|
||||
return this.items.slice(0, this.currentPageSize);
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
constructor(items: ResponseFacetQuery[] = [], pageSize: number = 5) {
|
||||
this.items = items
|
||||
.filter(item => {
|
||||
return item.count > 0;
|
||||
})
|
||||
.map(item => {
|
||||
return <ResponseFacetQuery> { ...item };
|
||||
});
|
||||
this.pageSize = pageSize;
|
||||
this.currentPageSize = pageSize;
|
||||
}
|
||||
|
||||
hasMoreItems(): boolean {
|
||||
return this.items.length > this.currentPageSize;
|
||||
}
|
||||
|
||||
showMoreItems() {
|
||||
this.currentPageSize += this.pageSize;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.currentPageSize = this.pageSize;
|
||||
this.items = [];
|
||||
}
|
||||
}
|
@@ -17,20 +17,25 @@
|
||||
</adf-search-widget-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel [expanded]="facetQueriesExpanded">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>Facet Queries</mat-panel-title>
|
||||
<mat-panel-title>{{ facetQueriesLabel | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="checklist">
|
||||
<ng-container *ngFor="let query of responseFacetQueries">
|
||||
<ng-container *ngFor="let query of responseFacetQueries.visibleItems">
|
||||
<mat-checkbox
|
||||
*ngIf="query.count > 0"
|
||||
[checked]="query.$checked"
|
||||
(change)="onFacetQueryToggle($event, query)">
|
||||
{{ query.label }} ({{ query.count }})
|
||||
</mat-checkbox>
|
||||
</ng-container>
|
||||
</div>
|
||||
<button mat-button
|
||||
*ngIf="responseFacetQueries.hasMoreItems()"
|
||||
(click)="responseFacetQueries.showMoreItems()">
|
||||
{{ 'SEARCH.FILTER.ACTIONS.SHOW-MORE' | translate }}
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel
|
||||
@@ -53,7 +58,7 @@
|
||||
<button mat-button
|
||||
*ngIf="field.hasMoreItems()"
|
||||
(click)="field.showMoreItems()">
|
||||
Show more
|
||||
{{ 'SEARCH.FILTER.ACTIONS.SHOW-MORE' | translate }}
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
</mat-expansion-panel>
|
||||
|
@@ -20,6 +20,7 @@ import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
import { SearchConfiguration } from '../../search-configuration.interface';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||
|
||||
describe('SearchSettingsComponent', () => {
|
||||
|
||||
@@ -119,9 +120,11 @@ describe('SearchSettingsComponent', () => {
|
||||
it('should unselect facet query and update builder', () => {
|
||||
const config: SearchConfiguration = {
|
||||
categories: [],
|
||||
facetQueries: [
|
||||
facetQueries: {
|
||||
queries: [
|
||||
{ label: 'q1', query: 'query1' }
|
||||
]
|
||||
}
|
||||
};
|
||||
appConfig.config.search = config;
|
||||
queryBuilder = new SearchQueryBuilderService(appConfig, null);
|
||||
@@ -155,10 +158,10 @@ describe('SearchSettingsComponent', () => {
|
||||
});
|
||||
|
||||
it('should fetch facet queries from response payload', () => {
|
||||
component.responseFacetQueries = [];
|
||||
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||
const queries = [
|
||||
{ label: 'q1', query: 'query1' },
|
||||
{ label: 'q2', query: 'query2' }
|
||||
{ label: 'q1', query: 'query1', count: 1 },
|
||||
{ label: 'q2', query: 'query2', count: 1 }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
@@ -171,11 +174,11 @@ describe('SearchSettingsComponent', () => {
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries.length).toBe(2);
|
||||
expect(component.responseFacetQueries).toEqual(queries);
|
||||
expect(component.responseFacetQueries.items).toEqual(queries);
|
||||
});
|
||||
|
||||
it('should not fetch facet queries from response payload', () => {
|
||||
component.responseFacetQueries = [];
|
||||
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||
|
||||
const data = {
|
||||
list: {
|
||||
@@ -192,11 +195,11 @@ describe('SearchSettingsComponent', () => {
|
||||
|
||||
it('should restore checked state for new response facet queries', () => {
|
||||
component.selectedFacetQueries = ['q3'];
|
||||
component.responseFacetQueries = [];
|
||||
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||
|
||||
const queries = [
|
||||
{ label: 'q1', query: 'query1' },
|
||||
{ label: 'q2', query: 'query2' }
|
||||
{ label: 'q1', query: 'query1', count: 1 },
|
||||
{ label: 'q2', query: 'query2', count: 1 }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
@@ -209,17 +212,17 @@ describe('SearchSettingsComponent', () => {
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries.length).toBe(2);
|
||||
expect((<any> component.responseFacetQueries[0]).$checked).toBeFalsy();
|
||||
expect((<any> component.responseFacetQueries[1]).$checked).toBeFalsy();
|
||||
expect((<any> component.responseFacetQueries.items[0]).$checked).toBeFalsy();
|
||||
expect((<any> component.responseFacetQueries.items[1]).$checked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not restore checked state for new response facet queries', () => {
|
||||
component.selectedFacetQueries = ['q2'];
|
||||
component.responseFacetQueries = [];
|
||||
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||
|
||||
const queries = [
|
||||
{ label: 'q1', query: 'query1' },
|
||||
{ label: 'q2', query: 'query2' }
|
||||
{ label: 'q1', query: 'query1', count: 1 },
|
||||
{ label: 'q2', query: 'query2', count: 1 }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
@@ -232,8 +235,8 @@ describe('SearchSettingsComponent', () => {
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries.length).toBe(2);
|
||||
expect((<any> component.responseFacetQueries[0]).$checked).toBeFalsy();
|
||||
expect((<any> component.responseFacetQueries[1]).$checked).toBeTruthy();
|
||||
expect((<any> component.responseFacetQueries.items[0]).$checked).toBeFalsy();
|
||||
expect((<any> component.responseFacetQueries.items[1]).$checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload', () => {
|
||||
@@ -309,7 +312,7 @@ describe('SearchSettingsComponent', () => {
|
||||
});
|
||||
|
||||
it('should reset queries and fields on empty response payload', () => {
|
||||
component.responseFacetQueries = [<any> {}, <any> {}];
|
||||
component.responseFacetQueries = new ResponseFacetQueryList([<any> {}, <any> {}]);
|
||||
component.responseFacetFields = [<any> {}, <any> {}];
|
||||
|
||||
const data = {
|
||||
|
@@ -19,11 +19,11 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { MatCheckboxChange } from '@angular/material';
|
||||
import { SearchService } from '@alfresco/adf-core';
|
||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
import { FacetQuery } from '../../facet-query.interface';
|
||||
import { ResponseFacetField } from '../../response-facet-field.interface';
|
||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
||||
import { SearchCategory } from '../../search-category.interface';
|
||||
import { ResponseFacetQuery } from '../../response-facet-query.interface';
|
||||
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-search-filter',
|
||||
@@ -36,10 +36,20 @@ export class SearchFilterComponent implements OnInit {
|
||||
|
||||
selectedFacetQueries: string[] = [];
|
||||
selectedBuckets: FacetFieldBucket[] = [];
|
||||
responseFacetQueries: FacetQuery[] = [];
|
||||
responseFacetQueries: ResponseFacetQueryList;
|
||||
responseFacetFields: ResponseFacetField[] = [];
|
||||
|
||||
facetQueriesLabel: string = 'Facet Queries';
|
||||
facetQueriesPageSize = 5;
|
||||
facetQueriesExpanded = false;
|
||||
|
||||
constructor(public queryBuilder: SearchQueryBuilderService, private search: SearchService) {
|
||||
if (queryBuilder.config && queryBuilder.config.facetQueries) {
|
||||
this.facetQueriesLabel = queryBuilder.config.facetQueries.label || 'Facet Queries';
|
||||
this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || 5;
|
||||
this.facetQueriesExpanded = queryBuilder.config.facetQueries.expanded;
|
||||
}
|
||||
|
||||
this.queryBuilder.updated.subscribe(query => {
|
||||
this.queryBuilder.execute();
|
||||
});
|
||||
@@ -138,11 +148,13 @@ export class SearchFilterComponent implements OnInit {
|
||||
const context = data.list.context;
|
||||
|
||||
if (context) {
|
||||
this.responseFacetQueries = (context.facetQueries || []).map(q => {
|
||||
const facetQueries = (context.facetQueries || []).map(q => {
|
||||
q.$checked = this.selectedFacetQueries.includes(q.label);
|
||||
return q;
|
||||
});
|
||||
|
||||
this.responseFacetQueries = new ResponseFacetQueryList(facetQueries, this.facetQueriesPageSize);
|
||||
|
||||
const expandedFields = this.responseFacetFields.filter(f => f.expanded).map(f => f.label);
|
||||
|
||||
this.responseFacetFields = (context.facetsFields || []).map(
|
||||
@@ -180,7 +192,7 @@ export class SearchFilterComponent implements OnInit {
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.responseFacetQueries = [];
|
||||
this.responseFacetQueries = new ResponseFacetQueryList([], this.facetQueriesPageSize);
|
||||
this.responseFacetFields = [];
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,15 @@
|
||||
<mat-slider
|
||||
[value]="value"
|
||||
[(value)]="value"
|
||||
[disabled]="disabled"
|
||||
[min]="min"
|
||||
[max]="max"
|
||||
[step]="step"
|
||||
[thumbLabel]="thumbLabel"
|
||||
(change)="onChangedHandler($event)">
|
||||
</mat-slider>
|
||||
|
||||
<div>
|
||||
<button mat-button color="primary" (click)="reset()">
|
||||
{{ 'SEARCH.FILTER.ACTIONS.CLEAR' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -70,4 +70,62 @@ describe('SearchSliderComponent', () => {
|
||||
expect(context.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reset the value for query builder', () => {
|
||||
const settings: any = {
|
||||
field: 'field1',
|
||||
min: 10,
|
||||
max: 100,
|
||||
step: 2,
|
||||
thumbLabel: true
|
||||
};
|
||||
|
||||
const context: any = {
|
||||
queryFragments: {},
|
||||
update() {}
|
||||
};
|
||||
|
||||
component.settings = settings;
|
||||
component.context = context;
|
||||
component.value = 20;
|
||||
component.id = 'slider';
|
||||
component.ngOnInit();
|
||||
|
||||
spyOn(context, 'update').and.stub();
|
||||
|
||||
component.reset();
|
||||
|
||||
expect(component.value).toBe(settings.min);
|
||||
expect(context.queryFragments['slider']).toBe('');
|
||||
expect(context.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reset to 0 if min not provided', () => {
|
||||
const settings: any = {
|
||||
field: 'field1',
|
||||
min: null,
|
||||
max: 100,
|
||||
step: 2,
|
||||
thumbLabel: true
|
||||
};
|
||||
|
||||
const context: any = {
|
||||
queryFragments: {},
|
||||
update() {}
|
||||
};
|
||||
|
||||
component.settings = settings;
|
||||
component.context = context;
|
||||
component.value = 20;
|
||||
component.id = 'slider';
|
||||
component.ngOnInit();
|
||||
|
||||
spyOn(context, 'update').and.stub();
|
||||
|
||||
component.reset();
|
||||
|
||||
expect(component.value).toBe(0);
|
||||
expect(context.queryFragments['slider']).toBe('');
|
||||
expect(context.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
|
||||
import { Component, ViewEncapsulation, OnInit, Input } from '@angular/core';
|
||||
import { SearchWidget } from '../../search-widget.interface';
|
||||
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
|
||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
@@ -37,7 +37,9 @@ export class SearchSliderComponent implements SearchWidget, OnInit {
|
||||
min: number;
|
||||
max: number;
|
||||
thumbLabel = false;
|
||||
value: number;
|
||||
|
||||
@Input()
|
||||
value: number | null;
|
||||
|
||||
ngOnInit() {
|
||||
if (this.settings) {
|
||||
@@ -57,11 +59,23 @@ export class SearchSliderComponent implements SearchWidget, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.value = this.min || 0;
|
||||
this.updateQuery(null);
|
||||
}
|
||||
|
||||
onChangedHandler(event: MatSliderChange) {
|
||||
this.value = event.value;
|
||||
this.updateQuery(this.value);
|
||||
}
|
||||
|
||||
private updateQuery(value: number | null) {
|
||||
if (this.id && this.context && this.settings && this.settings.field) {
|
||||
this.context.queryFragments[this.id] = `${this.settings.field}:[0 TO ${this.value}]`;
|
||||
if (value === null) {
|
||||
this.context.queryFragments[this.id] = '';
|
||||
} else {
|
||||
this.context.queryFragments[this.id] = `${this.settings.field}:[0 TO ${value}]`;
|
||||
}
|
||||
this.context.update();
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,9 @@
|
||||
<input
|
||||
matInput
|
||||
[placeholder]="settings?.placeholder"
|
||||
[value]="value"
|
||||
[(ngModel)]="value"
|
||||
(change)="onChangedHandler($event)">
|
||||
<button mat-button *ngIf="value" matSuffix mat-icon-button (click)="reset()">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
|
@@ -0,0 +1,5 @@
|
||||
.adf-search-text {
|
||||
.mat-input-container {
|
||||
width: 100%
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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 { SearchTextComponent } from './search-text.component';
|
||||
|
||||
describe('SearchTextComponent', () => {
|
||||
|
||||
let component: SearchTextComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
component = new SearchTextComponent();
|
||||
component.id = 'text';
|
||||
component.settings = {
|
||||
'pattern': "cm:name:'(.*?)'",
|
||||
'field': 'cm:name',
|
||||
'placeholder': 'Enter the name'
|
||||
};
|
||||
|
||||
component.context = <any> {
|
||||
queryFragments: {},
|
||||
update() {}
|
||||
};
|
||||
});
|
||||
|
||||
it('should parse value from the context at startup', () => {
|
||||
component.context.queryFragments[component.id] = "cm:name:'secret.pdf'";
|
||||
component.ngOnInit();
|
||||
|
||||
expect(component.value).toEqual('secret.pdf');
|
||||
});
|
||||
|
||||
it('should not parse value when pattern not defined', () => {
|
||||
component.settings.pattern = null;
|
||||
component.context.queryFragments[component.id] = "cm:name:'secret.pdf'";
|
||||
component.ngOnInit();
|
||||
|
||||
expect(component.value).toEqual('');
|
||||
});
|
||||
|
||||
it('should update query builder on change', () => {
|
||||
spyOn(component.context, 'update').and.stub();
|
||||
|
||||
component.onChangedHandler({
|
||||
target: {
|
||||
value: 'top-secret.doc'
|
||||
}
|
||||
});
|
||||
|
||||
expect(component.value).toBe('top-secret.doc');
|
||||
expect(component.context.queryFragments[component.id]).toBe("cm:name:'top-secret.doc'");
|
||||
expect(component.context.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reset query builder', () => {
|
||||
component.onChangedHandler({
|
||||
target: {
|
||||
value: 'top-secret.doc'
|
||||
}
|
||||
});
|
||||
|
||||
expect(component.value).toBe('top-secret.doc');
|
||||
expect(component.context.queryFragments[component.id]).toBe("cm:name:'top-secret.doc'");
|
||||
|
||||
component.onChangedHandler({
|
||||
target: {
|
||||
value: ''
|
||||
}
|
||||
});
|
||||
|
||||
expect(component.value).toBe('');
|
||||
expect(component.context.queryFragments[component.id]).toBe('');
|
||||
});
|
||||
|
||||
});
|
@@ -23,6 +23,7 @@ import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
@Component({
|
||||
selector: 'adf-search-text',
|
||||
templateUrl: './search-text.component.html',
|
||||
styleUrls: ['./search-text.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'adf-search-text' }
|
||||
})
|
||||
@@ -36,7 +37,7 @@ export class SearchTextComponent implements SearchWidget, OnInit {
|
||||
context: SearchQueryBuilderService;
|
||||
|
||||
ngOnInit() {
|
||||
if (this.context && this.settings) {
|
||||
if (this.context && this.settings && this.settings.pattern) {
|
||||
const pattern = new RegExp(this.settings.pattern, 'g');
|
||||
const match = pattern.exec(this.context.queryFragments[this.id] || '');
|
||||
|
||||
@@ -46,10 +47,19 @@ export class SearchTextComponent implements SearchWidget, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.value = '';
|
||||
this.updateQuery(null);
|
||||
}
|
||||
|
||||
onChangedHandler(event) {
|
||||
this.value = event.target.value;
|
||||
if (this.value) {
|
||||
this.context.queryFragments[this.id] = `${this.settings.field}:'${this.value}'`;
|
||||
this.updateQuery(this.value);
|
||||
}
|
||||
|
||||
private updateQuery(value: string) {
|
||||
if (this.context && this.settings && this.settings.field) {
|
||||
this.context.queryFragments[this.id] = value ? `${this.settings.field}:'${value}'` : '';
|
||||
this.context.update();
|
||||
}
|
||||
}
|
||||
|
@@ -19,3 +19,9 @@ export interface FacetQuery {
|
||||
query: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface ResponseFacetQuery {
|
||||
label?: string;
|
||||
filterQuery?: string;
|
||||
count?: number;
|
||||
}
|
||||
|
@@ -25,6 +25,11 @@ export interface SearchConfiguration {
|
||||
fields?: Array<string>;
|
||||
categories: Array<SearchCategory>;
|
||||
filterQueries?: Array<FilterQuery>;
|
||||
facetQueries?: Array<FacetQuery>;
|
||||
facetQueries?: {
|
||||
label?: string;
|
||||
pageSize?: number;
|
||||
expanded?: boolean;
|
||||
queries: Array<FacetQuery>;
|
||||
};
|
||||
facetFields?: Array<FacetField>;
|
||||
}
|
||||
|
@@ -120,10 +120,12 @@ describe('SearchQueryBuilder', () => {
|
||||
it('should fetch facet query from config', () => {
|
||||
const config: SearchConfiguration = {
|
||||
categories: [],
|
||||
facetQueries: [
|
||||
facetQueries: {
|
||||
queries: [
|
||||
{ query: 'q1', label: 'query1' },
|
||||
{ query: 'q2', label: 'query2' }
|
||||
]
|
||||
}
|
||||
};
|
||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||
const query = builder.getFacetQuery('query2');
|
||||
@@ -135,9 +137,11 @@ describe('SearchQueryBuilder', () => {
|
||||
it('should not fetch empty facet query from the config', () => {
|
||||
const config: SearchConfiguration = {
|
||||
categories: [],
|
||||
facetQueries: [
|
||||
facetQueries: {
|
||||
queries: [
|
||||
{ query: 'q1', label: 'query1' }
|
||||
]
|
||||
}
|
||||
};
|
||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||
|
||||
@@ -273,15 +277,17 @@ describe('SearchQueryBuilder', () => {
|
||||
categories: [
|
||||
<any> { id: 'cat1', enabled: true }
|
||||
],
|
||||
facetQueries: [
|
||||
facetQueries: {
|
||||
queries: [
|
||||
{ query: 'q1', label: 'q2' }
|
||||
]
|
||||
}
|
||||
};
|
||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||
builder.queryFragments['cat1'] = 'cm:name:test';
|
||||
|
||||
const compiled = builder.buildQuery();
|
||||
expect(compiled.facetQueries).toEqual(config.facetQueries);
|
||||
expect(compiled.facetQueries).toEqual(config.facetQueries.queries);
|
||||
});
|
||||
|
||||
it('should build query with custom facet fields', () => {
|
||||
|
@@ -69,7 +69,7 @@ export class SearchQueryBuilderService {
|
||||
|
||||
getFacetQuery(label: string): FacetQuery {
|
||||
if (label) {
|
||||
const queries = this.config.facetQueries || [];
|
||||
const queries = this.config.facetQueries.queries || [];
|
||||
return queries.find(q => q.label === label);
|
||||
}
|
||||
return null;
|
||||
@@ -115,7 +115,7 @@ export class SearchQueryBuilderService {
|
||||
paging: this.paging,
|
||||
fields: this.config.fields,
|
||||
filterQueries: this.filterQueries,
|
||||
facetQueries: this.config.facetQueries,
|
||||
facetQueries: this.facetQueries,
|
||||
facetFields: this.facetFields
|
||||
};
|
||||
|
||||
@@ -125,6 +125,18 @@ export class SearchQueryBuilderService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private get facetQueries(): FacetQuery[] {
|
||||
const config = this.config.facetQueries;
|
||||
|
||||
if (config && config.queries && config.queries.length > 0) {
|
||||
return config.queries.map(query => {
|
||||
return <FacetQuery> { ...query };
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private get facetFields(): RequestFacetFields {
|
||||
const facetFields = this.config.facetFields;
|
||||
|
||||
|
@@ -539,6 +539,23 @@
|
||||
}
|
||||
},
|
||||
"facetQueries": {
|
||||
"type": "object",
|
||||
"required": ["label", "queries"],
|
||||
"properties": {
|
||||
"label": {
|
||||
"description": "Category label text",
|
||||
"type": "string"
|
||||
},
|
||||
"pageSize": {
|
||||
"description": "Default page size of the category",
|
||||
"type": "number"
|
||||
},
|
||||
"expanded": {
|
||||
"description": "Toggles expanded state of the category",
|
||||
"type": "boolean"
|
||||
},
|
||||
"queries": {
|
||||
"description": "List of custom facet queries",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
@@ -548,6 +565,8 @@
|
||||
"label": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"categories": {
|
||||
"type": "array",
|
||||
|
Reference in New Issue
Block a user