Skip to content

Commit

Permalink
Added support for require.context similar to storyshots 2.x versions.
Browse files Browse the repository at this point in the history
Also added sample stories for require.context and normal require use cases.
  • Loading branch information
Aruna Herath committed Dec 23, 2016
1 parent b03e6da commit d7f8853
Show file tree
Hide file tree
Showing 13 changed files with 470 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"presets": [
"es2015"
"es2015", "react"
],
"plugins": [
"transform-runtime"
Expand Down
10 changes: 10 additions & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { configure } from '@kadira/storybook';

const req = require.context('../stories/required_with_context', true, /.stories.js$/)

function loadStories() {
req.keys().forEach((filename) => req(filename))
require('../stories/directly_required')
}

configure(loadStories, module);
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@
"scripts": {
"prepublish": "babel ./src --out-dir ./dist",
"lint": "standard",
"test": "npm run lint"
"test": "npm run lint",
"jest": "jest",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"devDependencies": {
"@kadira/storybook": "^2.21.0",
"babel-cli": "^6.14.0",
"babel-jest": "^18.0.0",
"babel-plugin-transform-runtime": "^6.15.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"jest": "^18.0.0",
"standard": "^8.6.0"
},
"dependencies": {
"react": "^15.4.1",
"babel-runtime": "^6.20.0",
"react-test-renderer": "^15.3.1",
"read-pkg-up": "^2.0.0"
Expand Down
21 changes: 14 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import renderer from 'react-test-renderer'
import path from 'path'
import readPkgUp from 'read-pkg-up'

import runWithRequireContext from './require_context'
const { describe, it, expect } = global

let storybook
let configPath

const babel = require('babel-core')

const pkg = readPkgUp.sync().pkg
const isStorybook =
(pkg.devDependencies && pkg.devDependencies['@kadira/storybook']) ||
Expand All @@ -18,21 +20,26 @@ const isRNStorybook =
export default function testStorySnapshots (options = {}) {
if (isStorybook) {
storybook = require.requireActual('@kadira/storybook')
const loadBabelConfig = require('@kadira/storybook/dist/server/babel_config').default
const configDirPath = path.resolve(options.configPath || '.storybook')
configPath = path.join(configDirPath, 'config.js')

const content = babel.transformFileSync(configPath, babelConfig).code
const contextOpts = {
filename: configPath,
dirname: configDirPath
}
const babelConfig = loadBabelConfig(configDirPath)

runWithRequireContext(content, contextOpts)
} else if (isRNStorybook) {
storybook = require.requireActual('@kadira/react-native-storybook')
configPath = path.resolve(options.configPath || 'storybook')
require.requireActual(configPath)
} else {
throw new Error('\'storyshots\' is intended only to be used with react storybook or react native storybook')
}

try {
require.requireActual(configPath)
} catch (e) {
throw new Error(`Could not load stories from ${configPath}. Check 'configPath' option`)
}

if (typeof describe !== 'function') {
throw new Error('\'testStorySnapshots\' is intended only to be used inside jest')
}
Expand Down
72 changes: 72 additions & 0 deletions src/require_context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import vm from 'vm'
import fs from 'fs'
import path from 'path'
import moduleSystem from 'module'

function requireModules (keys, root, directory, regExp, recursive) {
const files = fs.readdirSync(path.join(root, directory))

files.forEach((filename) => {
// webpack adds a './' to the begining of the key
// TODO: Check this in windows
const entryKey = `./${path.join(directory, filename)}`
if (regExp.test(entryKey)) {
// eslint-disable-next-line no-param-reassign, global-require, import/no-dynamic-require
keys[entryKey] = require(path.join(root, directory, filename))
return
}

if (!recursive) {
return
}

if (fs.statSync(path.join(root, directory, filename)).isDirectory()) {
requireModules(keys, root, path.join(directory, filename), regExp, recursive)
}
})
}

function isRelativeRequest (request) {
if (request.charCodeAt(0) !== 46/* . */) {
return false
}

if (request === '.' || '..') {
return true
}

return request.charCodeAt(1) === 47/* / */ || (
request.charCodeAt(1) === 46/* . */ && request.charCodeAt(2) === 47/* / */)
}

export default function runWithRequireContext (content, options) {
const { filename, dirname } = options

const newRequire = (request) => {
if (isRelativeRequest(request)) {
// eslint-disable-next-line global-require, import/no-dynamic-require
return require(path.resolve(dirname, request))
}

// eslint-disable-next-line global-require, import/no-dynamic-require
return require(request)
}

newRequire.resolve = require.resolve
newRequire.extensions = require.extensions
newRequire.main = require.main
newRequire.cache = require.cache

newRequire.context = (directory, useSubdirectories = false, regExp = /^\.\//) => {
const fullPath = path.resolve(dirname, directory)
const keys = {}
requireModules(keys, fullPath, '.', regExp, useSubdirectories)

const req = f => (keys[f])
req.keys = () => (Object.keys(keys))
return req
}

const compiledModule = vm.runInThisContext(moduleSystem.wrap(content))
compiledModule(module.exports, newRequire, module, filename, dirname)
}
200 changes: 200 additions & 0 deletions stories/__test__/__snapshots__/storyshots.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
exports[`Storyshots Another Button with some emoji 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Another Button with text 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
Hello Button
</button>
`;

exports[`Storyshots Button with some emoji 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
😀 😎 👍 💯
</button>
`;

exports[`Storyshots Button with text 1`] = `
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "#FFFFFF",
"border": "1px solid #eee",
"borderRadius": 3,
"cursor": "pointer",
"fontSize": 15,
"margin": 10,
"padding": "3px 10px",
}
}>
Hello Button
</button>
`;

exports[`Storyshots Welcome to Storybook 1`] = `
<div
style={
Object {
"fontFamily": "\"Helvetica Neue\", Helvetica, \"Segoe UI\", Arial, freesans, sans-serif",
"lineHeight": 1.4,
"margin": 15,
"maxWidth": 600,
}
}>
<h1>
Welcome to STORYBOOK
</h1>
<p>
This is a UI component dev environment for your app.
</p>
<p>
We\'ve added some basic stories inside the
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
src/stories
</code>
directory.
<br />
A story is a single state of one or more UI components. You can have as many stories as you want.
<br />
(Basically a story is like a visual test case.)
</p>
<p>
See these sample
<a
href="#"
onClick={[Function]}
style={
Object {
"borderBottom": "1px solid #1474f3",
"color": "#1474f3",
"paddingBottom": 2,
"textDecoration": "none",
}
}>
stories
</a>
for a component called
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
Button
</code>
.
</p>
<p>
Just like that, you can add your own components as stories.
<br />
You can also edit those components and see changes right away.
<br />
(Try editing the
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
Button
</code>
component located at
<code
style={
Object {
"backgroundColor": "#f3f2f2",
"border": "1px solid #eae9e9",
"borderRadius": 4,
"color": "#3a3a3a",
"fontSize": 15,
"fontWeight": 600,
"padding": "2px 5px",
}
}>
src/stories/Button.js
</code>
.)
</p>
<p>
This is just one thing you can do with Storybook.
<br />
Have a look at the
<a
href="https://github.com/kadirahq/react-storybook"
style={
Object {
"borderBottom": "1px solid #1474f3",
"color": "#1474f3",
"paddingBottom": 2,
"textDecoration": "none",
}
}
target="_blank">
React Storybook
</a>
repo for more information.
</p>
</div>
`;
2 changes: 2 additions & 0 deletions stories/__test__/storyshots.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import initStoryshots from '../../src'
initStoryshots()
27 changes: 27 additions & 0 deletions stories/directly_required/Button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'

const buttonStyles = {
border: '1px solid #eee',
borderRadius: 3,
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: 15,
padding: '3px 10px',
margin: 10
}

const Button = ({ children, onClick }) => (
<button
style={buttonStyles}
onClick={onClick}
>
{children}
</button>
)

Button.propTypes = {
children: React.PropTypes.string.isRequired,
onClick: React.PropTypes.func
}

export default Button
Loading

0 comments on commit d7f8853

Please sign in to comment.