From 9523f5c5ea55c50fa79826b7622ca91b8293f0c0 Mon Sep 17 00:00:00 2001 From: Jonathan Avendano Date: Tue, 3 Dec 2024 11:05:10 -0600 Subject: [PATCH] Adding click handler to open tooltip (#1057) --- cypress/e2e/dialogs/tooltips.cy.ts | 35 ++++++++++---- projects/swimlane/ngx-ui/CHANGELOG.md | 4 ++ .../lib/components/tooltip/show-types.enum.ts | 1 + .../tooltip/tooltip.directive.spec.ts | 17 +++++++ .../components/tooltip/tooltip.directive.ts | 20 +++++++- .../tooltip-page/tooltip-page.component.html | 46 +++++++++++++++++++ .../tooltip-page/tooltip-page.component.scss | 4 ++ .../tooltip-page/tooltip-page.component.ts | 9 +++- .../tooltip-page/tooltip-page.module.ts | 12 ++++- 9 files changed, 136 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/dialogs/tooltips.cy.ts b/cypress/e2e/dialogs/tooltips.cy.ts index 39f974f64..a5a688064 100644 --- a/cypress/e2e/dialogs/tooltips.cy.ts +++ b/cypress/e2e/dialogs/tooltips.cy.ts @@ -1,18 +1,37 @@ -describe('Large Format Dialog', () => { +describe('Tooltips', () => { before(() => { cy.visit('/tooltip'); cy.get('.page-loader').should('not.exist', { timeout: 20000 }); }); - beforeEach(() => { - cy.get('ngx-section [ngx-tooltip]').first().as('CUT'); + describe('Tooltip', () => { + beforeEach(() => { + cy.get('ngx-section [ngx-tooltip]').first().as('CUT'); + }); + + it('should show tooltip', () => { + cy.get('@CUT').whileHovering(() => { + cy.root().closest('body').find('ngx-tooltip-content').should('exist'); + cy.root().closest('body').find('ngx-tooltip-content').should('contain.text', 'Phishing Attack'); + }); + cy.get('ngx-tooltip-content').should('exist'); + }); }); - it('should show tooltip', () => { - cy.get('@CUT').whileHovering(() => { - cy.root().closest('body').find('ngx-tooltip-content').should('exist'); - cy.root().closest('body').find('ngx-tooltip-content').should('contain.text', 'Phishing Attack'); + describe('Tooltip - Popover click', () => { + it('should toggle on click', () => { + cy.dataCy('popover-click').as('BUTTON'); + cy.get('@BUTTON').scrollIntoView(); + cy.get('@BUTTON').whileHovering(() => { + // should not be visible on-hover + cy.root().closest('body').get('.popover-custom').should('not.exist'); + }); + cy.get('@BUTTON').click(); + // should be visible on first click + cy.get('.popover-custom').should('exist'); + cy.get('@BUTTON').click(); + // should no be visible on second click + cy.root().find('.popover-custom').should('not.exist'); }); - cy.get('ngx-tooltip-content').should('exist'); }); }); diff --git a/projects/swimlane/ngx-ui/CHANGELOG.md b/projects/swimlane/ngx-ui/CHANGELOG.md index 6dd518619..0a4e0dfa0 100644 --- a/projects/swimlane/ngx-ui/CHANGELOG.md +++ b/projects/swimlane/ngx-ui/CHANGELOG.md @@ -2,6 +2,10 @@ ## HEAD (unreleased) +- Feature (`ngx-tooltip`): Added new ShowTypes 'click' which can open/close the popover when clicking the component + +## 48.0.5 (2024-09-27) + - Enhancement (`ngx-calendar`): Should initialize with Date when `range` Input is used - Enhancement (`ngx-calendar`): Validation for time in date range selection - Fix (`ngx-calendar`): Possible bug when emitting date range selection and selecting AM/PM diff --git a/projects/swimlane/ngx-ui/src/lib/components/tooltip/show-types.enum.ts b/projects/swimlane/ngx-ui/src/lib/components/tooltip/show-types.enum.ts index cec1ec0fb..0d3816819 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/tooltip/show-types.enum.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/tooltip/show-types.enum.ts @@ -1,5 +1,6 @@ export enum ShowTypes { all = 'all', focus = 'focus', + click = 'click', mouseover = 'mouseover' } diff --git a/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.spec.ts b/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.spec.ts index 53a571725..ee45ddbe6 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.spec.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.spec.ts @@ -114,6 +114,23 @@ describe('TooltipDirective', () => { directive.onMouseClick(); expect(spy).not.toHaveBeenCalled(); }); + + it('should show tooltip - ShowTypes.click', () => { + const spy = spyOn(directive, 'showTooltip'); + directive.tooltipShowEvent = ShowTypes.click; + directive.onMouseClick(); + expect(spy).toHaveBeenCalled(); + }); + + it('should hide tooltip - ShowTypes.click', () => { + const spy = spyOn(directive, 'hideTooltip'); + directive.tooltipShowEvent = ShowTypes.click; + // open tooltip + directive.onMouseClick(); + // close tooltip + directive.onMouseClick(); + expect(spy).toHaveBeenCalled(); + }); }); describe('showTooltip', () => { diff --git a/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.ts b/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.ts index 507202e49..6317528ef 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/tooltip/tooltip.directive.ts @@ -103,6 +103,7 @@ export class TooltipDirective implements OnDestroy { private _tooltipCloseOnMouseLeave = true; private _tooltipHideTimeout = 300; private _tooltipShowTimeout = 100; + private _tooltipIsOpenFromClick = false; private get listensForFocus(): boolean { return this.tooltipShowEvent === ShowTypes.all || this.tooltipShowEvent === ShowTypes.focus; @@ -112,12 +113,20 @@ export class TooltipDirective implements OnDestroy { return this.tooltipShowEvent === ShowTypes.all || this.tooltipShowEvent === ShowTypes.mouseover; } + private get listensForClick(): boolean { + return this.tooltipShowEvent === ShowTypes.all || this.tooltipShowEvent === ShowTypes.click; + } + private component: ComponentRef; private timeout: any; private mouseLeaveContentEvent: () => void; private mouseEnterContentEvent: () => void; private documentClickEvent: () => void; + get tooltipIsOpenFromClick(): boolean { + return this._tooltipIsOpenFromClick; + } + constructor( private readonly ngZone: NgZone, private readonly tooltipService: TooltipService, @@ -153,7 +162,7 @@ export class TooltipDirective implements OnDestroy { @HostListener('mouseleave', ['$event']) onMouseLeave(event: { toElement: Node }): void { - if (this.listensForHover && this.tooltipCloseOnMouseLeave) { + if ((this.listensForHover && this.tooltipCloseOnMouseLeave) || this.listensForClick) { clearTimeout(this.timeout); /* istanbul ignore if */ @@ -171,6 +180,13 @@ export class TooltipDirective implements OnDestroy { onMouseClick(): void { if (this.tooltipShowEvent === ShowTypes.mouseover) { this.hideTooltip(true); + } else if (this.listensForClick) { + if (this._tooltipIsOpenFromClick) { + this.hideTooltip(true); + } else { + this._tooltipIsOpenFromClick = true; + this.showTooltip(true); + } } } @@ -224,6 +240,8 @@ export class TooltipDirective implements OnDestroy { } else { destroyFn(); } + + this._tooltipIsOpenFromClick = false; } addHideListeners(tooltip: HTMLElement): void { diff --git a/src/app/dialogs/tooltip-page/tooltip-page.component.html b/src/app/dialogs/tooltip-page/tooltip-page.component.html index 8944144bc..7b031635b 100644 --- a/src/app/dialogs/tooltip-page/tooltip-page.component.html +++ b/src/app/dialogs/tooltip-page/tooltip-page.component.html @@ -150,6 +150,52 @@

Tool tip custom content defined inside a template

]]> + + + + Click me to open + +
    +
  • +
  • +
+
+ + + Click me to open + +
    +
  • +
  • +
+
]]> +
+
diff --git a/src/app/dialogs/tooltip-page/tooltip-page.component.scss b/src/app/dialogs/tooltip-page/tooltip-page.component.scss index f6f72f739..6a5a1009d 100644 --- a/src/app/dialogs/tooltip-page/tooltip-page.component.scss +++ b/src/app/dialogs/tooltip-page/tooltip-page.component.scss @@ -1,3 +1,7 @@ +@import 'vendor/index'; +@import 'colors/variables'; +@import 'components/index'; + .appearance-table { max-width: 100%; diff --git a/src/app/dialogs/tooltip-page/tooltip-page.component.ts b/src/app/dialogs/tooltip-page/tooltip-page.component.ts index 9b185729d..1d0bbfb14 100644 --- a/src/app/dialogs/tooltip-page/tooltip-page.component.ts +++ b/src/app/dialogs/tooltip-page/tooltip-page.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; +import { TooltipDirective } from '@swimlane/ngx-ui'; @Component({ selector: 'app-tooltip-page', @@ -7,6 +8,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; styleUrls: ['./tooltip-page.component.scss'] }) export class TooltipPageComponent { + @ViewChild('customTooltip', { static: true }) customTooltip: TooltipDirective; + tooltipModel = { text: 'foo' }; @@ -20,4 +23,8 @@ export class TooltipPageComponent { scrollTo(id: string) { (document.getElementById(id) as HTMLElement)?.scrollIntoView({ behavior: 'smooth' }); } + + closeTooltip(): void { + this.customTooltip.hideTooltip(true); + } } diff --git a/src/app/dialogs/tooltip-page/tooltip-page.module.ts b/src/app/dialogs/tooltip-page/tooltip-page.module.ts index fb4824a1c..a0ba66764 100644 --- a/src/app/dialogs/tooltip-page/tooltip-page.module.ts +++ b/src/app/dialogs/tooltip-page/tooltip-page.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SectionModule, TabsModule, TooltipModule } from '@swimlane/ngx-ui'; +import { DropdownModule, SectionModule, TabsModule, TooltipModule } from '@swimlane/ngx-ui'; import { PrismModule } from '../../common/prism/prism.module'; import { TooltipPageRoutingModule } from './tooltip-page-routing.module'; @@ -9,6 +9,14 @@ import { TooltipPageComponent } from './tooltip-page.component'; @NgModule({ declarations: [TooltipPageComponent], - imports: [CommonModule, PrismModule, SectionModule, TooltipModule, TooltipPageRoutingModule, TabsModule] + imports: [ + CommonModule, + PrismModule, + SectionModule, + TooltipModule, + TooltipPageRoutingModule, + TabsModule, + DropdownModule + ] }) export class TooltipPageModule {}