From a7fb1958746c3ded91902881129aaf154acf3022 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Garcia Date: Tue, 10 Nov 2020 11:06:26 +0100 Subject: [PATCH] AAE-3936 Tooltip card directive (#6330) * AAE-3936 Tooltip card directive * AAE-3936 Fix styles --- lib/core/directives/directive.module.ts | 11 ++- lib/core/directives/public-api.ts | 1 + .../tooltip-card/tooltip-card.component.html | 5 + .../tooltip-card/tooltip-card.component.scss | 39 ++++++++ .../tooltip-card/tooltip-card.component.ts | 41 ++++++++ .../tooltip-card.directive.spec.ts | 97 +++++++++++++++++++ .../tooltip-card/tooltip-card.directive.ts | 63 ++++++++++++ lib/core/styles/_index.scss | 2 + 8 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 lib/core/directives/tooltip-card/tooltip-card.component.html create mode 100644 lib/core/directives/tooltip-card/tooltip-card.component.scss create mode 100644 lib/core/directives/tooltip-card/tooltip-card.component.ts create mode 100644 lib/core/directives/tooltip-card/tooltip-card.directive.spec.ts create mode 100644 lib/core/directives/tooltip-card/tooltip-card.directive.ts diff --git a/lib/core/directives/directive.module.ts b/lib/core/directives/directive.module.ts index 35837e842b..826c2d5572 100644 --- a/lib/core/directives/directive.module.ts +++ b/lib/core/directives/directive.module.ts @@ -28,11 +28,14 @@ import { NodeRestoreDirective } from './node-restore.directive'; import { UploadDirective } from './upload.directive'; import { NodeDownloadDirective } from './node-download.directive'; import { VersionCompatibilityDirective } from './version-compatibility.directive'; +import { TooltipCardDirective } from './tooltip-card/tooltip-card.directive'; +import { OverlayModule } from '@angular/cdk/overlay'; @NgModule({ imports: [ CommonModule, - MaterialModule + MaterialModule, + OverlayModule ], declarations: [ HighlightDirective, @@ -43,7 +46,8 @@ import { VersionCompatibilityDirective } from './version-compatibility.directive NodeRestoreDirective, NodeDownloadDirective, UploadDirective, - VersionCompatibilityDirective + VersionCompatibilityDirective, + TooltipCardDirective ], exports: [ HighlightDirective, @@ -54,7 +58,8 @@ import { VersionCompatibilityDirective } from './version-compatibility.directive NodeRestoreDirective, NodeDownloadDirective, UploadDirective, - VersionCompatibilityDirective + VersionCompatibilityDirective, + TooltipCardDirective ] }) export class DirectiveModule {} diff --git a/lib/core/directives/public-api.ts b/lib/core/directives/public-api.ts index 88267858bb..bb3ee3f069 100644 --- a/lib/core/directives/public-api.ts +++ b/lib/core/directives/public-api.ts @@ -24,5 +24,6 @@ export * from './node-restore.directive'; export * from './node-download.directive'; export * from './upload.directive'; export * from './version-compatibility.directive'; +export * from './tooltip-card/tooltip-card.directive'; export * from './directive.module'; diff --git a/lib/core/directives/tooltip-card/tooltip-card.component.html b/lib/core/directives/tooltip-card/tooltip-card.component.html new file mode 100644 index 0000000000..1b52d74e35 --- /dev/null +++ b/lib/core/directives/tooltip-card/tooltip-card.component.html @@ -0,0 +1,5 @@ +
+ {{text}} +
+

{{text}}

+
diff --git a/lib/core/directives/tooltip-card/tooltip-card.component.scss b/lib/core/directives/tooltip-card/tooltip-card.component.scss new file mode 100644 index 0000000000..852a0d7bbb --- /dev/null +++ b/lib/core/directives/tooltip-card/tooltip-card.component.scss @@ -0,0 +1,39 @@ +@import '~@angular/material/theming'; + +@mixin adf-tooltip-card-directive($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $foreground: map-get($theme, foreground); + $background: map-get($theme, background); + + :host { + display: block; + } + + div.adf-tooltip-card { + @include mat-elevation(8); + background-color: mat-color($background, card); + border: 1px solid mat-color($primary); + padding: 12px; + border-radius: 6px; + + p { + font-size: mat-font-size($alfresco-typography, caption); + color: mat-color($foreground, text, 0.75); + margin: 0; + } + + hr { + border: 1px solid mat-color($primary); + margin: 6px 0; + } + + img { + border-radius: 6px; + } + } + + ::ng-deep .cdk-overlay-connected-position-bounding-box { + margin-top: 10px !important; + } +} diff --git a/lib/core/directives/tooltip-card/tooltip-card.component.ts b/lib/core/directives/tooltip-card/tooltip-card.component.ts new file mode 100644 index 0000000000..6f1782421f --- /dev/null +++ b/lib/core/directives/tooltip-card/tooltip-card.component.ts @@ -0,0 +1,41 @@ +/*! + * @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 { Component, Input } from '@angular/core'; +import { animate, style, transition, trigger } from '@angular/animations'; + +@Component({ + selector: 'adf-tooltip-card-component', + styleUrls: ['./tooltip-card.component.scss'], + templateUrl: './tooltip-card.component.html', + animations: [ + trigger('tooltip', [ + transition(':enter', [ + style({ opacity: 0 }), + animate(500, style({ opacity: 1 })) + ]), + transition(':leave', [ + animate(500, style({ opacity: 0 })) + + ]) + ]) + ] +}) +export class TooltipCardComponent { + @Input() image = ''; + @Input() text = ''; + @Input() width = '300'; +} diff --git a/lib/core/directives/tooltip-card/tooltip-card.directive.spec.ts b/lib/core/directives/tooltip-card/tooltip-card.directive.spec.ts new file mode 100644 index 0000000000..74761e0fb4 --- /dev/null +++ b/lib/core/directives/tooltip-card/tooltip-card.directive.spec.ts @@ -0,0 +1,97 @@ +/*! + * @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 { Component, ElementRef, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { TooltipCardDirective } from './tooltip-card.directive'; +import { CommonModule } from '@angular/common'; +import { OverlayContainer, OverlayModule } from '@angular/cdk/overlay'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { By } from '@angular/platform-browser'; +import { TooltipCardComponent } from './tooltip-card.component'; + +@Component({ + template: `` +}) +class TestComponent { + @ViewChild(TooltipCardDirective, { static: true }) + public directive: TooltipCardDirective; + + @ViewChild('span', { static: true }) + public span: ElementRef; +} + +describe('TooltipCardDirective', () => { + let fixture: ComponentFixture; + let overlay: HTMLElement; + let overlayContainer: OverlayContainer; + + beforeEach((() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + OverlayModule, + NoopAnimationsModule + ], + declarations: [ + TooltipCardDirective, + TooltipCardComponent, + TestComponent + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + overlayContainer = TestBed.inject(OverlayContainer); + overlay = overlayContainer.getContainerElement(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); + + it('should display tooltip-card on mouse enter', () => { + let tooltipCard = overlay.querySelector('div.adf-tooltip-card'); + expect(tooltipCard).toBeNull(); + const span = fixture.debugElement.query(By.css('span.test-component')); + span.triggerEventHandler('mouseenter', {}); + fixture.detectChanges(); + tooltipCard = overlay.querySelector('div.adf-tooltip-card'); + expect(tooltipCard).not.toBeNull(); + const text = overlay.querySelector('div.adf-tooltip-card p'); + const img = overlay.querySelector('div.adf-tooltip-card img'); + expect(tooltipCard.getAttribute('style')).toBe('width: 400px;'); + expect(text.textContent.trim()).toEqual('Sample text'); + expect(img.getAttribute('src')).toEqual('/assets/testImg.png'); + expect(img.getAttribute('width')).toEqual('400'); + }); + + it('should hide tooltip-card on mouse leave', () => { + const span = fixture.debugElement.query(By.css('span.test-component')); + span.triggerEventHandler('mouseenter', {}); + fixture.detectChanges(); + let tooltipCard = overlay.querySelector('div.adf-tooltip-card'); + expect(tooltipCard).not.toBeNull(); + + span.triggerEventHandler('mouseout', {}); + fixture.detectChanges(); + tooltipCard = overlay.querySelector('div.adf-tooltip-card'); + expect(tooltipCard).toBeNull(); + }); +}); diff --git a/lib/core/directives/tooltip-card/tooltip-card.directive.ts b/lib/core/directives/tooltip-card/tooltip-card.directive.ts new file mode 100644 index 0000000000..5248ff4347 --- /dev/null +++ b/lib/core/directives/tooltip-card/tooltip-card.directive.ts @@ -0,0 +1,63 @@ +/*! + * @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 { ComponentRef, Directive, ElementRef, HostListener, Input, OnInit } from '@angular/core'; +import { Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { TooltipCardComponent } from './tooltip-card.component'; + +@Directive({ selector: '[adf-tooltip-card]' }) +export class TooltipCardDirective implements OnInit { + + @Input('adf-tooltip-card') text = ''; + @Input() image = ''; + @Input() width = '300'; + private overlayRef: OverlayRef; + + constructor( + private overlay: Overlay, + private overlayPositionBuilder: OverlayPositionBuilder, + private elementRef: ElementRef) { + } + + ngOnInit(): void { + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(this.elementRef) + .withPositions([{ + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -8 + }]); + + this.overlayRef = this.overlay.create({ positionStrategy }); + } + + @HostListener('mouseenter') + show() { + const tooltipRef: ComponentRef + = this.overlayRef.attach(new ComponentPortal(TooltipCardComponent)); + tooltipRef.instance.text = this.text; + tooltipRef.instance.image = this.image; + tooltipRef.instance.width = this.width; + } + + @HostListener('mouseout') + hide() { + this.overlayRef.detach(); + } +} diff --git a/lib/core/styles/_index.scss b/lib/core/styles/_index.scss index 619ff846b5..69a7e06779 100644 --- a/lib/core/styles/_index.scss +++ b/lib/core/styles/_index.scss @@ -37,6 +37,7 @@ @import '../../core/clipboard/clipboard.component'; @import '../../core/search-text/search-text-input.component'; @import './snackbar'; +@import '../directives/tooltip-card/tooltip-card.component'; @mixin adf-core-theme($theme) { @include adf-colors-theme($theme); @@ -79,4 +80,5 @@ @include mat-calendar-theme--fix($theme); @include mat-datetimepicker-theme--fix($theme); @include adf-search-text-input-theme($theme); + @include adf-tooltip-card-directive($theme); }