From 7698fb8edb04426369f3b612234bf83eab4c3dab Mon Sep 17 00:00:00 2001 From: Silviu Popa Date: Wed, 30 Oct 2019 09:44:37 +0200 Subject: [PATCH] [AAE-534] Core - add search cloud component (#5193) * [AAE-534] - add search cloud component * change doc file * more changes on doc file * remove empty scss file * add animation and more customizations * add preselect value property * fix unit tests --- .../core/components/search-cloud.component.md | 40 ++++++++ lib/core/core.module.ts | 7 +- lib/core/index.ts | 1 + lib/core/models/search-cloud.model.ts | 38 ++++++++ .../search-text-cloud.component.html | 14 +++ .../search-text-cloud.component.spec.ts | 57 +++++++++++ .../search-text-cloud.component.ts | 69 ++++++++++++++ lib/core/search-cloud/index.ts | 18 ++++ lib/core/search-cloud/public-api.ts | 19 ++++ .../search-cloud/search-cloud.component.scss | 33 +++++++ .../search-cloud.component.spec.ts | 61 ++++++++++++ .../search-cloud/search-cloud.component.ts | 95 +++++++++++++++++++ lib/core/search-cloud/search-cloud.module.ts | 38 ++++++++ lib/core/services/search-cloud.service.ts | 26 +++++ 14 files changed, 514 insertions(+), 2 deletions(-) create mode 100644 docs/core/components/search-cloud.component.md create mode 100644 lib/core/models/search-cloud.model.ts create mode 100644 lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.html create mode 100644 lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.spec.ts create mode 100644 lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.ts create mode 100644 lib/core/search-cloud/index.ts create mode 100644 lib/core/search-cloud/public-api.ts create mode 100644 lib/core/search-cloud/search-cloud.component.scss create mode 100644 lib/core/search-cloud/search-cloud.component.spec.ts create mode 100644 lib/core/search-cloud/search-cloud.component.ts create mode 100644 lib/core/search-cloud/search-cloud.module.ts create mode 100644 lib/core/services/search-cloud.service.ts diff --git a/docs/core/components/search-cloud.component.md b/docs/core/components/search-cloud.component.md new file mode 100644 index 0000000000..b953d245d3 --- /dev/null +++ b/docs/core/components/search-cloud.component.md @@ -0,0 +1,40 @@ +--- +Title: Search Cloud Component +Added: v3.5.0 +Status: Active +Last reviewed: 2019-10-24 +--- + +# [Search Cloud Component](../../../lib/core/search-cloud/search-cloud.component.ts "Defined in pagination.component.ts") + +Should manage search for cloud components + +## Basic Usage + +```html + + [type]="'text'" + [placeholder]="'placeholder'" + [debounceTime]="200" + [expandable]='false' + (change)="onSearchValueChanged($event)" + +``` + +## Class members + +### Properties + +| Name | Type | Default value | Description | +| ---- | ---- | ------------- | ----------- | +| type | [`SearchCloudTypesEnum`](../../../lib/core/models/search-cloud.model.ts) | | search type ('text'). | +| value | `string` | | preselected input value | +| expandable | `boolean` | false | The field should expand on click when this flag is true | +| placeholder | `string` | | placeholder content. | +| debounceTime | `number` | 500 | Time in miliseconds for debounce the event. | + +### Events + +| Name | Type | Description | +| ---- | ---- | ----------- | +| change | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted when search widget value is changed. | diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 042b8a247f..bf9b31548c 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -56,6 +56,7 @@ import { TranslateLoaderService } from './services/translate-loader.service'; import { ExtensionsModule } from '@alfresco/adf-extensions'; import { directionalityConfigFactory } from './services/directionality-config-factory'; import { DirectionalityConfigService } from './services/directionality-config.service'; +import { SearchCloudModule } from './search-cloud/search-cloud.module'; @NgModule({ imports: [ @@ -89,7 +90,8 @@ import { DirectionalityConfigService } from './services/directionality-config.se TemplateModule, IconModule, SortingPickerModule, - NotificationHistoryModule + NotificationHistoryModule, + SearchCloudModule ], exports: [ AboutModule, @@ -122,7 +124,8 @@ import { DirectionalityConfigService } from './services/directionality-config.se TemplateModule, SortingPickerModule, IconModule, - NotificationHistoryModule + NotificationHistoryModule, + SearchCloudModule ] }) export class CoreModule { diff --git a/lib/core/index.ts b/lib/core/index.ts index a78bb5c00b..d2dba02de9 100644 --- a/lib/core/index.ts +++ b/lib/core/index.ts @@ -42,6 +42,7 @@ export * from './clipboard/index'; export * from './dialogs/index'; export * from './icon/index'; export * from './notifications/index'; +export * from './search-cloud/index'; export * from './utils/index'; export * from './interface/index'; diff --git a/lib/core/models/search-cloud.model.ts b/lib/core/models/search-cloud.model.ts new file mode 100644 index 0000000000..8dfc10afb0 --- /dev/null +++ b/lib/core/models/search-cloud.model.ts @@ -0,0 +1,38 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + import { SearchTextCloudComponent } from '../search-cloud/components/search-text-cloud/search-text-cloud.component'; + + export interface SearchCloudProperties { + value?: string; + placeholder?: string; + debounceTime?: number; + expandable?: boolean; + } + + export enum SearchCloudTypesEnum { + text = 'text' + } + + export const SEARCH_CLOUD_TYPES = { + text: SearchTextCloudComponent + }; + + export interface SearchCloudWidget { + properties: SearchCloudProperties; + onChangedHandler(event: any); + } diff --git a/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.html b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.html new file mode 100644 index 0000000000..fb58038305 --- /dev/null +++ b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.html @@ -0,0 +1,14 @@ +
+ + + + close + + + search +
\ No newline at end of file diff --git a/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.spec.ts b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.spec.ts new file mode 100644 index 0000000000..6c598b9e06 --- /dev/null +++ b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.spec.ts @@ -0,0 +1,57 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SearchTextCloudComponent } from './search-text-cloud.component'; +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { setupTestBed } from 'core'; +import { CoreTestingModule } from '../../../testing/core.testing.module'; +import { SearchCloudService } from '../../../services/search-cloud.service'; + +describe('SearchTextCloudComponent', () => { + + let fixture: ComponentFixture; + let service: SearchCloudService; + + setupTestBed({ + imports: [CoreTestingModule] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SearchTextCloudComponent); + service = TestBed.get(SearchCloudService); + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should update search service value when is field is changed', async(() => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + const searchInput = fixture.nativeElement.querySelector('.adf-search-text-cloud input'); + searchInput.value = 'mock-search-text'; + searchInput.dispatchEvent(new Event('input')); + fixture.detectChanges(); + }); + + service.value.subscribe( value => { + expect(value).toBe('mock-search-text'); + }); + })); + +}); diff --git a/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.ts b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.ts new file mode 100644 index 0000000000..9599f50488 --- /dev/null +++ b/lib/core/search-cloud/components/search-text-cloud/search-text-cloud.component.ts @@ -0,0 +1,69 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ViewEncapsulation, Component, ViewChild, ElementRef, Renderer2, OnInit } from '@angular/core'; +import { Subject } from 'rxjs'; +import { SearchCloudService } from '../../../services/search-cloud.service'; +import { SearchCloudProperties, SearchCloudWidget } from '../../../models/search-cloud.model'; + +@Component({ + selector: 'adf-search-text-cloud', + templateUrl: './search-text-cloud.component.html', + encapsulation: ViewEncapsulation.None +}) +export class SearchTextCloudComponent implements OnInit, SearchCloudWidget { + + @ViewChild('searchContainer') + searchInput: ElementRef; + + properties: SearchCloudProperties = {}; + onDestroy$: Subject = new Subject(); + + expandedClass = 'app-field-expanded'; + + constructor( + private searchCloudService: SearchCloudService, + private renderer: Renderer2) {} + + ngOnInit() { + if (!this.isExpandable()) { + this.renderer.addClass(this.searchInput.nativeElement, this.expandedClass); + } + } + + onChangedHandler(event) { + this.searchCloudService.value.next(event.target.value); + } + + toggle() { + if (!this.isExpandable()) { return; } + + if (this.searchInput.nativeElement && this.searchInput.nativeElement.classList.contains(this.expandedClass)) { + this.renderer.removeClass(this.searchInput.nativeElement, this.expandedClass); + } else { + this.renderer.addClass(this.searchInput.nativeElement, this.expandedClass); + } + } + + private isExpandable() { + return this.properties && this.properties.expandable; + } + + clear() { + this.properties.value = ''; + } +} diff --git a/lib/core/search-cloud/index.ts b/lib/core/search-cloud/index.ts new file mode 100644 index 0000000000..a7e30cc675 --- /dev/null +++ b/lib/core/search-cloud/index.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './public-api'; diff --git a/lib/core/search-cloud/public-api.ts b/lib/core/search-cloud/public-api.ts new file mode 100644 index 0000000000..547f32411a --- /dev/null +++ b/lib/core/search-cloud/public-api.ts @@ -0,0 +1,19 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './search-cloud.component'; +export * from './search-cloud.module'; diff --git a/lib/core/search-cloud/search-cloud.component.scss b/lib/core/search-cloud/search-cloud.component.scss new file mode 100644 index 0000000000..72cca2c67d --- /dev/null +++ b/lib/core/search-cloud/search-cloud.component.scss @@ -0,0 +1,33 @@ +.adf-search-cloud { + + &-wrapper { + position: relative; + margin: 10px; + width: 260px; + + &.app-field-expanded { + mat-form-field { + display: block; + width: 220px; + } + } + } + + &-icon { + fill: currentColor; + width: 24px; + height: 24px; + line-height: 65px; + float: right; + } + + mat-form-field { + float: right; + width:0; + margin-left: 10px; + + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + transition: all 0.5s ease; + } +} diff --git a/lib/core/search-cloud/search-cloud.component.spec.ts b/lib/core/search-cloud/search-cloud.component.spec.ts new file mode 100644 index 0000000000..e2c172e4b9 --- /dev/null +++ b/lib/core/search-cloud/search-cloud.component.spec.ts @@ -0,0 +1,61 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { setupTestBed } from 'core'; +import { CoreTestingModule } from '../testing/core.testing.module'; +import { SearchCloudComponent } from './search-cloud.component'; +import { SearchCloudTypesEnum } from '../models/search-cloud.model'; + +describe('SearchCloudComponent', () => { + + let fixture: ComponentFixture; + let component: SearchCloudComponent; + + setupTestBed({ + imports: [CoreTestingModule] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SearchCloudComponent); + component = fixture.componentInstance; + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should emit search text when is field is changed', async(() => { + spyOn(component.change, 'emit'); + component.type = SearchCloudTypesEnum.text; + component.ngOnInit(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + const searchInput = fixture.nativeElement.querySelector('.adf-search-text-cloud input'); + searchInput.value = 'mock-search-text'; + searchInput.dispatchEvent(new Event('input')); + fixture.detectChanges(); + }); + + component.change.subscribe( emitValue => { + expect(emitValue).toBe('mock-search-text'); + }); + })); + +}); diff --git a/lib/core/search-cloud/search-cloud.component.ts b/lib/core/search-cloud/search-cloud.component.ts new file mode 100644 index 0000000000..08a9e11c4e --- /dev/null +++ b/lib/core/search-cloud/search-cloud.component.ts @@ -0,0 +1,95 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ViewEncapsulation, Component, OnInit, ComponentRef, ViewChild, ViewContainerRef, Input, ComponentFactoryResolver, OnDestroy, EventEmitter, Output } from '@angular/core'; +import { SearchCloudService } from '../services/search-cloud.service'; +import { Subject } from 'rxjs'; +import { takeUntil, debounceTime } from 'rxjs/operators'; +import { SearchCloudTypesEnum, SEARCH_CLOUD_TYPES, SearchCloudProperties } from '../models/search-cloud.model'; + +@Component({ + selector: 'adf-search-cloud', + template: '
', + styleUrls: ['./search-cloud.component.scss'], + encapsulation: ViewEncapsulation.None, + host: { + 'class': 'adf-search-cloud' + } +}) +export class SearchCloudComponent implements OnInit, OnDestroy { + + @Input() value: string = ''; + + @Input() debounceTime: number = 500; + + @Input() placeholder: string; + + @Input() expandable: boolean = false; + + @Input() type: SearchCloudTypesEnum; + + @ViewChild('container', { read: ViewContainerRef }) + container: ViewContainerRef; + + @Output() change: EventEmitter = new EventEmitter(); + + private componentRef: ComponentRef; + onDestroy$: Subject = new Subject(); + + constructor ( + private searchCloudService: SearchCloudService, + private componentFactoryResolver: ComponentFactoryResolver) {} + + ngOnInit() { + const componentType = SEARCH_CLOUD_TYPES[this.type]; + if (componentType) { + const factory = this.componentFactoryResolver.resolveComponentFactory(componentType); + if (factory) { + this.componentRef = this.container.createComponent(factory, 0); + this.setupWidget(); + } + } + + this.searchCloudService.value + .pipe( + debounceTime(this.debounceTime), + takeUntil(this.onDestroy$) + ) + .subscribe( (value: string) => { + this.change.emit(value); + }); + } + + setupWidget() { + if (this.componentRef && this.componentRef.instance) { + const properties: SearchCloudProperties = { + placeholder: this.placeholder, + debounceTime: this.debounceTime, + expandable: this.expandable, + value: this.value + }; + this.componentRef.instance.properties = properties; + } + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + this.componentRef = null; + } + } +} diff --git a/lib/core/search-cloud/search-cloud.module.ts b/lib/core/search-cloud/search-cloud.module.ts new file mode 100644 index 0000000000..3d3f7b6f74 --- /dev/null +++ b/lib/core/search-cloud/search-cloud.module.ts @@ -0,0 +1,38 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NgModule } from '@angular/core'; +import { SearchCloudComponent } from './search-cloud.component'; +import { SearchTextCloudComponent } from './components/search-text-cloud/search-text-cloud.component'; +import { CommonModule } from '@angular/common'; +import { MaterialModule } from '../material.module'; +import { FormsModule } from '@angular/forms'; + +@NgModule({ + declarations: [ + SearchCloudComponent, + SearchTextCloudComponent + ], + imports: [ + CommonModule, + MaterialModule, + FormsModule + ], + exports: [ SearchCloudComponent], + entryComponents: [ SearchTextCloudComponent ] +}) +export class SearchCloudModule {} diff --git a/lib/core/services/search-cloud.service.ts b/lib/core/services/search-cloud.service.ts new file mode 100644 index 0000000000..d2efbe4fe3 --- /dev/null +++ b/lib/core/services/search-cloud.service.ts @@ -0,0 +1,26 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class SearchCloudService { + value = new Subject(); +}