Skip to content

Commit 5016eff

Browse files
authored
[ACS-10083] Filter not behaving correctly in file and site (#11237)
* [ACS-10083]: fixes setValue flow; handles initial stream values properly * [ACS-10083]: migrates to category.id instead of column key for filter key * [ACS-10083]: refactoring to prevent setting nullish query * [ACS-10083]: unit tests * [ACS-10083]: adds method for getting operator by category id * [ACS-10083]: removes API call trigger from component on initial value set * [ACS-10083]: adds data for building initial query after page reload * [ACS-10083]: removes nodes API call if has filter in query * [ACS-10083]: no need to call submit inside of the comp on initial value * [ACS-10083]: updated unit tests * [ACS-10083]: minor fixes and refactorings * [ACS-10083]: removes redundant types
1 parent 6ab54be commit 5016eff

12 files changed

+180
-47
lines changed

lib/content-services/src/lib/document-list/components/document-list.component.spec.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,9 +1276,25 @@ describe('DocumentList', () => {
12761276
documentList.onNodeDblClick(node);
12771277
});
12781278

1279-
it('should load folder by ID on init', async () => {
1279+
it('should load folder by ID on init if no filterValue is provided', async () => {
12801280
spyOn(documentList, 'loadFolder').and.stub();
12811281

1282+
documentList.filterValue = {};
1283+
1284+
fixture.detectChanges();
1285+
1286+
documentList.ngOnChanges({ currentFolderId: new SimpleChange(undefined, '1d26e465-dea3-42f3-b415-faa8364b9692', true) });
1287+
1288+
await fixture.whenStable();
1289+
1290+
expect(documentList.loadFolder).not.toHaveBeenCalled();
1291+
});
1292+
1293+
it('should NOT load folder by ID on init if filterValue is provided', async () => {
1294+
spyOn(documentList, 'loadFolder').and.stub();
1295+
1296+
documentList.filterValue = undefined;
1297+
12821298
fixture.detectChanges();
12831299

12841300
documentList.ngOnChanges({ currentFolderId: new SimpleChange(undefined, '1d26e465-dea3-42f3-b415-faa8364b9692', true) });

lib/content-services/src/lib/document-list/components/document-list.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
599599
}
600600

601601
if (this.currentFolderId && changes['currentFolderId']?.currentValue !== changes['currentFolderId']?.previousValue) {
602-
this.loadFolder();
602+
!this.filterValue && this.loadFolder();
603603
}
604604

605605
if (this.data) {
@@ -816,7 +816,6 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
816816
this.preserveExistingSelection();
817817
}
818818
this.onPreselectNodes();
819-
this.setLoadingState(false);
820819
this.onDataReady(nodePaging);
821820
}
822821
}
@@ -1023,6 +1022,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
10231022
private onDataReady(nodePaging: NodePaging) {
10241023
this.ready.emit(nodePaging);
10251024
this.pagination.next(nodePaging.list.pagination);
1025+
this.setLoadingState(false);
10261026
}
10271027

10281028
updatePagination(requestPaginationModel: RequestPaginationModel) {

lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.spec.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,43 @@ describe('FilterHeaderComponent', () => {
109109
expect(setCurrentRootFolderIdSpy).toHaveBeenCalled();
110110
});
111111

112-
it('should set active filters when an initial value is set', async () => {
112+
it('should set filters if initial value is provided', async () => {
113+
spyOn(queryBuilder, 'setCurrentRootFolderId');
114+
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
115+
spyOn(queryBuilder, 'setActiveFilter');
116+
117+
component.value = { name: 'pinocchio' };
118+
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
119+
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
120+
fixture.detectChanges();
121+
await fixture.whenStable();
122+
123+
expect(queryBuilder.setActiveFilter).toHaveBeenCalledWith('name', 'pinocchio');
124+
});
125+
126+
it('should NOT set filters if initial value is not provided', async () => {
127+
spyOn(queryBuilder, 'setCurrentRootFolderId');
128+
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
129+
spyOn(queryBuilder, 'setActiveFilter');
130+
131+
component.value = undefined;
132+
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
133+
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
134+
fixture.detectChanges();
135+
await fixture.whenStable();
136+
137+
expect(queryBuilder.setActiveFilter).not.toHaveBeenCalled();
138+
});
139+
140+
it('should set active filters correctly', async () => {
113141
spyOn(queryBuilder, 'setCurrentRootFolderId');
114142
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
115143

116144
fixture.detectChanges();
117145
await fixture.whenStable();
118146
expect(queryBuilder.getActiveFilters().length).toBe(0);
119147

120-
const initialFilterValue = { name: 'pinocchio' };
121-
component.value = initialFilterValue;
148+
component.value = { name: 'pinocchio' };
122149
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
123150
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
124151
fixture.detectChanges();
@@ -129,6 +156,25 @@ describe('FilterHeaderComponent', () => {
129156
expect(queryBuilder.getActiveFilters()[0].value).toBe('pinocchio');
130157
});
131158

159+
it('should update queryParams if initial value is provided', async () => {
160+
spyOn(queryBuilder, 'setCurrentRootFolderId');
161+
spyOn(queryBuilder, 'isCustomSourceNode').and.returnValue(false);
162+
163+
fixture.detectChanges();
164+
await fixture.whenStable();
165+
expect(Object.keys(queryBuilder.filterRawParams).length).toBe(0);
166+
167+
component.value = { name: 'pinocchio' };
168+
const currentFolderNodeIdChange = new SimpleChange('current-node-id', 'next-node-id', true);
169+
component.ngOnChanges({ currentFolderId: currentFolderNodeIdChange });
170+
fixture.detectChanges();
171+
await fixture.whenStable();
172+
173+
expect(Object.keys(queryBuilder.filterRawParams).length).toBe(1);
174+
expect(queryBuilder.filterRawParams['name']).toBe('pinocchio');
175+
expect(queryBuilder.queryFragments['name']).toBe('pinocchio');
176+
});
177+
132178
it('should emit filterSelection when a filter is changed', (done) => {
133179
spyOn(queryBuilder, 'getActiveFilters').and.returnValue([{ key: 'name', value: 'pinocchio' }]);
134180

lib/content-services/src/lib/document-list/components/filter-header/filter-header.component.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ export class FilterHeaderComponent implements OnInit, OnChanges {
4646

4747
private readonly destroyRef = inject(DestroyRef);
4848

49-
constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any, private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) {
49+
constructor(
50+
@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private readonly documentList: any,
51+
private readonly searchFilterQueryBuilder: SearchHeaderQueryBuilderService
52+
) {
5053
this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive();
5154
}
5255

@@ -102,11 +105,17 @@ export class FilterHeaderComponent implements OnInit, OnChanges {
102105
}
103106

104107
private initSearchHeader(currentFolderId: string) {
105-
this.searchFilterQueryBuilder.setCurrentRootFolderId(currentFolderId);
106108
if (this.value) {
107-
Object.keys(this.value).forEach((columnKey) => {
108-
this.searchFilterQueryBuilder.setActiveFilter(columnKey, this.value[columnKey]);
109+
Object.keys(this.value).forEach((key) => {
110+
this.searchFilterQueryBuilder.setActiveFilter(key, this.value[key]);
111+
112+
const operator = this.searchFilterQueryBuilder.getOperatorForFilterId(key) || 'OR';
113+
this.searchFilterQueryBuilder.filterRawParams[key] = this.value[key];
114+
this.searchFilterQueryBuilder.queryFragments[key] = Array.isArray(this.value[key])
115+
? this.value[key].join(` ${operator} `)
116+
: this.value[key];
109117
});
110118
}
119+
this.searchFilterQueryBuilder.setCurrentRootFolderId(currentFolderId);
111120
}
112121
}

lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.spec.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,17 @@ describe('SearchCheckListComponent', () => {
203203
expect(checkedElements.length).toBe(0);
204204
});
205205

206-
it('should update query with startValue on init, if provided', () => {
206+
it('should check the checkbox with startValue on init, if provided', () => {
207207
component.id = 'checkList';
208208
component.options = new SearchFilterList<SearchListOption>([
209209
{ name: 'Folder', value: `TYPE:'cm:folder'`, checked: false },
210210
{ name: 'Document', value: `TYPE:'cm:content'`, checked: false }
211211
]);
212-
component.startValue = `TYPE:'cm:folder'`;
213-
component.context.queryFragments[component.id] = 'query';
212+
component.startValue = [`TYPE:'cm:folder'`];
214213
fixture.detectChanges();
215-
216-
expect(component.context.queryFragments[component.id]).toBe(`TYPE:'cm:folder'`);
217-
expect(component.context.update).toHaveBeenCalled();
214+
expect(component.options.items[0].checked).toBeTrue();
215+
expect(component.options.items[1].checked).toBeFalse();
216+
expect(component.isActive).toBeTrue();
218217
});
219218

220219
it('should set query context as blank and not call query update, if no start value was provided', () => {
@@ -231,6 +230,25 @@ describe('SearchCheckListComponent', () => {
231230
expect(component.context.update).not.toHaveBeenCalled();
232231
});
233232

233+
it('should handle initial populateFilters emission and no filter state properly', () => {
234+
component.id = 'checkList';
235+
component.options = new SearchFilterList<SearchListOption>([
236+
{ name: 'Folder', value: `TYPE:'cm:folder'`, checked: false },
237+
{ name: 'Document', value: `TYPE:'cm:content'`, checked: false }
238+
]);
239+
240+
component.context.filterLoaded = new ReplaySubject(1);
241+
spyOn(component.context.filterLoaded, 'next').and.stub();
242+
spyOn(component.displayValue$, 'next').and.stub();
243+
fixture.detectChanges();
244+
245+
component.context.populateFilters.next({});
246+
component.context.populateFilters.next({ checkList: [`TYPE:'cm:content'`] });
247+
fixture.detectChanges();
248+
249+
expect(component.context.filterLoaded.next).toHaveBeenCalledTimes(1);
250+
});
251+
234252
it('should populate filter state when populate filters event has been observed', () => {
235253
component.id = 'checkList';
236254
component.options = new SearchFilterList<SearchListOption>([

lib/content-services/src/lib/search/components/search-check-list/search-check-list.component.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { SearchQueryBuilderService } from '../../services/search-query-builder.s
2323
import { SearchFilterList } from '../../models/search-filter-list.model';
2424
import { TranslationService } from '@alfresco/adf-core';
2525
import { ReplaySubject } from 'rxjs';
26-
import { map } from 'rxjs/operators';
26+
import { filter, map } from 'rxjs/operators';
2727
import { CommonModule } from '@angular/common';
2828
import { TranslatePipe } from '@ngx-translate/core';
2929
import { MatButtonModule } from '@angular/material/button';
@@ -50,7 +50,7 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
5050
context?: SearchQueryBuilderService;
5151
options: SearchFilterList<SearchListOption>;
5252
operator: string = 'OR';
53-
startValue: string;
53+
startValue: string | string[];
5454
pageSize = 5;
5555
isActive = false;
5656
enableChangeUpdate = true;
@@ -81,9 +81,9 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
8181
}
8282
}
8383
this.context.populateFilters
84-
.asObservable()
8584
.pipe(
8685
map((filtersQueries) => filtersQueries[this.id]),
86+
filter((filterQuery) => filterQuery !== undefined),
8787
takeUntilDestroyed(this.destroyRef)
8888
)
8989
.subscribe((filterQuery) => {
@@ -160,8 +160,8 @@ export class SearchCheckListComponent implements SearchWidget, OnInit {
160160
}
161161

162162
setValue(value: any) {
163-
this.options.items.filter((item) => value.includes(item.value)).map((item) => (item.checked = true));
164-
this.submitValues();
163+
this.options.items.forEach((item) => (item.checked = value.includes(item.value)));
164+
this.isActive = true;
165165
}
166166

167167
private getCheckedValues() {

lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('SearchFilterContainerComponent', () => {
9494
await applyButton.click();
9595

9696
expect(queryBuilder.getActiveFilters().length).toBe(1);
97-
expect(queryBuilder.getActiveFilters()[0].key).toBe('name');
97+
expect(queryBuilder.getActiveFilters()[0].key).toBe('queryName');
9898
expect(queryBuilder.getActiveFilters()[0].value).toBe('searchText');
9999

100100
await menu.open();
@@ -103,12 +103,12 @@ describe('SearchFilterContainerComponent', () => {
103103

104104
await applyButton.click();
105105
expect(queryBuilder.getActiveFilters().length).toBe(1);
106-
expect(queryBuilder.getActiveFilters()[0].key).toBe('name');
106+
expect(queryBuilder.getActiveFilters()[0].key).toBe('queryName');
107107
expect(queryBuilder.getActiveFilters()[0].value).toBe('updated text');
108108
});
109109

110110
it('should remove active filter after the Clear button is clicked', async () => {
111-
queryBuilder.setActiveFilter('name', 'searchText');
111+
queryBuilder.setActiveFilter('queryName', 'searchText');
112112

113113
const menu = await loader.getHarness(MatMenuHarness);
114114
await menu.open();

lib/content-services/src/lib/search/components/search-filter-container/search-filter-container.component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class SearchFilterContainerComponent implements OnInit {
7676

7777
ngOnInit() {
7878
this.category = this.searchFilterQueryBuilder.getCategoryForColumn(this.col.key);
79-
this.initialValue = this.value?.[this.col.key] ? this.value[this.col.key] : undefined;
79+
this.initialValue = this.value?.[this.category?.id];
8080
}
8181

8282
onKeyPressed(event: KeyboardEvent, menuTrigger: MatMenuTrigger) {
@@ -88,7 +88,7 @@ export class SearchFilterContainerComponent implements OnInit {
8888

8989
onApply() {
9090
if (this.widgetContainer.hasValueSelected()) {
91-
this.searchFilterQueryBuilder.setActiveFilter(this.category.columnKey, this.widgetContainer.getCurrentValue());
91+
this.searchFilterQueryBuilder.setActiveFilter(this.category.id, this.widgetContainer.getCurrentValue());
9292
this.filterChange.emit();
9393
this.widgetContainer.applyInnerWidget();
9494
} else {
@@ -103,7 +103,7 @@ export class SearchFilterContainerComponent implements OnInit {
103103

104104
resetSearchFilter() {
105105
this.widgetContainer.resetInnerWidget();
106-
this.searchFilterQueryBuilder.removeActiveFilter(this.category.columnKey);
106+
this.searchFilterQueryBuilder.removeActiveFilter(this.category.id);
107107
this.filterChange.emit();
108108
}
109109

@@ -115,7 +115,7 @@ export class SearchFilterContainerComponent implements OnInit {
115115
}
116116

117117
isActive(): boolean {
118-
return this.searchFilterQueryBuilder.getActiveFilters().findIndex((f: FilterSearch) => f.key === this.category.columnKey) > -1;
118+
return this.searchFilterQueryBuilder.getActiveFilters().findIndex((f: FilterSearch) => f.key === this.category.id) > -1;
119119
}
120120

121121
onMenuOpen() {

lib/content-services/src/lib/search/components/search-text/search-text.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ describe('SearchTextComponent', () => {
123123

124124
expect(component.value).toBe('');
125125
expect(component.context.queryFragments[component.id]).toBe('');
126-
expect(component.context.filterRawParams[component.id]).toBeNull();
126+
expect(component.context.filterRawParams[component.id]).toBeUndefined();
127127
});
128128

129129
it('should update query with startValue on init, if provided', () => {

lib/content-services/src/lib/search/components/search-text/search-text.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ export class SearchTextComponent implements SearchWidget, OnInit {
9999

100100
reset(updateContext = true) {
101101
this.value = '';
102+
this.context.filterRawParams[this.id] = undefined;
103+
this.context.queryFragments[this.id] = '';
102104
this.updateQuery(null, updateContext);
103105
}
104106

@@ -111,7 +113,10 @@ export class SearchTextComponent implements SearchWidget, OnInit {
111113
}
112114

113115
private updateQuery(value: string, updateContext = true) {
114-
this.context.filterRawParams[this.id] = value;
116+
if (value !== null) {
117+
this.context.filterRawParams[this.id] = value;
118+
}
119+
115120
this.displayValue$.next(value);
116121
if (this.context && this.settings && this.settings.field) {
117122
this.context.queryFragments[this.id] = value ? `${this.settings.field}:'${this.getSearchPrefix()}${value}${this.getSearchSuffix()}'` : '';

0 commit comments

Comments
 (0)