Skip to content

Commit 419679c

Browse files
add docs and tests for unresolved param variables (#78)
1 parent e6dea0b commit 419679c

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

docs/params.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ Both attributes and properties support literal (quoted) and dynamic (unquoted) v
192192
The rules for value resolution are:
193193

194194
- Quoted values (`"value"` or `'value'`) are treated as literal strings
195-
- Unquoted values are resolved from the template context
195+
- Unquoted values are first attempted to be resolved from the template context
196+
- If resolution fails, the literal value is used as a fallback
196197
- Boolean values can be passed directly (`disabled=True`) or as strings (`disabled="True"`)
197198
- Both attributes and properties follow these same resolution rules
198199

@@ -238,6 +239,20 @@ Renders as:
238239
<button class="btn-secondary" variant="small" disabled>Click me</button>
239240
```
240241

242+
If an unquoted value cannot be resolved from the context, it falls back to using the literal string:
243+
244+
```htmldjango
245+
{% bird button class=undefined_class %}
246+
Click me
247+
{% endbird %}
248+
```
249+
250+
With empty context, renders as:
251+
252+
```html
253+
<button class="undefined_class">Click me</button>
254+
```
255+
241256
You can also access nested attributes using dot notation:
242257

243258
```htmldjango

tests/templatetags/test_bird.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,24 @@ def test_rendered_name(
202202
{"btn": {"class": "btn-success"}},
203203
'<button class="btn.class">Click me</button>',
204204
),
205+
(
206+
"<button {{ attrs }}>Click me</button>",
207+
"{% bird button class=undefined_class %}Click me{% endbird %}",
208+
{},
209+
'<button class="undefined_class">Click me</button>',
210+
),
211+
(
212+
"<button {{ attrs }}>Click me</button>",
213+
"{% bird button class=missing.attr %}Click me{% endbird %}",
214+
{},
215+
'<button class="missing.attr">Click me</button>',
216+
),
217+
(
218+
"<button {{ attrs }}>Click me</button>",
219+
"{% bird button class=user.preferences.theme %}Click me{% endbird %}",
220+
{"user": {"preferences": {}}},
221+
'<button class="user.preferences.theme">Click me</button>',
222+
),
205223
],
206224
)
207225
def test_rendered_attrs(
@@ -380,6 +398,30 @@ def get_template_libraries(self, libraries):
380398
{"active_class": "active"},
381399
"<button class='active'>Click me</button>",
382400
),
401+
(
402+
"{% bird:prop class %}<button class='{{ props.class }}'>{{ slot }}</button>",
403+
"{% bird button class=undefined_class %}Click me{% endbird %}",
404+
{},
405+
"<button class='undefined_class'>Click me</button>",
406+
),
407+
(
408+
"{% bird:prop class %}<button class='{{ props.class }}'>{{ slot }}</button>",
409+
"{% bird button class=missing.attr %}Click me{% endbird %}",
410+
{},
411+
"<button class='missing.attr'>Click me</button>",
412+
),
413+
(
414+
"{% bird:prop class %}<button class='{{ props.class }}'>{{ slot }}</button>",
415+
"{% bird button class=user.preferences.theme %}Click me{% endbird %}",
416+
{"user": {"preferences": {}}},
417+
"<button class='user.preferences.theme'>Click me</button>",
418+
),
419+
(
420+
"{% bird:prop class='fallback' %}<button class='{{ props.class }}'>{{ slot }}</button>",
421+
"{% bird button class=undefined_class %}Click me{% endbird %}",
422+
{},
423+
"<button class='undefined_class'>Click me</button>",
424+
),
383425
],
384426
)
385427
def test_with_props(

tests/test_params.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class TestValue:
1818
(Value(None), {}, None),
1919
(Value("True"), {}, True),
2020
(Value("False"), {}, None),
21+
(Value("undefined", quoted=False), {}, "undefined"),
22+
(Value("foo.bar", quoted=False), {}, "foo.bar"),
23+
(Value("foo.bar", quoted=False), {"foo": {}}, "foo.bar"),
2124
],
2225
)
2326
def test_resolve(self, value, context, expected):
@@ -46,6 +49,21 @@ class TestParam:
4649
{"item": {"name": "value"}},
4750
'class="item.name"',
4851
),
52+
(
53+
Param(name="class", value=Value("undefined", quoted=False)),
54+
{},
55+
'class="undefined"',
56+
),
57+
(
58+
Param(name="class", value=Value("foo.bar", quoted=False)),
59+
{},
60+
'class="foo.bar"',
61+
),
62+
(
63+
Param(name="class", value=Value("foo.bar", quoted=False)),
64+
{"foo": {}},
65+
'class="foo.bar"',
66+
),
4967
],
5068
)
5169
def test_render_attr(self, param, context, expected):
@@ -79,6 +97,21 @@ def test_render_attr(self, param, context, expected):
7997
{"user": {"name": "Alice"}},
8098
"user.name",
8199
),
100+
(
101+
Param(name="class", value=Value("undefined", quoted=False)),
102+
{},
103+
"undefined",
104+
),
105+
(
106+
Param(name="data", value=Value("user.name", quoted=False)),
107+
{},
108+
"user.name",
109+
),
110+
(
111+
Param(name="data", value=Value("user.name", quoted=False)),
112+
{"user": {}},
113+
"user.name",
114+
),
82115
],
83116
)
84117
def test_render_prop(self, param, context, expected):
@@ -144,6 +177,24 @@ class TestParams:
144177
{"class": "dynamic"},
145178
[],
146179
),
180+
(
181+
Params(
182+
attrs=[Param(name="class", value=Value("undefined", quoted=False))]
183+
),
184+
[PropNode(name="class", default=None, attrs=[])],
185+
{},
186+
{"class": "undefined"},
187+
[],
188+
),
189+
(
190+
Params(
191+
attrs=[Param(name="class", value=Value("user.name", quoted=False))]
192+
),
193+
[PropNode(name="class", default=None, attrs=[])],
194+
{},
195+
{"class": "user.name"},
196+
[],
197+
),
147198
],
148199
)
149200
def test_render_props(
@@ -186,6 +237,27 @@ def test_render_props(
186237
{"var": "dynamic"},
187238
'class="dynamic"',
188239
),
240+
(
241+
Params(
242+
attrs=[Param(name="class", value=Value("undefined", quoted=False))]
243+
),
244+
{},
245+
'class="undefined"',
246+
),
247+
(
248+
Params(
249+
attrs=[Param(name="class", value=Value("user.name", quoted=False))]
250+
),
251+
{},
252+
'class="user.name"',
253+
),
254+
(
255+
Params(
256+
attrs=[Param(name="class", value=Value("user.name", quoted=False))]
257+
),
258+
{"user": {}},
259+
'class="user.name"',
260+
),
189261
],
190262
)
191263
def test_render_attrs(self, params, context, expected):

0 commit comments

Comments
 (0)