[ACA-2261] improve UX on search input (#1004)

* Remove search on change

- search call would be triggered only on submit or on option change

* clicking search icon triggers search

* caches user changes for a possible future search

* caches non-empty user changes for a possible future search

* close search options menu on submit

* update queryBuilder and navigate to new search url

* add setting to enable/disable searching after typing on search input

* fix double search call

* Apply suggestions from code review

- custom name to distinguish between ADF and ACA settings

Co-Authored-By: suzanadirla <dirla.silvia.suzana@gmail.com>
This commit is contained in:
Suzana Dirla 2019-03-11 16:13:44 +02:00 committed by Denys Vuika
parent a52c3e463b
commit 8045e17c28
4 changed files with 65 additions and 22 deletions

View File

@ -191,6 +191,7 @@
} }
] ]
}, },
"aca:triggeredOnChange": false,
"filterQueries": [ "filterQueries": [
{ "query": "+TYPE:'cm:folder' OR +TYPE:'cm:content'" }, { "query": "+TYPE:'cm:folder' OR +TYPE:'cm:content'" },
{ {

View File

@ -3,6 +3,7 @@
mat-icon-button mat-icon-button
id="app-search-button" id="app-search-button"
class="app-search-button" class="app-search-button"
(click)="searchSubmit(searchTerm)"
[title]="'SEARCH.BUTTON.TOOLTIP' | translate" [title]="'SEARCH.BUTTON.TOOLTIP' | translate"
> >
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate" <mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate"

View File

@ -2,10 +2,12 @@
class="app-search-container searchMenuTrigger" class="app-search-container searchMenuTrigger"
[matMenuTriggerFor]="searchOptionsMenu" [matMenuTriggerFor]="searchOptionsMenu"
(menuOpened)="onMenuOpened()" (menuOpened)="onMenuOpened()"
(menuClosed)="syncInputValues()"
> >
<button <button
mat-icon-button mat-icon-button
class="app-search-button" class="app-search-button"
(click)="searchByOption()"
[title]="'SEARCH.BUTTON.TOOLTIP' | translate" [title]="'SEARCH.BUTTON.TOOLTIP' | translate"
> >
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate" <mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate"

View File

@ -48,6 +48,8 @@ import { SearchQueryBuilderService } from '@alfresco/adf-content-services';
import { ContentManagementService } from '../../../services/content-management.service'; import { ContentManagementService } from '../../../services/content-management.service';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service'; import { SearchLibrariesQueryBuilderService } from '../search-libraries-results/search-libraries-query-builder.service';
import { MatMenuTrigger } from '@angular/material';
import { AppConfigService } from '@alfresco/adf-core';
export enum SearchOptionIds { export enum SearchOptionIds {
Files = 'content', Files = 'content',
@ -67,6 +69,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {
hasNewChange = false; hasNewChange = false;
navigationTimer: any; navigationTimer: any;
has400LibraryError = false; has400LibraryError = false;
searchOnChange;
searchedWord = null; searchedWord = null;
searchOptions: Array<any> = [ searchOptions: Array<any> = [
@ -93,13 +96,22 @@ export class SearchInputComponent implements OnInit, OnDestroy {
@ViewChild('searchInputControl') @ViewChild('searchInputControl')
searchInputControl: SearchInputControlComponent; searchInputControl: SearchInputControlComponent;
@ViewChild(MatMenuTrigger)
trigger: MatMenuTrigger;
constructor( constructor(
private queryBuilder: SearchQueryBuilderService, private queryBuilder: SearchQueryBuilderService,
private queryLibrariesBuilder: SearchLibrariesQueryBuilderService, private queryLibrariesBuilder: SearchLibrariesQueryBuilderService,
private config: AppConfigService,
private content: ContentManagementService, private content: ContentManagementService,
private router: Router, private router: Router,
private store: Store<AppStore> private store: Store<AppStore>
) {} ) {
this.searchOnChange = this.config.get<boolean>(
'search.aca:triggeredOnChange',
true
);
}
ngOnInit() { ngOnInit() {
this.showInputValue(); this.showInputValue();
@ -122,20 +134,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {
showInputValue() { showInputValue() {
this.has400LibraryError = false; this.has400LibraryError = false;
this.searchedWord = ''; this.searchedWord = this.getUrlSearchTerm();
if (this.onSearchResults || this.onLibrariesSearchResults) {
const urlTree: UrlTree = this.router.parseUrl(this.router.url);
const urlSegmentGroup: UrlSegmentGroup =
urlTree.root.children[PRIMARY_OUTLET];
if (urlSegmentGroup) {
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
this.searchedWord = urlSegments[0].parameters['q']
? decodeURIComponent(urlSegments[0].parameters['q'])
: '';
}
}
if (this.searchInputControl) { if (this.searchInputControl) {
this.searchInputControl.searchTerm = this.searchedWord; this.searchInputControl.searchTerm = this.searchedWord;
@ -157,17 +156,23 @@ export class SearchInputComponent implements OnInit, OnDestroy {
* *
* @param event Parameters relating to the search * @param event Parameters relating to the search
*/ */
onSearchSubmit(event: KeyboardEvent) { onSearchSubmit(event: any) {
this.has400LibraryError = false; const searchTerm = event.target
const searchTerm = (event.target as HTMLInputElement).value; ? (event.target as HTMLInputElement).value
: event;
if (searchTerm) { if (searchTerm) {
this.searchedWord = searchTerm; this.searchedWord = searchTerm;
this.searchByOption(); this.searchByOption();
} }
this.trigger.closeMenu();
} }
onSearchChange(searchTerm: string) { onSearchChange(searchTerm: string) {
if (!this.searchOnChange) {
return;
}
this.has400LibraryError = false; this.has400LibraryError = false;
this.searchedWord = searchTerm; this.searchedWord = searchTerm;
@ -193,14 +198,15 @@ export class SearchInputComponent implements OnInit, OnDestroy {
} }
searchByOption() { searchByOption() {
this.syncInputValues();
this.has400LibraryError = false; this.has400LibraryError = false;
if (this.isLibrariesChecked()) { if (this.isLibrariesChecked()) {
if (this.searchedWord && !this.onLibrariesSearchResults) { if (this.onLibrariesSearchResults && this.isSameSearchTerm()) {
this.queryLibrariesBuilder.update();
} else if (this.searchedWord) {
this.store.dispatch( this.store.dispatch(
new SearchByTermAction(this.searchedWord, this.searchOptions) new SearchByTermAction(this.searchedWord, this.searchOptions)
); );
} else {
this.queryLibrariesBuilder.update();
} }
} else { } else {
if (this.isFoldersChecked() && !this.isFilesChecked()) { if (this.isFoldersChecked() && !this.isFilesChecked()) {
@ -211,7 +217,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {
this.removeContentFilters(); this.removeContentFilters();
} }
if (this.onSearchResults) { if (this.onSearchResults && this.isSameSearchTerm()) {
this.queryBuilder.update(); this.queryBuilder.update();
} else if (this.searchedWord) { } else if (this.searchedWord) {
this.store.dispatch( this.store.dispatch(
@ -277,4 +283,37 @@ export class SearchInputComponent implements OnInit, OnDestroy {
`+TYPE:'cm:${SearchOptionIds.Folders}'` `+TYPE:'cm:${SearchOptionIds.Folders}'`
); );
} }
syncInputValues() {
if (this.searchInputControl.searchTerm !== this.searchedWord) {
if (this.searchInputControl.searchTerm) {
this.searchedWord = this.searchInputControl.searchTerm;
} else {
this.searchInputControl.searchTerm = this.searchedWord;
}
}
}
getUrlSearchTerm(): string {
let searchTerm = '';
if (this.onSearchResults || this.onLibrariesSearchResults) {
const urlTree: UrlTree = this.router.parseUrl(this.router.url);
const urlSegmentGroup: UrlSegmentGroup =
urlTree.root.children[PRIMARY_OUTLET];
if (urlSegmentGroup) {
const urlSegments: UrlSegment[] = urlSegmentGroup.segments;
searchTerm = urlSegments[0].parameters['q']
? decodeURIComponent(urlSegments[0].parameters['q'])
: '';
}
}
return searchTerm;
}
isSameSearchTerm(): boolean {
const urlSearchTerm = this.getUrlSearchTerm();
return this.searchedWord === urlSearchTerm;
}
} }