diff --git a/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap b/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap
index 4eabc982f4a..6e5c6334281 100644
--- a/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap
+++ b/app/riot/src/client/preview/__snapshots__/render-riot.test.js.snap
@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`render a riot element can accept a constructor 1`] = `"HACKED : true ; simple test (with a parameter). Oh, by the way (value is mapped to riotValue)
"`;
+
exports[`render a riot element can nest several tags 1`] = `""`;
exports[`render a riot element can template some vars 1`] = `"simple test (with a parameter). Oh, by the way (value is mapped to riotValue)
"`;
diff --git a/app/riot/src/client/preview/compileNow.js b/app/riot/src/client/preview/compileNow.js
deleted file mode 100644
index c4cc78767c7..00000000000
--- a/app/riot/src/client/preview/compileNow.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import compiler from 'riot-compiler';
-
-export function asCompiledCode(text) {
- return compiler
- .compile(text, {})
- .replace('var riot = require("riot")', '')
- .replace('riot.tag2(', 'tag2(');
-}
-
-export function compileNow(tag2, text) {
- // eslint-disable-next-line no-eval
- return tag2 && eval(asCompiledCode(text));
-}
diff --git a/app/riot/src/client/preview/compileStageFunctions.js b/app/riot/src/client/preview/compileStageFunctions.js
new file mode 100644
index 00000000000..8c80595f185
--- /dev/null
+++ b/app/riot/src/client/preview/compileStageFunctions.js
@@ -0,0 +1,23 @@
+import compiler from 'riot-compiler';
+
+export const alreadyCompiledMarker = "var riot = require('riot')";
+
+export function asCompiledCode(text) {
+ return compiler
+ .compile(text, {})
+ .replace('var riot = require("riot")', '')
+ .replace('riot.tag2(', 'tag2(');
+}
+
+export function compileNow(tag2, text) {
+ // eslint-disable-next-line no-eval
+ return tag2 && eval(asCompiledCode(text));
+}
+
+export function getRidOfRiotNoise(compiled) {
+ return compiled.replace(/riot\.tag2/g, 'tag2').replace(alreadyCompiledMarker, '');
+}
+
+export function setConstructor(compiledSourceCode, constructor) {
+ return compiledSourceCode.replace(/function\(opts\)\s*{\s*}(?=\);$)/, constructor.toString());
+}
diff --git a/app/riot/src/client/preview/index.js b/app/riot/src/client/preview/index.js
index 1d952af2a1b..871e83da15b 100644
--- a/app/riot/src/client/preview/index.js
+++ b/app/riot/src/client/preview/index.js
@@ -3,7 +3,7 @@ import { start } from '@storybook/core/client';
import './globals';
import riot, { tag2, mount as vendorMount } from 'riot';
import render from './render';
-import { compileNow as unboundCompileNow, asCompiledCode } from './compileNow';
+import { compileNow as unboundCompileNow, asCompiledCode } from './compileStageFunctions';
const { clientApi, configApi, forceReRender } = start(render);
diff --git a/app/riot/src/client/preview/render-riot.test.js b/app/riot/src/client/preview/render-riot.test.js
index b5c387898ed..9ea55fad075 100644
--- a/app/riot/src/client/preview/render-riot.test.js
+++ b/app/riot/src/client/preview/render-riot.test.js
@@ -1,7 +1,8 @@
import { document } from 'global';
import { unregister, tag2, mount } from 'riot';
import compiler from 'riot-compiler';
-import { render } from './render-riot';
+import { render } from './rendering';
+import { asCompiledCode } from './compileStageFunctions';
const rootElement = document.createElement('div');
rootElement.id = 'root';
@@ -46,6 +47,14 @@ describe('render a riot element', () => {
// does only work in true mode, and not in jest mode
});
+ it('will be possible to compile a template before rendering it', () => {
+ const compiledTemplate = asCompiledCode('raw code
');
+
+ expect(compiledTemplate).toEqual(
+ "tag2('template', '
raw code
', '', '', function(opts) {\n});"
+ );
+ });
+
it('works with a json consisting in a tagName and opts', () => {
tag2('hello', 'Hello { opts.suffix }
', '', '', () => {});
@@ -94,4 +103,28 @@ describe('render a riot element', () => {
expect(rootElement.innerHTML).toMatchSnapshot();
});
+
+ it('can accept a constructor', () => {
+ expect(
+ render(
+ {
+ tags: [
+ {
+ content:
+ "HACKED : {opts.hacked} ; simple test ({opts.test || 'without parameter'}). Oh, by the way ({opts.riotValue || '... well, nothing'})
",
+ boundAs: 'mustBeUniquePlease',
+ },
+ ],
+ template:
+ '',
+ tagConstructor() {
+ this.hacked = true;
+ },
+ },
+ context
+ )
+ ).toBe(true);
+
+ expect(rootElement.innerHTML).toMatchSnapshot();
+ });
});
diff --git a/app/riot/src/client/preview/render.js b/app/riot/src/client/preview/render.js
index d352114b09a..e332210d03c 100644
--- a/app/riot/src/client/preview/render.js
+++ b/app/riot/src/client/preview/render.js
@@ -1,7 +1,7 @@
import { document } from 'global';
import { stripIndents } from 'common-tags';
import { unregister } from 'riot';
-import { render as renderRiot } from './render-riot';
+import { render as renderRiot } from './rendering';
export default function renderMain({
story,
diff --git a/app/riot/src/client/preview/rendering/compiledButUnmounted.js b/app/riot/src/client/preview/rendering/compiledButUnmounted.js
new file mode 100644
index 00000000000..5a8d92e47be
--- /dev/null
+++ b/app/riot/src/client/preview/rendering/compiledButUnmounted.js
@@ -0,0 +1,5 @@
+import { mount } from 'riot';
+
+export default function renderCompiledButUnmounted(component) {
+ mount('root', component.tagName, component.opts || {});
+}
diff --git a/app/riot/src/client/preview/rendering/index.js b/app/riot/src/client/preview/rendering/index.js
new file mode 100644
index 00000000000..752053ba02e
--- /dev/null
+++ b/app/riot/src/client/preview/rendering/index.js
@@ -0,0 +1,24 @@
+import renderCompiledButUnmounted from './compiledButUnmounted';
+import renderStringified from './stringified';
+import renderRaw from './raw';
+
+export function render(component) {
+ if (typeof component === 'string') {
+ renderRaw(component);
+ return true;
+ }
+ const { tags } = component || {};
+ if (Array.isArray(tags)) {
+ renderStringified(component);
+ return true;
+ }
+ if (component && component.tagName) {
+ renderCompiledButUnmounted(component);
+ return true;
+ }
+ if (component && component.length) {
+ // already rendered, nothing to do
+ return true;
+ }
+ return false;
+}
diff --git a/app/riot/src/client/preview/rendering/raw.js b/app/riot/src/client/preview/rendering/raw.js
new file mode 100644
index 00000000000..fd3d8155b31
--- /dev/null
+++ b/app/riot/src/client/preview/rendering/raw.js
@@ -0,0 +1,14 @@
+import { mount, tag2 as tag } from 'riot';
+import compiler from 'riot-compiler';
+import { alreadyCompiledMarker, getRidOfRiotNoise } from '../compileStageFunctions';
+
+export default function renderRaw(sourceCode) {
+ const tag2 = tag; // eslint-disable-line no-unused-vars
+ // eslint-disable-next-line no-eval
+ eval(
+ getRidOfRiotNoise(
+ `${compiler.compile(sourceCode.replace(alreadyCompiledMarker, '').trim(), {})}`
+ )
+ );
+ mount('root', /tag2\s*\(\s*'([^']+)'/.exec(sourceCode)[1], {});
+}
diff --git a/app/riot/src/client/preview/render-riot.js b/app/riot/src/client/preview/rendering/stringified.js
similarity index 60%
rename from app/riot/src/client/preview/render-riot.js
rename to app/riot/src/client/preview/rendering/stringified.js
index 890bb7f5611..bccfa836538 100644
--- a/app/riot/src/client/preview/render-riot.js
+++ b/app/riot/src/client/preview/rendering/stringified.js
@@ -1,8 +1,7 @@
-import { document } from 'global';
import { mount, unregister, tag2 as tag } from 'riot';
import compiler from 'riot-compiler';
-
-const alreadyCompiledMarker = "var riot = require('riot')";
+import { document } from 'global';
+import { alreadyCompiledMarker, getRidOfRiotNoise, setConstructor } from '../compileStageFunctions';
function guessRootName(stringified) {
const whiteSpaceLocation = stringified.indexOf(' ', stringified.indexOf('<') + 1);
@@ -31,12 +30,10 @@ function compileText(code, rootName) {
.trim();
}
-const getRidOfRiotNoise = compiled =>
- compiled.replace(/riot\.tag2/g, 'tag2').replace(alreadyCompiledMarker, '');
-
-function renderStringified({
+export default function renderStringified({
tags,
template = `<${(tags[0] || []).boundAs || guessRootName(tags[0] || '')}/>`,
+ tagConstructor,
}) {
const tag2 = tag; // eslint-disable-line no-unused-vars
tags.forEach(oneTag => {
@@ -49,43 +46,11 @@ function renderStringified({
eval(getRidOfRiotNoise(`${compiled}`)); // eslint-disable-line no-eval
});
const sourceCode = `${template}`;
- if (template !== '') eval(getRidOfRiotNoise(`${compiler.compile(sourceCode, {})}`)); // eslint-disable-line no-eval
+ const compiledRootSource = !tagConstructor
+ ? `${compiler.compile(sourceCode, {})}`
+ : setConstructor(`${compiler.compile(sourceCode, {})}`, tagConstructor);
+
+ if (template !== '') eval(getRidOfRiotNoise(compiledRootSource)); // eslint-disable-line no-eval
mount('*');
}
-
-function renderRaw(sourceCode) {
- const tag2 = tag; // eslint-disable-line no-unused-vars
- // eslint-disable-next-line no-eval
- eval(
- getRidOfRiotNoise(
- `${compiler.compile(sourceCode.replace(alreadyCompiledMarker, '').trim(), {})}`
- )
- );
- mount('root', /tag2\s*\(\s*'([^']+)'/.exec(sourceCode)[1], {});
-}
-
-function renderCompiledButUnmounted(component) {
- mount('root', component.tagName, component.opts || {});
-}
-
-export function render(component) {
- if (typeof component === 'string') {
- renderRaw(component);
- return true;
- }
- const { tags } = component || {};
- if (Array.isArray(tags)) {
- renderStringified(component);
- return true;
- }
- if (component && component.tagName) {
- renderCompiledButUnmounted(component);
- return true;
- }
- if (component && component.length) {
- // already rendered, nothing to do
- return true;
- }
- return false;
-}
diff --git a/examples/riot-kitchen-sink/src/stories/__snapshots__/story-code.stories.storyshot b/examples/riot-kitchen-sink/src/stories/__snapshots__/story-code.stories.storyshot
index 9eeb8dfb524..94cdcf3a719 100644
--- a/examples/riot-kitchen-sink/src/stories/__snapshots__/story-code.stories.storyshot
+++ b/examples/riot-kitchen-sink/src/stories/__snapshots__/story-code.stories.storyshot
@@ -61,6 +61,22 @@ exports[`Storyshots Story|How to create a story built with tag 1`] = `
`;
+exports[`Storyshots Story|How to create a story tags, template and tagConstructor at once 1`] = `
+
+
+
+ HACKED : true ; simple test (with a parameter). Oh, by the way (value is mapped to riotValue)
+
+
+
+`;
+
exports[`Storyshots Story|How to create a story the mount instruction is not necessary 1`] = `
({
+ tags: [
+ {
+ content:
+ "
HACKED : {opts.hacked} ; simple test ({opts.test || 'without parameter'}). Oh, by the way ({opts.riotValue || '... well, nothing'})
",
+ boundAs: 'mustBeUniquePlease',
+ },
+ ],
+ template:
+ '
',
+ tagConstructor() {
+ this.hacked = true;
+ },
+ }))
+
.add('built from the precompilation', () => mount('anothertest', {}), {
notes: 'WARN, only works in lower case, never upper case with precompiled templates',
})