Coverage for src / lexigram / ui / atoms / tooltip.py: 100%

15 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-10 04:11 +0800

1from __future__ import annotations 

2 

3from typing import Any 

4 

5from lexigram.ui.core.base import Component, el 

6 

7 

8class Tooltip(Component): 

9 """Tooltip component with auto-positioning and ARIA accessibility support. 

10 

11 The tooltip element receives ``role="tooltip"`` and a stable ``id``. 

12 If a trigger element is wired via ``trigger_id``, the wrapper element 

13 receives ``aria-describedby`` pointing to the tooltip's ``id``. 

14 

15 Args: 

16 content: The tooltip text shown on hover. 

17 position: Visual position hint (default: "top"). 

18 tooltip_id: Explicit ``id`` for the tooltip span. Defaults to a 

19 generated value so ``aria-describedby`` always resolves. 

20 trigger_id: Optional ``id`` of the associated trigger element. 

21 """ 

22 

23 def __init__( 

24 self, 

25 content: str, 

26 position: str = "top", 

27 tooltip_id: str | None = None, 

28 trigger_id: str | None = None, 

29 **props, 

30 ) -> None: 

31 super().__init__(content=content, position=position, **props) 

32 self.content = content 

33 self.position = position 

34 self.tooltip_id = tooltip_id or f"tooltip-{id(self)}" 

35 self.trigger_id = trigger_id 

36 

37 def render(self) -> Any: 

38 wrapper_attrs: dict[str, Any] = { 

39 "class_": "group relative flex justify-center", 

40 } 

41 if not self.trigger_id: 

42 wrapper_attrs["aria_describedby"] = self.tooltip_id 

43 

44 return el( 

45 "div", 

46 *self.children, 

47 el( 

48 "span", 

49 self.content, 

50 id=self.tooltip_id, 

51 role="tooltip", 

52 class_="absolute top-10 scale-0 transition-all rounded bg-popover p-2 text-xs text-popover-foreground group-hover:scale-100 z-50 whitespace-nowrap", 

53 ), 

54 **wrapper_attrs, 

55 )