Is it possible to access webpack config properties through the preview-head.html file for storybook.js? - ejs

I am trying to add storybook.js into my react project and there is a pre-requisite script I need to add to every storybook page so as to render some custom components.
The script's URL needs to be auto generated on the fly in webpack configuration.
This Story rendering section of storybook's documentation mentioned a preview-head.html through which script tags can be injected into the final index HTML file.
I am wondering whether it supports EJS syntax like below for me to access a config option value of the HtmlWebpackPlugin.
<script src="<%= htmlWebpackPlugin.options.foobar %>"></script>

As a workaround, I wrote a decorator in the preview.js file to purposely insert the <script /> tag into the storybook iframe with dynamically generated src field.
const loadAssets = () => {
const scriptElement = document.createElement('script');
// scriptElement.src = /* dynamically generated from dependencies */;
document.querySelector('head').appendChild(scriptElement);
};
const AssetLoader = props => {
React.useEffect(() => loadAssets(), []);
return <>{props.children}</>;
};
const assetLoaderDecorator = storyFn => (
<AssetLoader>{storyFn()}</AssetLoader>
);
addDecorator(assetLoaderDecorator);
It's a bit clumsy but it serves the purpose at the moment.
Inspired by https://github.com/jhta/storybook-external-links.

Related

How do I open a game compiled for WebGL in Unity in Blazor?

I have created a Blazor Server project. In it, I wanted to put my WebGL game created in Unity3d on a separate page. In the end, after doing everything according to the example, I still can't get it to work. Although, I think, all things considered, but I see that the code markup index.html from WebGL game is different from other examples and there I haven't found a line of code:
unityInstance = UnityLoader.instantiate("unityContainer", "unity/WebGL/Build/WebGL.loader.js", { onProgress: UnityProgress });
I think I hooked it up right:
<script src="~/unity/WebGL/Build/WebGL.loader.js"></script>
#*<script src="~/unity/WebGL/Build/WebGL.framework.js.gz"></script>*#
<script src="~/unity/WebGL/run.js"></script>
I believe I wrote the mime type correctly:
var provider = new FileExtensionContentTypeProvider();
provider.Mappings.Remove(".data.gz");
provider.Mappings[".data.gz"] = "application/octet-stream";
provider.Mappings.Remove(".wasm.gz");
provider.Mappings[".wasm.gz"] = "application/wasm";
provider.Mappings.Remove(".js.gz");
provider.Mappings[".js.gz"] = "application/javascript";
provider.Mappings.Remove(".symbols.json.gz");
provider.Mappings[".symbols.json.gz"] = "application/octet-stream";
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "unity", "WebGL", "Build")),
RequestPath = "/Build",
ContentTypeProvider = provider
});
app.UseStaticFiles();
The error in the JavaScript console reads:
UnityLoader is not defined
If interested, here is my entire project:
https://github.com/EgorPavlovich/FPS.Servers.Test
I had to use the index.html file from the game build

How to seperate Vue logic in a laravel app based on layout and page templates

I have a laravel app and a Vue instance attached to the body (or a div, just inside the body).
const app = new Vue({
el: '#app'
});
I think it makes sense to use the Vue instance for stuff relating to the layout (eg header, nav, footer logic).
Now I have a form that is visible on a specific route (e.g. example.com/thing/create). I want to add some logic to it, e.g. hiding a field based on selected option in the form. It is logic meant for just this form (not to be reused). I prefer not to put all the logic inline with the form but put it in the app.js. I could put it in the Vue instance bound to the body but that sounds odd as it only applies to the form that is much deeper into the dom.
I want to leave the markup of the form in the blade template (that inherits the layout).
I tried creating a component but am not sure how to bind this inside the main Vue instance. What is the best way to handle things for this form, put it in the app.js and have it somewhat structured, putting the variables somewhat into scope. Or is it really necessary to remove the main Vue instance bound to the full layout code?
What I tried was something like this, but it does not work (attaching it to the <form id="object-form"> seems to fail:
var ObjectForm = {
template: function() { return '#object-form'},
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
Things do work if I remove the #app Vue instance or when I put everything directly in the app Vue instance. But that seems messy, if I have similar variables for another form they should be seperated somewhat.
I would appreciate some advice regarding the structure (differentiate page layout and page specific forms) and if possible some example to put the form logic inside the main app.js.
I hope this helps kind of break things down for you and helps you understand Vue templating.
It is best to take advantage of Vue's components. For you it would look something like this. Some of this code depends on your file structure, but you should be able to understand it.
In your app.js file (or your main js file)
Vue.component('myform',require('./components/MyForm.vue'));
const app = new Vue({
el: "#app"
});
Then create the MyForm.vue file
<template>
<form>
Put Your Form Markup Here
</form>
</template>
<script>
// Here is where you would handle the form scripts.
// Use methods, props, data to help set up your component
module.exports = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
},
methods: {
// add methods for dynamically changing form values
}
}
</script>
Then you will be able to just call in your blade file.
<myform></myform>
I found out how to do it. The trick was to use an inline template. Surround the form in the view with:
<object-form inline-template>
<form>...</form>
</object-form>
Where object-form is the name of the component. In the ObjectForm code above I remove the template, like this:
var ObjectForm = {
data: function() {
return {
selectedOption: 1
}
},
computed: {
displayField: function() {
// return true or false depending on form state
return true;
}
}
};
I attach the component within the root vue app like this:
const app = new Vue({
el: 'body',
components: {
'object-form': ObjectForm
}
});
This way I can use the form as it was generated from the controller and view and I can separate it from the root (attached to body) methods and properties.
To organize it even better I can probably store the ObjectForm in a seperate .vue file the way #Tayler Foster suggested.

Create an instance of a React class from a string

I have a string which contains a name of the Class (this is coming from a json file). This string tells my Template Class which layout / template to use for the data (also in json). The issue is my layout is not displaying.
Home.jsx:
//a template or layout.
var Home = React.createClass({
render () {
return (
<div>Home layout</div>
)
}
});
Template.jsx:
var Template = React.createClass({
render: function() {
var Tag = this.props.template; //this is the name of the class eg. 'Home'
return (
<Tag />
);
}
});
I don't get any errors but I also don't see the layout / Home Class. I've checked the props.template and this logs the correct info. Also, I can see the home element in the DOM. However it looks like this:
<div id='template-holder>
<home></home>
</div>
If I change following line to:
var Tag = Home;
//this works but it's not dynamic!
Any ideas, how I can fix this? I'm sure it's either simple fix or I'm doing something stupid. Help would be appreciated. Apologies if this has already been asked (I couldn't find it).
Thanks,
Ewan
This will not work:
var Home = React.createClass({ ... });
var Component = "Home";
React.render(<Component />, ...);
However, this will:
var Home = React.createClass({ ... });
var Component = Home;
React.render(<Component />, ...);
So you simply need to find a way to map between the string "Home" and the component class Home. A simple object will work as a basic registry, and you can build from there if you need more features.
var components = {
"Home": Home,
"Other": OtherComponent
};
var Component = components[this.props.template];
No need to manually map your classes to a dictionary, or "registry", as in Michelle's answer. A wildcard import statement is already a dictionary!
import * as widgets from 'widgets';
const Type = widgets[this.props.template];
...
<Type />
You can make it work with multiple modules by merging all the dictionaries into one:
import * as widgets from 'widgets';
import * as widgets2 from 'widgets2';
const registry = Object.assign({}, widgets, widgets2);
const widget = registry[this.props.template];
I would totally do this to get dynamic dispatch of react components. In fact I think I am in a bunch of projects.
I had the same problem, and found out the solution by myself. I don't know if is the "best pratice" but it works and I'm using it currently in my solution.
You can simply make use of the "evil" eval function to dynamically create an instance of a react component. Something like:
function createComponent(componentName, props, children){
var component = React.createElement(eval(componentName), props, children);
return component;
}
Then, just call it where you want:
var homeComponent = createComponent('Home', [props], [...children]);
If it fits your needs, maybe you can consider something like this.
Hope it helps.
I wanted to know how to create React classes dynamically from a JSON spec loaded from a database and so I did some experimenting and figured it out. My basic idea was that I wanted to define a React app through a GUI instead of typing in code in a text editor.
This is compatible with React 16.3.2. Note React.createClass has been moved into its own module.
Here's condensed version of the essential parts:
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import createReactClass from 'create-react-class'
const spec = {
// getDefaultProps
// getInitialState
// propTypes: { ... }
render () {
return React.createElement('div', null, 'Some text to render')
}
}
const component = createReactClass(spec)
const factory = React.createFactory(component)
const instance = factory({ /* props */ })
const str = ReactDOMServer.renderToStaticMarkup(instance)
console.log(str)
You can see a more complete example here:
https://github.com/brennancheung/02-dynamic-react/blob/master/src/commands/tests/createClass.test.js
Here is the way it will work from a string content without embedding your components as statically linked code into your package, as others have suggested.
import React from 'react';
import { Button } from 'semantic-ui-react';
import createReactClass from 'create-react-class';
export default class Demo extends React.Component {
render() {
const s = "return { render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); } }"
const createComponentSpec = new Function("rce", "components", s);
const componentSpec = createComponentSpec(React.createElement, { "Button": Button });
const component = React.createElement(createReactClass(componentSpec), { propA: "content from property" }, null);
return (
<div>
{component}
</div>
)
}
}
The React class specification is in string s. Note the following:
rce stands for React.createElement and given as a first param when callingcreateComponentSpec.
components is a dictionary of extra component types and given as a second param when callingcreateComponentSpec. This is done so that you can provide components with clashing names.
For example string Button can be resolved to standard HTML button, or button from Semantic UI.
You can easily generate content for s by using https://babeljs.io as described in https://reactjs.org/docs/react-without-jsx.html. Essentially, the string can't contain JSX stuff, and has to be plain JavaScript. That's what BabelJS is doing by translating JSX into JavaScript.
All you need to do is replace React.createElement with rce, and resolve external components via components dictionary (if you don't use external components, that you can skip the dictionary stuff).
Here is equivalent what in the code above. The same <div> with two Semantic UI Buttons in it.
JSX render() code:
function render() {
return (
<div>
<Button content={this.props.propA}/>
<Button content='hardcoded content'/>
</div>
);
}
BabelJS translates it into:
function render() {
return React.createElement("div", null, React.createElement(Button, {
content: this.props.propA
}), React.createElement(Button, {
content: "hardcoded content"
}));
}
And you do replacement as outlined above:
render() { return rce('div', null, rce(components['Button'], {content: this.props.propA}), rce(components['Button'], {content: 'hardcoded content'})); }
Calling createComponentSpec function will create a spec for React class.
Which then converted into actual React class with createReactClass.
And then brought to life with React.createElement.
All you need to do is return it from main component render func.
When you use JSX you can either render HTML tags (strings) or React components (classes).
When you do var Tag = Home, it works because the JSX compiler transforms it to:
var Template = React.createElement(Tag, {});
with the variable Tag in the same scope and being a React class.
var Tag = Home = React.createClass({
render () {
return (
<div>Home layout</div>
)
}
});
When you do
var Tag = this.props.template; // example: Tag = "aClassName"
you are doing
var Template = React.createElement("aClassName", null);
But "aClassName" is not a valid HTML tag.
Look here

karma-runner: load scripts (and CSS) via DOM

I want to inject some CSS and JavaScript files via a preprocessor.
In my preprocessor I inject the html template to the body element.
I printed the result out via console.log(document.body) - you can see the result at the bottom. It looks good, but the script is not evaluated.
If I run console.log(window.foobar) in my test, it's undefined.
Actually I don't want to to inject simple scripts, I want to load some files via
<script src="build/app.js"></script>
I need it in every test, so I don't want to refactor every single test for the same code injection, that's the reason why I tried to put it into the html generated by karma.
<body><script> window.foobar = 'miau!';</script>
<!-- The scripts need to be at the end of body, so that some test running frameworks
(Angular Scenario, for example) need the body to be loaded so that it can insert its magic
into it. If it is before body, then it fails to find the body and crashes and burns in an epic
manner. -->
<script type="text/javascript">
// sets window.__karma__ and overrides console and error handling
// Use window.opener if this was opened by someone else - in a new window
if (window.opener) {
window.opener.karma.setupContext(window);
} else {
window.parent.karma.setupContext(window);
}
// All served files with the latest timestamps
window.__karma__.files = {
'/base/node_modules/mocha/mocha.js': '253e2fdce43a4b2eed46eb25139b784adbb5c47f',
'/base/node_modules/karma-mocha/lib/adapter.js': '3664759c75e6f4e496fef20ad115ce8233a0f7b5',
'/base/test/custom-test.js': 'abf5b0b3f4dbb62653c816b264a251c7fc264fb9',
'/base/test/build/build.css': 'df7e943e50164a1fc4b66e0a0c46fc86efdef656',
'/base/test/build/build.js': '9f0a39709e073846c73481453cdee8d37e528856',
'/base/test/build/test.js': '0ccd4711b9c887458f81cf1dedc04c6ed59abe43'
};
</script>
<!-- Dynamically replaced with <script> tags -->
<script type="text/javascript" src="/base/node_modules/mocha/mocha.js?253e2fdce43a4b2eed46eb25139b784adbb5c47f"></script>
<script type="text/javascript" src="/base/node_modules/karma-mocha/lib/adapter.js?3664759c75e6f4e496fef20ad115ce8233a0f7b5"></script>
<script type="text/javascript" src="/base/test/custom-test.js?abf5b0b3f4dbb62653c816b264a251c7fc264fb9"></script></body>
Karma introduces the page scripts/html just like ajax, so it wont execute once the append has finished.
You will need to append the files for each spec. I have a helper for this job:
function appendCSS(path){
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href='base/' + path;
document.body.appendChild(link)
}
function appendScript(path){
var link = document.createElement('script');
link.type = 'javascript';
link.src='base/' + path;
document.body.appendChild(link)
}
function loadAssets(page){
document.body.innerHTML = __html__['_site/' + (page || 'index') + '.html'];
appendCSS('_site/styles/demo.css');
appendCSS('_site/styles/' + page + '.css');
appendScript('_site/scripts/vendor.js');
appendScript('_site/scripts/' + page + '.js');
}
module.exports = {
loadAssets: loadAssets
};
In my spec i then simply call the helper, passing the name of the html page to be tested.
require('../helper').loadAssets('tested-page-name');
As you can see, i use the borwserify plugin, but i hope this helps.

Writing script src dynamically via wicket

I want my page to load javascript dynamically to my body:
<script type= "text/javascript" src="this path should be decided from wicket dynamically"/>
I am using wicket version 1.4 therefore JavaScriptResourceReference does not exist in my version (for my inspection it wasn't ' )
how can I solve this ?
thanks in advance :).
I specify my comment into an answer.
You can use this code snippet:
WebMarkupContainer scriptContainer = new WebMarkupContainer("scriptContainer ");
scriptContainer .add(new AttributeAppender("type", Model.of("text/javascript")));
scriptContainer .add(
new AttributeAppender("src", urlFor(
new JavaScriptResourceReference(
YourClass.class, "JavaScriptFile.js"), null).toString()));
add(scriptContainer );
and the corresponding html:
<script wicket:id="scriptContainer "></script>
Just change the string JavaScriptFile.js to load any other Javascript file.
JavascriptPackageResource.getHeaderContributor() does exactly what you need.
You need nothing in your markup, just add the HeaderContributor it returns to your page.
Update: For Wicket 1.5 see the migration guide, but it goes like this:
public class MyPage extends WebPage {
public MyPage() {
}
public void renderHead(IHeaderResponse response) {
response.renderJavaScriptReference(new PackageResourceReference(YuiLib.class,
"yahoo-dom-event/yahoo-dom-event.js"));
response.renderCSSReference(new PackageResourceReference(AbstractCalendar.class,
"assets/skins/sam/calendar.css"));
}
}
If you want to put your <script> element in the body, you can simply declare it as a WebMarkupContainer and add an AttributeModifier to set the src attribute. Although in that case wicket won't generate the relative URLs for you, you have to do it yourself.
I'm not sure I understood completely.
If you are trying to create and append a script to the body after the page is loaded you should do it this way:
<script type="text/javascript">
function load_js() {
var element = document.createElement("script");
element.src = "scripts/YOUR_SCRIPT_SRC.js"; // <---- HERE <-----
document.body.appendChild(element);
}
// Wait for the page to be loaded
if(window.addEventListener)
window.addEventListener("load",load_js,false);
else if(window.attachEvent)
window.attachEvent("onload",load_js);
else
window.onload = load_js;
</script>
What I did here is create a new script element, and then apply to it its source.
That way you can control dynamicaly the src. After that I append it to the body.
The last part is there so the new element is applied only after the page is loaded.