Skip to content

Commit

Permalink
Add new info tooltip component
Browse files Browse the repository at this point in the history
  • Loading branch information
MajaZarkova committed Jun 3, 2024
1 parent a63e70e commit 035acf2
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { expect, test } from "../../playwright/a11y";
import type { Locator } from "@playwright/test";
import { executeMatrixScreenshotTest } from "../../playwright/screenshots";
import OnyxInfoTooltip from "./OnyxInfoTooltip.vue";

test("should trigger with hover", async ({ mount, page }) => {
// ARRANGE
let component = await mount(OnyxInfoTooltip, {
props: {
text: "Test tooltip",
},
});

let tooltip = component.getByRole("tooltip");

// ASSERT
await expect(tooltip).toBeHidden();

// ACT
await component.hover();

// ASSERT
await expect(tooltip).toBeHidden(); // should use debounce to show tooltip only after a short delay
await expect(tooltip).toBeVisible();

await page.mouse.move(0, 0);
await expect(tooltip).toBeVisible(); // should use debounce to hide tooltip only after a short delay
await expect(tooltip).toBeHidden();

// ACT
await component.unmount();
component = await mount(<OnyxInfoTooltip text="Test tooltip" />);
tooltip = component.getByRole("tooltip");

await page.keyboard.press("Tab");

// ASSERT
await expect(tooltip).toBeVisible();

// ACT
await page.keyboard.press("Escape");

// ASSERT
await expect(tooltip).toBeHidden();
});

test.describe("Screenshot tests", () => {
const isTooltipVisible = async (tooltip: Locator) => {
await expect(tooltip).toBeVisible();
};

executeMatrixScreenshotTest({
name: "Info Tooltip",
columns: ["default", "long-text"],
rows: ["default", "bottom"],
component: (column, row) => {
return (
<OnyxInfoTooltip
text={column === "long-text" ? "Lorem ipsum dolor sit amet " : "Test tooltip"}
position={row === "bottom" ? "bottom" : "top"}
/>
);
},
// set component size to fully include the tooltip
beforeScreenshot: async (component, page, _column, _row) => {
const tooltip = page.getByRole("tooltip");

// set paddings to fit the full tooltip in the screenshot
await component.evaluate((element) => {
element.style.padding = `3rem 5rem`;
});

await component.hover();

await isTooltipVisible(tooltip);
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import OnyxInfoTooltip from "./OnyxInfoTooltip.vue";

/**
* Info tooltips offer contextual information or additional details while hovering an info icon.
*/
const meta: Meta<typeof OnyxInfoTooltip> = {
title: "support/InfoTooltip",
...defineStorybookActionsAndVModels({
component: OnyxInfoTooltip,
events: [],
decorators: [
(story) => ({
components: { story },
template: `
<div style="padding: 4rem 6rem; font-family: var(--onyx-font-family); color: var(--onyx-color-text-icons-neutral-intense)">
<story />
</div>`,
}),
],
}),
};

export default meta;
type Story = StoryObj<typeof OnyxInfoTooltip>;

/**
* This example shows a default tooltip.
*/
export const Default = {
args: {
text: "Info tooltip text",
},
} satisfies Story;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts" setup>
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import circleInformation from "@sit-onyx/icons/circle-information.svg?raw";
import OnyxTooltip from "../OnyxTooltip/OnyxTooltip.vue";
import type { OnyxInfoTooltipProps } from "./types";
import { injectI18n } from "../../i18n";
const props = defineProps<OnyxInfoTooltipProps>();
const { t } = injectI18n();
</script>

<template>
<OnyxTooltip
v-if="props.text"
class="onyx-info-tooltip"
:text="props.text"
:position="props.position"
>
<button :aria-label="t('showInfoTooltip')" class="onyx-info-tooltip__trigger">
<OnyxIcon :icon="circleInformation" color="neutral" size="12px" />
</button>
</OnyxTooltip>
</template>

<style lang="scss">
@use "../../styles/mixins/layers";
.onyx-info-tooltip {
@include layers.component() {
&__trigger {
border: none;
background-color: transparent;
padding: 0;
margin-left: var(--onyx-spacing-2xs);
}
}
}
</style>
3 changes: 3 additions & 0 deletions packages/sit-onyx/src/components/OnyxInfoTooltip/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { OnyxTooltipProps } from "../OnyxTooltip/types";

export type OnyxInfoTooltipProps = Pick<OnyxTooltipProps, "text" | "position">;
25 changes: 4 additions & 21 deletions packages/sit-onyx/src/components/OnyxTextarea/OnyxTextarea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { useDensity } from "../../composables/density";
import { useRequired } from "../../composables/required";
import { useCustomValidity } from "../../composables/useCustomValidity";
import OnyxSkeleton from "../OnyxSkeleton/OnyxSkeleton.vue";
import OnyxTooltip from "../OnyxTooltip/OnyxTooltip.vue";
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import circleInformation from "@sit-onyx/icons/circle-information.svg?raw";
import OnyxInfoTooltip from "../OnyxInfoTooltip/OnyxInfoTooltip.vue";
import type { OnyxTextareaProps } from "./types";
import { injectI18n } from "../../i18n";
Expand Down Expand Up @@ -108,11 +106,7 @@ const handleInput = (event: Event) => {
<div class="onyx-textarea__header">
<span class="onyx-truncation-ellipsis">{{ props.label }}</span>
<span :class="[props.required ? requiredMarkerClass : undefined]"></span>
<OnyxTooltip v-if="props.labelTooltip" :text="props.labelTooltip">
<button :aria-label="t('showInfoTooltip')" class="onyx-textarea__tooltip-trigger">
<OnyxIcon :icon="circleInformation" color="neutral" size="12px" />
</button>
</OnyxTooltip>
<OnyxInfoTooltip v-if="props.labelTooltip" :text="props.labelTooltip" />
<span v-if="!props.required" class="onyx-textarea__optional">{{ t("optional") }}</span>
</div>
</div>
Expand Down Expand Up @@ -149,16 +143,12 @@ const handleInput = (event: Event) => {

<div v-if="props.message || shouldShowCounter" class="onyx-textarea__footer onyx-text--small">
<span v-if="props.message" class="onyx-truncation-ellipsis">{{ props.message }}</span>
<OnyxTooltip
<OnyxInfoTooltip
v-if="props.messageTooltip"
class="onyx-textarea__message-tooltip"
position="bottom"
:text="props.messageTooltip"
>
<button :aria-label="t('showInfoTooltip')" class="onyx-textarea__tooltip-trigger">
<OnyxIcon :icon="circleInformation" color="neutral" size="12px" />
</button>
</OnyxTooltip>
/>
<span v-if="shouldShowCounter" class="onyx-textarea__counter">
{{ value.length }}/{{ props.maxlength }}
</span>
Expand Down Expand Up @@ -235,13 +225,6 @@ const handleInput = (event: Event) => {
align-self: center;
}
&__tooltip-trigger {
border: none;
background-color: transparent;
padding: 0;
margin-left: var(--onyx-spacing-2xs);
}
&__wrapper {
padding: 0;
height: unset;
Expand Down

0 comments on commit 035acf2

Please sign in to comment.