feat(ui): add reusable information tooltip

This commit is contained in:
2026-08-21 16:47:21 -03:00
parent 88c3a08a23
commit d51db00247
4 changed files with 119 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
<button
type="button"
class="tooltip-trigger"
[attr.aria-describedby]="tooltipId"
[attr.aria-label]="message()"
>
<i class="fa-solid fa-circle-info" aria-hidden="true"></i>
<span class="tooltip-message" [id]="tooltipId" role="tooltip">
{{ message() }}
</span>
</button>

View File

@@ -0,0 +1,62 @@
:host {
display: inline-flex;
align-items: center;
margin-left: 0.25rem;
vertical-align: middle;
}
.tooltip-trigger {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 0;
background: transparent;
color: var(--tenant-primary, var(--color-primary, #0d6efd));
font-size: 1em;
line-height: inherit;
&:focus-visible {
border-radius: 50%;
outline: 2px solid currentColor;
outline-offset: 2px;
}
&:hover .tooltip-message,
&:focus-visible .tooltip-message {
visibility: visible;
opacity: 1;
transform: translate(-50%, -0.25rem);
}
}
.tooltip-message {
position: absolute;
z-index: 1100;
bottom: calc(100% + 0.625rem);
left: 50%;
width: max-content;
max-width: min(16rem, 75vw);
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
border: 1px solid var(--tenant-primary, var(--color-primary, #0d6efd));
background: #fff;
color: #212529;
font-family: inherit;
font-size: 0.75rem;
font-weight: 400;
line-height: 1.35;
text-align: center;
text-transform: none;
letter-spacing: normal;
white-space: normal;
visibility: hidden;
opacity: 0;
pointer-events: none;
transform: translate(-50%, 0);
transition:
opacity 0.15s ease,
transform 0.15s ease,
visibility 0.15s ease;
}

View File

@@ -0,0 +1,32 @@
import { TestBed, getTestBed } from '@angular/core/testing';
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
import { afterEach, beforeAll, describe, expect, it } from 'vitest';
import { TooltipComponent } from './tooltip.component';
describe('TooltipComponent', () => {
beforeAll(() => {
try {
getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
} catch {
// Test environment may already be initialized by another setup entrypoint.
}
});
afterEach(() => TestBed.resetTestingModule());
it('renders the reusable danger information tooltip', async () => {
await TestBed.configureTestingModule({ imports: [TooltipComponent] }).compileComponents();
const fixture = TestBed.createComponent(TooltipComponent);
fixture.componentRef.setInput('message', 'Este producto no tiene stock disponible.');
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
const trigger = element.querySelector('.tooltip-trigger');
const message = element.querySelector('.tooltip-message');
expect(trigger?.querySelector('i.fa-solid.fa-circle-info')).not.toBeNull();
expect(message?.textContent?.trim()).toBe('Este producto no tiene stock disponible.');
expect(trigger?.getAttribute('aria-describedby')).toBe(message?.id);
});
});

View File

@@ -0,0 +1,14 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
let nextTooltipId = 0;
@Component({
selector: 'app-tooltip',
templateUrl: './tooltip.component.html',
styleUrl: './tooltip.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TooltipComponent {
readonly message = input.required<string>();
protected readonly tooltipId = `app-tooltip-${nextTooltipId++}`;
}