Coverage for src / lexigram / admin / ui / molecules / page_size_selector.py: 30%

20 statements  

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

1from __future__ import annotations 

2 

3from typing import Any 

4 

5from lexigram.ui import Component, Select, Zones, el 

6 

7 

8class PageSizeSelector(Component): 

9 """ 

10 Component for selecting the number of items per page. 

11 Uses Zone-based targeting for consistent HTMX behavior. 

12 """ 

13 

14 def __init__( 

15 self, 

16 per_page: int = 20, 

17 base_url: str = "", 

18 extra_query: str = "", 

19 hx_target: str | None = None, 

20 hx_swap: str = "innerHTML", 

21 hx_push_url: str = "true", 

22 size_options: list[int] | None = None, 

23 state: Any | None = None, 

24 **props, 

25 ) -> None: 

26 super().__init__(**props) 

27 self.per_page = per_page 

28 self.base_url = base_url 

29 self.extra_query = extra_query 

30 # Use Zones.DATA by default 

31 self.hx_target = hx_target or Zones.DATA.selector 

32 self.hx_swap = hx_swap 

33 self.hx_push_url = hx_push_url 

34 self.size_options = size_options or [10, 20, 50, 100] 

35 self.state = state 

36 

37 def render(self) -> Any: 

38 choices = [(str(size), str(size)) for size in self.size_options] 

39 

40 # Build consistent HTMX attrs using new API 

41 

42 if self.state: 

43 # Use clean URL with full state include to avoid duplication and state loss 

44 htmx_attrs = { 

45 "hx-get": f"{self.base_url.rstrip('/')}/", 

46 "hx-target": self.hx_target, 

47 "hx-swap": self.hx_swap, 

48 "hx-select": Zones.DATA.selector, 

49 "hx-push_url": "true", 

50 "hx-trigger": "change", 

51 "hx-include": f"{Zones.TABLE.selector} [name]", 

52 "hx-params": "*", 

53 } 

54 else: 

55 # Fallback for simple usage 

56 htmx_attrs = { 

57 "hx-get": f"{self.base_url}?page=1{self.extra_query}", 

58 "hx-target": self.hx_target, 

59 "hx-swap": self.hx_swap, 

60 "hx-select": Zones.DATA.selector, 

61 "hx-push_url": str(self.hx_push_url).lower(), 

62 "hx-trigger": "change", 

63 "hx-include": "this", 

64 } 

65 

66 return el( 

67 "div", 

68 el( 

69 "label", 

70 "per page", 

71 class_="text-sm text-muted-foreground mr-2 hidden lg:inline", 

72 ), 

73 Select( 

74 name="per_page", 

75 label=None, 

76 choices=choices, 

77 value=str(self.per_page), 

78 class_="w-20 text-sm", 

79 **{k.replace("-", "_"): v for k, v in htmx_attrs.items()}, # type: ignore[arg-type] 

80 ), 

81 class_="flex items-center", 

82 )