-
Notifications
You must be signed in to change notification settings - Fork 483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React Wrapper Step 1: Simple React Wrapper that never updates #7000
Changes from 14 commits
93553b4
47d4d24
4a93b2c
0adb3b5
7c2cf5f
a90d151
64d2a23
f55c146
2af9591
72cedcd
f584c35
76c5d1b
3a8d016
d2c678f
e142f4f
46a9f2b
0318de0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ var api = require('./api'); | |
var apiBlockly = require('./apiBlockly'); | ||
var dontMarshalApi = require('./dontMarshalApi'); | ||
var blocks = require('./blocks'); | ||
var AppView = require('../templates/AppView.jsx'); | ||
var page = require('../templates/page.html.ejs'); | ||
var dom = require('../dom'); | ||
var parseXmlElement = require('../xml').parseElement; | ||
|
@@ -646,29 +647,6 @@ Applab.init = function(config) { | |
}); | ||
} | ||
|
||
config.html = page({ | ||
assetUrl: studioApp.assetUrl, | ||
data: { | ||
localeDirection: studioApp.localeDirection(), | ||
visualization: require('./visualization.html.ejs')({ | ||
appWidth: Applab.appWidth, | ||
appHeight: Applab.footerlessAppHeight | ||
}), | ||
controls: firstControlsRow, | ||
extraControlRows: extraControlRows, | ||
blockUsed: undefined, | ||
idealBlockNumber: undefined, | ||
editCode: level.editCode, | ||
blockCounterClass: 'block-counter-default', | ||
pinWorkspaceToBottom: true, | ||
// TODO (brent) - seems a little gross that we've made this part of a | ||
// template shared across all apps | ||
// disable designMode if we're readonly | ||
hasDesignMode: !config.readonlyWorkspace, | ||
readonlyWorkspace: config.readonlyWorkspace | ||
} | ||
}); | ||
|
||
config.loadAudio = function() { | ||
studioApp.loadAudio(skin.failureSound, 'failure'); | ||
}; | ||
|
@@ -761,90 +739,118 @@ Applab.init = function(config) { | |
AppStorage.populateTable(level.dataTables, false); // overwrite = false | ||
AppStorage.populateKeyValue(level.dataProperties, false); // overwrite = false | ||
|
||
studioApp.init(config); | ||
|
||
var viz = document.getElementById('visualization'); | ||
var vizCol = document.getElementById('visualizationColumn'); | ||
|
||
if (!config.noPadding) { | ||
viz.className += " with_padding"; | ||
vizCol.className += " with_padding"; | ||
} | ||
|
||
if (config.embed || config.hideSource) { | ||
// no responsive styles active in embed or hideSource mode, so set sizes: | ||
viz.style.width = Applab.appWidth + 'px'; | ||
viz.style.height = (shouldRenderFooter() ? Applab.appHeight : Applab.footerlessAppHeight) + 'px'; | ||
// Use offsetWidth of viz so we can include any possible border width: | ||
vizCol.style.maxWidth = viz.offsetWidth + 'px'; | ||
} | ||
React.render(React.createElement(AppView, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a great way of transitioning from our current world to our new world. However, long term I think we want to discourage having this much content in our createElement call (to some extent referring to number of properties, but mostly referring to having full function definitions). If it's not difficult, I'd love to get to the place in the nearish future where we can have JSX instead of createElement which will discourage huge objects like this. In any case, no changes are needed in this regards for this PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be happy to go through and extract the parameters into local variables, to make the expression of these props as compact as possible. That said, I think some of that will be mitigated as we get further into this process and break up page.html.ejs into smaller pieces that need fewer parameters. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Bjvanminnen would you like these better if the functions were simply defined outside the React.createElement call, then passed in by name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I would like that better. Though again, I think this is a fine first step (and results in a more minimal change with whitespace ignored). Moving out these functions might be a good step for a future PR. |
||
renderCodeApp: function () { | ||
return page({ | ||
assetUrl: studioApp.assetUrl, | ||
data: { | ||
localeDirection: studioApp.localeDirection(), | ||
visualization: require('./visualization.html.ejs')({ | ||
appWidth: Applab.appWidth, | ||
appHeight: Applab.footerlessAppHeight | ||
}), | ||
controls: firstControlsRow, | ||
extraControlRows: extraControlRows, | ||
blockUsed: undefined, | ||
idealBlockNumber: undefined, | ||
editCode: level.editCode, | ||
blockCounterClass: 'block-counter-default', | ||
pinWorkspaceToBottom: true, | ||
// TODO (brent) - seems a little gross that we've made this part of a | ||
// template shared across all apps | ||
// disable designMode if we're readonly | ||
hasDesignMode: !config.readonlyWorkspace, | ||
readonlyWorkspace: config.readonlyWorkspace | ||
} | ||
}); | ||
}.bind(this), | ||
onMount: function () { | ||
studioApp.init(config); | ||
|
||
if (debuggerUi) { | ||
debuggerUi.initializeAfterDomCreated({ | ||
defaultStepSpeed: config.level.sliderSpeed | ||
}); | ||
} | ||
var viz = document.getElementById('visualization'); | ||
var vizCol = document.getElementById('visualizationColumn'); | ||
|
||
window.addEventListener('resize', Applab.renderVisualizationOverlay); | ||
if (!config.noPadding) { | ||
viz.className += " with_padding"; | ||
vizCol.className += " with_padding"; | ||
} | ||
|
||
var finishButton = document.getElementById('finishButton'); | ||
if (finishButton) { | ||
dom.addClickTouchEvent(finishButton, Applab.onPuzzleFinish); | ||
} | ||
if (config.embed || config.hideSource) { | ||
// no responsive styles active in embed or hideSource mode, so set sizes: | ||
viz.style.width = Applab.appWidth + 'px'; | ||
viz.style.height = (shouldRenderFooter() ? Applab.appHeight : Applab.footerlessAppHeight) + 'px'; | ||
// Use offsetWidth of viz so we can include any possible border width: | ||
vizCol.style.maxWidth = viz.offsetWidth + 'px'; | ||
} | ||
|
||
var submitButton = document.getElementById('submitButton'); | ||
if (submitButton) { | ||
dom.addClickTouchEvent(submitButton, Applab.onPuzzleSubmit); | ||
} | ||
if (debuggerUi) { | ||
debuggerUi.initializeAfterDomCreated({ | ||
defaultStepSpeed: config.level.sliderSpeed | ||
}); | ||
} | ||
|
||
var unsubmitButton = document.getElementById('unsubmitButton'); | ||
if (unsubmitButton) { | ||
dom.addClickTouchEvent(unsubmitButton, Applab.onPuzzleUnsubmit); | ||
} | ||
window.addEventListener('resize', Applab.renderVisualizationOverlay); | ||
|
||
if (level.editCode) { | ||
// Prevent the backspace key from navigating back. Make sure it's still | ||
// allowed on other elements. | ||
// Based on http://stackoverflow.com/a/2768256/2506748 | ||
$(document).on('keydown', function (event) { | ||
var doPrevent = false; | ||
if (event.keyCode !== KeyCodes.BACKSPACE) { | ||
return; | ||
} | ||
var d = event.srcElement || event.target; | ||
if ((d.tagName.toUpperCase() === 'INPUT' && ( | ||
d.type.toUpperCase() === 'TEXT' || | ||
d.type.toUpperCase() === 'PASSWORD' || | ||
d.type.toUpperCase() === 'FILE' || | ||
d.type.toUpperCase() === 'EMAIL' || | ||
d.type.toUpperCase() === 'SEARCH' || | ||
d.type.toUpperCase() === 'NUMBER' || | ||
d.type.toUpperCase() === 'DATE' )) || | ||
d.tagName.toUpperCase() === 'TEXTAREA') { | ||
doPrevent = d.readOnly || d.disabled; | ||
var finishButton = document.getElementById('finishButton'); | ||
if (finishButton) { | ||
dom.addClickTouchEvent(finishButton, Applab.onPuzzleFinish); | ||
} | ||
else { | ||
doPrevent = !d.isContentEditable; | ||
|
||
var submitButton = document.getElementById('submitButton'); | ||
if (submitButton) { | ||
dom.addClickTouchEvent(submitButton, Applab.onPuzzleSubmit); | ||
} | ||
|
||
if (doPrevent) { | ||
event.preventDefault(); | ||
var unsubmitButton = document.getElementById('unsubmitButton'); | ||
if (unsubmitButton) { | ||
dom.addClickTouchEvent(unsubmitButton, Applab.onPuzzleUnsubmit); | ||
} | ||
}); | ||
|
||
designMode.addKeyboardHandlers(); | ||
if (level.editCode) { | ||
// Prevent the backspace key from navigating back. Make sure it's still | ||
// allowed on other elements. | ||
// Based on http://stackoverflow.com/a/2768256/2506748 | ||
$(document).on('keydown', function (event) { | ||
var doPrevent = false; | ||
if (event.keyCode !== KeyCodes.BACKSPACE) { | ||
return; | ||
} | ||
var d = event.srcElement || event.target; | ||
if ((d.tagName.toUpperCase() === 'INPUT' && ( | ||
d.type.toUpperCase() === 'TEXT' || | ||
d.type.toUpperCase() === 'PASSWORD' || | ||
d.type.toUpperCase() === 'FILE' || | ||
d.type.toUpperCase() === 'EMAIL' || | ||
d.type.toUpperCase() === 'SEARCH' || | ||
d.type.toUpperCase() === 'NUMBER' || | ||
d.type.toUpperCase() === 'DATE' )) || | ||
d.tagName.toUpperCase() === 'TEXTAREA') { | ||
doPrevent = d.readOnly || d.disabled; | ||
} | ||
else { | ||
doPrevent = !d.isContentEditable; | ||
} | ||
|
||
if (doPrevent) { | ||
event.preventDefault(); | ||
} | ||
}); | ||
|
||
designMode.renderDesignWorkspace(); | ||
designMode.addKeyboardHandlers(); | ||
|
||
designMode.configurePlaySpaceHeader(); | ||
designMode.renderDesignWorkspace(); | ||
|
||
designMode.toggleDesignMode(Applab.startInDesignMode()); | ||
designMode.configurePlaySpaceHeader(); | ||
|
||
designMode.configureDragAndDrop(); | ||
designMode.toggleDesignMode(Applab.startInDesignMode()); | ||
|
||
var designModeViz = document.getElementById('designModeViz'); | ||
designModeViz.addEventListener('click', designMode.onDesignModeVizClick); | ||
} | ||
designMode.configureDragAndDrop(); | ||
|
||
var designModeViz = document.getElementById('designModeViz'); | ||
designModeViz.addEventListener('click', designMode.onDesignModeVizClick); | ||
} | ||
}.bind(this) | ||
}), document.getElementById(config.containerId)); | ||
}; | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it safe to silently ignore an undefined config.html?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, config.html comes from the server and can be empty. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we do our top-level rendering with React (prior to StudioApp.init) we want to skip this step, which would clobber everything React just rendered.
In my prototype I originally skipped this with a more explicit config variable:
That felt a little like duplicating state though - why even assign config.html if we're not going to use it, and then why introduce another config variable to say we're not using config.html when we can just check if it's empty?
Alternatively, I could tear out this line altogether if I also give NetSim a top-level React wrapper (it's the one remaining user of this old approach).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining, it was not clear that this conditional is based on whether this app is using react to render. I think it would suffice to add a comment noting that
config.html
will be empty for apps which are rendering via React.