Unverified Commit 8b59c6d5 by Ari Rizzitano Committed by GitHub

Merge pull request #16639 from edx/ari/react-renderer

mako/react bridge code
parents 9b92a3b7 8ca0fe9d
......@@ -552,6 +552,7 @@ from openedx.core.djangolib.js_utils import (
%endif
</div>
<%static:webpack entry="StudioIndex">
var enableReruns = ${allow_course_reruns and rerun_creator_status and course_creator_status=='granted' | n, dump_js_escaped_json};
new StudioCourseIndex(
......
<%page expression_filter="h"/>
<%!
import logging
import json
from django.contrib.staticfiles.storage import staticfiles_storage
from pipeline_mako import compressed_css, compressed_js
from django.utils.translation import get_language_bidi
from mako.exceptions import TemplateLookupException
from edxmako.shortcuts import marketing_link
from openedx.core.djangolib.js_utils import js_escaped_string
from openedx.core.djangolib.js_utils import js_escaped_string, dump_js_escaped_json
from openedx.core.djangolib.markup import HTML
from openedx.core.djangoapps.site_configuration.helpers import (
page_title_breadcrumbs,
get_value,
......@@ -18,6 +20,7 @@ from openedx.core.djangoapps.theming.helpers import (
is_request_in_themed_site,
)
from certificates.api import get_asset_url_by_slug
from webpack_loader.templatetags.webpack_loader import render_bundle
logger = logging.getLogger(__name__)
%>
......@@ -97,25 +100,15 @@ source, template_path = Loader(engine).load_template_source(path)
-include it as the first script in this block
</%doc>
<%
from django.template import Template, Context
from webpack_loader.exceptions import WebpackLoaderBadStatsError
import json
body = capture(caller.body)
body_dict = json.loads(body)
body_dict['lang'] = lang
return Template("""
<script type="text/javascript" id='studioContext'>
var studioContext = {% autoescape off %}{{ body }}{% endautoescape %};
</script>
<div id="root"></div>
{% load render_bundle from webpack_loader %}
{% render_bundle page %}
""").render(Context({
'body': json.dumps(body_dict),
'page': page
}))
%>
<script type="text/javascript" id='courseContext'>
var studioContext = ${ body | n, decode.utf8};
</script>
<div id="root"></div>
${HTML(render_bundle(page))}
</%def>
<%def name="webpack(entry)">
......@@ -124,21 +117,41 @@ source, template_path = Loader(engine).load_template_source(path)
Uses the Django template engine because our webpack loader only provides template tags for Jinja and Django.
</%doc>
<%
from django.template import Template, Context
from webpack_loader.exceptions import WebpackLoaderBadStatsError
return Template("""
{% load render_bundle from webpack_loader %}
{% render_bundle entry %}
{% if body %}
<script type="text/javascript">
{% autoescape off %}{{ body }}{% endautoescape %}
</script>
{% endif %}
""").render(Context({
'entry': entry,
'body': capture(caller.body)
}))
body = capture(caller.body)
%>
${HTML(render_bundle(entry))}
% if body:
<script type="text/javascript">
${body | n, decode.utf8}
</script>
% endif
</%def>
<%def name="renderReact(component, id, props={})">
<%doc>
Wrapper function to load a React component via webpack() and render
it onto the page, passing an optional context object via props.
component: (string) The component to render, as specified by the name
of its Webpack entry point.
id: (string) A unique id to apply to the component's container div.
props: (dict, optional) An object containing data to pass into the
component as props.
</%doc>
${HTML(render_bundle(component))}
${HTML(render_bundle('ReactRenderer'))}
<div id="${id}"></div>
<script type="text/javascript">
var c;
try { c = ${component | n, decode.utf8}; } catch (e) { c = null; }
new ReactRenderer({
component: c,
selector: '#${id | n, decode.utf8}',
componentName: '${component | n, js_escaped_string}',
props: ${props | n, dump_js_escaped_json}
});
</script>
</%def>
<%def name="require_module(module_name, class_name)">
......
import React from 'react';
import ReactDOM from 'react-dom';
class ReactRendererException extends Error {
constructor(message) {
super(`ReactRendererException: ${message}`);
Error.captureStackTrace(this, ReactRendererException);
}
}
export class ReactRenderer {
constructor({ component, selector, componentName, props = {} }) {
Object.assign(this, {
component,
selector,
componentName,
props,
});
this.handleArgumentErrors();
this.targetElement = this.getTargetElement();
this.renderComponent();
}
handleArgumentErrors() {
if (this.component === null) {
throw new ReactRendererException(
`Component ${this.componentName} is not defined. Make sure you're ` +
`using a non-default export statement for the ${this.componentName} ` +
`class, that ${this.componentName} has an entry point defined ` +
'within the \'entry\' section of webpack.common.config.js, and that the ' +
'entry point is pointing at the correct file path.',
);
}
if (!(this.props instanceof Object && this.props.constructor === Object)) {
let propsType = typeof this.props;
if (Array.isArray(this.props)) {
propsType = 'array';
} else if (this.props === null) {
propsType = 'null';
}
throw new ReactRendererException(
`Invalid props passed to component ${this.componentName}. Expected ` +
`an object, but received a ${propsType}.`,
);
}
}
getTargetElement() {
const elementList = document.querySelectorAll(this.selector);
if (elementList.length !== 1) {
throw new ReactRendererException(
`Expected 1 element match for selector "${this.selector}" ` +
`but received ${elementList.length} matches.`,
);
} else {
return elementList[0];
}
}
renderComponent() {
ReactDOM.render(
React.createElement(this.component, this.props, null),
this.targetElement,
);
}
}
"""
Default unit test configuration and fixtures.
"""
from __future__ import absolute_import, unicode_literals
import pytest
# Import hooks and fixture overrides from the cms package to
# avoid duplicating the implementation
from cms.conftest import _django_clear_site_cache, pytest_configure # pylint: disable=unused-import
@pytest.fixture(autouse=True)
def no_webpack_loader(monkeypatch):
monkeypatch.setattr(
"webpack_loader.templatetags.webpack_loader.render_bundle",
lambda x: ''
)
......@@ -35,7 +35,10 @@ module.exports = {
Currency: './openedx/features/course_experience/static/course_experience/js/currency.js',
Enrollment: './openedx/features/course_experience/static/course_experience/js/Enrollment.js',
LatestUpdate: './openedx/features/course_experience/static/course_experience/js/LatestUpdate.js',
WelcomeMessage: './openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js'
WelcomeMessage: './openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js',
// Common
ReactRenderer: './common/static/js/src/ReactRenderer.jsx'
},
output: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment