diff --git a/demo-shell-ng2/app/components/search/search-bar.component.html b/demo-shell-ng2/app/components/search/search-bar.component.html index 2fcc5b92dc..1f3b2a73a8 100644 --- a/demo-shell-ng2/app/components/search/search-bar.component.html +++ b/demo-shell-ng2/app/components/search/search-bar.component.html @@ -4,7 +4,6 @@ [highlight]="true" (searchSubmit)="onSearchSubmit($event);" (searchChange)="onSearchTermChange($event);" - (expand)="onExpandToggle($event);" (fileSelect)="onItemClicked($event)"> diff --git a/demo-shell-ng2/app/components/search/search-bar.component.ts b/demo-shell-ng2/app/components/search/search-bar.component.ts index 9978723e6e..c34e75d9d0 100644 --- a/demo-shell-ng2/app/components/search/search-bar.component.ts +++ b/demo-shell-ng2/app/components/search/search-bar.component.ts @@ -64,8 +64,4 @@ export class SearchBarComponent { onSearchTermChange(event) { this.searchTerm = event.value; } - - onExpandToggle(event) { - this.expand.emit(event); - } } diff --git a/docs/search-control.component.md b/docs/search-control.component.md index eda804946f..367c01c10b 100644 --- a/docs/search-control.component.md +++ b/docs/search-control.component.md @@ -31,9 +31,9 @@ | --- | --- | --- | --- | | searchTerm | string | | Search term to pre-populate the field with | | inputType | string | "text" | Type of the input field to render, e.g. "search" or "text" (default) | -| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. | | autocomplete | boolean | true | Whether the browser should offer field auto-completion for the input field to the user. | | highlight | boolean | false | Use the true value if you want to see the searched word highlighted. | +| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. | | liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. | | liveSearchRoot | string | "-root-" | NodeRef or node name where the search should start. | | liveSearchResultType | string | | Node type to filter live search results by, e.g. 'cm:content'. | @@ -47,7 +47,6 @@ | searchChange | Emitted when the search term is changed. The search term is provided in the 'value' property of the returned object. If the term is less than three characters in length then the term is truncated to an empty string. | | searchSubmit | Emitted when the search form is submitted. The search term is provided in the 'value' property of the returned object. | | fileSelect | Emitted when a file item from the list of find-as-you-type results is selected | -| expand | Emitted when the expanded state of the control changes based on focus events and the content of the input control | ## Details diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css index c1e8888ec2..f808aeda21 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-orange.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-purple.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-purple.css index daf5b69f58..1912ae3c3b 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-purple.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-blue-purple.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-orange.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-orange.css index d21590ce78..9003d394ac 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-orange.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-orange.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-purple.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-purple.css index 601a220177..e3aa775937 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-purple.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-cyan-purple.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-orange.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-orange.css index 48455652d4..3024494b67 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-orange.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-orange.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-purple.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-purple.css index c1516d427b..4c768ac0c4 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-purple.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-green-purple.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-indigo-pink.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-indigo-pink.css index 0f5e5cbee0..87bcdb9c22 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-indigo-pink.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-indigo-pink.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-pink-bluegrey.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-pink-bluegrey.css index 69acf6f1fb..ef9d7e24a8 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-pink-bluegrey.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-pink-bluegrey.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-purple-green.css b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-purple-green.css index 44baae18fd..5ccdb09424 100644 --- a/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-purple-green.css +++ b/ng2-components/ng2-alfresco-core/prebuilt-themes/adf-purple-green.css @@ -3157,6 +3157,7 @@ adf-file-uploading-list-row:not(:first-child) { padding: 0; } .adf-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; min-width: 20px; diff --git a/ng2-components/ng2-alfresco-documentlist/index.ts b/ng2-components/ng2-alfresco-documentlist/index.ts index 07ea75257c..339651a37a 100644 --- a/ng2-components/ng2-alfresco-documentlist/index.ts +++ b/ng2-components/ng2-alfresco-documentlist/index.ts @@ -31,11 +31,11 @@ import { EmptyFolderContentDirective } from './src/components/empty-folder/empty import { DropdownSitesComponent } from './src/components/site-dropdown/sites-dropdown.component'; import { MaterialModule } from './src/material.module'; +import { ContentNodeSelectorService } from './src/components/content-node-selector/content-node-selector.service'; import { DocumentActionsService } from './src/services/document-actions.service'; import { DocumentListService } from './src/services/document-list.service'; import { FolderActionsService } from './src/services/folder-actions.service'; import { NodeActionsService } from './src/services/node-actions.service'; -import { ContentNodeSelectorService } from './src/components/content-node-selector/content-node-selector.service'; // components export * from './src/components/document-list.component'; diff --git a/ng2-components/ng2-alfresco-search/README.md b/ng2-components/ng2-alfresco-search/README.md index 77df2b6c11..45acdb6242 100644 --- a/ng2-components/ng2-alfresco-search/README.md +++ b/ng2-components/ng2-alfresco-search/README.md @@ -48,9 +48,9 @@ Contains the Search and Search Results components. | --- | --- | --- | --- | | searchTerm | string | | Search term to pre-populate the field with | | inputType | string | "text" | Type of the input field to render, e.g. "search" or "text" (default) | -| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. | | autocomplete | boolean | true | Whether the browser should offer field auto-completion for the input field to the user. | | highlight | boolean | false | Use the true value if you want to see the searched word highlighted. | +| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. | | liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. | | liveSearchRoot | string | "-root-" | NodeRef or node name where the search should start. | | liveSearchResultType | string | | Node type to filter live search results by, e.g. 'cm:content'. | @@ -64,7 +64,6 @@ Contains the Search and Search Results components. | searchChange | Emitted when the search term is changed. The search term is provided in the 'value' property of the returned object. If the term is less than three characters in length then the term is truncated to an empty string. | | searchSubmit | Emitted when the search form is submitted. The search term is provided in the 'value' property of the returned object. | | fileSelect | Emitted when a file item from the list of find-as-you-type results is selected | -| expand | Emitted when the expanded state of the control changes based on focus events and the content of the input control | ### Details diff --git a/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.html b/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.html index 57532e823f..4866f7c36e 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.html +++ b/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.html @@ -1,7 +1,7 @@ - + - + - + diff --git a/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.ts b/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.ts index c82dc1a058..fe5d1c2b93 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.ts +++ b/ng2-components/ng2-alfresco-search/src/components/search-autocomplete.component.ts @@ -123,7 +123,7 @@ export class SearchAutocompleteComponent implements OnChanges { results => { this.results = results.list.entries.slice(0, this.maxResults); - if (results && results.list && results.list.entries.length > 0) { + if (results && results.list) { this.startAnimation(); } diff --git a/ng2-components/ng2-alfresco-search/src/components/search-control.component.html b/ng2-components/ng2-alfresco-search/src/components/search-control.component.html index a86bddddd8..1d130f262a 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-control.component.html +++ b/ng2-components/ng2-alfresco-search/src/components/search-control.component.html @@ -2,8 +2,9 @@
diff --git a/ng2-components/ng2-alfresco-search/src/components/search-control.component.scss b/ng2-components/ng2-alfresco-search/src/components/search-control.component.scss index efe0ae8756..5543b52803 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-control.component.scss +++ b/ng2-components/ng2-alfresco-search/src/components/search-control.component.scss @@ -4,10 +4,11 @@ .adf { &-search-button.mat-button { + margin-right: 10px; border-radius: 50%; height: 32px; - min-width: 20px; width: 32px; + min-width: 20px; padding: 0; overflow: hidden; color: inherit; @@ -31,6 +32,10 @@ background-color: mat-color($background, card); } + .mat-form-field-underline { + background-color: mat-color($background, card); + } + .mat-input-element { font-size: 16px; } diff --git a/ng2-components/ng2-alfresco-search/src/components/search-control.component.spec.ts b/ng2-components/ng2-alfresco-search/src/components/search-control.component.spec.ts index 03fa000e2f..c71e63e765 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-control.component.spec.ts +++ b/ng2-components/ng2-alfresco-search/src/components/search-control.component.spec.ts @@ -18,7 +18,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ThumbnailService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, CoreModule, SearchService } from 'ng2-alfresco-core'; -import { results } from './../assets/search.component.mock'; +import { noResult, results } from './../assets/search.component.mock'; import { TranslationMock } from './../assets/translation.service.mock'; import { SearchAutocompleteComponent } from './search-autocomplete.component'; import { SearchControlComponent } from './search-control.component'; @@ -103,7 +103,28 @@ describe('SearchControlComponent', () => { fixture.detectChanges(); }); - describe('Component rendering', () => { + describe('expandable option false', () => { + + beforeEach(() => { + component.expandable = false; + }); + + afterEach(() => { + component.expandable = true; + }); + + it('search button should be hide', () => { + let searchButton: any = element.querySelector('#adf-search-button'); + expect(searchButton).toBe(null); + }); + + it('should not have animation', () => { + component.ngOnInit(); + expect(component.subscriptAnimationState).toBe('no-animation'); + }); + }); + + describe('component rendering', () => { it('should display a text input field by default', () => { fixture.detectChanges(); @@ -129,7 +150,7 @@ describe('SearchControlComponent', () => { }); }); - describe('Autocomplete list', () => { + describe('autocomplete list', () => { let inputEl: HTMLInputElement; @@ -159,6 +180,25 @@ describe('SearchControlComponent', () => { window.setTimeout(() => { fixture.detectChanges(); expect(component.liveSearchComponent.panelAnimationState).not.toBe('void'); + let resultElement: Element = element.querySelector('#adf-search-results'); + expect(resultElement).not.toBe(null); + done(); + }, 100); + }); + + it('should show autocomplete list noe results cwhen search box has focus and there is search result with length 0', (done) => { + spyOn(searchService, 'getQueryNodesPromise') + .and.returnValue(Promise.resolve(noResult)); + + component.liveSearchTerm = 'test'; + + fixture.detectChanges(); + inputEl.dispatchEvent(new FocusEvent('focus')); + window.setTimeout(() => { + fixture.detectChanges(); + expect(component.liveSearchComponent.panelAnimationState).not.toBe('void'); + let noResultElement: Element = element.querySelector('#search_no_result'); + expect(noResultElement).not.toBe(null); done(); }, 100); }); @@ -285,45 +325,48 @@ describe('SearchControlComponent', () => { }); - describe('component focus', () => { + describe('search button', () => { - it('should fire an event when the search box receives focus', (done) => { - spyOn(component.expand, 'emit'); - let inputEl: HTMLElement = element.querySelector('input'); - inputEl.dispatchEvent(new FocusEvent('focus')); - window.setTimeout(() => { - expect(component.expand.emit).toHaveBeenCalledWith({ - expanded: true - }); + it('click on the search button should close the input box when is open', (done) => { + fixture.detectChanges(); + component.subscriptAnimationState = 'active'; + + let searchButton: any = element.querySelector('#adf-search-button'); + searchButton.click(); + + setTimeout(() => { + expect(component.subscriptAnimationState).toBe('inactive'); done(); - }, 100); + }, 500); }); - it('should fire an event when the search box loses focus', (done) => { - spyOn(component.expand, 'emit'); - let inputEl: HTMLElement = element.querySelector('input'); - inputEl.dispatchEvent(new FocusEvent('blur')); - window.setTimeout(() => { - expect(component.expand.emit).toHaveBeenCalledWith({ - expanded: false - }); + it('click on the search button should open the input box when is close', (done) => { + fixture.detectChanges(); + component.subscriptAnimationState = 'inactive'; + + let searchButton: any = element.querySelector('#adf-search-button'); + searchButton.click(); + + setTimeout(() => { + expect(component.subscriptAnimationState).toBe('active'); done(); - }, 100); + }, 300); }); - it('should NOT fire an event when the search box receives/loses focus but the component is not expandable', - (done) => { - spyOn(component.expand, 'emit'); - component.expandable = false; - let inputEl: HTMLElement = element.querySelector('input'); - inputEl.dispatchEvent(new FocusEvent('focus')); - inputEl.dispatchEvent(new FocusEvent('blur')); - window.setTimeout(() => { - expect(component.expand.emit).not.toHaveBeenCalled(); - done(); - }, 100); - }); + it('Search button should not change the input state too often', (done) => { + fixture.detectChanges(); + component.subscriptAnimationState = 'active'; + let searchButton: any = element.querySelector('#adf-search-button'); + searchButton.click(); + searchButton.click(); + + setTimeout(() => { + expect(component.subscriptAnimationState).toBe('inactive'); + done(); + }, 400); + + }); }); describe('file preview', () => { @@ -338,13 +381,17 @@ describe('SearchControlComponent', () => { }); }); - it('should set deactivate the search after file/folder is clicked', () => { + it('should set deactivate the search after file/folder is clicked', (done) => { component.subscriptAnimationState = 'active'; component.onFileClicked({ value: 'node12345' }); - expect(component.subscriptAnimationState).toBe('inactive'); + setTimeout(() => { + expect(component.subscriptAnimationState).toBe('inactive'); + done(); + }, 300); + }); it('should NOT reset the search term after file/folder is clicked', () => { diff --git a/ng2-components/ng2-alfresco-search/src/components/search-control.component.ts b/ng2-components/ng2-alfresco-search/src/components/search-control.component.ts index 1c5a0888bd..7b2bb4aad1 100644 --- a/ng2-components/ng2-alfresco-search/src/components/search-control.component.ts +++ b/ng2-components/ng2-alfresco-search/src/components/search-control.component.ts @@ -29,10 +29,11 @@ import { SearchAutocompleteComponent } from './search-autocomplete.component'; animations: [ trigger('transitionMessages', [ state('active', style({transform: 'translateX(0%)'})), - state('inactive', style({transform: 'translateX(89%)'})), - transition('void => active, inactive => active', + state('inactive', style({transform: 'translateX(86%)'})), + state('no-animation', style({transform: 'translateX(0%)', width: '100%'})), + transition('inactive => active', animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')), - transition('active => inactive, void => inactive', + transition('active => inactive', animate('300ms cubic-bezier(0.55, 0, 0.55, 0.2)')) ]) ], @@ -64,9 +65,6 @@ export class SearchControlComponent implements OnInit, OnDestroy { @Output() fileSelect = new EventEmitter(); - @Output() - expand = new EventEmitter(); - searchControl: FormControl; @ViewChild('searchInput', {}) @@ -96,16 +94,30 @@ export class SearchControlComponent implements OnInit, OnDestroy { private focusSubject = new Subject(); - subscriptAnimationState: string = 'inactive'; + private toggleSearch = new Subject(); + + subscriptAnimationState: string; constructor() { this.searchControl = new FormControl( this.searchTerm, Validators.compose([Validators.required, SearchTermValidator.minAlphanumericChars(3)]) ); + + this.toggleSearch.debounceTime(200).subscribe(() => { + if (this.expandable) { + this.subscriptAnimationState = this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive'; + + if (this.subscriptAnimationState === 'inactive') { + this.searchTerm = ''; + } + } + }); } ngOnInit(): void { + this.subscriptAnimationState = this.expandable ? 'inactive' : 'no-animation'; + this.searchControl.valueChanges.debounceTime(400).distinctUntilChanged() .subscribe((value: string) => { this.onSearchTermChange(value); @@ -177,27 +189,16 @@ export class SearchControlComponent implements OnInit, OnDestroy { } onFocus($event): void { - if (this.expandable) { - this.expand.emit({ - expanded: true - }); - } this.focusSubject.next($event); } onBlur($event): void { - if (this.expandable && (this.searchControl.value === '' || this.searchControl.value === undefined)) { - this.expand.emit({ - expanded: false - }); - } this.focusSubject.next($event); } onEscape(): void { this.hideAutocomplete(); this.toggleSearchBar(); - this.searchTerm = ''; } onArrowDown(): void { @@ -221,11 +222,7 @@ export class SearchControlComponent implements OnInit, OnDestroy { this.hideAutocomplete(); } - onClickSearch() { - this.subscriptAnimationState = 'active'; - } - toggleSearchBar() { - this.subscriptAnimationState = this.subscriptAnimationState === 'inactive' ? 'active' : 'inactive'; + this.toggleSearch.next(); } }
{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}
@@ -31,8 +30,7 @@
{{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }}