Skip to content

Commit 1330565

Browse files
committed
Revert "Revert "When PopOverButton popover opens, close tooltip on the same element" (#7941)"
This reverts commit 473e73c, reversing changes made to de3d02d.
1 parent d2192ab commit 1330565

File tree

4 files changed

+64
-37
lines changed

4 files changed

+64
-37
lines changed

packages/components/src/components/button/component.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,22 @@ import clsx from 'clsx';
6767
export class KolButtonWc implements InternalButtonAPI, FocusableElement {
6868
@Element() private readonly host?: HTMLKolButtonWcElement;
6969
private buttonRef?: HTMLButtonElement;
70+
private tooltipRef?: HTMLKolTooltipWcElement;
7071

7172
private readonly internalDescriptionById = nonce();
7273

73-
private readonly catchRef = (ref?: HTMLButtonElement) => {
74-
this.buttonRef = ref;
75-
};
76-
7774
@Method()
7875
// eslint-disable-next-line @typescript-eslint/require-await
7976
public async kolFocus() {
8077
this.buttonRef?.focus();
8178
}
8279

80+
@Method()
81+
// eslint-disable-next-line @typescript-eslint/require-await
82+
public async hideTooltip() {
83+
void this.tooltipRef?.hideTooltip();
84+
}
85+
8386
private readonly onClick = (event: MouseEvent) => {
8487
if (this.state._type === 'submit') {
8588
propagateSubmitEventToForm({
@@ -122,7 +125,7 @@ export class KolButtonWc implements InternalButtonAPI, FocusableElement {
122125
return (
123126
<Host>
124127
<button
125-
ref={this.catchRef}
128+
ref={(ref) => (this.buttonRef = ref)}
126129
accessKey={this.state._accessKey || undefined}
127130
aria-controls={this.state._ariaControls}
128131
aria-describedby={hasAriaDescription ? this.internalDescriptionById : undefined}
@@ -158,6 +161,7 @@ export class KolButtonWc implements InternalButtonAPI, FocusableElement {
158161
</KolSpanFc>
159162
</button>
160163
<KolTooltipWcTag
164+
ref={(ref) => (this.tooltipRef = ref)}
161165
/**
162166
* Dieses Aria-Hidden verhindert das doppelte Vorlesen des Labels,
163167
* verhindert aber nicht das Aria-Labelledby vorgelesen wird.

packages/components/src/components/popover-button/component.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,15 @@ export class KolPopoverButton implements PopoverButtonProps {
6060
// Reset the flag after the event loop tick.
6161
this.justClosed = false;
6262
}, 10); // timeout of 0 should be sufficient but doesn't work in Safari Mobile (needs further investigation).
63-
} else if (this.refPopover) {
64-
/**
65-
* Avoid "flicker" by hiding the element until the position is set in the `toggle` event handler. `alignFloatingElements` is responsible for setting the visibility back to 'visible'.
66-
*/
67-
this.refPopover.style.visibility = 'hidden';
63+
} else {
64+
if (this.refPopover) {
65+
/**
66+
* Avoid "flicker" by hiding the element until the position is set in the `toggle` event handler. `alignFloatingElements` is responsible for setting the visibility back to 'visible'.
67+
*/
68+
this.refPopover.style.visibility = 'hidden';
69+
}
70+
71+
void this.refButton?.hideTooltip();
6872
}
6973
}
7074

packages/components/src/components/popover-button/popover-button.e2e.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,20 @@ test.describe('kol-popover-button', () => {
3737
await button.click({ force: true });
3838
await expect(popover).not.toBeVisible();
3939
});
40+
41+
test('should hide its tooltip when popover is shown', async ({ page }) => {
42+
await page.setContent(`
43+
<kol-popover-button _label="Toggle popover" _icons="codicon codicon-info" _hide-label>
44+
Popover content
45+
</kol-popover-button>
46+
`);
47+
const button = page.getByTestId('popover-button').locator('button');
48+
const tooltip = page.locator('kol-tooltip-wc');
49+
50+
await button.hover();
51+
await expect(tooltip).toBeVisible();
52+
53+
await button.click();
54+
await expect(tooltip).not.toBeVisible();
55+
});
4056
});

packages/components/src/components/tooltip/component.tsx

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ import { autoUpdate } from '@floating-ui/dom';
22
import type { AlignPropType, BadgeTextPropType, IdPropType, LabelPropType, TooltipAPI, TooltipStates } from '../../schema';
33
import { getDocument, validateBadgeText, validateAlign, validateId, validateLabel } from '../../schema';
44
import type { JSX } from '@stencil/core';
5+
import { Method } from '@stencil/core';
56
import { Component, Element, h, Host, Prop, State, Watch } from '@stencil/core';
67

78
import { alignFloatingElements } from '../../utils/align-floating-elements';
89
import { hideOverlay, showOverlay } from '../../utils/overlay';
910
import { KolSpanFc } from '../../functional-components';
1011

12+
// Timing Guidelines for Exposing Hidden Content: https://www.nngroup.com/articles/timing-exposing-content/
13+
const TOOLTIP_DELAY = 300;
14+
1115
/**
1216
* @internal
1317
*/
@@ -21,8 +25,6 @@ export class KolTooltipWc implements TooltipAPI {
2125
private arrowElement?: HTMLDivElement;
2226
private previousSibling?: Element | null;
2327
private tooltipElement?: HTMLDivElement;
24-
private hasFocusIn = false;
25-
private hasMouseIn = false;
2628

2729
private cleanupAutoPositioning?: () => void;
2830

@@ -51,7 +53,26 @@ export class KolTooltipWc implements TooltipAPI {
5153
}
5254
};
5355

54-
private hideTooltip = (): void => {
56+
private showTooltipTimeout?: ReturnType<typeof setTimeout>;
57+
private showTooltipWithDelay = (): void => {
58+
clearTimeout(this.showTooltipTimeout);
59+
this.showTooltipTimeout = setTimeout(() => {
60+
this.showTooltip();
61+
}, TOOLTIP_DELAY);
62+
};
63+
64+
private hideTooltipTimeout?: ReturnType<typeof setTimeout>;
65+
private hideTooltipWithDelay = (): void => {
66+
clearTimeout(this.hideTooltipTimeout);
67+
this.hideTooltipTimeout = setTimeout(() => {
68+
void this.hideTooltip();
69+
}, TOOLTIP_DELAY);
70+
};
71+
72+
@Method()
73+
// eslint-disable-next-line @typescript-eslint/require-await
74+
public async hideTooltip() {
75+
clearTimeout(this.showTooltipTimeout); // Cancel scheduled tooltips
5576
if (this.tooltipElement /* SSR instanceof HTMLElement */) {
5677
hideOverlay(this.tooltipElement);
5778
this.tooltipElement.style.setProperty('display', 'none');
@@ -62,29 +83,25 @@ export class KolTooltipWc implements TooltipAPI {
6283
}
6384
}
6485
getDocument().removeEventListener('keyup', this.hideTooltipByEscape);
65-
};
86+
}
6687

6788
private hideTooltipByEscape = (event: KeyboardEvent): void => {
6889
if (event.key === 'Escape') {
69-
this.hideTooltip();
90+
void this.hideTooltip();
7091
}
7192
};
7293

7394
private handleMouseEnter() {
74-
this.hasMouseIn = true;
75-
this.showOrHideTooltip();
95+
this.showTooltipWithDelay();
7696
}
7797
private handleMouseleave() {
78-
this.hasMouseIn = false;
79-
this.showOrHideTooltip();
98+
this.hideTooltipWithDelay();
8099
}
81100
private handleFocusIn() {
82-
this.hasFocusIn = true;
83-
this.showOrHideTooltip();
101+
this.showTooltipWithDelay();
84102
}
85103
private handleFocusout() {
86-
this.hasFocusIn = false;
87-
this.showOrHideTooltip();
104+
this.hideTooltipWithDelay();
88105
}
89106

90107
private addListeners = (el: Element): void => {
@@ -183,20 +200,6 @@ export class KolTooltipWc implements TooltipAPI {
183200
});
184201
}
185202

186-
private overFocusTimeout?: ReturnType<typeof setTimeout>;
187-
188-
private showOrHideTooltip = (): void => {
189-
clearTimeout(this.overFocusTimeout);
190-
this.overFocusTimeout = setTimeout(() => {
191-
if (this.hasMouseIn || this.hasFocusIn) {
192-
this.showTooltip();
193-
} else {
194-
this.hideTooltip();
195-
}
196-
// Timing Guidelines for Exposing Hidden Content: https://www.nngroup.com/articles/timing-exposing-content/
197-
}, 300);
198-
};
199-
200203
public componentWillLoad(): void {
201204
this.validateBadgeText(this._badgeText);
202205
this.validateAlign(this._align);

0 commit comments

Comments
 (0)