diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts index 07895edc7c9aa5622948043a663ea3c940e0375b..ca1b1fc3be1443515d09b5d40d9260ab3e7af10c 100644 --- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts +++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.spec.ts @@ -51,7 +51,7 @@ describe('TooltipDirective', () => { describe('createTooltip', () => { beforeEach(() => { directive.viewContainerRef.createComponent = jest.fn().mockReturnValue({ location: { nativeElement: {} } }); - directive.setDescribedBy = jest.fn(); + directive.setAriaDescribedBy = jest.fn(); directive.setTooltipProperties = jest.fn(); }); @@ -67,10 +67,10 @@ describe('TooltipDirective', () => { expect(directive.elementRef.nativeElement.appendChild).toHaveBeenCalled(); }); - it('should set aria described by attribute to parent', () => { + it('should set aria-describedby attribute to parent', () => { directive.createTooltip(); - expect(directive.setDescribedBy).toHaveBeenCalled(); + expect(directive.setAriaDescribedBy).toHaveBeenCalled(); }); it('should set tooltip properties', () => { @@ -136,7 +136,7 @@ describe('TooltipDirective', () => { }); }); - describe('setDescribedBy', () => { + describe('setAriaDescribedBy', () => { beforeEach(() => { directive.getFocusableElement = jest.fn(); directive.renderer.setAttribute = jest.fn(); @@ -144,31 +144,31 @@ describe('TooltipDirective', () => { }); it('should check if parent element focusable', () => { - directive.setDescribedBy(); + directive.setAriaDescribedBy(); expect(directive.interactivityChecker.isFocusable).toHaveBeenCalled(); }); it('should get focusable element if parent not focusable', () => { - directive.setDescribedBy(); + directive.setAriaDescribedBy(); expect(directive.getFocusableElement).toHaveBeenCalled(); }); - it('should set describedby attribute', () => { - directive.setDescribedBy(); + it('should set aria-describedby attribute', () => { + directive.setAriaDescribedBy(); expect(directive.renderer.setAttribute).toHaveBeenCalled(); }); }); - describe('removeDescribedBy', () => { + describe('removeAriaDescribedBy', () => { beforeEach(() => { directive.renderer.removeAttribute = jest.fn(); }); - it('should remove describedby attribute', () => { - directive.removeDescribedBy(); + it('should remove aria-describedby attribute', () => { + directive.removeAriaDescribedBy(); expect(directive.renderer.removeAttribute).toHaveBeenCalled(); }); @@ -197,7 +197,7 @@ describe('TooltipDirective', () => { describe('destroy', () => { beforeEach(() => { directive.componentRef = mockComponentRef; - directive.removeDescribedBy = jest.fn(); + directive.removeAriaDescribedBy = jest.fn(); }); it('should set component ref to null', () => { @@ -206,10 +206,10 @@ describe('TooltipDirective', () => { expect(directive.componentRef).toBeNull(); }); - it('should remove describedby attribute', () => { + it('should remove aria-describedby attribute', () => { directive.destroy(); - expect(directive.removeDescribedBy).toHaveBeenCalled(); + expect(directive.removeAriaDescribedBy).toHaveBeenCalled(); }); it('should set focusable element to null', () => { diff --git a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts index 6af9df79e87cf64bdb3c73867a9a631e1f2b210a..75d6524111e0f8f052924cb96bf065a7dd52f55d 100644 --- a/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts +++ b/alfa-client/libs/design-system/src/lib/tooltip/tooltip.directive.ts @@ -1,4 +1,4 @@ -import { isEscapeKey } from '@alfa-client/tech-shared'; +import { isEscapeKey, isNotNull } from '@alfa-client/tech-shared'; import { InteractivityChecker } from '@angular/cdk/a11y'; import { ComponentRef, @@ -11,9 +11,11 @@ import { Renderer2, ViewContainerRef, } from '@angular/core'; -import { uniqueId } from 'lodash-es'; +import { isNull, uniqueId } from 'lodash-es'; import { TooltipComponent } from './tooltip.component'; +const OUTLINE_INDENT = 4; // Outline offset (2) + outline width (2) + @Directive({ selector: '[tooltip]', standalone: true, @@ -37,14 +39,13 @@ export class TooltipDirective implements OnDestroy { @HostListener('mouseenter') @HostListener('focusin') createTooltip(): void { - if (this.componentRef === null) { - const nativeElement: HTMLElement = this.elementRef.nativeElement; - const attachedToFocused: boolean = nativeElement.contains(document.activeElement); - this.componentRef = this.viewContainerRef.createComponent(TooltipComponent); - nativeElement.appendChild(this.componentRef.location.nativeElement); - this.setDescribedBy(); - this.setTooltipProperties(attachedToFocused); - } + if (isNotNull(this.componentRef)) return; + const nativeElement: HTMLElement = this.elementRef.nativeElement; + const attachedToFocused: boolean = nativeElement.contains(document.activeElement); + this.componentRef = this.viewContainerRef.createComponent(TooltipComponent); + nativeElement.appendChild(this.componentRef.location.nativeElement); + this.setAriaDescribedBy(); + this.setTooltipProperties(attachedToFocused); } @HostListener('mouseleave') @@ -62,16 +63,15 @@ export class TooltipDirective implements OnDestroy { } setTooltipProperties(attachedToFocused = false): void { - if (this.componentRef !== null) { - const { left, right, bottom } = this.elementRef.nativeElement.getBoundingClientRect(); - this.componentRef.instance.left = (right + left) / 2; - this.componentRef.instance.top = attachedToFocused ? bottom + 4 : bottom; - this.componentRef.instance.text = this.tooltip; - this.componentRef.instance.id = this.tooltipId; - } + if (isNull(this.componentRef)) return; + const { left, right, bottom } = this.elementRef.nativeElement.getBoundingClientRect(); + this.componentRef.instance.left = (right + left) / 2; + this.componentRef.instance.top = attachedToFocused ? bottom + OUTLINE_INDENT : bottom; + this.componentRef.instance.text = this.tooltip; + this.componentRef.instance.id = this.tooltipId; } - setDescribedBy(): void { + setAriaDescribedBy(): void { const nativeElement: HTMLElement = this.elementRef.nativeElement; this.tooltipId = uniqueId('tooltip'); this.focusableElement = @@ -79,7 +79,7 @@ export class TooltipDirective implements OnDestroy { this.renderer.setAttribute(this.focusableElement, 'aria-describedby', this.tooltipId); } - removeDescribedBy(): void { + removeAriaDescribedBy(): void { this.renderer.removeAttribute(this.focusableElement, 'aria-describedby'); } @@ -88,11 +88,10 @@ export class TooltipDirective implements OnDestroy { } destroy(): void { - if (this.componentRef !== null) { - this.componentRef.destroy(); - this.componentRef = null; - this.removeDescribedBy(); - this.focusableElement = null; - } + if (isNull(this.componentRef)) return; + this.componentRef.destroy(); + this.componentRef = null; + this.removeAriaDescribedBy(); + this.focusableElement = null; } }