You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Discovered while implementing #76 (Select/Textarea/FormField attrs rollout), fixing a crash in the pre-existing test_form_field_endpoint_renders_input integration test. It affects Button too (#72/#73) — this is not specific to the three primitives #76 added, just newly surfaced because FormField already had a real-Catalog integration test and Button didn't.
The bug
JinjaX reserves the prop name attrs for its own extra-kwargs collector (jinjax/catalog.py, ARGS_ATTRS = "attrs") and unconditionally overwrites whatever a component's own {#def} declares for attrs, on every render — regardless of what the caller passed.
Two distinct consequences fall out of this:
Bare extra kwargs work correctly.catalog.render("Cf:Select", data_event="submit") — JinjaX auto-collects data_event into an HTMLAttrs object, which (after Roll out attrs passthrough to Select, Textarea, FormField #76's fix) flows through cf-ui's render_attrs() validation/escaping/collision-guard correctly. This is the live, working pattern for FastAPI/Litestar consumers.
An explicit attrs={...} dict keyword is silently discarded.catalog.render("Cf:Select", attrs={"data-event": "submit"}) — the exact syntax docs/primitives.md documents (:attrs="{...}" in JinjaX/cotton usage) — produces no error and no output. JinjaX's Catalog layer never lets the dict reach the component at all; render_attrs()'s current hasattr(attrs, "as_dict") unwrap only handles the case where JinjaX does hand it an HTMLAttrs object (the bare-kwargs path) — it never sees the caller's explicit dict to unwrap in the first place.
Reproduction
Verified against a real JinjaX Catalog (via install_cf_ui):
A collision attempt via the same path (attrs={"id": "override"}) is also silently swallowed rather than raising PrimitiveConfigError — the guard never fires because the dict never reaches render_attrs().
Impact
Every documented :attrs="{...}" JinjaX usage in docs/primitives.md currently no-ops silently under a real Catalog, for every primitive that has adopted attrs (button, select, textarea, form-field). The bare-kwargs pattern works and should probably become the documented/recommended one, but the discrepancy between documented and actual behavior needs resolving either way (fix the reservation collision, or fix the docs).
Suggested approach
Needs investigation into whether JinjaX exposes any way to opt a specific prop out of the ARGS_ATTRS reservation, or whether cf-ui needs to rename its prop (e.g. extra_attrs) to avoid the collision entirely — mirroring how class was already renamed to extra_class for a similar reserved-word collision. A rename is a breaking change for the already-shipped Button.attrs API, so this needs a deliberate decision, not a quick patch.
Background
Discovered while implementing #76 (Select/Textarea/FormField attrs rollout), fixing a crash in the pre-existing
test_form_field_endpoint_renders_inputintegration test. It affectsButtontoo (#72/#73) — this is not specific to the three primitives #76 added, just newly surfaced because FormField already had a real-Catalog integration test and Button didn't.The bug
JinjaX reserves the prop name
attrsfor its own extra-kwargs collector (jinjax/catalog.py,ARGS_ATTRS = "attrs") and unconditionally overwrites whatever a component's own{#def}declares forattrs, on every render — regardless of what the caller passed.Two distinct consequences fall out of this:
catalog.render("Cf:Select", data_event="submit")— JinjaX auto-collectsdata_eventinto anHTMLAttrsobject, which (after Roll out attrs passthrough to Select, Textarea, FormField #76's fix) flows through cf-ui'srender_attrs()validation/escaping/collision-guard correctly. This is the live, working pattern for FastAPI/Litestar consumers.attrs={...}dict keyword is silently discarded.catalog.render("Cf:Select", attrs={"data-event": "submit"})— the exact syntaxdocs/primitives.mddocuments (:attrs="{...}"in JinjaX/cotton usage) — produces no error and no output. JinjaX's Catalog layer never lets the dict reach the component at all;render_attrs()'s currenthasattr(attrs, "as_dict")unwrap only handles the case where JinjaX does hand it anHTMLAttrsobject (the bare-kwargs path) — it never sees the caller's explicit dict to unwrap in the first place.Reproduction
Verified against a real JinjaX
Catalog(viainstall_cf_ui):A collision attempt via the same path (
attrs={"id": "override"}) is also silently swallowed rather than raisingPrimitiveConfigError— the guard never fires because the dict never reachesrender_attrs().Impact
Every documented
:attrs="{...}"JinjaX usage indocs/primitives.mdcurrently no-ops silently under a realCatalog, for every primitive that has adoptedattrs(button,select,textarea,form-field). The bare-kwargs pattern works and should probably become the documented/recommended one, but the discrepancy between documented and actual behavior needs resolving either way (fix the reservation collision, or fix the docs).Suggested approach
Needs investigation into whether JinjaX exposes any way to opt a specific prop out of the
ARGS_ATTRSreservation, or whether cf-ui needs to rename its prop (e.g.extra_attrs) to avoid the collision entirely — mirroring howclasswas already renamed toextra_classfor a similar reserved-word collision. A rename is a breaking change for the already-shippedButton.attrsAPI, so this needs a deliberate decision, not a quick patch.Related: #70, #72, #73, #76