Skip to content

Commit

Permalink
feat(core/tooltip): add position property (#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielleroux committed Mar 27, 2023
1 parent 64cd604 commit 0d18866
Show file tree
Hide file tree
Showing 17 changed files with 246 additions and 20 deletions.
4 changes: 2 additions & 2 deletions packages/angular/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1664,14 +1664,14 @@ export declare interface IxToggle extends Components.IxToggle {


@ProxyCmp({
inputs: ['for', 'interactive', 'titleContent']
inputs: ['for', 'interactive', 'placement', 'titleContent']
})
@Component({
selector: 'ix-tooltip',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['for', 'interactive', 'titleContent'],
inputs: ['for', 'interactive', 'placement', 'titleContent'],
})
export class IxTooltip {
protected el: HTMLElement;
Expand Down
35 changes: 35 additions & 0 deletions packages/core/component-doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8875,6 +8875,41 @@
"optional": false,
"required": false
},
{
"name": "placement",
"type": "\"bottom\" | \"left\" | \"right\" | \"top\"",
"mutable": false,
"attr": "placement",
"reflectToAttr": false,
"docs": "Initial placement of the tooltip. If the placement don\"t have enough space,\nthe tooltip will placed on another location.",
"docsTags": [
{
"name": "since",
"text": "1.5.0"
}
],
"default": "'top'",
"values": [
{
"value": "bottom",
"type": "string"
},
{
"value": "left",
"type": "string"
},
{
"value": "right",
"type": "string"
},
{
"value": "top",
"type": "string"
}
],
"optional": false,
"required": false
},
{
"name": "titleContent",
"type": "string",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"test:e2e": "playwright test --reporter html",
"test:e2e:list": "playwright test --reporter list",
"playwright.docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/packages/core -it mcr.microsoft.com/playwright:v1.24.0-focal /bin/bash",
"host-root": "http-server ./ -a 127.0.0.1 -p 8080",
"host-root": "http-server ./ -a 127.0.0.1 -p 8080 -c-1",
"generate": "stencil generate",
"generate:theme": "ts-node -P ./scripts/generate-theme/tsconfig.json ./scripts/generate-theme/generate-theme"
},
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,11 @@ export namespace Components {
* Define if the user can access the tooltip via mouse.
*/
"interactive": boolean;
/**
* Initial placement of the tooltip. If the placement don"t have enough space, the tooltip will placed on another location.
* @since 1.5.0
*/
"placement": 'top' | 'right' | 'bottom' | 'left';
/**
* Title of the tooltip
*/
Expand Down Expand Up @@ -3959,6 +3964,11 @@ declare namespace LocalJSX {
* Define if the user can access the tooltip via mouse.
*/
"interactive"?: boolean;
/**
* Initial placement of the tooltip. If the placement don"t have enough space, the tooltip will placed on another location.
* @since 1.5.0
*/
"placement"?: 'top' | 'right' | 'bottom' | 'left';
/**
* Title of the tooltip
*/
Expand Down
69 changes: 57 additions & 12 deletions packages/core/src/components/tooltip/tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@ import {
arrow,
autoUpdate,
computePosition,
ComputePositionReturn,
flip,
offset,
shift,
} from '@floating-ui/dom';
import { Component, Element, h, Host, Prop, State } from '@stencil/core';

type ArrowPosition = {
top?: string;
left?: string;
right?: string;
};

const numberToPixel = (value: number) => (value != null ? `${value}px` : '');

/**
* @slot title-icon - Icon of tooltip title
* @slot title-content - Content of tooltip title
Expand Down Expand Up @@ -43,6 +52,14 @@ export class Tooltip {
*/
@Prop() interactive = false;

/**
* Initial placement of the tooltip. If the placement don"t have enough space,
* the tooltip will placed on another location.
*
* @since 1.5.0
*/
@Prop() placement: 'top' | 'right' | 'bottom' | 'left' = 'top';

@State() visible = false;

@Element() hostElement: HTMLIxTooltipElement;
Expand Down Expand Up @@ -77,18 +94,53 @@ export class Tooltip {
this.destroyAutoUpdate();
}

private computeArrowPosition({
placement,
middlewareData,
}: ComputePositionReturn): ArrowPosition {
let { x, y } = middlewareData.arrow;

if (placement.startsWith('top')) {
return {
left: numberToPixel(x),
top: numberToPixel(y),
};
}

if (placement.startsWith('right')) {
return {
left: numberToPixel(-4),
top: numberToPixel(y),
};
}

if (placement.startsWith('bottom')) {
return {
left: numberToPixel(x),
top: numberToPixel(-4),
};
}

if (placement.startsWith('left')) {
return {
right: numberToPixel(-4),
top: numberToPixel(y),
};
}
}

private async computeTooltipPosition(target: HTMLElement) {
this.disposeAutoUpdate = autoUpdate(
target,
this.hostElement,
async () => {
requestAnimationFrame(async () => {
setTimeout(async () => {
const computeResponse = await computePosition(
target,
this.hostElement,
{
strategy: 'fixed',
placement: 'top',
placement: this.placement,
middleware: [
shift(),
offset(8),
Expand All @@ -97,22 +149,15 @@ export class Tooltip {
}),
flip({
fallbackStrategy: 'initialPlacement',
padding: 10,
}),
],
}
);

if (computeResponse.middlewareData.arrow) {
let { x, y } = computeResponse.middlewareData.arrow;

if (computeResponse.placement === 'bottom') {
y = -4;
}

Object.assign(this.arrowElement.style, {
left: x != null ? `${x}px` : '',
top: y != null ? `${y}px` : '',
});
const arrowPosition = this.computeArrowPosition(computeResponse);
Object.assign(this.arrowElement.style, arrowPosition);
}

const { x, y } = computeResponse;
Expand Down
70 changes: 69 additions & 1 deletion packages/core/src/tests/tooltip/tooltip.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import { expect } from '@playwright/test';
import { regressionTest } from '@utils/test';
import { regressionTest, test } from '@utils/test';

regressionTest.describe('tooltip', () => {
regressionTest('Long Text long words', async ({ page }) => {
Expand Down Expand Up @@ -70,4 +70,72 @@ regressionTest.describe('tooltip', () => {
maxDiffPixelRatio: 0.01,
});
});

test('tooltip position top', async ({ mount, page }) => {
await mount(`
<div style="padding: 10rem">
<ix-button id="trigger">Trigger</ix-button>
<ix-tooltip for="#trigger" placement="top">
Tooltip content 123 123
</ix-tooltip>
</div>
`);

const trigger = await page.waitForSelector('#trigger');
await trigger.hover();
await page.waitForTimeout(500);

expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});

test('tooltip position right', async ({ mount, page }) => {
await mount(`
<div style="padding: 10rem">
<ix-button id="trigger">Trigger</ix-button>
<ix-tooltip for="#trigger" placement="right">
Tooltip content 123 123
</ix-tooltip>
</div>
`);

const trigger = await page.waitForSelector('#trigger');
await trigger.hover();
await page.waitForTimeout(500);

expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});

test('tooltip position bottom', async ({ mount, page }) => {
await mount(`
<div style="padding: 10rem">
<ix-button id="trigger">Trigger</ix-button>
<ix-tooltip for="#trigger" placement="bottom">
Tooltip content 123 123
</ix-tooltip>
</div>
`);

const trigger = await page.waitForSelector('#trigger');
await trigger.hover();
await page.waitForTimeout(500);

expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});

test('tooltip position left', async ({ mount, page }) => {
await mount(`
<div style="width: 10rem; height: 10rem">
<ix-button id="trigger" style="position: absolute; left: 20rem; top: 10rem;">Trigger</ix-button>
<ix-tooltip for="#trigger" placement="left">
Tooltip content 123 123
</ix-tooltip>
</div>
`);

const trigger = await page.waitForSelector('#trigger');
await trigger.hover();
await page.waitForTimeout(500);

expect(await page.screenshot({ fullPage: true })).toMatchSnapshot();
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions packages/core/src/tests/utils/ct/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--
SPDX-FileCopyrightText: 2023 Siemens AG
SPDX-License-Identifier: MIT
-->

<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
/>
<title>Stencil Component Starter</title>
</head>
<body>
<div id="mount"></div>

<script src="http://127.0.0.1:8080/scripts/e2e/load-e2e-runtime.js"></script>
</body>
</html>
52 changes: 49 additions & 3 deletions packages/core/src/tests/utils/test/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { Page, test as base, TestInfo } from '@playwright/test';
import { ElementHandle, Page, test as base, TestInfo } from '@playwright/test';

async function extendPageFixture(page: Page, testInfo: TestInfo) {
const originalGoto = page.goto.bind(page);
Expand All @@ -21,7 +20,6 @@ async function extendPageFixture(page: Page, testInfo: TestInfo) {
options
);

// Inital timeout for webKit to render Web Components
await page.waitForTimeout(1000);
return response;
};
Expand All @@ -35,3 +33,51 @@ export const regressionTest = base.extend({
await use(page);
},
});

export const test = base.extend<{
mount: (selector: string) => Promise<ElementHandle<HTMLElement>>;
createElement: (
selector: string,
appendTo?: ElementHandle<Element>
) => Promise<ElementHandle<HTMLElement>>;
}>({
createElement: async ({ page }, use) => {
use((selector, appendTo) =>
page.evaluateHandle(
async ({ selector, appendTo }) => {
const elm = document.createElement(selector);

if (appendTo) {
appendTo.appendChild(elm);
}

return elm;
},
{
selector,
appendTo,
}
)
);
},
mount: async ({ page }, use, testInfo) => {
const theme = testInfo.project.metadata.theme;
testInfo.annotations.push({
type: theme,
});
await page.goto(
`http://127.0.0.1:8080/src/tests/utils/ct/index.html?theme=${theme}`
);
use((selector) => {
return page.evaluateHandle(
async ({ componentSelector }) => {
await window.customElements.whenDefined('ix-button');
const mount = document.querySelector('#mount');
mount.innerHTML = componentSelector;
return mount.children.item(0) as HTMLElement;
},
{ componentSelector: selector }
);
});
},
});
3 changes: 2 additions & 1 deletion packages/vue/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ export const IxToggle = /*@__PURE__*/ defineContainer<JSX.IxToggle>('ix-toggle',
export const IxTooltip = /*@__PURE__*/ defineContainer<JSX.IxTooltip>('ix-tooltip', undefined, [
'for',
'titleContent',
'interactive'
'interactive',
'placement'
]);


Expand Down

0 comments on commit 0d18866

Please sign in to comment.