mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[MNT-25408]: ensure loader is not shown on nullish query (#4885)
* [MNT-25408]: ensure loader is not shown on nullish query * [MNT-25408]: moves config change processing to search inout component * [MNT-25408]: adds tests * [MNT-25408]: fixes logic; adds tests * [MNT-25408]: minor fix * [MNT-25408]: fixes build issue * [MNT-25408]: unit test fix
This commit is contained in:
+48
-2
@@ -29,7 +29,9 @@ import { AppStore } from '@alfresco/aca-shared/store';
|
|||||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
import { SearchInputComponent } from './search-input.component';
|
import { SearchInputComponent } from './search-input.component';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { of } from 'rxjs';
|
import { of, Subject } from 'rxjs';
|
||||||
|
import { NavigationStart, Router, RouterEvent } from '@angular/router';
|
||||||
|
import { SearchConfiguration, SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
||||||
import { HarnessLoader } from '@angular/cdk/testing';
|
import { HarnessLoader } from '@angular/cdk/testing';
|
||||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||||
import { MatMenuHarness } from '@angular/material/menu/testing';
|
import { MatMenuHarness } from '@angular/material/menu/testing';
|
||||||
@@ -41,6 +43,9 @@ describe('SearchInputComponent', () => {
|
|||||||
let store: jasmine.SpyObj<Store<AppStore>>;
|
let store: jasmine.SpyObj<Store<AppStore>>;
|
||||||
let unitTestingUtils: UnitTestingUtils;
|
let unitTestingUtils: UnitTestingUtils;
|
||||||
let loader: HarnessLoader;
|
let loader: HarnessLoader;
|
||||||
|
let router: Router;
|
||||||
|
const routerEventsSubject = new Subject<RouterEvent>();
|
||||||
|
const configUpdatedSubject = new Subject<SearchConfiguration>();
|
||||||
|
|
||||||
function getFirstError(): string {
|
function getFirstError(): string {
|
||||||
const error = unitTestingUtils.getByDirective(MatError);
|
const error = unitTestingUtils.getByDirective(MatError);
|
||||||
@@ -77,18 +82,31 @@ describe('SearchInputComponent', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const storeSpy = jasmine.createSpyObj<Store<AppStore>>('Store', ['dispatch', 'pipe']);
|
const storeSpy = jasmine.createSpyObj<Store<AppStore>>('Store', ['dispatch', 'pipe']);
|
||||||
|
|
||||||
|
const queryBuilderSpy = {
|
||||||
|
configUpdated: configUpdatedSubject,
|
||||||
|
removeFilterQuery: () => {}
|
||||||
|
} as Partial<SearchQueryBuilderService>;
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [AppTestingModule, SearchInputComponent],
|
imports: [AppTestingModule, SearchInputComponent],
|
||||||
providers: [{ provide: Store, useValue: storeSpy }]
|
providers: [
|
||||||
|
{ provide: Store, useValue: storeSpy },
|
||||||
|
{ provide: SearchQueryBuilderService, useValue: queryBuilderSpy }
|
||||||
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SearchInputComponent);
|
fixture = TestBed.createComponent(SearchInputComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
store = TestBed.inject(Store) as jasmine.SpyObj<Store<AppStore>>;
|
store = TestBed.inject(Store) as jasmine.SpyObj<Store<AppStore>>;
|
||||||
|
router = TestBed.inject(Router);
|
||||||
store.pipe.and.returnValue(of([]));
|
store.pipe.and.returnValue(of([]));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||||
|
|
||||||
|
Object.defineProperty(router, 'events', {
|
||||||
|
get: () => routerEventsSubject.asObservable()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show required error when field is empty and touched', async () => {
|
it('should show required error when field is empty and touched', async () => {
|
||||||
@@ -147,4 +165,32 @@ describe('SearchInputComponent', () => {
|
|||||||
component.onSearchSubmit({ target: { value: '' } });
|
component.onSearchSubmit({ target: { value: '' } });
|
||||||
expect(store.dispatch).not.toHaveBeenCalled();
|
expect(store.dispatch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('queryBuilder configUpdated handling', () => {
|
||||||
|
it('should call searchByOption when searchedWord set and navigation has query params', () => {
|
||||||
|
spyOn(component, 'searchByOption').and.stub();
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
component.searchedWord = 'term';
|
||||||
|
|
||||||
|
routerEventsSubject.next(new NavigationStart(1, '/path?q=term'));
|
||||||
|
configUpdatedSubject.next({});
|
||||||
|
|
||||||
|
expect(component.searchByOption).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT call searchByOption when searchedWord set and navigation has NO query params', () => {
|
||||||
|
component.searchedWord = 'term';
|
||||||
|
|
||||||
|
routerEventsSubject.next(new NavigationStart(1, '/path'));
|
||||||
|
|
||||||
|
configUpdatedSubject.next({});
|
||||||
|
spyOn(component, 'searchByOption').and.stub();
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
|
||||||
|
expect(component.searchByOption).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+29
-2
@@ -28,7 +28,17 @@ import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
|||||||
import { AppConfigService, NotificationService } from '@alfresco/adf-core';
|
import { AppConfigService, NotificationService } from '@alfresco/adf-core';
|
||||||
import { Component, DestroyRef, inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { Component, DestroyRef, inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { ActivatedRoute, NavigationSkipped, Params, PRIMARY_OUTLET, Router, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router';
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
NavigationSkipped,
|
||||||
|
NavigationStart,
|
||||||
|
Params,
|
||||||
|
PRIMARY_OUTLET,
|
||||||
|
Router,
|
||||||
|
UrlSegment,
|
||||||
|
UrlSegmentGroup,
|
||||||
|
UrlTree
|
||||||
|
} from '@angular/router';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
|
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
|
||||||
import { SearchNavigationService } from '../search-navigation.service';
|
import { SearchNavigationService } from '../search-navigation.service';
|
||||||
@@ -45,7 +55,7 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { extractSearchedWordFromEncodedQuery } from '../../../utils/aca-search-utils';
|
import { extractSearchedWordFromEncodedQuery } from '../../../utils/aca-search-utils';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { merge } from 'rxjs/internal/observable/merge';
|
import { merge } from 'rxjs/internal/observable/merge';
|
||||||
import { filter, map, withLatestFrom } from 'rxjs';
|
import { filter, map, startWith, withLatestFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -140,6 +150,23 @@ export class SearchInputComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.queryBuilder.configUpdated
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
withLatestFrom(
|
||||||
|
this.router.events.pipe(
|
||||||
|
filter((event): event is NavigationStart => event instanceof NavigationStart),
|
||||||
|
startWith(null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe(([, navigationStartEvent]) => {
|
||||||
|
const hasQueryParams = navigationStartEvent?.url.includes('?');
|
||||||
|
if (this.searchedWord && hasQueryParams) {
|
||||||
|
this.searchByOption();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.appHookService.library400Error.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
this.appHookService.library400Error.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
||||||
this.has400LibraryError = true;
|
this.has400LibraryError = true;
|
||||||
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
|
||||||
|
|||||||
+29
-19
@@ -1,8 +1,6 @@
|
|||||||
<aca-page-layout [class.aca-search-results-active-search-ai-input]="searchAiInputState.active">
|
<aca-page-layout [class.aca-search-results-active-search-ai-input]="searchAiInputState.active">
|
||||||
<div class="aca-page-layout-header">
|
<div class="aca-page-layout-header">
|
||||||
<aca-search-ai-input-container
|
<aca-search-ai-input-container *ngIf="searchAiInputState.active" [agentId]="searchAiInputState.selectedAgentId" />
|
||||||
*ngIf="searchAiInputState.active"
|
|
||||||
[agentId]="searchAiInputState.selectedAgentId" />
|
|
||||||
<div class="aca-header-container">
|
<div class="aca-header-container">
|
||||||
<aca-search-input />
|
<aca-search-input />
|
||||||
<aca-bulk-actions-dropdown *ngIf="bulkActions && ('isBulkActionsAvailable' | isFeatureSupportedInCurrentAcs | async)" [items]="bulkActions" />
|
<aca-bulk-actions-dropdown *ngIf="bulkActions && ('isBulkActionsAvailable' | isFeatureSupportedInCurrentAcs | async)" [items]="bulkActions" />
|
||||||
@@ -26,23 +24,25 @@
|
|||||||
<div class="aca-content__advanced-filters--header">
|
<div class="aca-content__advanced-filters--header">
|
||||||
<p>{{ 'APP.BROWSE.SEARCH.ADVANCED_FILTERS' | translate }}</p>
|
<p>{{ 'APP.BROWSE.SEARCH.ADVANCED_FILTERS' | translate }}</p>
|
||||||
<div class="aca-content__advanced-filters--header--action-buttons">
|
<div class="aca-content__advanced-filters--header--action-buttons">
|
||||||
<button
|
<button
|
||||||
*ngIf="initialSavedSearch !== undefined else saveSearchButton"
|
*ngIf="initialSavedSearch !== undefined; else saveSearchButton"
|
||||||
mat-button
|
mat-button
|
||||||
[disabled]="!encodedQuery"
|
[disabled]="!encodedQuery"
|
||||||
class="aca-content__save-search-action"
|
class="aca-content__save-search-action"
|
||||||
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}"
|
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}"
|
||||||
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate "
|
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate"
|
||||||
[matMenuTriggerFor]="saveSearchOptionsMenu">
|
[matMenuTriggerFor]="saveSearchOptionsMenu"
|
||||||
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
>
|
||||||
<mat-icon iconPositionEnd>keyboard_arrow_down</mat-icon>
|
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
||||||
</button>
|
<mat-icon iconPositionEnd>keyboard_arrow_down</mat-icon>
|
||||||
|
</button>
|
||||||
<mat-menu #saveSearchOptionsMenu="matMenu">
|
<mat-menu #saveSearchOptionsMenu="matMenu">
|
||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
(click)="editSavedSearch(initialSavedSearch)"
|
(click)="editSavedSearch(initialSavedSearch)"
|
||||||
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate }}"
|
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate }}"
|
||||||
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate ">
|
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate"
|
||||||
|
>
|
||||||
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate }}
|
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_CHANGES' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -50,7 +50,8 @@
|
|||||||
acaSaveSearch
|
acaSaveSearch
|
||||||
[acaSaveSearchQuery]="encodedQuery"
|
[acaSaveSearchQuery]="encodedQuery"
|
||||||
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate }}"
|
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate }}"
|
||||||
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate ">
|
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate"
|
||||||
|
>
|
||||||
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate }}
|
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.SAVE_AS_NEW' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
@@ -62,16 +63,19 @@
|
|||||||
[disabled]="!encodedQuery"
|
[disabled]="!encodedQuery"
|
||||||
class="aca-content__save-search-action"
|
class="aca-content__save-search-action"
|
||||||
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}"
|
title="{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}"
|
||||||
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate ">
|
[attr.aria-label]="'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate"
|
||||||
|
>
|
||||||
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<button
|
<button
|
||||||
|
[disabled]="!(areFiltersActive$ | async)"
|
||||||
mat-button
|
mat-button
|
||||||
adf-reset-search
|
adf-reset-search
|
||||||
class="aca-content__reset-action"
|
class="aca-content__reset-action"
|
||||||
title="{{ 'APP.BROWSE.SEARCH.RESET_ACTION' | translate }}"
|
title="{{ 'APP.BROWSE.SEARCH.RESET_ACTION' | translate }}"
|
||||||
[attr.aria-label]="'APP.BROWSE.SEARCH.RESET_ACTION' | translate ">
|
[attr.aria-label]="'APP.BROWSE.SEARCH.RESET_ACTION' | translate"
|
||||||
|
>
|
||||||
{{ 'APP.BROWSE.SEARCH.RESET' | translate }}
|
{{ 'APP.BROWSE.SEARCH.RESET' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,7 +101,13 @@
|
|||||||
(node-dblclick)="handleNodeClick($event)"
|
(node-dblclick)="handleNodeClick($event)"
|
||||||
>
|
>
|
||||||
<data-columns>
|
<data-columns>
|
||||||
<data-column id="app.search.thumbnail" key="$thumbnail" type="image" [sr-title]="'ADF-DOCUMENT-LIST.LAYOUT.THUMBNAIL'" [sortable]="false">
|
<data-column
|
||||||
|
id="app.search.thumbnail"
|
||||||
|
key="$thumbnail"
|
||||||
|
type="image"
|
||||||
|
[sr-title]="'ADF-DOCUMENT-LIST.LAYOUT.THUMBNAIL'"
|
||||||
|
[sortable]="false"
|
||||||
|
>
|
||||||
<ng-template let-context>
|
<ng-template let-context>
|
||||||
<aca-custom-thumbnail-column [context]="context" />
|
<aca-custom-thumbnail-column [context]="context" />
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
+76
-11
@@ -22,13 +22,13 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||||
import { SearchResultsComponent } from './search-results.component';
|
import { SearchResultsComponent } from './search-results.component';
|
||||||
import { AppConfigService, NotificationService, TranslationService } from '@alfresco/adf-core';
|
import { AppConfigService, NotificationService, TranslationService } from '@alfresco/adf-core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { NavigateToFolder } from '@alfresco/aca-shared/store';
|
import { NavigateToFolder } from '@alfresco/aca-shared/store';
|
||||||
import { Pagination, SearchRequest } from '@alfresco/js-api';
|
import { Pagination, SearchRequest } from '@alfresco/js-api';
|
||||||
import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
import { FacetFieldBucket, SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
||||||
import { ActivatedRoute, Event, NavigationStart, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Event, NavigationStart, Params, Router } from '@angular/router';
|
||||||
import { BehaviorSubject, Observable, of, Subject, throwError } from 'rxjs';
|
import { BehaviorSubject, Observable, of, Subject, throwError } from 'rxjs';
|
||||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
@@ -60,9 +60,11 @@ describe('SearchComponent', () => {
|
|||||||
let showErrorSpy: jasmine.Spy<(message: string, action?: string, interpolateArgs?: any, showAction?: boolean) => MatSnackBarRef<any>>;
|
let showErrorSpy: jasmine.Spy<(message: string, action?: string, interpolateArgs?: any, showAction?: boolean) => MatSnackBarRef<any>>;
|
||||||
let showInfoSpy: jasmine.Spy<(message: string, action?: string, interpolateArgs?: any, showAction?: boolean) => MatSnackBarRef<any>>;
|
let showInfoSpy: jasmine.Spy<(message: string, action?: string, interpolateArgs?: any, showAction?: boolean) => MatSnackBarRef<any>>;
|
||||||
let loader: HarnessLoader;
|
let loader: HarnessLoader;
|
||||||
|
let updatedSubjectMock: Subject<SearchRequest>;
|
||||||
|
|
||||||
const editSavedSearchesSpy = jasmine.createSpy('editSavedSearch');
|
const editSavedSearchesSpy = jasmine.createSpy('editSavedSearch');
|
||||||
const getSavedSearchButton = (): HTMLButtonElement => fixture.nativeElement.querySelector('.aca-content__save-search-action');
|
const getSavedSearchButton = (): HTMLButtonElement => fixture.nativeElement.querySelector('.aca-content__save-search-action');
|
||||||
|
const getResetSearchButton = (): HTMLButtonElement => fixture.nativeElement.querySelector('.aca-content__reset-action');
|
||||||
|
|
||||||
const encodeQuery = (query: any): string => {
|
const encodeQuery = (query: any): string => {
|
||||||
return Buffer.from(JSON.stringify(query)).toString('base64');
|
return Buffer.from(JSON.stringify(query)).toString('base64');
|
||||||
@@ -72,6 +74,7 @@ describe('SearchComponent', () => {
|
|||||||
params = new BehaviorSubject({ q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' });
|
params = new BehaviorSubject({ q: 'TYPE: "cm:folder" AND %28=cm: name: email OR cm: name: budget%29' });
|
||||||
queryParams = new Subject();
|
queryParams = new Subject();
|
||||||
routerEvents = new Subject();
|
routerEvents = new Subject();
|
||||||
|
updatedSubjectMock = new Subject();
|
||||||
|
|
||||||
const routerMock = jasmine.createSpyObj<Router>('Router', ['navigate'], {
|
const routerMock = jasmine.createSpyObj<Router>('Router', ['navigate'], {
|
||||||
url: '/mock-search-url',
|
url: '/mock-search-url',
|
||||||
@@ -92,9 +95,7 @@ describe('SearchComponent', () => {
|
|||||||
{
|
{
|
||||||
provide: SavedSearchesContextService,
|
provide: SavedSearchesContextService,
|
||||||
useValue: {
|
useValue: {
|
||||||
getSavedSearches: jasmine
|
savedSearches$: of([{ name: 'test', encodedUrl: encodeQuery({ name: 'test' }), order: 0 }]),
|
||||||
.createSpy('getSavedSearches')
|
|
||||||
.and.returnValue(of([{ name: 'test', encodedUrl: encodeQuery({ name: 'test' }), order: 0 }])),
|
|
||||||
editSavedSearch: editSavedSearchesSpy
|
editSavedSearch: editSavedSearchesSpy
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -125,6 +126,8 @@ describe('SearchComponent', () => {
|
|||||||
router = TestBed.inject(Router);
|
router = TestBed.inject(Router);
|
||||||
route = TestBed.inject(ActivatedRoute);
|
route = TestBed.inject(ActivatedRoute);
|
||||||
|
|
||||||
|
queryBuilder.updated = updatedSubjectMock;
|
||||||
|
|
||||||
const notificationService = TestBed.inject(NotificationService);
|
const notificationService = TestBed.inject(NotificationService);
|
||||||
showErrorSpy = spyOn(notificationService, 'showError');
|
showErrorSpy = spyOn(notificationService, 'showError');
|
||||||
showInfoSpy = spyOn(notificationService, 'showInfo');
|
showInfoSpy = spyOn(notificationService, 'showInfo');
|
||||||
@@ -257,12 +260,6 @@ describe('SearchComponent', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the user query whenever configuration changed', () => {
|
|
||||||
component.searchedWord = 'orange';
|
|
||||||
queryBuilder.configUpdated.next({ 'app:fields': ['cm:tag'] } as any);
|
|
||||||
expect(queryBuilder.userQuery).toBe(`((cm:tag:"orange*"))`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get initial saved search when url matches', () => {
|
it('should get initial saved search when url matches', () => {
|
||||||
route.queryParams = of({ q: encodeQuery({ name: 'test' }) });
|
route.queryParams = of({ q: encodeQuery({ name: 'test' }) });
|
||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
@@ -372,5 +369,73 @@ describe('SearchComponent', () => {
|
|||||||
expect(queryBuilder.userQuery).toBe('(test)');
|
expect(queryBuilder.userQuery).toBe('(test)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set loading to true in updated stream for non-nullish query', fakeAsync(() => {
|
||||||
|
spyOn(queryBuilder, 'execute').and.stub();
|
||||||
|
|
||||||
|
expect(component.isLoading).toBeFalse();
|
||||||
|
|
||||||
|
updatedSubjectMock.next(null);
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.isLoading).toBeFalse();
|
||||||
|
|
||||||
|
updatedSubjectMock.next({} as SearchRequest);
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(component.isLoading).toBeTrue();
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('reset button', () => {
|
||||||
|
it('should enable the reset button when there are queryFragments', fakeAsync(() => {
|
||||||
|
queryBuilder.queryFragmentsUpdate.next({ test: 'test-value' });
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const resetBtn = getResetSearchButton();
|
||||||
|
|
||||||
|
expect(resetBtn).toBeDefined();
|
||||||
|
expect(resetBtn.getAttribute('disabled')).toBeFalsy();
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should enable the reset button when there are userFacetBuckets', fakeAsync(() => {
|
||||||
|
queryBuilder.userFacetBucketsUpdate.next({ test: [{ label: 'test-value' }] as FacetFieldBucket[] });
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const resetBtn = getResetSearchButton();
|
||||||
|
|
||||||
|
expect(resetBtn).toBeDefined();
|
||||||
|
expect(resetBtn.getAttribute('disabled')).toBeFalsy();
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should disable the reset button when there are no filters applied', fakeAsync(() => {
|
||||||
|
queryBuilder.queryFragmentsUpdate.next({});
|
||||||
|
queryBuilder.userFacetBucketsUpdate.next({});
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const resetBtn = getResetSearchButton();
|
||||||
|
|
||||||
|
expect(resetBtn).toBeDefined();
|
||||||
|
expect(resetBtn.getAttribute('disabled')).toBeTruthy();
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
testHeader(SearchResultsComponent, false);
|
testHeader(SearchResultsComponent, false);
|
||||||
});
|
});
|
||||||
|
|||||||
+19
-17
@@ -64,7 +64,7 @@ import {
|
|||||||
} from '@alfresco/aca-shared';
|
} from '@alfresco/aca-shared';
|
||||||
import { SearchSortingDefinition } from '@alfresco/adf-content-services/lib/search/models/search-sorting-definition.interface';
|
import { SearchSortingDefinition } from '@alfresco/adf-content-services/lib/search/models/search-sorting-definition.interface';
|
||||||
import { filter, first, map, startWith, switchMap, take, tap, toArray } from 'rxjs/operators';
|
import { filter, first, map, startWith, switchMap, take, tap, toArray } from 'rxjs/operators';
|
||||||
import { CommonModule } from '@angular/common';
|
import { AsyncPipe, CommonModule } from '@angular/common';
|
||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import { SearchInputComponent } from '../search-input/search-input.component';
|
import { SearchInputComponent } from '../search-input/search-input.component';
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
@@ -84,7 +84,7 @@ import {
|
|||||||
formatSearchTerm
|
formatSearchTerm
|
||||||
} from '../../../utils/aca-search-utils';
|
} from '../../../utils/aca-search-utils';
|
||||||
import { SaveSearchDirective } from '../search-save/directive/save-search.directive';
|
import { SaveSearchDirective } from '../search-save/directive/save-search.directive';
|
||||||
import { combineLatest, of } from 'rxjs';
|
import { combineLatest, merge, Observable, of } from 'rxjs';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { IsFeatureSupportedInCurrentAcsPipe } from '../../../pipes/is-feature-supported.pipe';
|
import { IsFeatureSupportedInCurrentAcsPipe } from '../../../pipes/is-feature-supported.pipe';
|
||||||
@@ -123,7 +123,8 @@ import { SavedSearchesContextService } from '../../../services/saved-searches-co
|
|||||||
BulkActionsDropdownComponent,
|
BulkActionsDropdownComponent,
|
||||||
SearchAiInputContainerComponent,
|
SearchAiInputContainerComponent,
|
||||||
SaveSearchDirective,
|
SaveSearchDirective,
|
||||||
IsFeatureSupportedInCurrentAcsPipe
|
IsFeatureSupportedInCurrentAcsPipe,
|
||||||
|
AsyncPipe
|
||||||
],
|
],
|
||||||
selector: 'aca-search-results',
|
selector: 'aca-search-results',
|
||||||
templateUrl: './search-results.component.html',
|
templateUrl: './search-results.component.html',
|
||||||
@@ -135,6 +136,8 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
infoDrawerPreview$ = this.store.select(infoDrawerPreview);
|
infoDrawerPreview$ = this.store.select(infoDrawerPreview);
|
||||||
|
|
||||||
|
protected readonly areFiltersActive$: Observable<boolean>;
|
||||||
|
|
||||||
searchedWord: string;
|
searchedWord: string;
|
||||||
queryParamName = 'q';
|
queryParamName = 'q';
|
||||||
data: ResultSetPaging;
|
data: ResultSetPaging;
|
||||||
@@ -168,8 +171,14 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
this.queryBuilder.configUpdated.pipe(takeUntilDestroyed()).subscribe((searchConfig) => {
|
this.queryBuilder.configUpdated.pipe(takeUntilDestroyed()).subscribe((searchConfig) => {
|
||||||
this.searchConfig = searchConfig;
|
this.searchConfig = searchConfig;
|
||||||
this.updateUserQuery();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.areFiltersActive$ = merge(this.queryBuilder.queryFragmentsUpdate, this.queryBuilder.userFacetBucketsUpdate).pipe(
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
map((v) => {
|
||||||
|
return Object.values(v).some((filterValue) => (Array.isArray(filterValue) ? filterValue.length > 0 : !!filterValue));
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -179,12 +188,10 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
this.sorting = this.getSorting();
|
this.sorting = this.getSorting();
|
||||||
|
|
||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.queryBuilder.updated.subscribe((query) => {
|
this.queryBuilder.updated.pipe(filter(Boolean)).subscribe(() => {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
if (query) {
|
this.sorting = this.getSorting();
|
||||||
this.sorting = this.getSorting();
|
this.changeDetectorRef.detectChanges();
|
||||||
this.changeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
this.queryBuilder.executed.subscribe((data) => {
|
this.queryBuilder.executed.subscribe((data) => {
|
||||||
@@ -211,7 +218,7 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
.pipe(
|
.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
switchMap((params) =>
|
switchMap((params) =>
|
||||||
this.savedSearchesService.getSavedSearches().pipe(
|
this.savedSearchesService.savedSearches$.pipe(
|
||||||
first(),
|
first(),
|
||||||
map((savedSearches) => savedSearches.find((savedSearch) => savedSearch.encodedUrl === encodeURIComponent(params[this.queryParamName])))
|
map((savedSearches) => savedSearches.find((savedSearch) => savedSearch.encodedUrl === encodeURIComponent(params[this.queryParamName])))
|
||||||
)
|
)
|
||||||
@@ -224,18 +231,18 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
combineLatest([
|
combineLatest([
|
||||||
this.route.queryParams,
|
this.route.queryParams,
|
||||||
this.router.events.pipe(
|
this.router.events.pipe(
|
||||||
filter((e): e is NavigationStart => e instanceof NavigationStart),
|
filter((event): event is NavigationStart => event instanceof NavigationStart),
|
||||||
startWith(null)
|
startWith(null)
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
tap(([params]) => {
|
tap(([params]) => {
|
||||||
|
this.queryBuilder.userQuery = '';
|
||||||
this.encodedQuery = params[this.queryParamName];
|
this.encodedQuery = params[this.queryParamName];
|
||||||
this.isLoading = !!this.encodedQuery;
|
this.isLoading = !!this.encodedQuery;
|
||||||
|
|
||||||
this.searchedWord = extractSearchedWordFromEncodedQuery(this.encodedQuery);
|
this.searchedWord = extractSearchedWordFromEncodedQuery(this.encodedQuery);
|
||||||
this.updateUserQuery();
|
|
||||||
|
|
||||||
const filtersFromEncodedQuery = extractFiltersFromEncodedQuery(this.encodedQuery);
|
const filtersFromEncodedQuery = extractFiltersFromEncodedQuery(this.encodedQuery);
|
||||||
this.queryBuilder.populateFilters.next(filtersFromEncodedQuery || {});
|
this.queryBuilder.populateFilters.next(filtersFromEncodedQuery || {});
|
||||||
@@ -355,11 +362,6 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateUserQuery(): void {
|
|
||||||
const updatedUserQuery = formatSearchTerm(this.searchedWord, this.searchConfig['app:fields']);
|
|
||||||
this.queryBuilder.userQuery = updatedUserQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
private shouldExecuteQuery(navigationStartEvent: NavigationStart | null, query: string | undefined): boolean {
|
private shouldExecuteQuery(navigationStartEvent: NavigationStart | null, query: string | undefined): boolean {
|
||||||
const hasQueryChanged = query !== this.previousEncodedQuery;
|
const hasQueryChanged = query !== this.previousEncodedQuery;
|
||||||
this.previousEncodedQuery = query;
|
this.previousEncodedQuery = query;
|
||||||
|
|||||||
Reference in New Issue
Block a user