Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class MockTooltipComponent {
showTooltip = jest.fn();
hideTooltip = jest.fn();
toggleTooltip = jest.fn();
openWith = jest.fn(() => "both" as "hover" | "click" | "both");
}

@Component({
Expand Down Expand Up @@ -52,30 +53,127 @@ describe("TooltipTriggerComponent", () => {
});

describe("Event listeners", () => {
it("should call toggleTooltip on click", () => {
hostEl.click();
expect(tooltip.toggleTooltip).toHaveBeenCalled();
describe("click", () => {
it("should call toggleTooltip when openWith is 'click'", () => {
tooltip.openWith = jest.fn(() => "click");
hostEl.click();
expect(tooltip.toggleTooltip).toHaveBeenCalled();
});

it("should call toggleTooltip when openWith is 'both'", () => {
tooltip.openWith = jest.fn(() => "both");
hostEl.click();
expect(tooltip.toggleTooltip).toHaveBeenCalled();
});

it("should not call toggleTooltip when openWith is 'hover'", () => {
tooltip.openWith = jest.fn(() => "hover");
hostEl.click();
expect(tooltip.toggleTooltip).not.toHaveBeenCalled();
});
});

it("should call showTooltip on mouseenter", () => {
hostEl.dispatchEvent(new Event("mouseenter"));
expect(tooltip.showTooltip).toHaveBeenCalled();
describe("mouseenter", () => {
it("should call showTooltip when openWith is 'hover'", () => {
tooltip.openWith = jest.fn(() => "hover");
hostEl.dispatchEvent(new Event("mouseenter"));
expect(tooltip.showTooltip).toHaveBeenCalled();
});

it("should call showTooltip when openWith is 'both'", () => {
tooltip.openWith = jest.fn(() => "both");
hostEl.dispatchEvent(new Event("mouseenter"));
expect(tooltip.showTooltip).toHaveBeenCalled();
});

it("should not call showTooltip when openWith is 'click'", () => {
tooltip.openWith = jest.fn(() => "click");
hostEl.dispatchEvent(new Event("mouseenter"));
expect(tooltip.showTooltip).not.toHaveBeenCalled();
});
});

it("should call showTooltip on focusin", () => {
hostEl.dispatchEvent(new Event("focusin"));
expect(tooltip.showTooltip).toHaveBeenCalled();
describe("mouseleave", () => {
beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it("should call hideTooltip after delay when openWith is 'hover'", () => {
tooltip.openWith = jest.fn(() => "hover");
hostEl.dispatchEvent(new Event("mouseleave"));

expect(tooltip.hideTooltip).not.toHaveBeenCalled();
jest.advanceTimersByTime(100);
expect(tooltip.hideTooltip).toHaveBeenCalled();
});

it("should call hideTooltip after delay when openWith is 'both'", () => {
tooltip.openWith = jest.fn(() => "both");
hostEl.dispatchEvent(new Event("mouseleave"));

expect(tooltip.hideTooltip).not.toHaveBeenCalled();
jest.advanceTimersByTime(100);
expect(tooltip.hideTooltip).toHaveBeenCalled();
});

it("should not call hideTooltip when openWith is 'click'", () => {
tooltip.openWith = jest.fn(() => "click");
hostEl.dispatchEvent(new Event("mouseleave"));

jest.advanceTimersByTime(100);
expect(tooltip.hideTooltip).not.toHaveBeenCalled();
});
});

it("should call hideTooltip on focusout when not hovering content", () => {
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).toHaveBeenCalled();
describe("focusin", () => {
it("should call showTooltip when openWith is 'hover'", () => {
tooltip.openWith = jest.fn(() => "hover");
hostEl.dispatchEvent(new Event("focusin"));
expect(tooltip.showTooltip).toHaveBeenCalled();
});

it("should call showTooltip when openWith is 'both'", () => {
tooltip.openWith = jest.fn(() => "both");
hostEl.dispatchEvent(new Event("focusin"));
expect(tooltip.showTooltip).toHaveBeenCalled();
});

it("should not call showTooltip when openWith is 'click'", () => {
tooltip.openWith = jest.fn(() => "click");
hostEl.dispatchEvent(new Event("focusin"));
expect(tooltip.showTooltip).not.toHaveBeenCalled();
});
});

it("should not hideTooltip on focusout when content is hovered", () => {
tooltip.isContentHovered = jest.fn(() => true);
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).not.toHaveBeenCalled();
describe("focusout", () => {
it("should call hideTooltip on focusout when not hovering content and openWith is 'hover'", () => {
tooltip.openWith = jest.fn(() => "hover");
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).toHaveBeenCalled();
});

it("should call hideTooltip on focusout when not hovering content and openWith is 'both'", () => {
tooltip.openWith = jest.fn(() => "both");
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).toHaveBeenCalled();
});

it("should not call hideTooltip on focusout when openWith is 'click'", () => {
tooltip.openWith = jest.fn(() => "click");
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).not.toHaveBeenCalled();
});

it("should not hideTooltip on focusout when content is hovered", () => {
tooltip.openWith = jest.fn(() => "hover");
tooltip.isContentHovered = jest.fn(() => true);
hostEl.dispatchEvent(new Event("focusout"));
expect(tooltip.hideTooltip).not.toHaveBeenCalled();
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,46 @@ export class TooltipTriggerComponent implements AfterContentChecked {

@HostListener("click")
onClick() {
this.tooltip.toggleTooltip();
if (
this.tooltip.openWith() === "both" ||
this.tooltip.openWith() === "click"
) {
this.tooltip.toggleTooltip();
}
}

@HostListener("mouseenter")
onMouseEnter() {
this.tooltip.showTooltip();
if (
this.tooltip.openWith() === "both" ||
this.tooltip.openWith() === "hover"
) {
this.tooltip.showTooltip();
}
}

@HostListener("mouseleave")
onMouseLeave() {
clearTimeout(this.tooltip.hideTimeout);
if (
this.tooltip.openWith() === "both" ||
this.tooltip.openWith() === "hover"
) {
clearTimeout(this.tooltip.hideTimeout);

this.tooltip.hideTimeout = setTimeout(() => {
this.tooltip.hideTooltip();
}, this.tooltip.timeoutDelay());
this.tooltip.hideTimeout = setTimeout(() => {
this.tooltip.hideTooltip();
}, this.tooltip.timeoutDelay());
}
}

@HostListener("focusin")
onFocusIn() {
this.tooltip.showTooltip();
if (
this.tooltip.openWith() === "both" ||
this.tooltip.openWith() === "hover"
) {
this.tooltip.showTooltip();
}
}

@HostListener("focusout")
Expand All @@ -57,7 +77,12 @@ export class TooltipTriggerComponent implements AfterContentChecked {
return;
}

this.tooltip.hideTooltip();
if (
this.tooltip.openWith() === "both" ||
this.tooltip.openWith() === "hover"
) {
this.tooltip.hideTooltip();
}
}

ngAfterContentChecked(): void {
Expand Down
7 changes: 7 additions & 0 deletions tedi/components/overlay/tooltip/tooltip.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { TooltipTriggerComponent } from "./tooltip-trigger/tooltip-trigger.component";

export type TooltipPosition = `${NgxFloatUiPlacements}`;
export type TooltipOpenWith = "hover" | "click" | "both";

@Component({
standalone: true,
Expand All @@ -39,6 +40,12 @@ export class TooltipComponent implements AfterContentChecked {
*/
readonly preventOverflow = input(true);

/**
* How tooltip can opened?
* @default both
*/
readonly openWith = input<TooltipOpenWith>("both");

/**
* Append floating element to given selector.
* Use 'body' to append at the end of DOM or empty string to append next to trigger element.
Expand Down
19 changes: 18 additions & 1 deletion tedi/components/overlay/tooltip/tooltip.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const POSITIONS: TooltipPosition[] = [
"left-start",
"left-end",
];
const OPEN_WITH = ["hover", "click", "both"];

/**
* <a href="https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=5797-117363&amp;m=dev" target="_blank">Figma ↗</a><br>
Expand Down Expand Up @@ -71,6 +72,21 @@ export default {
},
},
},
openWith: {
control: "radio",
description: "How tooltip can opened?",
options: OPEN_WITH,
table: {
category: "tooltip",
type: {
summary: "TooltipOpenWith",
detail: "hover \nclick \nboth",
},
defaultValue: {
summary: "both",
},
},
},
preventOverflow: {
control: "boolean",
description:
Expand Down Expand Up @@ -144,11 +160,12 @@ export const Default: Story = {
appendTo: "body",
timeoutDelay: 100,
maxWidth: "medium",
openWith: "both",
},
render: (args) => ({
props: args,
template: `
<tedi-tooltip [position]="position" [timeoutDelay]="timeoutDelay" [preventOverflow]="preventOverflow" [appendTo]="appendTo">
<tedi-tooltip [position]="position" [timeoutDelay]="timeoutDelay" [preventOverflow]="preventOverflow" [appendTo]="appendTo" [openWith]="openWith">
<tedi-tooltip-trigger>
<button tedi-info-button></button>
</tedi-tooltip-trigger>
Expand Down
Loading