Skip to content

Commit

Permalink
riot integration
Browse files Browse the repository at this point in the history
  • Loading branch information
libetl committed Aug 26, 2018
1 parent b71117d commit 42c6070
Show file tree
Hide file tree
Showing 51 changed files with 1,428 additions and 13 deletions.
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
- examples/vue-kitchen-sink/node_modules
- examples/svelte-kitchen-sink/node_modules
- examples/marko-cli/node_modules
- examples/riot-kitchen-sink/node_modules
- addons
- app
- lib
Expand Down Expand Up @@ -96,6 +97,11 @@ jobs:
command: |
cd examples/mithril-kitchen-sink
yarn build-storybook
- run:
name: Build riot kitchen-sink
command: |
cd examples/riot-kitchen-sink
yarn build-storybook
- run:
name: Run image snapshots
command: yarn test --image
Expand Down Expand Up @@ -148,6 +154,11 @@ jobs:
command: |
cd examples/mithril-kitchen-sink
yarn storybook --smoke-test
- run:
name: Run riot kitchen-sink (smoke test)
command: |
cd examples/riot-kitchen-sink
yarn storybook --smoke-test
react-native:
<<: *defaults
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ enum class StorybookApp(val appName: String, val exampleDir: String, val merged:
HTML("HTML", "html-kitchen-sink"),
MARKO("Marko", "marko-cli"),
HYPERAPP("Hyperapp", "hyperapp-kitchen-sink", false),
SVELTE("Svelte", "svelte-kitchen-sink");
SVELTE("Svelte", "svelte-kitchen-sink"),
RIOT("Riot", "riot-kitchen-sink");

val lowerName = appName.toLowerCase()

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ For additional help, join us [in our Slack](https://now-examples-slackin-rrirkqo
- [Marko](app/marko) <sup>alpha</sup> [![Marko](https://img.shields.io/npm/dt/@storybook/marko.svg)](app/marko)
- [HTML](app/html) <sup>alpha</sup> [![HTML](https://img.shields.io/npm/dt/@storybook/html.svg)](app/html)
- [Svelte](app/svelte) <sup>alpha</sup> [![Svelte](https://img.shields.io/npm/dt/@storybook/svelte.svg)](app/svelte)
- [Riot](app/riot) <sup>alpha</sup> [![Riot](https://img.shields.io/npm/dt/@storybook/riot.svg)](app/riot)

### Sub Projects

Expand Down
2 changes: 2 additions & 0 deletions app/riot/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src
.babelrc
26 changes: 26 additions & 0 deletions app/riot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Storybook for Riot <sup>alpha</sup>

* * *

Storybook for Riot is a UI development environment for the components written with riot.js.
With it, you can visualize different states of your UI components and develop them interactively.

![Storybook Screenshot](https://github.com/storybooks/storybook/blob/master/media/storybook-intro.gif)

Storybook runs outside of your app.
So you can develop UI components in isolation without worrying about app specific dependencies and requirements.

## Getting Started

```sh
npm i -g @storybook/cli
cd my-app
getstorybook --html
```

For more information visit: [storybook.js.org](https://storybook.js.org)

* * *

Storybook also comes with a lot of [addons](https://storybook.js.org/addons/introduction) and a great API to customize as you wish.
You can also build a [static version](https://storybook.js.org/basics/exporting-storybook) of your storybook and deploy it anywhere you want.
4 changes: 4 additions & 0 deletions app/riot/bin/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

process.env.NODE_ENV = process.env.NODE_ENV || 'production';
require('../dist/server/build');
3 changes: 3 additions & 0 deletions app/riot/bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

require('../dist/server');
49 changes: 49 additions & 0 deletions app/riot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@storybook/riot",
"version": "4.0.0-alpha.18",
"description": "Storybook for riot.js: View riot snippets in isolation with Hot Reloading.",
"homepage": "https://github.com/storybooks/storybook/tree/master/app/riot",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybooks/storybook.git"
},
"license": "MIT",
"main": "dist/client/index.js",
"bin": {
"build-storybook": "./bin/build.js",
"start-storybook": "./bin/index.js",
"storybook-server": "./bin/index.js"
},
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@babel/runtime": "^7.0.0-rc.2",
"@storybook/core": "4.0.0-alpha.18",
"common-tags": "^1.8.0",
"global": "^4.3.2",
"moment": "^2.22.2",
"riot": "^3.11.1",
"riot-hot-reload": "^1.0.0",
"riot-tag-loader": "^2.1.0",
"raw-loader": "^0.5.1",
"riot-compiler": "^3.5.1",
"react": "^16.4.2",
"react-dom": "^16.4.2"
},
"devDependencies": {
"cross-env": "^5.2.0",
"@babel/preset-flow": "^7.0.0-rc.2",
"@babel/preset-react": "^7.0.0-rc.2",
"@babel/runtime": "^7.0.0-rc.2",
"@babel/preset-env": "^7.0.0-rc.2",
"@babel/plugin-transform-runtime": "^7.0.0-rc.2",
"@babel/plugin-transform-modules-commonjs": "^7.0.0-rc.2"
},
"peerDependencies": {
"babel-core": "^7.0.0 || ^8.0.0 || ^8.0.0-beta.6"
}
}
9 changes: 9 additions & 0 deletions app/riot/src/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {
storiesOf,
setAddon,
addDecorator,
addParameters,
configure,
getStorybook,
forceReRender,
} from './preview';
3 changes: 3 additions & 0 deletions app/riot/src/client/preview/globals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { window } from 'global';

window.STORYBOOK_ENV = 'riot';
18 changes: 18 additions & 0 deletions app/riot/src/client/preview/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { start } from '@storybook/core/client';

import './globals';
import render from './render';

const { clientApi, configApi, forceReRender } = start(render);

export const {
storiesOf,
setAddon,
addDecorator,
addParameters,
clearDecorators,
getStorybook,
} = clientApi;

export const { configure } = configApi;
export { forceReRender };
72 changes: 72 additions & 0 deletions app/riot/src/client/preview/render-riot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { document } from 'global';

export function guessRootName(stringified) {
const whiteSpaceLocation = stringified.indexOf(' ', stringified.indexOf('<') + 1);
const firstWhitespace = whiteSpaceLocation === -1 ? stringified.length : whiteSpaceLocation;
const supposedName = stringified.trim().match(/^<[^ >]+\/>$/)
? stringified.trim().replace(/[<>/]/g, '')
: stringified.substring(
stringified.indexOf('<') + 1,
Math.min(firstWhitespace, stringified.indexOf('>'))
);
const matchingBuiltInTag = document.createElement(supposedName).constructor.name;
return matchingBuiltInTag === 'HTMLUnknownElement' ? supposedName : 'root';
}

export function renderStringified(
{ tags, scenario = `<${(tags[0] || []).boundAs || guessRootName(tags[0] || '')}/>` },
{ unregister, tag2, mount, compiler } // eslint-disable-line no-unused-vars
) {
unregister('root');
tags.forEach(oneTag => {
const rootName = oneTag.boundAs || guessRootName(oneTag);
const { content } = oneTag || {};
const sourceCodeWithoutRoot = content ? content.trim() : oneTag;
const sourceCode =
rootName === 'root' ? `<root>${sourceCodeWithoutRoot}</root>` : sourceCodeWithoutRoot;
const compiled = compiler
.compile(sourceCode, {})
.replace("var riot = require('riot')", '')
.trim();
unregister(rootName);
eval(`${compiled}`.replace(/riot\.tag2/g, 'tag2')); // eslint-disable-line no-eval
});
const sourceCode = `<root>${scenario}</root>`;
if (scenario !== '<root/>')
eval(`${compiler.compile(sourceCode, {})}`.replace(/riot\.tag2/g, 'tag2')); // eslint-disable-line no-eval
mount('*');
}

// eslint-disable-next-line no-unused-vars
export function renderRaw(sourceCode, { unregister, mount, compiler, tag2 }) {
unregister('root');
// eslint-disable-next-line no-eval
eval(
`${compiler.compile(sourceCode.replace("var riot = require('riot')", '').trim(), {})}`.replace(
/riot.tag2/g,
'tag2'
)
);
mount('root', /riot\.tag2\s*\(\s*'([^']+)'/.exec(sourceCode)[1], {});
}

export function renderCompiledObject(component, { rootElement }) {
if (component.length) rootElement.appendChild(component[0].__.root); // eslint-disable-line no-underscore-dangle
}

export function render(component, context) {
const { tags } = component || {};
if (typeof component === 'string') {
renderRaw(component, context);
return true;
}
if (Array.isArray(tags)) {
renderStringified(component, context);
return true;
}
if (component && Array.isArray(component)) {
renderCompiledObject(component, context);
return true;
}
return false;
}
137 changes: 137 additions & 0 deletions app/riot/src/client/preview/render-riot.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { document } from 'global';
import { unregister, tag2, mount } from 'riot/riot';
import compiler from 'riot-compiler';
import { render } from './render-riot';

document.body = document.createElement('body');
const rootElement = document.body.appendChild(document.createElement('root'));

const context = {
unregister,
rootElement,
compiler,
tag2,
mount,
};

beforeEach(() => {
rootElement.innerHTML = '';
unregister('root');
});

describe('render a riot element', () => {
it('should not work with nothing', () => {
expect(render(null, context)).toBe(false);

expect(rootElement.innerHTML).toEqual('');
});

it('can work with some text', () => {
expect(render({ tags: ['<div><p>some tests</p></div>'] }, context)).toBe(true);

expect(rootElement.innerHTML).toEqual('<div><p>some tests</p></div>');
});

it('can work with raw code', () => {
expect(render("riot.tag2('root', '<div>raw code</div>', '', '', () => {})", context)).toBe(
true
);

expect(rootElement.innerHTML).toEqual('<div>raw code</div>');
});

it('can work with compiled code', () => {
expect(render([], context)).toBe(true);
// does only work in true mode, and not in jest mode
});

it('can nest several tags', () => {
expect(
render(
{
tags: [
'<Tag1><div>Inside tag1:<ul><li><Tag2><yield/></Tag2></li></ul></div></Tag1>',
'<Tag2><div>Inside tag2:<ul><li><Tag3><yield/></Tag3></li></ul></div></Tag2>',
'<Tag3><div>Inside tag3:<ul><li><Tag4><yield/></Tag4></li></ul></div></Tag3>',
'<Tag4><div>Inside tag4:<ul><li><Tag5><yield/></Tag5></li></ul></div></Tag4>',
'<Tag5><div>Inside tag5:<ul><li><yield/></li></ul></div></Tag5>',
],
scenario: '<Matriochka><div><Tag1>Content</Tag1></div></Matriochka>',
},
context
)
).toBe(true);

expect(rootElement.innerHTML).toEqual(
`
<matriochka>
<div>
<tag1>
<div>Inside tag1:
<ul>
<li>
<tag2>
<div>Inside tag2:
<ul>
<li>
<tag3>
<div>Inside tag3:
<ul>
<li>
<tag4>
<div>Inside tag4:
<ul>
<li>
<tag5>
<div>Inside tag5:
<ul>
<li>Content</li>
</ul>
</div>
</tag5>
</li>
</ul>
</div>
</tag4>
</li>
</ul>
</div>
</tag3>
</li>
</ul>
</div>
</tag2>
</li>
</ul>
</div>
</tag1>
</div>
</matriochka>`
.trim()
.replace(/\n\s*</g, '<')
);
});

it('can template some vars', () => {
expect(
render(
{
tags: [
{
content:
"<SimpleTest><div>simple test ({opts.test || 'without parameter'}). Oh, by the way ({opts.riotValue || '... well, nothing'})</div></SimpleTest>",
boundAs: 'mustBeUniquePlease',
},
],
scenario:
'<SimpleTest test={ "with a parameter" } value={"value is mapped to riotValue"}></SimpleTest>',
},
context
)
).toBe(true);

expect(rootElement.innerHTML).toEqual(
'<simpletest test="with a parameter"><div>simple test (with a parameter). Oh, by the way (value is mapped to riotValue)</div></simpletest>'
);
});
});

0 comments on commit 42c6070

Please sign in to comment.