Coverage for src / lexigram / contracts / admin / widget_content.py: 100%

60 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-15 20:19 +0800

1"""Structured widget content value types for the admin contributor system. 

2 

3Host-side rendering turns these into HTML — contributors return 

4``WidgetContent`` from ``render_widget`` instead of hand-building markup. 

5""" 

6 

7from __future__ import annotations 

8 

9from dataclasses import dataclass 

10from enum import StrEnum 

11from typing import Literal 

12 

13from lexigram.contracts.admin.health_payload import HealthCheckPayload 

14 

15 

16class Tone(StrEnum): 

17 """Visual tone/severity for widget content elements.""" 

18 

19 DEFAULT = "default" 

20 PRIMARY = "primary" 

21 SUCCESS = "success" 

22 WARNING = "warning" 

23 DANGER = "danger" 

24 INFO = "info" 

25 

26 

27class WidgetKind(StrEnum): 

28 """Kind of dashboard widget, declared by the widget definition.""" 

29 

30 STAT = "stat" 

31 TABLE = "table" 

32 HEALTH = "health" 

33 MESSAGE = "message" 

34 EMPTY = "empty" 

35 CHART = "chart" 

36 

37 

38@dataclass(frozen=True) 

39class Stat: 

40 """A single labeled stat value.""" 

41 

42 label: str 

43 value: str 

44 delta: str | None = None 

45 tone: Tone = Tone.DEFAULT 

46 icon: str | None = None 

47 sparkline_data: tuple[float, ...] = () 

48 

49 

50@dataclass(frozen=True) 

51class StatContent: 

52 """One stat or an N-stat grid.""" 

53 

54 stats: tuple[Stat, ...] 

55 

56 

57@dataclass(frozen=True) 

58class TableCell: 

59 """A single table cell.""" 

60 

61 text: str 

62 tone: Tone = Tone.DEFAULT 

63 

64 

65@dataclass(frozen=True) 

66class TableContent: 

67 """A table of rows.""" 

68 

69 columns: tuple[str, ...] 

70 rows: tuple[tuple[TableCell, ...], ...] 

71 empty_message: str = "No data available." 

72 

73 

74@dataclass(frozen=True) 

75class MessageContent: 

76 """A plain status message.""" 

77 

78 text: str 

79 tone: Tone = Tone.DEFAULT 

80 

81 

82@dataclass(frozen=True) 

83class EmptyContent: 

84 """An empty / no-data state.""" 

85 

86 title: str = "No data available" 

87 message: str = "There's nothing to display yet." 

88 icon: str = "inbox" 

89 

90 

91@dataclass(frozen=True) 

92class ChartPoint: 

93 """A single named bar-chart point.""" 

94 

95 label: str 

96 value: float 

97 tone: Tone = Tone.DEFAULT 

98 secondary_value: float | None = None 

99 

100 

101@dataclass(frozen=True) 

102class ChartContent: 

103 """A small chart of named points.""" 

104 

105 points: tuple[ChartPoint, ...] 

106 chart_type: Literal["bar", "line", "pie", "area"] = "bar" 

107 

108 

109WidgetContent = ( 

110 StatContent 

111 | TableContent 

112 | HealthCheckPayload # from lexigram.contracts.admin.health_payload — reused, not duplicated 

113 | MessageContent 

114 | EmptyContent 

115 | ChartContent 

116) 

117 

118__all__ = [ 

119 "ChartContent", 

120 "ChartPoint", 

121 "EmptyContent", 

122 "MessageContent", 

123 "Stat", 

124 "StatContent", 

125 "TableCell", 

126 "TableContent", 

127 "Tone", 

128 "WidgetContent", 

129 "WidgetKind", 

130]