mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACS-5179] Add search facet tabbed component for creator and modifier (#8775)
This commit is contained in:
parent
8eb43b00ad
commit
f8f72d7f1a
49
docs/content-services/components/search-facet-chip-tabbed.md
Normal file
49
docs/content-services/components/search-facet-chip-tabbed.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
Title: Search facet chip tabbed component
|
||||||
|
Added: v6.2.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2023-07-18
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Search facet chip tabbed component](../../../lib/content-services/src/lib/search/components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component.ts "Defined in search-facet-chip-tabbed.component.ts")
|
||||||
|
|
||||||
|
Implements a [facet widget](../../../lib/content-services/src/lib/search/models/facet-widget.interface.ts) consisting of creator and modifier facets inside tabbed component.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
When both creator and modifier facets are present in config file as stated below they will be merged into this component.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"search": {
|
||||||
|
"facetFields": {
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"mincount": 1,
|
||||||
|
"field": "creator",
|
||||||
|
"label": "SEARCH.FACET_FIELDS.CREATOR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mincount": 1,
|
||||||
|
"field": "modifier",
|
||||||
|
"label": "SEARCH.FACET_FIELDS.MODIFIER",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| tabbedFacet | [TabbedFacetField](../../../lib/content-services/src/lib/search/models/tabbed-facet-field.interface.ts) | Tabbed facet configuration containing label, fields and facets to display. Required value |
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Search Configuration Guide](../../user-guide/search-configuration-guide.md)
|
||||||
|
- [Search Query Builder service](../services/search-query-builder.service.md)
|
||||||
|
- [Search Widget Interface](../interfaces/search-widget.interface.md)
|
||||||
|
- [Search chip autocomplete input component](search-chip-autocomplete-input.component.md)
|
BIN
docs/docassets/images/search-facet-chip-tabbed.png
Normal file
BIN
docs/docassets/images/search-facet-chip-tabbed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
@ -51,6 +51,7 @@ backend services have been tested with each released version of ADF.
|
|||||||
- [Search Date Range Advanced Component](content-services/components/search-date-range-advanced.component.md)
|
- [Search Date Range Advanced Component](content-services/components/search-date-range-advanced.component.md)
|
||||||
- [Search Date Range Advanced Tabbed Component](content-services/components/search-date-range-advanced-tabbed.component.md)
|
- [Search Date Range Advanced Tabbed Component](content-services/components/search-date-range-advanced-tabbed.component.md)
|
||||||
- [Search Filter Tabbed Component](content-services/components/search-filter-tabbed.component.md)
|
- [Search Filter Tabbed Component](content-services/components/search-filter-tabbed.component.md)
|
||||||
|
- [Search Facet Chip Tabbed Component](content-services/components/search-facet-chip-tabbed.md)
|
||||||
|
|
||||||
<!--v620 end-->
|
<!--v620 end-->
|
||||||
|
|
||||||
|
@ -336,7 +336,12 @@
|
|||||||
"SEARCH_FILTER": "Search Filter List",
|
"SEARCH_FILTER": "Search Filter List",
|
||||||
"OPTIONS-SELECTION": "Options Selection"
|
"OPTIONS-SELECTION": "Options Selection"
|
||||||
},
|
},
|
||||||
"ANY": "Any"
|
"ANY": "Any",
|
||||||
|
"PEOPLE": "People"
|
||||||
|
},
|
||||||
|
"FACET_FIELDS": {
|
||||||
|
"MODIFIER_LABEL": "Modified by",
|
||||||
|
"CREATOR_LABEL": "Created by"
|
||||||
},
|
},
|
||||||
"ICONS": {
|
"ICONS": {
|
||||||
"ft_ic_raster_image": "Image file",
|
"ft_ic_raster_image": "Image file",
|
||||||
|
@ -105,7 +105,7 @@ export class SearchChipAutocompleteInputComponent implements OnInit, OnDestroy,
|
|||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (changes.autocompleteOptions) {
|
if (changes.autocompleteOptions) {
|
||||||
this.filteredOptions = changes.autocompleteOptions.currentValue.length > 0 ? this.filter(changes.autocompleteOptions.currentValue, this.formCtrl.value) : [];
|
this.filteredOptions = changes.autocompleteOptions.currentValue?.length > 0 ? this.filter(changes.autocompleteOptions.currentValue, this.formCtrl.value) : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +54,15 @@ describe('SearchFacetFieldComponent', () => {
|
|||||||
spyOn(queryBuilder, 'addUserFacetBucket').and.callThrough();
|
spyOn(queryBuilder, 'addUserFacetBucket').and.callThrough();
|
||||||
|
|
||||||
const event: any = { checked: true };
|
const event: any = { checked: true };
|
||||||
const field: FacetField = { field: 'f1', label: 'f1', buckets: new SearchFilterList() };
|
const facetField: FacetField = { field: 'f1', label: 'f1', buckets: new SearchFilterList() };
|
||||||
const bucket: FacetFieldBucket = { checked: false, filterQuery: 'q1', label: 'q1', count: 1 };
|
const bucket: FacetFieldBucket = { checked: false, filterQuery: 'q1', label: 'q1', count: 1 };
|
||||||
component.field = field;
|
component.field = facetField;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onToggleBucket(event, field, bucket);
|
component.onToggleBucket(event, facetField, bucket);
|
||||||
|
|
||||||
expect(bucket.checked).toBeTruthy();
|
expect(bucket.checked).toBeTruthy();
|
||||||
expect(queryBuilder.addUserFacetBucket).toHaveBeenCalledWith(field, bucket);
|
expect(queryBuilder.addUserFacetBucket).toHaveBeenCalledWith(facetField.field, bucket);
|
||||||
expect(queryBuilder.update).toHaveBeenCalled();
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -72,15 +72,15 @@ describe('SearchFacetFieldComponent', () => {
|
|||||||
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
||||||
|
|
||||||
const event: any = { checked: false };
|
const event: any = { checked: false };
|
||||||
const field: FacetField = { field: 'f1', label: 'f1', buckets: new SearchFilterList() };
|
const facetField: FacetField = { field: 'f1', label: 'f1', buckets: new SearchFilterList() };
|
||||||
const bucket: FacetFieldBucket = { checked: true, filterQuery: 'q1', label: 'q1', count: 1 };
|
const bucket: FacetFieldBucket = { checked: true, filterQuery: 'q1', label: 'q1', count: 1 };
|
||||||
|
|
||||||
component.field = field;
|
component.field = facetField;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onToggleBucket(event, field, bucket);
|
component.onToggleBucket(event, facetField, bucket);
|
||||||
|
|
||||||
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(field, bucket);
|
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(facetField.field, bucket);
|
||||||
expect(queryBuilder.update).toHaveBeenCalled();
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -91,15 +91,15 @@ describe('SearchFacetFieldComponent', () => {
|
|||||||
|
|
||||||
const event: any = { checked: false };
|
const event: any = { checked: false };
|
||||||
const query = { checked: true, label: 'q1', filterQuery: 'query1' };
|
const query = { checked: true, label: 'q1', filterQuery: 'query1' };
|
||||||
const field = { field: 'q1', type: 'query', label: 'label1', buckets: new SearchFilterList([ query ] ) } as FacetField;
|
const facetField = { field: 'q1', type: 'query', label: 'label1', buckets: new SearchFilterList([ query ] ) } as FacetField;
|
||||||
|
|
||||||
component.field = field;
|
component.field = facetField;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.onToggleBucket(event, field, query as any);
|
component.onToggleBucket(event, facetField, query as any);
|
||||||
|
|
||||||
expect(query.checked).toEqual(false);
|
expect(query.checked).toEqual(false);
|
||||||
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(field, query);
|
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(facetField.field, query);
|
||||||
expect(queryBuilder.update).toHaveBeenCalled();
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
expect(searchFacetFiltersService.updateSelectedBuckets).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@ export class SearchFacetFieldComponent implements FacetWidget {
|
|||||||
selectFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
selectFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
bucket.checked = true;
|
bucket.checked = true;
|
||||||
this.queryBuilder.addUserFacetBucket(field, bucket);
|
this.queryBuilder.addUserFacetBucket(field.field, bucket);
|
||||||
this.searchFacetFiltersService.updateSelectedBuckets();
|
this.searchFacetFiltersService.updateSelectedBuckets();
|
||||||
if (this.canUpdateOnChange) {
|
if (this.canUpdateOnChange) {
|
||||||
this.updateDisplayValue();
|
this.updateDisplayValue();
|
||||||
@ -73,7 +73,7 @@ export class SearchFacetFieldComponent implements FacetWidget {
|
|||||||
unselectFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
unselectFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
bucket.checked = false;
|
bucket.checked = false;
|
||||||
this.queryBuilder.removeUserFacetBucket(field, bucket);
|
this.queryBuilder.removeUserFacetBucket(field.field, bucket);
|
||||||
this.searchFacetFiltersService.updateSelectedBuckets();
|
this.searchFacetFiltersService.updateSelectedBuckets();
|
||||||
if (this.canUpdateOnChange) {
|
if (this.canUpdateOnChange) {
|
||||||
this.updateDisplayValue();
|
this.updateDisplayValue();
|
||||||
@ -93,7 +93,7 @@ export class SearchFacetFieldComponent implements FacetWidget {
|
|||||||
if (field && field.buckets) {
|
if (field && field.buckets) {
|
||||||
for (const bucket of field.buckets.items) {
|
for (const bucket of field.buckets.items) {
|
||||||
bucket.checked = false;
|
bucket.checked = false;
|
||||||
this.queryBuilder.removeUserFacetBucket(field, bucket);
|
this.queryBuilder.removeUserFacetBucket(field.field, bucket);
|
||||||
}
|
}
|
||||||
this.searchFacetFiltersService.updateSelectedBuckets();
|
this.searchFacetFiltersService.updateSelectedBuckets();
|
||||||
if (this.canUpdateOnChange) {
|
if (this.canUpdateOnChange) {
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
<mat-chip [attr.data-automation-id]="'search-filter-chip-tabbed-' + tabbedFacet.label"
|
||||||
|
disableRipple
|
||||||
|
class="adf-search-filter-chip-tabbed"
|
||||||
|
[class.adf-search-toggle-chip]="(displayValue$ | async) || menuTrigger.menuOpen"
|
||||||
|
[disabled]="!isPopulated"
|
||||||
|
tabIndex="0"
|
||||||
|
[matMenuTriggerFor]="menu"
|
||||||
|
(onMenuOpen)="onMenuOpen()"
|
||||||
|
(keydown.enter)="onEnterKeydown()"
|
||||||
|
(keydown.escape)="onEscKeydown()"
|
||||||
|
[attr.title]="displayValue$ | async"
|
||||||
|
#menuTrigger="matMenuTrigger">
|
||||||
|
|
||||||
|
<span class="adf-search-filter-placeholder">{{ tabbedFacet.label | translate }}:</span>
|
||||||
|
|
||||||
|
<span class="adf-search-filter-ellipsis adf-filter-value" *ngIf="displayValue$ | async as displayValue; else showAny">
|
||||||
|
{{ displayValue | translate }}
|
||||||
|
</span>
|
||||||
|
<ng-template #showAny><span class="adf-search-filter-ellipsis adf-filter-value"> {{ 'SEARCH.FILTER.ANY' | translate }}</span></ng-template>
|
||||||
|
<mat-icon *ngIf="isPopulated; else disabledIcon">{{ chipIcon }}</mat-icon>
|
||||||
|
<ng-template #disabledIcon>
|
||||||
|
<mat-icon>remove</mat-icon>
|
||||||
|
</ng-template>
|
||||||
|
</mat-chip>
|
||||||
|
|
||||||
|
<mat-menu #menu="matMenu" class="adf-search-widget-extra-width" backdropClass="adf-search-filter-chip-menu" (closed)="onClosed()">
|
||||||
|
<div #menuContainer [attr.data-automation-id]="'search-field-' + tabbedFacet.label">
|
||||||
|
<adf-search-filter-menu-card (click)="$event.stopPropagation()"
|
||||||
|
(keydown.tab)="$event.stopPropagation();"
|
||||||
|
(keydown.shift.tab)="$event.stopPropagation()"
|
||||||
|
(close)="menuTrigger.closeMenu()">
|
||||||
|
<ng-container ngProjectAs="filter-title">
|
||||||
|
{{ tabbedFacet.label | translate }}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container ngProjectAs="filter-content">
|
||||||
|
<adf-search-filter-tabbed>
|
||||||
|
<ng-container *ngFor="let field of tabbedFacet.fields">
|
||||||
|
<adf-search-chip-autocomplete-input
|
||||||
|
*adf-search-filter-tab="tabbedFacet.facets[field]?.label"
|
||||||
|
[autocompleteOptions]="autocompleteOptions[field]"
|
||||||
|
[onReset$]="reset$"
|
||||||
|
[allowOnlyPredefinedValues]="true"
|
||||||
|
[compareOption]="optionComparator"
|
||||||
|
(optionsChanged)="onOptionsChange($event, field)">
|
||||||
|
</adf-search-chip-autocomplete-input>
|
||||||
|
</ng-container>
|
||||||
|
</adf-search-filter-tabbed>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container ngProjectAs="filter-actions">
|
||||||
|
<button mat-button class="adf-search-action-button" (click)="onRemove()" id="cancel-filter-button">
|
||||||
|
{{ 'SEARCH.FILTER.BUTTONS.CLEAR' | translate }}
|
||||||
|
</button>
|
||||||
|
<button mat-flat-button class="adf-search-action-button" color="primary" (click)="onApply()" id="apply-filter-button">
|
||||||
|
{{ 'SEARCH.FILTER.BUTTONS.APPLY' | translate }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</adf-search-filter-menu-card>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
@ -0,0 +1,17 @@
|
|||||||
|
adf-search-facet-chip-tabbed {
|
||||||
|
.adf-search-filter-chip-tabbed {
|
||||||
|
&[disabled] {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-search-widget-extra-width {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adf-search-filter-tabbed {
|
||||||
|
.mat-tab-body-wrapper {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,232 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ContentTestingModule } from '../../../../testing/content.testing.module';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { SearchQueryBuilderService } from '../../../services/search-query-builder.service';
|
||||||
|
import { SearchFilterList } from '../../../models/search-filter-list.model';
|
||||||
|
import { SearchFacetChipTabbedComponent } from './search-facet-chip-tabbed.component';
|
||||||
|
import { FacetField } from '../../../models/facet-field.interface';
|
||||||
|
import { SearchFacetFiltersService } from '../../../services/search-facet-filters.service';
|
||||||
|
import { SimpleChange } from '@angular/core';
|
||||||
|
|
||||||
|
describe('SearchFacetChipTabbedComponent', () => {
|
||||||
|
let component: SearchFacetChipTabbedComponent;
|
||||||
|
let fixture: ComponentFixture<SearchFacetChipTabbedComponent>;
|
||||||
|
let queryBuilder: SearchQueryBuilderService;
|
||||||
|
let searchFacetService: SearchFacetFiltersService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), ContentTestingModule]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(SearchFacetChipTabbedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
queryBuilder = TestBed.inject(SearchQueryBuilderService);
|
||||||
|
searchFacetService = TestBed.inject(SearchFacetFiltersService);
|
||||||
|
spyOn(queryBuilder, 'update').and.stub();
|
||||||
|
|
||||||
|
const facet1: FacetField = { type: 'field', label: 'field', field: 'field', buckets: new SearchFilterList() };
|
||||||
|
const facet2: FacetField = { type: 'field', label: 'field2', field: 'field2', buckets: new SearchFilterList() };
|
||||||
|
|
||||||
|
component.tabbedFacet = {
|
||||||
|
fields: ['field', 'field2'],
|
||||||
|
label: 'LABEL',
|
||||||
|
facets: {
|
||||||
|
field: facet1,
|
||||||
|
field2: facet2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
function openFacet() {
|
||||||
|
const chip = fixture.debugElement.query(By.css('mat-chip'));
|
||||||
|
chip.triggerEventHandler('click', {});
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplayValue(): string {
|
||||||
|
return fixture.debugElement.query(By.css('.adf-search-filter-ellipsis.adf-filter-value')).nativeElement.innerText.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTabs(): HTMLDivElement[] {
|
||||||
|
return fixture.debugElement.queryAll(By.css('.mat-tab-label-content')).map((element) => element.nativeElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTab(tabIndex: number) {
|
||||||
|
getTabs()[tabIndex].click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerComponentChanges() {
|
||||||
|
component.ngOnChanges({
|
||||||
|
tabbedFacet: new SimpleChange(null, component.tabbedFacet, false)
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBucketItem(field: string, displayValue: string) {
|
||||||
|
component.tabbedFacet.facets[field].buckets.items.push({
|
||||||
|
count: 1,
|
||||||
|
label: displayValue,
|
||||||
|
display: displayValue,
|
||||||
|
filterQuery: ''
|
||||||
|
});
|
||||||
|
triggerComponentChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should display correct label for tabbed facet', () => {
|
||||||
|
const label = fixture.debugElement.query(By.css('.adf-search-filter-placeholder')).nativeElement.innerText;
|
||||||
|
expect(label).toBe(component.tabbedFacet.label + ':');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display any as display value when nothing is selected', () => {
|
||||||
|
const displayValue = getDisplayValue();
|
||||||
|
expect(displayValue).toBe('SEARCH.FILTER.ANY');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display remove icon and disable facet when no items are loaded', () => {
|
||||||
|
const chip = fixture.debugElement.query(By.css('mat-chip'));
|
||||||
|
const icon = fixture.debugElement.query(By.css('mat-chip mat-icon')).nativeElement.innerText;
|
||||||
|
expect(chip.classes['mat-chip-disabled']).toBeTrue();
|
||||||
|
expect(icon).toEqual('remove');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not open context menu when no items are loaded', () => {
|
||||||
|
spyOn(component.menuTrigger, 'openMenu');
|
||||||
|
const chip = fixture.debugElement.query(By.css('mat-chip')).nativeElement;
|
||||||
|
chip.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
|
||||||
|
expect(component.menuTrigger.openMenu).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display correct title when facet is opened', () => {
|
||||||
|
openFacet();
|
||||||
|
const title = fixture.debugElement.query(By.css('.adf-search-filter-title')).nativeElement.innerText.split('\n')[0];
|
||||||
|
expect(title).toBe(component.tabbedFacet.label);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display 2 tabs with specific labels', () => {
|
||||||
|
openFacet();
|
||||||
|
const tabLabels = getTabs();
|
||||||
|
expect(tabLabels.length).toBe(2);
|
||||||
|
expect(tabLabels[0].innerText).toBe(component.tabbedFacet.facets['field'].label);
|
||||||
|
expect(tabLabels[1].innerText).toBe(component.tabbedFacet.facets['field2'].label);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display creator tab as active initially and allow navigation', () => {
|
||||||
|
openFacet();
|
||||||
|
let activeTabLabel = fixture.debugElement.query(By.css('.mat-tab-label-active .mat-tab-label-content')).nativeElement.innerText;
|
||||||
|
expect(activeTabLabel).toBe(component.tabbedFacet.facets['field'].label);
|
||||||
|
|
||||||
|
changeTab(1);
|
||||||
|
activeTabLabel = fixture.debugElement.query(By.css('.mat-tab-label-active .mat-tab-label-content')).nativeElement.innerText;
|
||||||
|
expect(activeTabLabel).toBe(component.tabbedFacet.facets['field2'].label);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display arrow down icon and not disable the chip when items are loaded', () => {
|
||||||
|
addBucketItem('field', 'test');
|
||||||
|
const chip = fixture.debugElement.query(By.css('mat-chip'));
|
||||||
|
const icon = fixture.debugElement.query(By.css('mat-chip mat-icon')).nativeElement.innerText;
|
||||||
|
expect(chip.classes['mat-chip-disabled']).toBeUndefined();
|
||||||
|
expect(icon).toEqual('keyboard_arrow_down');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display arrow up icon when menu is opened', () => {
|
||||||
|
addBucketItem('field', 'test');
|
||||||
|
openFacet();
|
||||||
|
const icon = fixture.debugElement.query(By.css('mat-chip mat-icon')).nativeElement.innerText;
|
||||||
|
expect(icon).toEqual('keyboard_arrow_up');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create empty selected options for each tab initially', () => {
|
||||||
|
expect(component.selectedOptions['field']).toEqual([]);
|
||||||
|
expect(component.selectedOptions['field2']).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update autocomplete options when buckets change', () => {
|
||||||
|
addBucketItem('field', 'test');
|
||||||
|
addBucketItem('field2', 'test2');
|
||||||
|
expect(component.autocompleteOptions['field'].length).toBe(1);
|
||||||
|
expect(component.autocompleteOptions['field'][0]).toEqual({value: 'test'});
|
||||||
|
expect(component.autocompleteOptions['field2'].length).toBe(1);
|
||||||
|
expect(component.autocompleteOptions['field2'][0]).toEqual({value: 'test2'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add buckets when items are selected', () => {
|
||||||
|
spyOn(queryBuilder, 'addUserFacetBucket');
|
||||||
|
addBucketItem('field', 'test');
|
||||||
|
addBucketItem('field2', 'test2');
|
||||||
|
component.onOptionsChange([{ value: 'test' }], 'field');
|
||||||
|
expect(queryBuilder.addUserFacetBucket).toHaveBeenCalledWith('field',component.tabbedFacet.facets['field'].buckets.items[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove buckets when items are unselected', () => {
|
||||||
|
spyOn(queryBuilder, 'removeUserFacetBucket');
|
||||||
|
addBucketItem('field', 'test');
|
||||||
|
addBucketItem('field2', 'test2');
|
||||||
|
component.onOptionsChange([], 'field');
|
||||||
|
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith('field',component.tabbedFacet.facets['field'].buckets.items[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update display value when next elements are selected', () => {
|
||||||
|
const selectedOption1 = 'test';
|
||||||
|
const selectedOption2 = 'test2';
|
||||||
|
addBucketItem('field', selectedOption1);
|
||||||
|
addBucketItem('field', selectedOption2);
|
||||||
|
component.onOptionsChange([{ value: selectedOption1 }, { value: selectedOption2 }],'field');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getDisplayValue()).toBe(`${component.tabbedFacet.facets['field'].label}_LABEL: ${selectedOption1}, ${selectedOption2}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update display value when elements from both tabs are selected', () => {
|
||||||
|
const selectedOption1 = 'test';
|
||||||
|
const selectedOption2 = 'test2';
|
||||||
|
addBucketItem('field', selectedOption1);
|
||||||
|
addBucketItem('field2', selectedOption2);
|
||||||
|
component.onOptionsChange([{ value: selectedOption1 }], 'field');
|
||||||
|
component.onOptionsChange([{ value: selectedOption2 }], 'field2');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(getDisplayValue()).toBe(`${component.tabbedFacet.facets['field'].label}_LABEL: ${selectedOption1} ${component.tabbedFacet.facets['field2'].label}_LABEL: ${selectedOption2}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update search query and display value when apply btn is clicked', () => {
|
||||||
|
spyOn(component.menuTrigger, 'closeMenu').and.callThrough();
|
||||||
|
spyOn(component, 'updateDisplayValue').and.callThrough();
|
||||||
|
spyOn(searchFacetService, 'updateSelectedBuckets').and.callThrough();
|
||||||
|
openFacet();
|
||||||
|
const applyButton = fixture.debugElement.query(By.css('#apply-filter-button'));
|
||||||
|
applyButton.triggerEventHandler('click', {});
|
||||||
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
|
expect(component.menuTrigger.closeMenu).toHaveBeenCalled();
|
||||||
|
expect(component.updateDisplayValue).toHaveBeenCalled();
|
||||||
|
expect(searchFacetService.updateSelectedBuckets).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update search query and display value when cancel btn is clicked', () => {
|
||||||
|
spyOn(component.menuTrigger, 'closeMenu').and.callThrough();
|
||||||
|
spyOn(component, 'updateDisplayValue').and.callThrough();
|
||||||
|
openFacet();
|
||||||
|
const applyButton = fixture.debugElement.query(By.css('#cancel-filter-button'));
|
||||||
|
applyButton.triggerEventHandler('click', {});
|
||||||
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
|
expect(component.menuTrigger.closeMenu).toHaveBeenCalled();
|
||||||
|
expect(component.updateDisplayValue).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,174 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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, ElementRef, Inject, Input, OnChanges, OnInit, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { ConfigurableFocusTrap, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
||||||
|
import { MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { TabbedFacetField } from '../../../models/tabbed-facet-field.interface';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { SearchQueryBuilderService } from '../../../services/search-query-builder.service';
|
||||||
|
import { SEARCH_QUERY_SERVICE_TOKEN } from '../../../search-query-service.token';
|
||||||
|
import { FacetWidget } from '../../../models/facet-widget.interface';
|
||||||
|
import { TranslationService } from '@alfresco/adf-core';
|
||||||
|
import { SearchFacetFiltersService } from '../../../services/search-facet-filters.service';
|
||||||
|
import { AutocompleteOption } from '../../../models/autocomplete-option.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-facet-chip-tabbed',
|
||||||
|
templateUrl: './search-facet-chip-tabbed.component.html',
|
||||||
|
styleUrls: ['./search-facet-chip-tabbed.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class SearchFacetChipTabbedComponent implements OnInit, OnChanges, FacetWidget {
|
||||||
|
@Input()
|
||||||
|
tabbedFacet: TabbedFacetField;
|
||||||
|
|
||||||
|
@ViewChild('menuContainer', { static: false })
|
||||||
|
menuContainer: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('menuTrigger', { static: false })
|
||||||
|
menuTrigger: MatMenuTrigger;
|
||||||
|
|
||||||
|
private resetSubject$ = new Subject<void>();
|
||||||
|
|
||||||
|
displayValue$ = new Subject<string>();
|
||||||
|
reset$ = this.resetSubject$.asObservable();
|
||||||
|
focusTrap: ConfigurableFocusTrap;
|
||||||
|
chipIcon = 'keyboard_arrow_down';
|
||||||
|
autocompleteOptions = {};
|
||||||
|
selectedOptions = {};
|
||||||
|
isPopulated = false;
|
||||||
|
|
||||||
|
constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) private queryBuilder: SearchQueryBuilderService,
|
||||||
|
private translationService: TranslationService,
|
||||||
|
private searchFacetFiltersService: SearchFacetFiltersService,
|
||||||
|
private focusTrapFactory: ConfigurableFocusTrapFactory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.tabbedFacet.fields.forEach((field) => {
|
||||||
|
Object.defineProperty(this.selectedOptions, field, {
|
||||||
|
value: [],
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes.tabbedFacet) {
|
||||||
|
this.isPopulated = this.tabbedFacet.fields.some((field) => this.tabbedFacet.facets[field]?.buckets.items.length > 0);
|
||||||
|
this.tabbedFacet.fields.forEach((field) => {
|
||||||
|
const options: AutocompleteOption[] = this.tabbedFacet.facets[field].buckets.items.map((item) => ({ value: item.display }));
|
||||||
|
Object.defineProperty(this.autocompleteOptions, field, {
|
||||||
|
value: options,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMenuOpen() {
|
||||||
|
if (this.menuContainer && !this.focusTrap) {
|
||||||
|
this.focusTrap = this.focusTrapFactory.create(this.menuContainer.nativeElement);
|
||||||
|
}
|
||||||
|
this.chipIcon = 'keyboard_arrow_up';
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosed() {
|
||||||
|
this.focusTrap.destroy();
|
||||||
|
this.focusTrap = null;
|
||||||
|
this.chipIcon = 'keyboard_arrow_down';
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove() {
|
||||||
|
this.reset();
|
||||||
|
this.menuTrigger.closeMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
onApply() {
|
||||||
|
this.submitValues();
|
||||||
|
this.menuTrigger.closeMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnterKeydown() {
|
||||||
|
if (this.isPopulated) {
|
||||||
|
if (!this.menuTrigger.menuOpen) {
|
||||||
|
this.menuTrigger.openMenu();
|
||||||
|
} else {
|
||||||
|
this.menuTrigger.closeMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onEscKeydown() {
|
||||||
|
if (this.menuTrigger.menuOpen) {
|
||||||
|
this.menuTrigger.closeMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onOptionsChange(selectedOptions: AutocompleteOption[], field: string) {
|
||||||
|
this.selectedOptions[field] = selectedOptions.map((selectedOption) => selectedOption.value);
|
||||||
|
this.isPopulated = this.tabbedFacet.fields.some((facetField) => this.selectedOptions[facetField].length > 0);
|
||||||
|
this.updateDisplayValue();
|
||||||
|
this.updateUserFacetBuckets();
|
||||||
|
this.queryBuilder.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisplayValue() {
|
||||||
|
let displayValue = '';
|
||||||
|
this.tabbedFacet.fields.forEach((field) => {
|
||||||
|
if (this.selectedOptions[field].length > 0) {
|
||||||
|
const stackedOptions = this.selectedOptions[field].join(', ');
|
||||||
|
displayValue += `${this.translationService.instant(this.tabbedFacet.facets[field].label + '_LABEL')}: ${stackedOptions} `;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.displayValue$.next(displayValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.resetSubject$.next();
|
||||||
|
this.updateUserFacetBuckets();
|
||||||
|
this.updateDisplayValue();
|
||||||
|
this.queryBuilder.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
submitValues() {
|
||||||
|
this.updateUserFacetBuckets();
|
||||||
|
this.searchFacetFiltersService.updateSelectedBuckets();
|
||||||
|
this.updateDisplayValue();
|
||||||
|
this.queryBuilder.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
optionComparator(option1: AutocompleteOption, option2: AutocompleteOption): boolean {
|
||||||
|
return option1.value.toUpperCase() === option2.value.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateUserFacetBuckets() {
|
||||||
|
this.tabbedFacet.fields.forEach((field) => {
|
||||||
|
this.tabbedFacet.facets[field].buckets.items.forEach((item) => {
|
||||||
|
const matchedOption = this.selectedOptions[field].find((option) => option === item.display);
|
||||||
|
if (matchedOption) {
|
||||||
|
item.checked = true;
|
||||||
|
this.queryBuilder.addUserFacetBucket(field, item);
|
||||||
|
} else {
|
||||||
|
item.checked = false;
|
||||||
|
this.queryBuilder.removeUserFacetBucket(field, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,12 @@
|
|||||||
<adf-search-widget-chip [category]="category"></adf-search-widget-chip>
|
<adf-search-widget-chip [category]="category"></adf-search-widget-chip>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="facetFiltersService.tabbedFacet && showContextFacets">
|
||||||
|
<adf-search-facet-chip-tabbed
|
||||||
|
[tabbedFacet]="facetFiltersService.tabbedFacet"
|
||||||
|
[attr.data-automation-id]="facetChipTabbedId" >
|
||||||
|
</adf-search-facet-chip-tabbed>
|
||||||
|
</ng-container>
|
||||||
<ng-container *ngIf="facetFiltersService.responseFacets && showContextFacets">
|
<ng-container *ngIf="facetFiltersService.responseFacets && showContextFacets">
|
||||||
<ng-container *ngFor="let field of facetFiltersService.responseFacets">
|
<ng-container *ngFor="let field of facetFiltersService.responseFacets">
|
||||||
<adf-search-facet-chip [field]="field" [attr.data-automation-id]="'search-fact-chip-' + field.field" ></adf-search-facet-chip>
|
<adf-search-facet-chip [field]="field" [attr.data-automation-id]="'search-fact-chip-' + field.field" ></adf-search-facet-chip>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@use '@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
.adf-search-filter-chip {
|
.adf-search-filter-chip,
|
||||||
|
.adf-search-filter-chip-tabbed {
|
||||||
&.mat-chip {
|
&.mat-chip {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
transition: border 500ms ease-in-out;
|
transition: border 500ms ease-in-out;
|
||||||
|
@ -75,7 +75,7 @@ describe('SearchFilterChipsComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList()}
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList()}
|
||||||
];
|
];
|
||||||
searchFacetFiltersService.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
searchFacetFiltersService.queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
@ -125,7 +125,7 @@ describe('SearchFilterChipsComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList()}
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList()}
|
||||||
];
|
];
|
||||||
queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
@ -174,7 +174,7 @@ describe('SearchFilterChipsComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList() }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList() }
|
||||||
];
|
];
|
||||||
queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {}
|
context: {}
|
||||||
|
@ -19,6 +19,8 @@ import { Component, Inject, Input, ViewEncapsulation } from '@angular/core';
|
|||||||
import { SearchFacetFiltersService } from '../../services/search-facet-filters.service';
|
import { SearchFacetFiltersService } from '../../services/search-facet-filters.service';
|
||||||
import { SEARCH_QUERY_SERVICE_TOKEN } from '../../search-query-service.token';
|
import { SEARCH_QUERY_SERVICE_TOKEN } from '../../search-query-service.token';
|
||||||
import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
|
import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-search-filter-chips',
|
selector: 'adf-search-filter-chips',
|
||||||
@ -27,13 +29,27 @@ import { SearchQueryBuilderService } from '../../services/search-query-builder.s
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class SearchFilterChipsComponent {
|
export class SearchFilterChipsComponent {
|
||||||
|
private onDestroy$ = new Subject<void>();
|
||||||
|
|
||||||
/** Toggles whether to show or not the context facet filters. */
|
/** Toggles whether to show or not the context facet filters. */
|
||||||
@Input()
|
@Input()
|
||||||
showContextFacets: boolean = true;
|
showContextFacets: boolean = true;
|
||||||
|
|
||||||
|
facetChipTabbedId = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(SEARCH_QUERY_SERVICE_TOKEN)
|
@Inject(SEARCH_QUERY_SERVICE_TOKEN)
|
||||||
public queryBuilder: SearchQueryBuilderService,
|
public queryBuilder: SearchQueryBuilderService,
|
||||||
public facetFiltersService: SearchFacetFiltersService) {}
|
public facetFiltersService: SearchFacetFiltersService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.queryBuilder.executed.asObservable()
|
||||||
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe(() => this.facetChipTabbedId = 'search-fact-chip-tabbed-' + this.facetFiltersService.tabbedFacet?.fields.join('-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next();
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ describe('SearchFilterComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList([]) }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList([]) }
|
||||||
];
|
];
|
||||||
queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
@ -138,7 +138,7 @@ describe('SearchFilterComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList([]) }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList([]) }
|
||||||
];
|
];
|
||||||
searchFacetFiltersService.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
searchFacetFiltersService.queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
@ -182,7 +182,7 @@ describe('SearchFilterComponent', () => {
|
|||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }]) },
|
||||||
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList() }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: new SearchFilterList() }
|
||||||
];
|
];
|
||||||
searchFacetFiltersService.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
searchFacetFiltersService.queryBuilder.addUserFacetBucket('f1', searchFacetFiltersService.responseFacets[0].buckets.items[0]);
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {}
|
context: {}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 { FacetField } from './facet-field.interface';
|
||||||
|
|
||||||
|
export interface TabbedFacetField {
|
||||||
|
/* array of fields that tabbed facet will consist of */
|
||||||
|
fields: string[];
|
||||||
|
/* label to display for tabbed facet */
|
||||||
|
label: string;
|
||||||
|
/* facets to populate tabbed facet tabs */
|
||||||
|
facets: {
|
||||||
|
[propName: string]: FacetField;
|
||||||
|
};
|
||||||
|
}
|
@ -27,6 +27,7 @@ export * from './models/search-configuration.interface';
|
|||||||
export * from './services/search-query-builder.service';
|
export * from './services/search-query-builder.service';
|
||||||
export * from './models/search-range.interface';
|
export * from './models/search-range.interface';
|
||||||
export * from './models/search-form.interface';
|
export * from './models/search-form.interface';
|
||||||
|
export * from './models/tabbed-facet-field.interface';
|
||||||
|
|
||||||
export * from './search-query-service.token';
|
export * from './search-query-service.token';
|
||||||
export * from './services/search-header-query-builder.service';
|
export * from './services/search-header-query-builder.service';
|
||||||
@ -67,5 +68,6 @@ export * from './components/search-filter-tabbed/search-filter-tabbed.component'
|
|||||||
export * from './components/reset-search.directive';
|
export * from './components/reset-search.directive';
|
||||||
export * from './components/search-chip-autocomplete-input/search-chip-autocomplete-input.component';
|
export * from './components/search-chip-autocomplete-input/search-chip-autocomplete-input.component';
|
||||||
export * from './components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
export * from './components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
||||||
|
export * from './components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component';
|
||||||
|
|
||||||
export * from './search.module';
|
export * from './search.module';
|
||||||
|
@ -56,6 +56,7 @@ import { SearchFilterTabbedComponent } from './components/search-filter-tabbed/s
|
|||||||
import { SearchDateRangeAdvancedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component';
|
import { SearchDateRangeAdvancedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component';
|
||||||
import { SearchDateRangeAdvancedTabbedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component';
|
import { SearchDateRangeAdvancedTabbedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component';
|
||||||
import { SearchFilterTabDirective } from './components/search-filter-tabbed/search-filter-tab.directive';
|
import { SearchFilterTabDirective } from './components/search-filter-tabbed/search-filter-tab.directive';
|
||||||
|
import { SearchFacetChipTabbedComponent } from './components/search-filter-chips/search-facet-chip-tabbed/search-facet-chip-tabbed.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -98,7 +99,8 @@ import { SearchFilterTabDirective } from './components/search-filter-tabbed/sear
|
|||||||
SearchFilterTabbedComponent,
|
SearchFilterTabbedComponent,
|
||||||
SearchDateRangeAdvancedComponent,
|
SearchDateRangeAdvancedComponent,
|
||||||
SearchDateRangeAdvancedTabbedComponent,
|
SearchDateRangeAdvancedTabbedComponent,
|
||||||
SearchFilterTabDirective
|
SearchFilterTabDirective,
|
||||||
|
SearchFacetChipTabbedComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
@ -126,7 +128,8 @@ import { SearchFilterTabDirective } from './components/search-filter-tabbed/sear
|
|||||||
SearchLogicalFilterComponent,
|
SearchLogicalFilterComponent,
|
||||||
SearchFilterTabbedComponent,
|
SearchFilterTabbedComponent,
|
||||||
SearchDateRangeAdvancedComponent,
|
SearchDateRangeAdvancedComponent,
|
||||||
ResetSearchDirective
|
ResetSearchDirective,
|
||||||
|
SearchFacetChipTabbedComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: SEARCH_QUERY_SERVICE_TOKEN, useExisting: SearchQueryBuilderService }
|
{ provide: SEARCH_QUERY_SERVICE_TOKEN, useExisting: SearchQueryBuilderService }
|
||||||
|
@ -183,14 +183,14 @@ export abstract class BaseQueryBuilderService {
|
|||||||
* @param field The target field
|
* @param field The target field
|
||||||
* @param bucket Bucket to add
|
* @param bucket Bucket to add
|
||||||
*/
|
*/
|
||||||
addUserFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
addUserFacetBucket(field: string, bucket: FacetFieldBucket) {
|
||||||
if (field && field.field && bucket) {
|
if (field && bucket) {
|
||||||
const buckets = this.userFacetBuckets[field.field] || [];
|
const buckets = this.userFacetBuckets[field] || [];
|
||||||
const existing = buckets.find((facetBucket) => facetBucket.label === bucket.label);
|
const existing = buckets.find((facetBucket) => facetBucket.label === bucket.label);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
buckets.push(bucket);
|
buckets.push(bucket);
|
||||||
}
|
}
|
||||||
this.userFacetBuckets[field.field] = buckets;
|
this.userFacetBuckets[field] = buckets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,10 +210,10 @@ export abstract class BaseQueryBuilderService {
|
|||||||
* @param field The target field
|
* @param field The target field
|
||||||
* @param bucket Bucket to remove
|
* @param bucket Bucket to remove
|
||||||
*/
|
*/
|
||||||
removeUserFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
removeUserFacetBucket(field: string, bucket: FacetFieldBucket) {
|
||||||
if (field && field.field && bucket) {
|
if (field && bucket) {
|
||||||
const buckets = this.userFacetBuckets[field.field] || [];
|
const buckets = this.userFacetBuckets[field] || [];
|
||||||
this.userFacetBuckets[field.field] = buckets
|
this.userFacetBuckets[field] = buckets
|
||||||
.filter((facetBucket) => facetBucket.label !== bucket.label);
|
.filter((facetBucket) => facetBucket.label !== bucket.label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,7 +497,7 @@ describe('SearchFacetFiltersService', () => {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
let data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facets: fields
|
facets: fields
|
||||||
@ -513,6 +513,55 @@ describe('SearchFacetFiltersService', () => {
|
|||||||
expect(searchFacetFiltersService.responseFacets.length).toEqual(2);
|
expect(searchFacetFiltersService.responseFacets.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should extract creator and modifier facets and create tabbed facet for them', () => {
|
||||||
|
searchFacetFiltersService.responseFacets = null;
|
||||||
|
queryBuilder.config = {
|
||||||
|
categories: [],
|
||||||
|
facetFields: { fields: [
|
||||||
|
{ label: 'creator', field: 'creator' },
|
||||||
|
{ label: 'modifier', field: 'modifier' }
|
||||||
|
]},
|
||||||
|
facetQueries: {
|
||||||
|
queries: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const serverResponseFields: any = [
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
label: 'creator',
|
||||||
|
buckets: [
|
||||||
|
{ label: 'b1', metrics: [{value: {count: 10}}] },
|
||||||
|
{ label: 'b2', metrics: [{value: {count: 1}}] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
label: 'modifier',
|
||||||
|
buckets: [
|
||||||
|
{ label: 'c1', metrics: [{value: {count: 10}}] },
|
||||||
|
{ label: 'c2', metrics: [{value: {count: 1}}] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const data = {
|
||||||
|
list: {
|
||||||
|
context: {
|
||||||
|
facets: serverResponseFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
searchFacetFiltersService.onDataLoaded(data);
|
||||||
|
expect(searchFacetFiltersService.responseFacets.length).toEqual(0);
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.fields).toEqual(['creator', 'modifier']);
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.label).toEqual('SEARCH.FILTER.PEOPLE');
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.facets['creator'].buckets.items[0].label).toEqual('b1');
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.facets['creator'].buckets.items[1].label).toEqual('b2');
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.facets['modifier'].buckets.items[0].label).toEqual('c1');
|
||||||
|
expect(searchFacetFiltersService.tabbedFacet.facets['modifier'].buckets.items[1].label).toEqual('c2');
|
||||||
|
});
|
||||||
|
|
||||||
describe('Bucket sorting', () => {
|
describe('Bucket sorting', () => {
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import { GenericBucket, GenericFacetResponse, ResultSetContext, ResultSetPaging
|
|||||||
import { SearchFilterList } from '../models/search-filter-list.model';
|
import { SearchFilterList } from '../models/search-filter-list.model';
|
||||||
import { FacetFieldBucket } from '../models/facet-field-bucket.interface';
|
import { FacetFieldBucket } from '../models/facet-field-bucket.interface';
|
||||||
import { CategoryService } from '../../category/services/category.service';
|
import { CategoryService } from '../../category/services/category.service';
|
||||||
|
import { TabbedFacetField } from '../models/tabbed-facet-field.interface';
|
||||||
|
|
||||||
export interface SelectedBucket {
|
export interface SelectedBucket {
|
||||||
field: FacetField;
|
field: FacetField;
|
||||||
@ -45,6 +46,8 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
* the newly received items are added to the responseFacets.
|
* the newly received items are added to the responseFacets.
|
||||||
*/
|
*/
|
||||||
responseFacets: FacetField[] = null;
|
responseFacets: FacetField[] = null;
|
||||||
|
/* tabbed facet incorporating creator and modifier facets */
|
||||||
|
tabbedFacet: TabbedFacetField = null;
|
||||||
|
|
||||||
/** shows the facet chips */
|
/** shows the facet chips */
|
||||||
selectedBuckets: SelectedBucket[] = [];
|
selectedBuckets: SelectedBucket[] = [];
|
||||||
@ -95,17 +98,18 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
this.parseFacetIntervals(context);
|
this.parseFacetIntervals(context);
|
||||||
this.parseFacetQueries(context);
|
this.parseFacetQueries(context);
|
||||||
this.sortFacets();
|
this.sortFacets();
|
||||||
|
this.parseTabbedFacetField();
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string) {
|
private parseFacetItems(context: ResultSetContext, configFacetFields: FacetField[], itemType: string) {
|
||||||
configFacetFields.forEach((field) => {
|
configFacetFields.forEach((facetField) => {
|
||||||
const responseField = this.findFacet(context, itemType, field.label);
|
const responseField = this.findFacet(context, itemType, facetField.label);
|
||||||
const responseBuckets = this.getResponseBuckets(responseField, field)
|
const responseBuckets = this.getResponseBuckets(responseField, facetField)
|
||||||
.filter(this.getFilterByMinCount(field.mincount));
|
.filter(this.getFilterByMinCount(facetField.mincount));
|
||||||
this.sortFacetBuckets(responseBuckets, field.settings?.bucketSortBy, field.settings?.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING);
|
this.sortFacetBuckets(responseBuckets, facetField.settings?.bucketSortBy, facetField.settings?.bucketSortDirection ?? FacetBucketSortDirection.ASCENDING);
|
||||||
const alreadyExistingField = this.findResponseFacet(itemType, field.label);
|
const alreadyExistingField = this.findResponseFacet(itemType, facetField.label);
|
||||||
|
|
||||||
if (field.field === 'cm:categories'){
|
if (facetField.field === 'cm:categories'){
|
||||||
this.loadCategoryNames(responseBuckets);
|
this.loadCategoryNames(responseBuckets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,18 +119,18 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
|
this.updateExistingBuckets(responseField, responseBuckets, alreadyExistingField, alreadyExistingBuckets);
|
||||||
} else if (responseField) {
|
} else if (responseField) {
|
||||||
if (responseBuckets.length > 0) {
|
if (responseBuckets.length > 0) {
|
||||||
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize);
|
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, facetField.pageSize);
|
||||||
bucketList.filter = this.getBucketFilterFunction(bucketList);
|
bucketList.filter = this.getBucketFilterFunction(bucketList);
|
||||||
|
|
||||||
if (!this.responseFacets) {
|
if (!this.responseFacets) {
|
||||||
this.responseFacets = [];
|
this.responseFacets = [];
|
||||||
}
|
}
|
||||||
this.responseFacets.push({
|
this.responseFacets.push({
|
||||||
...field,
|
...facetField,
|
||||||
type: responseField.type || itemType,
|
type: responseField.type || itemType,
|
||||||
label: field.label,
|
label: facetField.label,
|
||||||
pageSize: field.pageSize | DEFAULT_PAGE_SIZE,
|
pageSize: facetField.pageSize | DEFAULT_PAGE_SIZE,
|
||||||
currentPageSize: field.pageSize | DEFAULT_PAGE_SIZE,
|
currentPageSize: facetField.pageSize | DEFAULT_PAGE_SIZE,
|
||||||
buckets: bucketList
|
buckets: bucketList
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -134,6 +138,33 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseTabbedFacetField() {
|
||||||
|
if (this.responseFacets) {
|
||||||
|
const fields = this.responseFacets.reduce((acc, facet) => `${acc},${facet.field}`, '');
|
||||||
|
const tabbedFacetField: TabbedFacetField = {
|
||||||
|
fields: ['creator', 'modifier'],
|
||||||
|
label: 'SEARCH.FILTER.PEOPLE',
|
||||||
|
facets: {}
|
||||||
|
};
|
||||||
|
this.extractCreatorAndModifier(tabbedFacetField, fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractCreatorAndModifier(tabbedFacet: TabbedFacetField, fields: string) {
|
||||||
|
if (fields.includes('creator') && fields.includes('modifier')) {
|
||||||
|
for (let i = this.responseFacets.length - 1; i >= 0; i--) {
|
||||||
|
if (this.responseFacets[i].field === 'creator' || this.responseFacets[i].field === 'modifier') {
|
||||||
|
const removedFacet = this.responseFacets.splice(i, 1)[0];
|
||||||
|
Object.defineProperty(tabbedFacet.facets, removedFacet.field, {
|
||||||
|
value: removedFacet,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tabbedFacet = tabbedFacet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private parseFacetFields(context: ResultSetContext) {
|
private parseFacetFields(context: ResultSetContext) {
|
||||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||||
this.parseFacetItems(context, configFacetFields, 'field');
|
this.parseFacetItems(context, configFacetFields, 'field');
|
||||||
@ -191,7 +222,6 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sortFacets() {
|
private sortFacets() {
|
||||||
@ -358,10 +388,10 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unselectFacetBucket(field: FacetField, bucket: FacetFieldBucket) {
|
unselectFacetBucket(facetField: FacetField, bucket: FacetFieldBucket) {
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
bucket.checked = false;
|
bucket.checked = false;
|
||||||
this.queryBuilder.removeUserFacetBucket(field, bucket);
|
this.queryBuilder.removeUserFacetBucket(facetField.field, bucket);
|
||||||
this.updateSelectedBuckets();
|
this.updateSelectedBuckets();
|
||||||
this.queryBuilder.update();
|
this.queryBuilder.update();
|
||||||
}
|
}
|
||||||
@ -371,12 +401,14 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
updateSelectedBuckets() {
|
updateSelectedBuckets() {
|
||||||
if (this.responseFacets) {
|
if (this.responseFacets) {
|
||||||
this.selectedBuckets = [];
|
this.selectedBuckets = [];
|
||||||
for (const field of this.responseFacets) {
|
let facetFields = this.tabbedFacet === null ? [] : Object.keys(this.tabbedFacet?.fields).map(field => this.tabbedFacet.facets[field]);
|
||||||
if (field.buckets) {
|
facetFields = [...facetFields, ...this.responseFacets];
|
||||||
|
for (const facetField of facetFields) {
|
||||||
|
if (facetField?.buckets) {
|
||||||
this.selectedBuckets.push(
|
this.selectedBuckets.push(
|
||||||
...this.queryBuilder.getUserFacetBuckets(field.field)
|
...this.queryBuilder.getUserFacetBuckets(facetField.field)
|
||||||
.filter((bucket) => bucket.checked)
|
.filter((bucket) => bucket.checked)
|
||||||
.map((bucket) => ({field, bucket}))
|
.map((bucket) => ({field: facetField, bucket}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,11 +423,11 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetAllSelectedBuckets() {
|
resetAllSelectedBuckets() {
|
||||||
this.responseFacets.forEach((field) => {
|
this.responseFacets.forEach((facetField) => {
|
||||||
if (field && field.buckets) {
|
if (facetField?.buckets) {
|
||||||
for (const bucket of field.buckets.items) {
|
for (const bucket of facetField.buckets.items) {
|
||||||
bucket.checked = false;
|
bucket.checked = false;
|
||||||
this.queryBuilder.removeUserFacetBucket(field, bucket);
|
this.queryBuilder.removeUserFacetBucket(facetField.field, bucket);
|
||||||
}
|
}
|
||||||
this.updateSelectedBuckets();
|
this.updateSelectedBuckets();
|
||||||
}
|
}
|
||||||
@ -411,6 +443,7 @@ export class SearchFacetFiltersService implements OnDestroy {
|
|||||||
reset() {
|
reset() {
|
||||||
this.responseFacets = [];
|
this.responseFacets = [];
|
||||||
this.selectedBuckets = [];
|
this.selectedBuckets = [];
|
||||||
|
this.tabbedFacet = null;
|
||||||
this.queryBuilder.resetToDefaults();
|
this.queryBuilder.resetToDefaults();
|
||||||
this.queryBuilder.update();
|
this.queryBuilder.update();
|
||||||
}
|
}
|
||||||
|
@ -637,10 +637,10 @@ describe('SearchQueryBuilder', () => {
|
|||||||
|
|
||||||
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
|
const builder = new SearchQueryBuilderService(buildConfig(config), alfrescoApiService);
|
||||||
|
|
||||||
builder.addUserFacetBucket(field1, field1buckets[0]);
|
builder.addUserFacetBucket(field1.field, field1buckets[0]);
|
||||||
builder.addUserFacetBucket(field1, field1buckets[1]);
|
builder.addUserFacetBucket(field1.field, field1buckets[1]);
|
||||||
builder.addUserFacetBucket(field2, field2buckets[0]);
|
builder.addUserFacetBucket(field2.field, field2buckets[0]);
|
||||||
builder.addUserFacetBucket(field2, field2buckets[1]);
|
builder.addUserFacetBucket(field2.field, field2buckets[1]);
|
||||||
|
|
||||||
const compiledQuery = builder.buildQuery();
|
const compiledQuery = builder.buildQuery();
|
||||||
const expectedResult = '(f1-q1 OR f1-q2) AND (f2-q1 OR f2-q2)';
|
const expectedResult = '(f1-q1 OR f1-q2) AND (f2-q1 OR f2-q2)';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user