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
« 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.
3Host-side rendering turns these into HTML — contributors return
4``WidgetContent`` from ``render_widget`` instead of hand-building markup.
5"""
7from __future__ import annotations
9from dataclasses import dataclass
10from enum import StrEnum
11from typing import Literal
13from lexigram.contracts.admin.health_payload import HealthCheckPayload
16class Tone(StrEnum):
17 """Visual tone/severity for widget content elements."""
19 DEFAULT = "default"
20 PRIMARY = "primary"
21 SUCCESS = "success"
22 WARNING = "warning"
23 DANGER = "danger"
24 INFO = "info"
27class WidgetKind(StrEnum):
28 """Kind of dashboard widget, declared by the widget definition."""
30 STAT = "stat"
31 TABLE = "table"
32 HEALTH = "health"
33 MESSAGE = "message"
34 EMPTY = "empty"
35 CHART = "chart"
38@dataclass(frozen=True)
39class Stat:
40 """A single labeled stat value."""
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, ...] = ()
50@dataclass(frozen=True)
51class StatContent:
52 """One stat or an N-stat grid."""
54 stats: tuple[Stat, ...]
57@dataclass(frozen=True)
58class TableCell:
59 """A single table cell."""
61 text: str
62 tone: Tone = Tone.DEFAULT
65@dataclass(frozen=True)
66class TableContent:
67 """A table of rows."""
69 columns: tuple[str, ...]
70 rows: tuple[tuple[TableCell, ...], ...]
71 empty_message: str = "No data available."
74@dataclass(frozen=True)
75class MessageContent:
76 """A plain status message."""
78 text: str
79 tone: Tone = Tone.DEFAULT
82@dataclass(frozen=True)
83class EmptyContent:
84 """An empty / no-data state."""
86 title: str = "No data available"
87 message: str = "There's nothing to display yet."
88 icon: str = "inbox"
91@dataclass(frozen=True)
92class ChartPoint:
93 """A single named bar-chart point."""
95 label: str
96 value: float
97 tone: Tone = Tone.DEFAULT
98 secondary_value: float | None = None
101@dataclass(frozen=True)
102class ChartContent:
103 """A small chart of named points."""
105 points: tuple[ChartPoint, ...]
106 chart_type: Literal["bar", "line", "pie", "area"] = "bar"
109WidgetContent = (
110 StatContent
111 | TableContent
112 | HealthCheckPayload # from lexigram.contracts.admin.health_payload — reused, not duplicated
113 | MessageContent
114 | EmptyContent
115 | ChartContent
116)
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]