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,16 +65,19 @@
|
|||||||
{ "field": "modifier", "mincount": 1, "label": "Modifier" },
|
{ "field": "modifier", "mincount": 1, "label": "Modifier" },
|
||||||
{ "field": "created", "mincount": 1, "label": "Created" }
|
{ "field": "created", "mincount": 1, "label": "Created" }
|
||||||
],
|
],
|
||||||
"facetQueries": [
|
"facetQueries": {
|
||||||
{ "query": "created:2018", "label": "Created This Year" },
|
"label": "My facet queries",
|
||||||
{ "query": "content.mimetype", "label": "Type" },
|
"queries": [
|
||||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
{ "query": "created:2018", "label": "Created This Year" },
|
||||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
{ "query": "content.mimetype", "label": "Type" },
|
||||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "Size: large" },
|
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "Size: xtra large" },
|
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
||||||
{ "query": "content.size:[134217728 TO MAX]", "label": "Size: XX large" }
|
{ "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": [
|
"categories": [
|
||||||
{
|
{
|
||||||
"id": "queryName",
|
"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,
|
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.
|
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
|
### Configuration
|
||||||
|
|
||||||
The configuration should be provided via the `search` entry in the `app.config.json` file.
|
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
|
### Settings
|
||||||
|
|
||||||
Every use case will have a different set of settings.
|
Every use case will have a different set of settings.
|
||||||
@@ -161,24 +170,35 @@ If there are more than 5 entries, the "Show more" button is displayed to allow d
|
|||||||
|
|
||||||
### Facet Queries
|
### Facet Queries
|
||||||
|
|
||||||
|
Provides a custom category based on admin-defined facet queries.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"search": {
|
"search": {
|
||||||
"facetQueries": [
|
"facetQueries": {
|
||||||
{ "query": "created:2018", "label": "Created This Year" },
|
"label": "Facet queries",
|
||||||
{ "query": "content.mimetype", "label": "Type" },
|
"pageSize": 5,
|
||||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
"expanded": true,
|
||||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
"queries": [
|
||||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
{ "query": "created:2018", "label": "Created This Year" },
|
||||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "Size: large" },
|
{ "query": "content.mimetype", "label": "Type" },
|
||||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "Size: xtra large" },
|
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||||
{ "query": "content.size:[134217728 TO MAX]", "label": "Size: XX large" }
|
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||||
]
|
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
||||||
|
{ "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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The queries declared in the `facetQueries` are collected into a single collapsible category.
|
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
|
### Text Widget
|
||||||
|
|
||||||
```json
|
```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
|
## Custom Widgets
|
||||||
|
|
||||||
### Implementing custom widget
|
### Implementing custom widget
|
||||||
|
@@ -174,7 +174,8 @@
|
|||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"CLEAR": "Clear",
|
"CLEAR": "Clear",
|
||||||
"APPLY": "Apply",
|
"APPLY": "Apply",
|
||||||
"CLEAR-ALL": "Clear all"
|
"CLEAR-ALL": "Clear all",
|
||||||
|
"SHOW-MORE": "Show more"
|
||||||
},
|
},
|
||||||
"RANGE": {
|
"RANGE": {
|
||||||
"FROM": "From",
|
"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>
|
</adf-search-widget-container>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<mat-expansion-panel>
|
<mat-expansion-panel [expanded]="facetQueriesExpanded">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>Facet Queries</mat-panel-title>
|
<mat-panel-title>{{ facetQueriesLabel | translate }}</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<div class="checklist">
|
<div class="checklist">
|
||||||
<ng-container *ngFor="let query of responseFacetQueries">
|
<ng-container *ngFor="let query of responseFacetQueries.visibleItems">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
*ngIf="query.count > 0"
|
|
||||||
[checked]="query.$checked"
|
[checked]="query.$checked"
|
||||||
(change)="onFacetQueryToggle($event, query)">
|
(change)="onFacetQueryToggle($event, query)">
|
||||||
{{ query.label }} ({{ query.count }})
|
{{ query.label }} ({{ query.count }})
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<mat-expansion-panel
|
<mat-expansion-panel
|
||||||
@@ -53,7 +58,7 @@
|
|||||||
<button mat-button
|
<button mat-button
|
||||||
*ngIf="field.hasMoreItems()"
|
*ngIf="field.hasMoreItems()"
|
||||||
(click)="field.showMoreItems()">
|
(click)="field.showMoreItems()">
|
||||||
Show more
|
{{ 'SEARCH.FILTER.ACTIONS.SHOW-MORE' | translate }}
|
||||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
@@ -20,6 +20,7 @@ import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
|||||||
import { SearchConfiguration } from '../../search-configuration.interface';
|
import { SearchConfiguration } from '../../search-configuration.interface';
|
||||||
import { AppConfigService } from '@alfresco/adf-core';
|
import { AppConfigService } from '@alfresco/adf-core';
|
||||||
import { Subject } from 'rxjs/Subject';
|
import { Subject } from 'rxjs/Subject';
|
||||||
|
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||||
|
|
||||||
describe('SearchSettingsComponent', () => {
|
describe('SearchSettingsComponent', () => {
|
||||||
|
|
||||||
@@ -119,9 +120,11 @@ describe('SearchSettingsComponent', () => {
|
|||||||
it('should unselect facet query and update builder', () => {
|
it('should unselect facet query and update builder', () => {
|
||||||
const config: SearchConfiguration = {
|
const config: SearchConfiguration = {
|
||||||
categories: [],
|
categories: [],
|
||||||
facetQueries: [
|
facetQueries: {
|
||||||
{ label: 'q1', query: 'query1' }
|
queries: [
|
||||||
]
|
{ label: 'q1', query: 'query1' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
appConfig.config.search = config;
|
appConfig.config.search = config;
|
||||||
queryBuilder = new SearchQueryBuilderService(appConfig, null);
|
queryBuilder = new SearchQueryBuilderService(appConfig, null);
|
||||||
@@ -155,10 +158,10 @@ describe('SearchSettingsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet queries from response payload', () => {
|
it('should fetch facet queries from response payload', () => {
|
||||||
component.responseFacetQueries = [];
|
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||||
const queries = [
|
const queries = [
|
||||||
{ label: 'q1', query: 'query1' },
|
{ label: 'q1', query: 'query1', count: 1 },
|
||||||
{ label: 'q2', query: 'query2' }
|
{ label: 'q2', query: 'query2', count: 1 }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
@@ -171,11 +174,11 @@ describe('SearchSettingsComponent', () => {
|
|||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries.length).toBe(2);
|
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', () => {
|
it('should not fetch facet queries from response payload', () => {
|
||||||
component.responseFacetQueries = [];
|
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
@@ -192,11 +195,11 @@ describe('SearchSettingsComponent', () => {
|
|||||||
|
|
||||||
it('should restore checked state for new response facet queries', () => {
|
it('should restore checked state for new response facet queries', () => {
|
||||||
component.selectedFacetQueries = ['q3'];
|
component.selectedFacetQueries = ['q3'];
|
||||||
component.responseFacetQueries = [];
|
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||||
|
|
||||||
const queries = [
|
const queries = [
|
||||||
{ label: 'q1', query: 'query1' },
|
{ label: 'q1', query: 'query1', count: 1 },
|
||||||
{ label: 'q2', query: 'query2' }
|
{ label: 'q2', query: 'query2', count: 1 }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
@@ -209,17 +212,17 @@ describe('SearchSettingsComponent', () => {
|
|||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries.length).toBe(2);
|
expect(component.responseFacetQueries.length).toBe(2);
|
||||||
expect((<any> component.responseFacetQueries[0]).$checked).toBeFalsy();
|
expect((<any> component.responseFacetQueries.items[0]).$checked).toBeFalsy();
|
||||||
expect((<any> component.responseFacetQueries[1]).$checked).toBeFalsy();
|
expect((<any> component.responseFacetQueries.items[1]).$checked).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not restore checked state for new response facet queries', () => {
|
it('should not restore checked state for new response facet queries', () => {
|
||||||
component.selectedFacetQueries = ['q2'];
|
component.selectedFacetQueries = ['q2'];
|
||||||
component.responseFacetQueries = [];
|
component.responseFacetQueries = new ResponseFacetQueryList();
|
||||||
|
|
||||||
const queries = [
|
const queries = [
|
||||||
{ label: 'q1', query: 'query1' },
|
{ label: 'q1', query: 'query1', count: 1 },
|
||||||
{ label: 'q2', query: 'query2' }
|
{ label: 'q2', query: 'query2', count: 1 }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
@@ -232,8 +235,8 @@ describe('SearchSettingsComponent', () => {
|
|||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries.length).toBe(2);
|
expect(component.responseFacetQueries.length).toBe(2);
|
||||||
expect((<any> component.responseFacetQueries[0]).$checked).toBeFalsy();
|
expect((<any> component.responseFacetQueries.items[0]).$checked).toBeFalsy();
|
||||||
expect((<any> component.responseFacetQueries[1]).$checked).toBeTruthy();
|
expect((<any> component.responseFacetQueries.items[1]).$checked).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet fields from response payload', () => {
|
it('should fetch facet fields from response payload', () => {
|
||||||
@@ -309,7 +312,7 @@ describe('SearchSettingsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should reset queries and fields on empty response payload', () => {
|
it('should reset queries and fields on empty response payload', () => {
|
||||||
component.responseFacetQueries = [<any> {}, <any> {}];
|
component.responseFacetQueries = new ResponseFacetQueryList([<any> {}, <any> {}]);
|
||||||
component.responseFacetFields = [<any> {}, <any> {}];
|
component.responseFacetFields = [<any> {}, <any> {}];
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
@@ -19,11 +19,11 @@ import { Component, ViewEncapsulation, OnInit } from '@angular/core';
|
|||||||
import { MatCheckboxChange } from '@angular/material';
|
import { MatCheckboxChange } from '@angular/material';
|
||||||
import { SearchService } from '@alfresco/adf-core';
|
import { SearchService } from '@alfresco/adf-core';
|
||||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||||
import { FacetQuery } from '../../facet-query.interface';
|
|
||||||
import { ResponseFacetField } from '../../response-facet-field.interface';
|
import { ResponseFacetField } from '../../response-facet-field.interface';
|
||||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
||||||
import { SearchCategory } from '../../search-category.interface';
|
import { SearchCategory } from '../../search-category.interface';
|
||||||
import { ResponseFacetQuery } from '../../response-facet-query.interface';
|
import { ResponseFacetQuery } from '../../response-facet-query.interface';
|
||||||
|
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-search-filter',
|
selector: 'adf-search-filter',
|
||||||
@@ -36,10 +36,20 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
|
|
||||||
selectedFacetQueries: string[] = [];
|
selectedFacetQueries: string[] = [];
|
||||||
selectedBuckets: FacetFieldBucket[] = [];
|
selectedBuckets: FacetFieldBucket[] = [];
|
||||||
responseFacetQueries: FacetQuery[] = [];
|
responseFacetQueries: ResponseFacetQueryList;
|
||||||
responseFacetFields: ResponseFacetField[] = [];
|
responseFacetFields: ResponseFacetField[] = [];
|
||||||
|
|
||||||
|
facetQueriesLabel: string = 'Facet Queries';
|
||||||
|
facetQueriesPageSize = 5;
|
||||||
|
facetQueriesExpanded = false;
|
||||||
|
|
||||||
constructor(public queryBuilder: SearchQueryBuilderService, private search: SearchService) {
|
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.updated.subscribe(query => {
|
||||||
this.queryBuilder.execute();
|
this.queryBuilder.execute();
|
||||||
});
|
});
|
||||||
@@ -138,11 +148,13 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
const context = data.list.context;
|
const context = data.list.context;
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
this.responseFacetQueries = (context.facetQueries || []).map(q => {
|
const facetQueries = (context.facetQueries || []).map(q => {
|
||||||
q.$checked = this.selectedFacetQueries.includes(q.label);
|
q.$checked = this.selectedFacetQueries.includes(q.label);
|
||||||
return q;
|
return q;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.responseFacetQueries = new ResponseFacetQueryList(facetQueries, this.facetQueriesPageSize);
|
||||||
|
|
||||||
const expandedFields = this.responseFacetFields.filter(f => f.expanded).map(f => f.label);
|
const expandedFields = this.responseFacetFields.filter(f => f.expanded).map(f => f.label);
|
||||||
|
|
||||||
this.responseFacetFields = (context.facetsFields || []).map(
|
this.responseFacetFields = (context.facetsFields || []).map(
|
||||||
@@ -180,7 +192,7 @@ export class SearchFilterComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.responseFacetQueries = [];
|
this.responseFacetQueries = new ResponseFacetQueryList([], this.facetQueriesPageSize);
|
||||||
this.responseFacetFields = [];
|
this.responseFacetFields = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,15 @@
|
|||||||
<mat-slider
|
<mat-slider
|
||||||
[value]="value"
|
[(value)]="value"
|
||||||
|
[disabled]="disabled"
|
||||||
[min]="min"
|
[min]="min"
|
||||||
[max]="max"
|
[max]="max"
|
||||||
[step]="step"
|
[step]="step"
|
||||||
[thumbLabel]="thumbLabel"
|
[thumbLabel]="thumbLabel"
|
||||||
(change)="onChangedHandler($event)">
|
(change)="onChangedHandler($event)">
|
||||||
</mat-slider>
|
</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();
|
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.
|
* 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 { SearchWidget } from '../../search-widget.interface';
|
||||||
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
|
import { SearchWidgetSettings } from '../../search-widget-settings.interface';
|
||||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||||
@@ -37,7 +37,9 @@ export class SearchSliderComponent implements SearchWidget, OnInit {
|
|||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
thumbLabel = false;
|
thumbLabel = false;
|
||||||
value: number;
|
|
||||||
|
@Input()
|
||||||
|
value: number | null;
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.settings) {
|
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) {
|
onChangedHandler(event: MatSliderChange) {
|
||||||
this.value = event.value;
|
this.value = event.value;
|
||||||
|
this.updateQuery(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateQuery(value: number | null) {
|
||||||
if (this.id && this.context && this.settings && this.settings.field) {
|
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();
|
this.context.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,9 @@
|
|||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
[placeholder]="settings?.placeholder"
|
[placeholder]="settings?.placeholder"
|
||||||
[value]="value"
|
[(ngModel)]="value"
|
||||||
(change)="onChangedHandler($event)">
|
(change)="onChangedHandler($event)">
|
||||||
|
<button mat-button *ngIf="value" matSuffix mat-icon-button (click)="reset()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
</mat-form-field>
|
</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({
|
@Component({
|
||||||
selector: 'adf-search-text',
|
selector: 'adf-search-text',
|
||||||
templateUrl: './search-text.component.html',
|
templateUrl: './search-text.component.html',
|
||||||
|
styleUrls: ['./search-text.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'adf-search-text' }
|
host: { class: 'adf-search-text' }
|
||||||
})
|
})
|
||||||
@@ -36,7 +37,7 @@ export class SearchTextComponent implements SearchWidget, OnInit {
|
|||||||
context: SearchQueryBuilderService;
|
context: SearchQueryBuilderService;
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.context && this.settings) {
|
if (this.context && this.settings && this.settings.pattern) {
|
||||||
const pattern = new RegExp(this.settings.pattern, 'g');
|
const pattern = new RegExp(this.settings.pattern, 'g');
|
||||||
const match = pattern.exec(this.context.queryFragments[this.id] || '');
|
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) {
|
onChangedHandler(event) {
|
||||||
this.value = event.target.value;
|
this.value = event.target.value;
|
||||||
if (this.value) {
|
this.updateQuery(this.value);
|
||||||
this.context.queryFragments[this.id] = `${this.settings.field}:'${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();
|
this.context.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,3 +19,9 @@ export interface FacetQuery {
|
|||||||
query: string;
|
query: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ResponseFacetQuery {
|
||||||
|
label?: string;
|
||||||
|
filterQuery?: string;
|
||||||
|
count?: number;
|
||||||
|
}
|
||||||
|
@@ -25,6 +25,11 @@ export interface SearchConfiguration {
|
|||||||
fields?: Array<string>;
|
fields?: Array<string>;
|
||||||
categories: Array<SearchCategory>;
|
categories: Array<SearchCategory>;
|
||||||
filterQueries?: Array<FilterQuery>;
|
filterQueries?: Array<FilterQuery>;
|
||||||
facetQueries?: Array<FacetQuery>;
|
facetQueries?: {
|
||||||
|
label?: string;
|
||||||
|
pageSize?: number;
|
||||||
|
expanded?: boolean;
|
||||||
|
queries: Array<FacetQuery>;
|
||||||
|
};
|
||||||
facetFields?: Array<FacetField>;
|
facetFields?: Array<FacetField>;
|
||||||
}
|
}
|
||||||
|
@@ -120,10 +120,12 @@ describe('SearchQueryBuilder', () => {
|
|||||||
it('should fetch facet query from config', () => {
|
it('should fetch facet query from config', () => {
|
||||||
const config: SearchConfiguration = {
|
const config: SearchConfiguration = {
|
||||||
categories: [],
|
categories: [],
|
||||||
facetQueries: [
|
facetQueries: {
|
||||||
{ query: 'q1', label: 'query1' },
|
queries: [
|
||||||
{ query: 'q2', label: 'query2' }
|
{ query: 'q1', label: 'query1' },
|
||||||
]
|
{ query: 'q2', label: 'query2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||||
const query = builder.getFacetQuery('query2');
|
const query = builder.getFacetQuery('query2');
|
||||||
@@ -135,9 +137,11 @@ describe('SearchQueryBuilder', () => {
|
|||||||
it('should not fetch empty facet query from the config', () => {
|
it('should not fetch empty facet query from the config', () => {
|
||||||
const config: SearchConfiguration = {
|
const config: SearchConfiguration = {
|
||||||
categories: [],
|
categories: [],
|
||||||
facetQueries: [
|
facetQueries: {
|
||||||
{ query: 'q1', label: 'query1' }
|
queries: [
|
||||||
]
|
{ query: 'q1', label: 'query1' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||||
|
|
||||||
@@ -273,15 +277,17 @@ describe('SearchQueryBuilder', () => {
|
|||||||
categories: [
|
categories: [
|
||||||
<any> { id: 'cat1', enabled: true }
|
<any> { id: 'cat1', enabled: true }
|
||||||
],
|
],
|
||||||
facetQueries: [
|
facetQueries: {
|
||||||
{ query: 'q1', label: 'q2' }
|
queries: [
|
||||||
]
|
{ query: 'q1', label: 'q2' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||||
builder.queryFragments['cat1'] = 'cm:name:test';
|
builder.queryFragments['cat1'] = 'cm:name:test';
|
||||||
|
|
||||||
const compiled = builder.buildQuery();
|
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', () => {
|
it('should build query with custom facet fields', () => {
|
||||||
|
@@ -69,7 +69,7 @@ export class SearchQueryBuilderService {
|
|||||||
|
|
||||||
getFacetQuery(label: string): FacetQuery {
|
getFacetQuery(label: string): FacetQuery {
|
||||||
if (label) {
|
if (label) {
|
||||||
const queries = this.config.facetQueries || [];
|
const queries = this.config.facetQueries.queries || [];
|
||||||
return queries.find(q => q.label === label);
|
return queries.find(q => q.label === label);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -115,7 +115,7 @@ export class SearchQueryBuilderService {
|
|||||||
paging: this.paging,
|
paging: this.paging,
|
||||||
fields: this.config.fields,
|
fields: this.config.fields,
|
||||||
filterQueries: this.filterQueries,
|
filterQueries: this.filterQueries,
|
||||||
facetQueries: this.config.facetQueries,
|
facetQueries: this.facetQueries,
|
||||||
facetFields: this.facetFields
|
facetFields: this.facetFields
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,6 +125,18 @@ export class SearchQueryBuilderService {
|
|||||||
return null;
|
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 {
|
private get facetFields(): RequestFacetFields {
|
||||||
const facetFields = this.config.facetFields;
|
const facetFields = this.config.facetFields;
|
||||||
|
|
||||||
|
@@ -539,13 +539,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"facetQueries": {
|
"facetQueries": {
|
||||||
"type": "array",
|
"type": "object",
|
||||||
"items": {
|
"required": ["label", "queries"],
|
||||||
"type": "object",
|
"properties": {
|
||||||
"required": [ "query", "label" ],
|
"label": {
|
||||||
"properties": {
|
"description": "Category label text",
|
||||||
"query": { "type": "string" },
|
"type": "string"
|
||||||
"label": { "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",
|
||||||
|
"required": [ "query", "label" ],
|
||||||
|
"properties": {
|
||||||
|
"query": { "type": "string" },
|
||||||
|
"label": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user