Repository: joshuadavidthomas/django-bird

Issue:
Adjust Documentation section of README with recent feature changes
A bit out of date as we now have props in addition to attrs, plus dynamic component names. Maybe some other changes too..

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92d6d82..89e6b51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,10 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/
 
 ## [Unreleased]
 
+### Changed
+
+- Improved handling of boolean attributes to support all forms of Django template syntax and string values. The attribute name alone (`disabled`), explicit booleans (`disabled=True`), or string values (`disabled="True"`) all work as expected - rendering just the attribute name when true and omitting it when false.
+
 ## [0.3.0]
 
 ### Added
diff --git a/README.md b/README.md
index 7977422..d520c16 100644
--- a/README.md
+++ b/README.md
@@ -68,19 +68,22 @@ templates/
     └── button.html
 ```
 
-In `button.html`, create a simple HTML button. Use `{{ slot }}` to indicate where the main content will go.
+In `button.html`, create a simple HTML button. Use `{{ slot }}` to indicate where the main content will go. We will also define a component property via the `{% bird:prop %}` templatetag and add `{{ attrs }}` for passing in arbitrary HTML attributes.
 
 ```htmldjango
 {# templates/bird/button.html #}
-<button>
+{% bird:prop class="btn" %}
+{% bird:prop data_attr="button" %}
+
+<button class="{{ props.class }}" data-attr="{{ props.data_attr }}" {{ attrs }}>
     {{ slot }}
 </button>
 ```
 
-To use your component in a Django template, use the `{% bird %}` templatetag. The content between `{% bird %}` and `{% endbird %}` becomes the `{{ slot }}` content.
+To use your component in a Django template, use the `{% bird %}` templatetag. The content between `{% bird %}` and `{% endbird %}` becomes the `{{ slot }}` content. Properties and attributes are set as parameters on the `{% bird %}` tag itself.
 
 ```htmldjango
-{% bird button %}
+{% bird button class="btn-primary" disabled=True %}
     Click me!
 {% endbird %}
 ```
@@ -88,7 +91,7 @@ To use your component in a Django template, use the `{% bird %}` templatetag. Th
 django-bird automatically recognizes components in the bird directory, so no manual registration is needed. When Django processes the template, django-bird replaces the `{% bird %}` tag with the component's HTML, inserting the provided content into the slot, resulting in:
 
 ```html
-<button>
+<button class="btn-primary" data-attr="button" disabled>
     Click me!
 </button>
 ```
@@ -100,9 +103,10 @@ You now have a button component that can be easily reused across your Django pro
 
 django-bird offers features for creating flexible components, such as:
 
-- Passing attributes to components
-- Named slots for organizing content within components
-- Subcomponents for building complex component structures
+- [Defining and registering components](https://django-bird.readthedocs.io/en/latest/naming.html) entirely within Django templates, without writing a custom templatetag
+- Passing [attributes and properties](https://django-bird.readthedocs.io/en/latest/params.html) to components
+- [Named slots](https://django-bird.readthedocs.io/en/latest/slots.html#named-slots) for organizing content within components
+- [Subcomponents](https://django-bird.readthedocs.io/en/latest/organization.html) for building complex component structures
 
 For a full overview of the features and configuration options, please refer to the [documentation](https://django-bird.readthedocs.io).
 
diff --git a/docs/params.md b/docs/params.md
index 44c7e41..ce47db2 100644
--- a/docs/params.md
+++ b/docs/params.md
@@ -9,7 +9,7 @@ Note that these parameters are distinct from [Slots](slots.md) - they are used t
 For example, a button component might use properties to control its styling and attributes to set HTML attributes, while using slots to define its content:
 
 ```htmldjango
-{% bird button variant="primary" data-analytics="signup" %}
+{% bird button variant="primary" data_analytics="signup" %}
     Click here  {# This content will go in the default slot #}
 {% endbird %}
 ```
@@ -81,6 +81,7 @@ Here's a simple example of a button component that uses props:
 :caption: templates/bird/button.html
 
 {% bird:prop variant='primary' %}
+
 <button class="btn btn-{{ props.variant }}" {{ attrs }}>
     {{ slot }}
 </button>
@@ -120,6 +121,7 @@ Components often need multiple props to control different aspects of their behav
 
 {% bird:prop variant='primary' %}
 {% bird:prop size='md' %}
+
 <button
     class="btn btn-{{ props.variant }} btn-{{ props.size }}"
     {{ attrs }}
@@ -153,6 +155,7 @@ Props can be defined with or without default values:
 ```htmldjango
 {% bird:prop id %}                 {#  No default value  #}
 {% bird:prop variant='primary' %}  {# With default value #}
+
 <button
     id="{{ props.id }}"
     class="btn btn-{{ props.variant }}"
diff --git a/src/django_bird/components/params.py b/src/django_bird/components/params.py
index 57e35c1..c8ebfef 100644
--- a/src/django_bird/components/params.py
+++ b/src/django_bird/components/params.py
@@ -18,9 +18,13 @@ class Param:
     value: str | bool | None
 
     def render(self, context: Context) -> str:
-        if self.value is None:
+        if self.value is None or (
+            isinstance(self.value, str) and self.value == "False"
+        ):
             return ""
-        if isinstance(self.value, bool) and self.value:
+        if (isinstance(self.value, bool) and self.value) or (
+            isinstance(self.value, str) and self.value == "True"
+        ):
             return self.name
         try:
             value = template.Variable(str(self.value)).resolve(context)


Test patch:
diff --git a/tests/templatetags/test_bird.py b/tests/templatetags/test_bird.py
index bb69b7d..0e53f77 100644
--- a/tests/templatetags/test_bird.py
+++ b/tests/templatetags/test_bird.py
@@ -127,6 +127,24 @@ class TestTemplateTag:
                 {},
                 '<button class="btn">Click me</button>',
             ),
+            (
+                "<button {{ attrs }}>Click me</button>",
+                "{% bird button disabled %}Click me{% endbird %}",
+                {},
+                "<button disabled>Click me</button>",
+            ),
+            (
+                "<button {{ attrs }}>Click me</button>",
+                "{% bird button disabled=True %}Click me{% endbird %}",
+                {},
+                "<button disabled>Click me</button>",
+            ),
+            (
+                "<button {{ attrs }}>Click me</button>",
+                "{% bird button disabled=False %}Click me{% endbird %}",
+                {},
+                "<button>Click me</button>",
+            ),
             (
                 "<button {{ attrs }}>Click me</button>",
                 "{% bird button class='btn' id='my-btn' disabled %}Click me{% endbird %}",
@@ -281,6 +299,12 @@ class TestTemplateTag:
                 {},
                 '<button id="default" class="btn" data-test="value">Click me</button>',
             ),
+            (
+                '{% bird:prop class="btn" %}{% bird:prop data_attr="button" %}<button class="{{ props.class }}" data-attr="{{ props.data_attr }}" {{ attrs }}>{{ slot }}</button>',
+                '{% bird button class="btn-primary" disabled=True %}Click me{% endbird %}',
+                {},
+                '<button class="btn-primary" data-attr="button" disabled>Click me</button>',
+            ),
         ],
     )
     def test_with_props(