Phase 2: convert primitives to nodes via a whitelist element factory
Generic leaf builders (Div, Span, Td, Tr, Th, Ul, Li, Strong, Label, Template, P) are now generated from one _html_element factory over the single Element class — the tag name is data, not a per-tag body. Only elements that add classes/behaviour (Button, Pill, Checkbox, Radio, Input, A, SearchField, H1, Modal, AddForm, tables) stay hand-written. All primitives now return Node objects; string-built widgets (Icon, SimpleTable, YearPicker) return Safe, and YearPicker declares its datepicker media. Raw concatenation (_popover_html, Popover slot) uses Fragment. Node.__str__/__html__ now return a SafeString: a node's rendered output is safe HTML by construction, so str(node) stays safe when fed back into a child list or template (matching the old SafeText behaviour and preventing double-escaping). Consumers adapted: the form widgets (SearchSelectWidget, PrimitiveCheckboxWidget) return render(component) so Django gets a safe string; the session form's manual field markup joins via str(row). Component tests render nodes to HTML before asserting. https://claude.ai/code/session_01BKurBhE3Qj25p7Bfsg7EeK
This commit is contained in:
@@ -5,10 +5,36 @@ import django
|
||||
|
||||
from django.utils.safestring import SafeText, mark_safe
|
||||
|
||||
from common import components
|
||||
from common import components as _components
|
||||
from common.components.core import Node
|
||||
from games.models import Platform, Game, Purchase, Session
|
||||
|
||||
|
||||
class _RenderingComponents:
|
||||
"""Test accessor that renders lazy component nodes to safe HTML strings.
|
||||
|
||||
Component builders now return ``Node`` objects (the lazy tree). These tests
|
||||
assert on rendered HTML, so we render any node a capitalized builder returns
|
||||
to a ``SafeText`` string. Internals (``_render_element``) and the legacy
|
||||
string-returning ``Component()`` are untouched (non-node results pass
|
||||
through), so cache/escaping tests keep working unchanged.
|
||||
"""
|
||||
|
||||
def __getattr__(self, name):
|
||||
attr = getattr(_components, name)
|
||||
if not (callable(attr) and name[:1].isupper()):
|
||||
return attr
|
||||
|
||||
def rendered(*args, **kwargs):
|
||||
result = attr(*args, **kwargs)
|
||||
return str(result) if isinstance(result, Node) else result
|
||||
|
||||
return rendered
|
||||
|
||||
|
||||
components = _RenderingComponents()
|
||||
|
||||
|
||||
class ComponentIntegrationTest(unittest.TestCase):
|
||||
"""Test Component() works correctly with caching transparent."""
|
||||
|
||||
@@ -822,7 +848,16 @@ class SimpleTableRenderingTest(unittest.TestCase):
|
||||
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from common.components.primitives import Checkbox, Radio
|
||||
from common.components.primitives import Checkbox as _Checkbox, Radio as _Radio
|
||||
|
||||
|
||||
# Checkbox/Radio are lazy nodes; render to safe HTML for the assertions below.
|
||||
def Checkbox(*args, **kwargs):
|
||||
return str(_Checkbox(*args, **kwargs))
|
||||
|
||||
|
||||
def Radio(*args, **kwargs):
|
||||
return str(_Radio(*args, **kwargs))
|
||||
|
||||
|
||||
class ComponentPrimitivesTest(SimpleTestCase):
|
||||
@@ -867,6 +902,7 @@ class PrimitiveWidgetsTest(SimpleTestCase):
|
||||
|
||||
def test_primitive_checkbox_widget_renders_headless(self):
|
||||
from games.forms import PrimitiveCheckboxWidget
|
||||
|
||||
widget = PrimitiveCheckboxWidget()
|
||||
html = widget.render(name="agree", value=True)
|
||||
self.assertNotIn("<label", html)
|
||||
|
||||
Reference in New Issue
Block a user