From 6d9c2bda9827852c9b49b5d7f02d4a5c23029157 Mon Sep 17 00:00:00 2001 From: Michael Yin Date: Tue, 16 Jan 2024 16:27:16 +0800 Subject: [PATCH] Feature/31 custom action (#34) --- docs/source/conf.py | 4 ++ docs/source/turbo_stream.md | 57 +++++++++++++++---- src/turbo_helper/stream.py | 32 +++++------ src/turbo_helper/templatetags/turbo_helper.py | 33 +++++++---- tests/conftest.py | 18 ++++++ tests/test_stream.py | 17 +----- tests/test_tags.py | 12 ++++ 7 files changed, 120 insertions(+), 53 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 1cfd254..ca31bdb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -76,3 +76,7 @@ def _get_version() -> str: # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". + +html_theme_options = { + "announcement": 'If you are new to Hotwire, you may be interested in this free eBook Hotwire Django Tutorial', +} diff --git a/docs/source/turbo_stream.md b/docs/source/turbo_stream.md index dd50043..283f83f 100644 --- a/docs/source/turbo_stream.md +++ b/docs/source/turbo_stream.md @@ -12,7 +12,12 @@ turbo_stream.append("dom_id", "OK") turbo_stream.append("dom_id", content="OK") # template example -turbo_stream.append("dom_id", template="simple.html", context={"msg": "my content"}, request=request) +turbo_stream.append( + "dom_id", + template="simple.html", + context={"msg": "my content"}, + request=request +) ``` Turbo Stream built-in actions are supported in syntax `turbo_stream.xxx`: @@ -34,8 +39,18 @@ from turbo_helper import TurboStreamResponse # render multiple turbo stream elements in one response return TurboStreamResponse([ - turbo_stream.append("message", template="message.html", context={"msg": "my content"}, request=request), - turbo_stream.update("form", template="form.html", context={"form": form}, request=request), + turbo_stream.append( + "message", + template="message.html", + context={"msg": "my content"}, + request=request + ), + turbo_stream.update( + "form", + template="form.html", + context={"form": form}, + request=request + ), ]) ``` @@ -46,8 +61,18 @@ from turbo_helper import turbo_stream # render multiple turbo stream elements in one response return turbo_stream.response([ - turbo_stream.append("message", template="message.html", context={"msg": "my content"}, request=request), - turbo_stream.update("form", template="form.html", context={"form": form}, request=request), + turbo_stream.append( + "message", + template="message.html", + context={"msg": "my content"}, + request=request + ), + turbo_stream.update( + "form", + template="form.html", + context={"form": form}, + request=request + ), ]) ``` @@ -80,20 +105,30 @@ You can extend Turbo Stream Action by `register_turbo_stream_action` decorator. ```python from turbo_helper import ( - register_turbo_stream_action, - turbo_stream, + register_turbo_stream_action, + turbo_stream, ) + # register toast action @register_turbo_stream_action("toast") -def toast(target, message, position="left"): - return turbo_stream.render_action( - "toast", target=target, data_message=message, data_position=position +def toast(target, content=None, **kwargs): + position = kwargs.get('position', 'left') + return turbo_stream.action( + "toast", target=target, message=kwargs['message'], position=position ) turbo_stream.toast("dom_id", message="hello world", position="right") -# +# +``` + +Or you can do it in template: + +```django +{% load turbo_helper %} + +{% turbo_stream "toast" "target" message="Hello Word" position="right" %}{% endturbo_stream %} ``` ## Targeting Multiple Elements diff --git a/src/turbo_helper/stream.py b/src/turbo_helper/stream.py index b4ab174..44e3de3 100644 --- a/src/turbo_helper/stream.py +++ b/src/turbo_helper/stream.py @@ -15,7 +15,7 @@ def __init__(self): def is_registered(self, name): return name in self.registered_actions - def render_action(self, action, target, content=None, **kwargs): + def action(self, action, target, content=None, **kwargs): if not content and kwargs.get("template", None): # render template content template = kwargs.pop("template") @@ -28,7 +28,7 @@ def render_action(self, action, target, content=None, **kwargs): action=action, content=content, target=target, attributes=kwargs ) - def render_action_all(self, action, targets, content=None, **kwargs): + def action_all(self, action, targets, content=None, **kwargs): if not content and kwargs.get("template", None): # render template content template = kwargs.pop("template") @@ -69,37 +69,37 @@ def decorator(func): @register_turbo_stream_action("append") def append(target, content=None, **kwargs): - return turbo_stream.render_action("append", target, content, **kwargs) + return turbo_stream.action("append", target, content, **kwargs) @register_turbo_stream_action("after") def after(target, content=None, **kwargs): - return turbo_stream.render_action("after", target, content, **kwargs) + return turbo_stream.action("after", target, content, **kwargs) @register_turbo_stream_action("before") def before(target, content=None, **kwargs): - return turbo_stream.render_action("before", target, content, **kwargs) + return turbo_stream.action("before", target, content, **kwargs) @register_turbo_stream_action("prepend") def prepend(target, content=None, **kwargs): - return turbo_stream.render_action("prepend", target, content, **kwargs) + return turbo_stream.action("prepend", target, content, **kwargs) @register_turbo_stream_action("remove") def remove(target, **kwargs): - return turbo_stream.render_action("remove", target, **kwargs) + return turbo_stream.action("remove", target, **kwargs) @register_turbo_stream_action("replace") def replace(target, content=None, **kwargs): - return turbo_stream.render_action("replace", target, content, **kwargs) + return turbo_stream.action("replace", target, content, **kwargs) @register_turbo_stream_action("update") def update(target, content=None, **kwargs): - return turbo_stream.render_action("update", target, content, **kwargs) + return turbo_stream.action("update", target, content, **kwargs) ################################################################################ @@ -107,34 +107,34 @@ def update(target, content=None, **kwargs): @register_turbo_stream_action("append_all") def append_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("append", targets, content, **kwargs) + return turbo_stream.action_all("append", targets, content, **kwargs) @register_turbo_stream_action("after_all") def after_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("after", targets, content, **kwargs) + return turbo_stream.action_all("after", targets, content, **kwargs) @register_turbo_stream_action("before_all") def before_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("before", targets, content, **kwargs) + return turbo_stream.action_all("before", targets, content, **kwargs) @register_turbo_stream_action("prepend_all") def prepend_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("prepend", targets, content, **kwargs) + return turbo_stream.action_all("prepend", targets, content, **kwargs) @register_turbo_stream_action("remove_all") def remove_all(targets, **kwargs): - return turbo_stream.render_action_all("remove", targets, **kwargs) + return turbo_stream.action_all("remove", targets, **kwargs) @register_turbo_stream_action("replace_all") def replace_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("replace", targets, content, **kwargs) + return turbo_stream.action_all("replace", targets, content, **kwargs) @register_turbo_stream_action("update_all") def update_all(targets, content=None, **kwargs): - return turbo_stream.render_action_all("update", targets, content, **kwargs) + return turbo_stream.action_all("update", targets, content, **kwargs) diff --git a/src/turbo_helper/templatetags/turbo_helper.py b/src/turbo_helper/templatetags/turbo_helper.py index 99a47ba..f3df06e 100644 --- a/src/turbo_helper/templatetags/turbo_helper.py +++ b/src/turbo_helper/templatetags/turbo_helper.py @@ -5,11 +5,8 @@ from django.template import Node, TemplateSyntaxError from django.template.base import token_kwargs -from turbo_helper.renderers import ( - render_turbo_frame, - render_turbo_stream, - render_turbo_stream_from, -) +from turbo_helper import turbo_stream +from turbo_helper.renderers import render_turbo_frame, render_turbo_stream_from register = template.Library() @@ -106,13 +103,25 @@ def render(self, context): for key, value in self.extra_context.items() } - return render_turbo_stream( - action=self.action.resolve(context), - target=self.target.resolve(context) if self.target else None, - targets=self.targets.resolve(context) if self.targets else None, - content=children, - attributes=attributes, - ) + target = self.target.resolve(context) if self.target else None + targets = self.targets.resolve(context) if self.targets else None + + if target: + action = self.action.resolve(context) + func = getattr(turbo_stream, f"{action}") + return func( + target=target, + content=children, + **attributes, + ) + elif targets: + action = self.action.resolve(context) + func = getattr(turbo_stream, f"{action}_all") + return func( + targets=targets, + content=children, + **attributes, + ) class TurboStreamFromTagNode(Node): diff --git a/tests/conftest.py b/tests/conftest.py index eb8d859..65598d7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,3 +52,21 @@ def todo(): from tests.testapp.models import TodoItem return TodoItem.objects.create(description="test") + + +@pytest.fixture() +def register_toast_action(): + from turbo_helper import register_turbo_stream_action, turbo_stream + + @register_turbo_stream_action("toast") + def toast(target, content=None, **kwargs): + position = kwargs.get("position", "left") + return turbo_stream.action( + "toast", target=target, message=kwargs["message"], position=position + ) + + yield + + # cleanup + delattr(turbo_stream, "toast") + turbo_stream.registered_actions.remove("toast") diff --git a/tests/test_stream.py b/tests/test_stream.py index ef7e48d..eb87833 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -1,11 +1,7 @@ from django.http import HttpRequest from django.utils.safestring import mark_safe -from turbo_helper import ( - TURBO_STREAM_CONTENT_TYPE, - register_turbo_stream_action, - turbo_stream, -) +from turbo_helper import TURBO_STREAM_CONTENT_TYPE, turbo_stream class TestTurboStream: @@ -65,17 +61,10 @@ def test_template_multiple_targets(self): assert "my content" in s assert '' in s - def test_custom_register(self): - # register toast action - @register_turbo_stream_action("toast") - def toast(target, message, position="left"): - return turbo_stream.render_action( - "toast", target=target, data_message=message, data_position=position - ) - + def test_custom_register(self, register_toast_action): s = turbo_stream.toast("dom_id", message="hello world", position="right") assert ( - '' + '' in s ) diff --git a/tests/test_tags.py b/tests/test_tags.py index fa2d32b..447912c 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -106,6 +106,18 @@ def test_dom_id_variable(self): == '' ) + def test_custom_register(self, register_toast_action): + template = """ + {% load turbo_helper %} + + {% turbo_stream "toast" dom_id message="Hello Word" position="right" %}{% endturbo_stream %} + """ + output = render(template, {"dom_id": "test"}).strip() + assert ( + '' + in output + ) + class TestStreamAll: def test_string(self):