Fix #143: ability to preload components (thanks @David-Guillot)

This commit is contained in:
Emil Stenström 2022-05-31 22:29:48 +02:00 committed by GitHub
commit 9632c4540f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 14 deletions

View file

@ -15,6 +15,9 @@ from django_components.middleware import (
register = template.Library()
RENDERED_COMMENT_TEMPLATE = "<!-- _RENDERED {name} -->"
def get_components_from_registry(registry):
"""Returns a list unique components from the registry."""
@ -27,13 +30,36 @@ def get_components_from_registry(registry):
return components
def get_components_from_preload_str(preload_str):
"""Returns a list of unique components from a comma-separated str"""
components = []
for component_name in preload_str.split(","):
component_name = component_name.strip()
if not component_name:
continue
component_class = registry.get(component_name)
components.append(component_class(component_name))
return components
@register.simple_tag(name="component_dependencies")
def component_dependencies_tag():
def component_dependencies_tag(preload=""):
"""Marks location where CSS link and JS script tags should be rendered."""
if is_dependency_middleware_active():
preloaded_dependencies = []
for component in get_components_from_preload_str(preload):
preloaded_dependencies.append(
RENDERED_COMMENT_TEMPLATE.format(
name=component._component_name
)
)
return mark_safe(
CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER
"\n".join(preloaded_dependencies)
+ CSS_DEPENDENCY_PLACEHOLDER
+ JS_DEPENDENCY_PLACEHOLDER
)
else:
rendered_dependencies = []
@ -44,11 +70,20 @@ def component_dependencies_tag():
@register.simple_tag(name="component_css_dependencies")
def component_css_dependencies_tag():
def component_css_dependencies_tag(preload=""):
"""Marks location where CSS link tags should be rendered."""
if is_dependency_middleware_active():
return mark_safe(CSS_DEPENDENCY_PLACEHOLDER)
preloaded_dependencies = []
for component in get_components_from_preload_str(preload):
preloaded_dependencies.append(
RENDERED_COMMENT_TEMPLATE.format(
name=component._component_name
)
)
return mark_safe(
"\n".join(preloaded_dependencies) + CSS_DEPENDENCY_PLACEHOLDER
)
else:
rendered_dependencies = []
for component in get_components_from_registry(registry):
@ -58,11 +93,20 @@ def component_css_dependencies_tag():
@register.simple_tag(name="component_js_dependencies")
def component_js_dependencies_tag():
def component_js_dependencies_tag(preload=""):
"""Marks location where JS script tags should be rendered."""
if is_dependency_middleware_active():
return mark_safe(JS_DEPENDENCY_PLACEHOLDER)
preloaded_dependencies = []
for component in get_components_from_preload_str(preload):
preloaded_dependencies.append(
RENDERED_COMMENT_TEMPLATE.format(
name=component._component_name
)
)
return mark_safe(
"\n".join(preloaded_dependencies) + JS_DEPENDENCY_PLACEHOLDER
)
else:
rendered_dependencies = []
for component in get_components_from_registry(registry):
@ -197,7 +241,9 @@ class ComponentNode(Node):
rendered_component = self.component.render(context)
if self.should_render_dependencies:
return (
f"<!-- _RENDERED {self.component._component_name} -->"
RENDERED_COMMENT_TEMPLATE.format(
name=self.component._component_name
)
+ rendered_component
)
else:

View file

@ -52,12 +52,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
"{% load component_tags %}{% component_dependencies %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js>"', rendered, count=0)
self.assertInHTML(
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
rendered,
count=0,
)
self.assertInHTML('<script src="script.js">', rendered, count=0)
self.assertInHTML(
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
rendered,
@ -71,7 +66,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
"{% load component_tags %}{% component_js_dependencies %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js>"', rendered, count=0)
self.assertInHTML('<script src="script.js">', rendered, count=0)
def test_no_css_dependencies_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent)
@ -86,6 +81,42 @@ class ComponentMediaRenderingTests(SimpleTestCase):
count=0,
)
def test_preload_dependencies_render_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent)
template = Template(
"{% load component_tags %}{% component_dependencies preload='test' %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1)
self.assertInHTML(
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
rendered,
count=1,
)
def test_preload_js_dependencies_render_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent)
template = Template(
"{% load component_tags %}{% component_js_dependencies preload='test' %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1)
def test_preload_css_dependencies_render_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent)
template = Template(
"{% load component_tags %}{% component_css_dependencies preload='test' %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML(
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
rendered,
count=1,
)
def test_single_component_dependencies_render_when_used(self):
component.registry.register(name="test", component=SimpleComponent)
@ -101,6 +132,21 @@ class ComponentMediaRenderingTests(SimpleTestCase):
)
self.assertInHTML('<script src="script.js">', rendered, count=1)
def test_preload_dependencies_render_once_when_used(self):
component.registry.register(name="test", component=SimpleComponent)
template = Template(
"{% load component_tags %}{% component_dependencies preload='test' %}"
"{% component 'test' variable='foo' %}"
)
rendered = create_and_process_template_response(template)
self.assertInHTML(
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
rendered,
count=1,
)
self.assertInHTML('<script src="script.js">', rendered, count=1)
def test_placeholder_removed_when_single_component_rendered(self):
component.registry.register(name="test", component=SimpleComponent)
@ -111,6 +157,15 @@ class ComponentMediaRenderingTests(SimpleTestCase):
rendered = create_and_process_template_response(template)
self.assertNotIn("_RENDERED", rendered)
def test_placeholder_removed_when_preload_rendered(self):
component.registry.register(name="test", component=SimpleComponent)
template = Template(
"{% load component_tags %}{% component_dependencies preload='test' %}"
)
rendered = create_and_process_template_response(template)
self.assertNotIn("_RENDERED", rendered)
def test_single_component_css_dependencies(self):
component.registry.register(name="test", component=SimpleComponent)