Skip to content

Commit

Permalink
[ACS-8751] Adapt search results to handle query encoding and state pr…
Browse files Browse the repository at this point in the history
…opagation
  • Loading branch information
MichalKinas authored and dominikiwanekhyland committed Oct 10, 2024
1 parent e6a707a commit ab0ef88
Show file tree
Hide file tree
Showing 13 changed files with 463 additions and 259 deletions.
36 changes: 29 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@ngrx/store": "^15.2.0",
"@ngrx/store-devtools": "^15.2.0",
"@ngx-translate/core": "^14.0.0",
"buffer": "^6.0.3",
"date-fns": "^2.30.0",
"material-icons": "^1.13.12",
"minimatch-browser": "^1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ import { AppHookService, AppService } from '@alfresco/aca-shared';
import { map } from 'rxjs/operators';
import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
import { SearchNavigationService } from '../search-navigation.service';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { NotificationService } from '@alfresco/adf-core';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { Buffer } from 'buffer';
import { ActivatedRoute } from '@angular/router';

describe('SearchInputComponent', () => {
let fixture: ComponentFixture<SearchInputComponent>;
let component: SearchInputComponent;
let actions$: Actions;
let appHookService: AppHookService;
let route: ActivatedRoute;
let searchInputService: SearchNavigationService;
let showErrorSpy: jasmine.Spy;

Expand All @@ -50,6 +53,10 @@ describe('SearchInputComponent', () => {
toggleAppNavBar$: new Subject()
};

const encodeQuery = (query: any): string => {
return Buffer.from(JSON.stringify(query)).toString('base64');
};

beforeEach(() => {
appServiceMock.setAppNavbarMode.calls.reset();
TestBed.configureTestingModule({
Expand All @@ -68,6 +75,7 @@ describe('SearchInputComponent', () => {
fixture = TestBed.createComponent(SearchInputComponent);
appHookService = TestBed.inject(AppHookService);
searchInputService = TestBed.inject(SearchNavigationService);
route = TestBed.inject(ActivatedRoute);
component = fixture.componentInstance;

const notificationService = TestBed.inject(NotificationService);
Expand Down Expand Up @@ -147,35 +155,10 @@ describe('SearchInputComponent', () => {
});

describe('onSearchChange()', () => {
it('should call search action with correct search options', (done) => {
const searchedTerm = 's';
const currentSearchOptions = [{ key: 'SEARCH.INPUT.FILES' }];
actions$
.pipe(
ofType<SearchByTermAction>(SearchActionTypes.SearchByTerm),
map((action) => {
expect(action.searchOptions[0].key).toBe(currentSearchOptions[0].key);
})
)
.subscribe(() => {
done();
});
component.onSearchChange(searchedTerm);
});

it('should call search action with correct searched term', (done) => {
it('should call search action with correct searched term', () => {
const searchedTerm = 's';
actions$
.pipe(
ofType<SearchByTermAction>(SearchActionTypes.SearchByTerm),
map((action) => {
expect(action.payload).toBe(searchedTerm);
})
)
.subscribe(() => {
done();
});
component.onSearchChange(searchedTerm);
expect(component.searchedWord).toBe(searchedTerm);
});

it('should show snack for empty search', () => {
Expand Down Expand Up @@ -246,4 +229,14 @@ describe('SearchInputComponent', () => {

expect(appServiceMock.setAppNavbarMode).toHaveBeenCalledWith('expanded');
});

it('should extract searched word from query params', (done) => {
route.queryParams = of({ q: encodeQuery({ userQuery: 'cm:name:"test*"' }) });
route.queryParams.subscribe(() => {
fixture.detectChanges();
expect(component.searchedWord).toBe('test');
done();
});
fixture.detectChanges();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
import { AppConfigService, NotificationService } from '@alfresco/adf-core';
import { Component, inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
import { NavigationEnd, PRIMARY_OUTLET, Router, RouterEvent, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router';
import { ActivatedRoute, Params, PRIMARY_OUTLET, Router, UrlSegment, UrlSegmentGroup, UrlTree } from '@angular/router';
import { Store } from '@ngrx/store';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import { takeUntil } from 'rxjs/operators';
import { SearchInputControlComponent } from '../search-input-control/search-input-control.component';
import { SearchNavigationService } from '../search-navigation.service';
import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service';
Expand All @@ -44,6 +44,7 @@ import { MatInputModule } from '@angular/material/input';
import { A11yModule } from '@angular/cdk/a11y';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { FormsModule } from '@angular/forms';
import { extractSearchedWordFromEncodedQuery } from '../../../utils/aca-search-utils';

@Component({
standalone: true,
Expand All @@ -70,9 +71,6 @@ export class SearchInputComponent implements OnInit, OnDestroy {
private notificationService = inject(NotificationService);

onDestroy$: Subject<boolean> = new Subject<boolean>();
hasOneChange = false;
hasNewChange = false;
navigationTimer: any;
has400LibraryError = false;
hasLibrariesConstraint = false;
searchOnChange: boolean;
Expand Down Expand Up @@ -110,6 +108,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {
private queryLibrariesBuilder: SearchLibrariesQueryBuilderService,
private config: AppConfigService,
private router: Router,
private route: ActivatedRoute,
private store: Store<AppStore>,
private appHookService: AppHookService,
private appService: AppService,
Expand All @@ -121,14 +120,14 @@ export class SearchInputComponent implements OnInit, OnDestroy {
ngOnInit() {
this.showInputValue();

this.router.events
.pipe(takeUntil(this.onDestroy$))
.pipe(filter((e) => e instanceof RouterEvent))
.subscribe((event) => {
if (event instanceof NavigationEnd) {
this.showInputValue();
}
});
this.route.queryParams.pipe(takeUntil(this.onDestroy$)).subscribe((params: Params) => {
const encodedQuery = params['q'] ? params['q'] : null;
this.searchedWord = extractSearchedWordFromEncodedQuery(encodedQuery);

if (this.searchInputControl) {
this.searchInputControl.searchTerm = this.searchedWord;
}
});

this.appHookService.library400Error.pipe(takeUntil(this.onDestroy$)).subscribe(() => {
this.has400LibraryError = true;
Expand Down Expand Up @@ -192,24 +191,6 @@ export class SearchInputComponent implements OnInit, OnDestroy {
this.has400LibraryError = false;
this.hasLibrariesConstraint = this.evaluateLibrariesConstraint();
this.searchedWord = searchTerm;

if (this.hasOneChange) {
this.hasNewChange = true;
} else {
this.hasOneChange = true;
}

if (this.hasNewChange) {
clearTimeout(this.navigationTimer);
this.hasNewChange = false;
}

this.navigationTimer = setTimeout(() => {
if (searchTerm) {
this.store.dispatch(new SearchByTermAction(searchTerm, this.searchOptions));
}
this.hasOneChange = false;
}, 1000);
}

searchByOption() {
Expand Down Expand Up @@ -305,7 +286,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {

if (urlSegmentGroup) {
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
searchTerm = urlSegments[0].parameters['q'] ? decodeURIComponent(urlSegments[0].parameters['q']) : '';
searchTerm = extractSearchedWordFromEncodedQuery(urlSegments[0].parameters['q']);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ import { AppTestingModule } from '../../../testing/app-testing.module';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { SearchLibrariesResultsComponent } from './search-libraries-results.component';
import { SearchLibrariesQueryBuilderService } from './search-libraries-query-builder.service';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { AppService } from '@alfresco/aca-shared';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { ActivatedRoute } from '@angular/router';
import { Buffer } from 'buffer';

describe('SearchLibrariesResultsComponent', () => {
let component: SearchLibrariesResultsComponent;
let fixture: ComponentFixture<SearchLibrariesResultsComponent>;
let route: ActivatedRoute;

const emptyPage = { list: { pagination: { totalItems: 0 }, entries: [] } };
const appServiceMock = {
Expand All @@ -42,6 +45,10 @@ describe('SearchLibrariesResultsComponent', () => {
setAppNavbarMode: jasmine.createSpy('setAppNavbarMode')
};

const encodeQuery = (query: any): string => {
return Buffer.from(JSON.stringify(query)).toString('base64');
};

beforeEach(() => {
appServiceMock.setAppNavbarMode.calls.reset();
TestBed.configureTestingModule({
Expand All @@ -56,6 +63,7 @@ describe('SearchLibrariesResultsComponent', () => {
]
});

route = TestBed.inject(ActivatedRoute);
fixture = TestBed.createComponent(SearchLibrariesResultsComponent);
component = fixture.componentInstance;
});
Expand All @@ -72,4 +80,14 @@ describe('SearchLibrariesResultsComponent', () => {

expect(appServiceMock.setAppNavbarMode).toHaveBeenCalledWith('collapsed');
});

it('should extract searched word from query params', (done) => {
route.queryParams = of({ q: encodeQuery({ userQuery: 'cm:name:"test*"' }) });
route.queryParams.subscribe(() => {
fixture.detectChanges();
expect(component.searchedWord).toBe('test');
done();
});
fixture.detectChanges();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import { CustomEmptyContentTemplateDirective, DataColumnComponent, DataColumnLis
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { DocumentListDirective } from '../../../directives/document-list.directive';
import { DocumentListComponent } from '@alfresco/adf-content-services';
import { extractSearchedWordFromEncodedQuery } from '../../../utils/aca-search-utils';
import { takeUntil } from 'rxjs/operators';

@Component({
standalone: true,
Expand Down Expand Up @@ -128,14 +130,12 @@ export class SearchLibrariesResultsComponent extends PageComponent implements On
);

if (this.route) {
this.route.params.forEach((params: Params) => {
// eslint-disable-next-line no-prototype-builtins
this.searchedWord = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
const query = this.formatSearchQuery(this.searchedWord);

if (query && query.length > 1) {
this.route.queryParams.pipe(takeUntil(this.onDestroy$)).subscribe((params: Params) => {
const encodedQuery = params[this.queryParamName] ? params[this.queryParamName] : null;
this.searchedWord = extractSearchedWordFromEncodedQuery(encodedQuery);
if (this.searchedWord?.length > 1) {
this.librariesQueryBuilder.paging.skipCount = 0;
this.librariesQueryBuilder.userQuery = query;
this.librariesQueryBuilder.userQuery = this.searchedWord;
this.librariesQueryBuilder.update();
} else {
this.librariesQueryBuilder.userQuery = null;
Expand All @@ -147,13 +147,6 @@ export class SearchLibrariesResultsComponent extends PageComponent implements On
}
}

private formatSearchQuery(userInput: string) {
if (!userInput) {
return null;
}
return userInput.trim();
}

onSearchResultLoaded(nodePaging: NodePaging) {
this.data = nodePaging;
this.totalResults = this.getNumberOfResults();
Expand Down
Loading

0 comments on commit ab0ef88

Please sign in to comment.