",
- views.view_to_component_iframe, # type: ignore[arg-type]
- name="view_to_component",
+ views.view_to_iframe, # type: ignore[arg-type]
+ name="view_to_iframe",
),
]
diff --git a/src/reactpy_django/http/views.py b/src/reactpy_django/http/views.py
index 12129791..5fbae810 100644
--- a/src/reactpy_django/http/views.py
+++ b/src/reactpy_django/http/views.py
@@ -39,20 +39,19 @@ async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
return HttpResponse(file_contents, content_type="text/javascript")
-async def view_to_component_iframe(
- request: HttpRequest, view_path: str
+async def view_to_iframe(
+ request: HttpRequest, view_path: str, *args, **kwargs
) -> HttpResponse:
- """Returns a view that was registered by view_to_component.
- This view is intended to be used as iframe, for compatibility purposes."""
- from reactpy_django.config import REACTPY_VIEW_COMPONENT_IFRAMES
+ """Returns a view that was registered by reactpy_django.components.view_to_iframe."""
+ from reactpy_django.config import REACTPY_REGISTERED_IFRAMES
# Get the view from REACTPY_REGISTERED_IFRAMES
- iframe = REACTPY_VIEW_COMPONENT_IFRAMES.get(view_path)
+ iframe = REACTPY_REGISTERED_IFRAMES.get(view_path)
if not iframe:
return HttpResponseNotFound()
# Render the view
- response = await render_view(iframe.view, request, iframe.args, iframe.kwargs)
+ response = await render_view(iframe.view, request, args, kwargs)
# Ensure page can be rendered as an iframe
response["X-Frame-Options"] = "SAMEORIGIN"
diff --git a/src/reactpy_django/types.py b/src/reactpy_django/types.py
index 3c77a1eb..83d29790 100644
--- a/src/reactpy_django/types.py
+++ b/src/reactpy_django/types.py
@@ -18,6 +18,7 @@
from django.db.models.query import QuerySet
from django.http import HttpRequest
from django.views.generic import View
+from reactpy.types import ComponentType
from reactpy.types import Connection as _Connection
from typing_extensions import ParamSpec
@@ -29,7 +30,7 @@
"Query",
"Mutation",
"Connection",
- "ViewComponentIframe",
+ "IframeComponent",
"AsyncPostprocessor",
"SyncPostprocessor",
"QueryOptions",
@@ -75,10 +76,8 @@ class Mutation(Generic[_Params]):
@dataclass
-class ViewComponentIframe:
+class IframeComponent:
view: View | Callable
- args: Sequence
- kwargs: dict
class AsyncPostprocessor(Protocol):
@@ -134,3 +133,10 @@ class ComponentParams:
args: Sequence
kwargs: MutableMapping[str, Any]
+
+
+class ViewComponentConstructor(Protocol):
+ def __call__(
+ self, request: HttpRequest | None = None, *args: Any, **kwargs: Any
+ ) -> ComponentType:
+ ...
From 0ab58227ff58ae40f34978915f02844978355bff Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 18 Sep 2023 23:01:31 -0700
Subject: [PATCH 02/28] add changelog
---
CHANGELOG.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8f0ec63..62027875 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,10 @@ Using the following categories, list your changes in this order:
- Renamed undocumented utility function `reactpy_django.utils.ComponentPreloader` to `reactpy_django.utils.RootComponentFinder`.
+### Deprecated
+
+- The `compatibility` argument on `reactpy_django.components.view_to_component` is deprecated. Use `view_to_iframe_component` instead.
+
## [3.5.1] - 2023-09-07
### Added
From 79462f97a981957d074d889ff041b36aa593ff9b Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Thu, 21 Sep 2023 18:45:14 -0700
Subject: [PATCH 03/28] functional view_to_iframe
---
src/reactpy_django/checks.py | 2 +-
src/reactpy_django/components.py | 17 +++++++++++++----
src/reactpy_django/http/views.py | 10 +++++++---
src/reactpy_django/types.py | 2 ++
tests/test_app/components.py | 11 ++++++++++-
tests/test_app/templates/base.html | 5 ++++-
tests/test_app/tests/test_components.py | 5 +++++
tests/test_app/views.py | 16 +++++++++++++++-
8 files changed, 57 insertions(+), 11 deletions(-)
diff --git a/src/reactpy_django/checks.py b/src/reactpy_django/checks.py
index 489888eb..77bb4153 100644
--- a/src/reactpy_django/checks.py
+++ b/src/reactpy_django/checks.py
@@ -39,7 +39,7 @@ def reactpy_warnings(app_configs, **kwargs):
# ReactPy URLs exist
try:
reverse("reactpy:web_modules", kwargs={"file": "example"})
- reverse("reactpy:view_to_component", kwargs={"view_path": "example"})
+ reverse("reactpy:view_to_iframe", kwargs={"view_path": "example"})
except Exception:
warnings.append(
Warning(
diff --git a/src/reactpy_django/components.py b/src/reactpy_django/components.py
index 09bca072..009ded3a 100644
--- a/src/reactpy_django/components.py
+++ b/src/reactpy_django/components.py
@@ -3,6 +3,7 @@
import json
import os
from typing import Any, Callable, Sequence, Union, cast, overload
+from urllib.parse import urlencode
from warnings import warn
from django.contrib.staticfiles.finders import find
@@ -71,7 +72,7 @@ async def async_render():
DeprecationWarning,
)
- return view_to_iframe(view)(_args, **_kwargs)
+ return view_to_iframe(view)(*_args, **_kwargs)
# Return the view if it's been rendered via the `async_render` hook
return converted_view
@@ -161,11 +162,19 @@ def view_to_iframe(view: Callable | View):
@component
def _view_to_iframe(*args: Any, **kwargs: Any):
+ query_string = ""
+ query = {}
+ if args:
+ query["_args"] = args
+ if kwargs:
+ query.update(kwargs)
+ if args or kwargs:
+ query_string = f"?{urlencode(query, doseq=True)}"
+
return html.iframe(
{
- "src": reverse(
- "reactpy:view_to_iframe", args=[dotted_path, *args], kwargs=kwargs
- ),
+ "src": reverse("reactpy:view_to_iframe", args=[dotted_path])
+ + query_string,
"loading": "lazy",
}
)
diff --git a/src/reactpy_django/http/views.py b/src/reactpy_django/http/views.py
index 5fbae810..4e4424bd 100644
--- a/src/reactpy_django/http/views.py
+++ b/src/reactpy_django/http/views.py
@@ -1,4 +1,5 @@
import os
+from urllib.parse import parse_qs
from aiofile import async_open
from django.core.cache import caches
@@ -39,9 +40,7 @@ async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
return HttpResponse(file_contents, content_type="text/javascript")
-async def view_to_iframe(
- request: HttpRequest, view_path: str, *args, **kwargs
-) -> HttpResponse:
+async def view_to_iframe(request: HttpRequest, view_path: str) -> HttpResponse:
"""Returns a view that was registered by reactpy_django.components.view_to_iframe."""
from reactpy_django.config import REACTPY_REGISTERED_IFRAMES
@@ -50,6 +49,11 @@ async def view_to_iframe(
if not iframe:
return HttpResponseNotFound()
+ # Get args and kwargs from the request
+ query = request.META.get("QUERY_STRING", "")
+ kwargs = {k: v if len(v) > 1 else v[0] for k, v in parse_qs(query).items()}
+ args = kwargs.pop("_args", [])
+
# Render the view
response = await render_view(iframe.view, request, args, kwargs)
diff --git a/src/reactpy_django/types.py b/src/reactpy_django/types.py
index 83d29790..7fb47166 100644
--- a/src/reactpy_django/types.py
+++ b/src/reactpy_django/types.py
@@ -77,6 +77,8 @@ class Mutation(Generic[_Params]):
@dataclass
class IframeComponent:
+ """Views registered by `view_to_iframe`."""
+
view: View | Callable
diff --git a/tests/test_app/components.py b/tests/test_app/components.py
index ec53c031..db118343 100644
--- a/tests/test_app/components.py
+++ b/tests/test_app/components.py
@@ -7,7 +7,7 @@
from django.http import HttpRequest
from django.shortcuts import render
from reactpy import component, hooks, html, web
-from reactpy_django.components import view_to_component
+from reactpy_django.components import view_to_component, view_to_iframe
from reactpy_django.types import QueryOptions
from test_app.models import (
@@ -468,6 +468,7 @@ async def on_change(event):
_view_to_component_template_view_class_compatibility = view_to_component(
views.ViewToComponentTemplateViewClassCompatibility, compatibility=True
)
+_view_to_iframe_args = view_to_iframe(views.view_to_iframe_args)
view_to_component_script = view_to_component(views.view_to_component_script)
_view_to_component_request = view_to_component(views.view_to_component_request)
_view_to_component_args = view_to_component(views.view_to_component_args)
@@ -514,6 +515,14 @@ def view_to_component_template_view_class_compatibility():
)
+@component
+def view_to_iframe_args():
+ return html.div(
+ {"id": inspect.currentframe().f_code.co_name}, # type: ignore
+ _view_to_iframe_args("Arg1", "Arg2", kwarg1="Kwarg1", kwarg2="Kwarg2"),
+ )
+
+
@component
def view_to_component_request():
request, set_request = hooks.use_state(None)
diff --git a/tests/test_app/templates/base.html b/tests/test_app/templates/base.html
index 03dd3ba3..f2042fd4 100644
--- a/tests/test_app/templates/base.html
+++ b/tests/test_app/templates/base.html
@@ -81,6 +81,8 @@ ReactPy Test Page
{% component "test_app.components.view_to_component_template_view_class_compatibility" %}
+ {% component "test_app.components.view_to_iframe_args" %}
+
{% component "test_app.components.view_to_component_decorator" %}
{% component "test_app.components.view_to_component_decorator_args" %}
@@ -92,7 +94,8 @@ ReactPy Test Page
{% component "test_app.components.hello_world" host="https://example.com/" %}
- {% component "test_app.components.broken_postprocessor_query" %}
+ {% component "test_app.components.broken_postprocessor_query" %}
+