Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dsfr/templates/dsfr/button.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{% load dsfr_tags %}
<button class="fr-btn{% if self.extra_classes %} {{ self.extra_classes }}{% endif %}"
onclick="{{ self.onclick }}"
{% if self.is_disabled %}disabled{% endif %}
type="{{ self.type | default:'submit' }}"
{% if self.name %}name="{{ self.name }}"{% endif %}>
{% if self.name %}name="{{ self.name }}"{% endif %}
{{ self|data_attributes|safe }}>
{{ self.label }}
</button>
13 changes: 13 additions & 0 deletions dsfr/templatetags/dsfr_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.template.context import Context
from django.utils.html import format_html, format_html_join


from dsfr.checksums import (
INTEGRITY_CSS,
INTEGRITY_UTILITY_CSS,
Expand Down Expand Up @@ -1703,6 +1704,18 @@ def dsfr_inline(field):
return field


@register.filter
def data_attributes(tag_data):
if tag_data.get("data_attributes"):
return " ".join(
[
f'data-{key}="{value}"'
for key, value in tag_data["data_attributes"].items()
]
)
return ""


# Deprecated tags
@register.simple_tag(takes_context=True)
def dsfr_form(context: Context):
Expand Down
20 changes: 19 additions & 1 deletion dsfr/test/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
INTEGRITY_JS_MODULE,
INTEGRITY_JS_NOMODULE,
)
from dsfr.templatetags.dsfr_tags import concatenate, hyphenate
from dsfr.templatetags.dsfr_tags import concatenate, hyphenate, data_attributes


class DsfrCssTagTest(SimpleTestCase):
Expand Down Expand Up @@ -1307,3 +1307,21 @@ def test_single_kwargs(self):
)
),
)


class DataAttributesFilterTest(SimpleTestCase):
def test_returns_empty_string_if_no_data_attributes(self):
self.assertEqual(data_attributes({}), "")

def test_returns_single_data_attribute(self):
self.assertEqual(
data_attributes({"data_attributes": {"role": "admin"}}), 'data-role="admin"'
)

def test_returns_multiple_data_attributes(self):
result = data_attributes({"data_attributes": {"role": "admin", "id": "123"}})
# Since dict order is preserved in Python 3.7+, output order will follow insertion order
self.assertEqual(result, 'data-role="admin" data-id="123"')

def test_returns_empty_string_if_data_attributes_empty_dict(self):
self.assertEqual(data_attributes({"data_attributes": {}}), "")
47 changes: 46 additions & 1 deletion dsfr/test/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.test import SimpleTestCase
from dsfr.utils import generate_random_id, generate_summary_items
from dsfr.utils import generate_random_id, generate_summary_items, parse_tag_args


class GenerateRandomIdTestCase(SimpleTestCase):
Expand Down Expand Up @@ -29,3 +29,48 @@ def test_generate_summary_items(self):
{"label": "Dernier", "link": "#dernier"},
],
)


class TestParseTagArgs(SimpleTestCase):
def test_parse_tag_args_with_args_only(self):
self.assertEqual(
parse_tag_args([{"coucou": "youpi"}], {}, ["coucou"]), {"coucou": "youpi"}
)

def test_parse_tag_args_with_kwargs_only(self):
self.assertEqual(
parse_tag_args(None, {"coucou": "youpi"}, ["coucou"]), {"coucou": "youpi"}
)

def test_parse_tag_args_with_data_attributes(self):
self.assertEqual(
parse_tag_args(
[{}],
{"data_test": "value", "data_another": "thing"},
[],
),
{"data_attributes": {"test": "value", "another": "thing"}},
)

def test_parse_tag_args_with_allowed_and_data_attributes(self):
self.assertEqual(
parse_tag_args(
[{"foo": "bar"}],
{"coucou": "yes", "data_role": "admin"},
["coucou", "foo"],
),
{"foo": "bar", "coucou": "yes", "data_attributes": {"role": "admin"}},
)

def test_parse_tag_args_with_disallowed_keys(self):
# "not_allowed" should be ignored completely
self.assertEqual(
parse_tag_args(None, {"not_allowed": "oops"}, ["coucou"]),
{},
)

def test_parse_tag_args_with_empty_inputs(self):
self.assertEqual(
parse_tag_args(None, {}, []),
{},
)
6 changes: 6 additions & 0 deletions dsfr/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,20 @@ def parse_tag_args(args, kwargs, allowed_keys: list) -> dict:
Allows to use a tag with either all the arguments in a dict or by declaring them separately
"""
tag_data = {}
data_attributes = {}

if args:
tag_data = args[0].copy()

for k in kwargs:
if k in allowed_keys:
tag_data[k] = kwargs[k]
elif k.startswith("data_"):
_, _, attribute = k.partition("data_")
data_attributes[attribute] = kwargs[k]

if data_attributes:
tag_data = {**tag_data, "data_attributes": data_attributes}
return tag_data


Expand Down