Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
feat: init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
johnleider committed Oct 15, 2019
0 parents commit 3d86d59
Show file tree
Hide file tree
Showing 18 changed files with 375 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
/node_modules/
13 changes: 13 additions & 0 deletions .release-it.json
@@ -0,0 +1,13 @@
{
"git": {
"commitMessage": "chore: release v%s",
"tagName": "v%s"
},
"npm": {
"publish": true
},
"github": {
"release": true,
"releaseName": "v${version}"
}
}
44 changes: 44 additions & 0 deletions generator/index.js
@@ -0,0 +1,44 @@
const fs = require('fs')

module.exports = api => {
api.render('./template')

api.extendPackage({
scripts: {
'serve:storybook': 'start-storybook -p 6006',
},
devDependencies: {
'@babel/preset-react': '^7.6.3',
'@mdi/font': '^4.5.95',
'@storybook/addon-a11y': '^5.2',
'@storybook/addon-actions': '^5.2',
'@storybook/addon-knobs': '^5.2',
'@storybook/addon-notes': '^5.2',
'@storybook/addon-viewport': '^5.2',
'@storybook/addons': '^5.2',
'@storybook/vue': '^5.2',
'babel-preset-vue': '^2.0.2',
'prism-react-renderer': '^0.1.7',
'prismjs': '^1.17.1',
'vue-storybook': '^1.1.0',
}
})

api.onCreateComplete(() => {
const path = api.resolve('./babel.config.js')
const config = fs.existsSync(path) ? require(path) : {}

config.presets = config.presets || []

// Add presets to config
for (let preset of ['env', 'react']) {
preset = `@babel/preset-${preset}`

if (config.presets.includes(preset)) continue

config.presets.push(preset)
}

fs.writeFileSync(path, api.genJSConfig(config))
})
}
23 changes: 23 additions & 0 deletions generator/template/_storybook/addon-show-vue-markup/decorator.js
@@ -0,0 +1,23 @@
// Utilities
import addons, { makeDecorator } from '@storybook/addons'
import beautify from 'js-beautify'
import { EVENT_ID } from './register'

const withTemplate = makeDecorator({
name: 'withTemplate',
wrapper: (storyFn, context) => {
const story = storyFn(context)

const template = story.options &&
story.options.STORYBOOK_WRAPS &&
story.options.STORYBOOK_WRAPS.options &&
story.options.STORYBOOK_WRAPS.options.template || null

const channel = addons.getChannel()
channel.emit(EVENT_ID, { markup: beautify.html(template, { indent_size: 2 }) })

return story
}
})

export default withTemplate
3 changes: 3 additions & 0 deletions generator/template/_storybook/addon-show-vue-markup/index.js
@@ -0,0 +1,3 @@
import withTemplate from './decorator'

export { withTemplate }
60 changes: 60 additions & 0 deletions generator/template/_storybook/addon-show-vue-markup/register.js
@@ -0,0 +1,60 @@
import React, { createElement } from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import './styles.css'

import addons, { types } from '@storybook/addons'

const ADDON_ID = 'show-vue-markup'
const PANEL_ID = `${ADDON_ID}/panel`
export const EVENT_ID = `${ADDON_ID}/markup`

class MarkupPanel extends React.Component {
state = { markup: '' }

componentDidMount() {
const { channel } = this.props

channel.on(EVENT_ID, this.onStoryChange)
}

componentWillUnmount() {
const { channel } = this.props

channel.off(EVENT_ID, this.onStoryChange)
}

onStoryChange = ({ markup }) => {
this.setState({ markup })
}

render() {
const { markup } = this.state
const { active } = this.props

return active ? createElement(Highlight, {
...defaultProps,
code: markup,
language: 'html',
children: ({ className, style, tokens, getLineProps, getTokenProps }) => {
return createElement('pre', {
className,
style
}, tokens.map((line, i) => createElement('div', {
...getLineProps({ line, key: i })
}, line.map((token, key) => createElement('span', {
...getTokenProps({ token, key})
})))))
}
}) : null
}
}

addons.register(ADDON_ID, () => {
const channel = addons.getChannel()

addons.add(PANEL_ID, {
type: types.PANEL,
title: 'Markup',
render: ({ active, key }) => createElement(MarkupPanel, { active, key, channel })
})
})
@@ -0,0 +1,3 @@
pre.prism-code {
font-size: 18px;
}
41 changes: 41 additions & 0 deletions generator/template/_storybook/addon-vuetify/decorator.js
@@ -0,0 +1,41 @@
// Imports
import Vue from 'vue'
import Vuetify from 'vuetify'
import { makeDecorator } from '@storybook/addons'

// Utilities
import deepmerge from 'deepmerge'

// Vuetify
import 'vuetify/dist/vuetify.min.css'
import '@mdi/font/css/materialdesignicons.min.css'

Vue.use(Vuetify)

export default makeDecorator({
name: 'withVuetify',
parameterName: 'vuetify',
wrapper: (storyFn, context, { parameters = {} }) => {
// Reduce to one new URL?
const searchParams = new URL(window.location).searchParams
const dark = searchParams.get('eyes-variation') === 'dark'
const rtl = searchParams.get('eyes-variation') === 'rtl'
const vuetify = new Vuetify(deepmerge({
rtl,
theme: { dark }
}, parameters))
const WrappedComponent = storyFn(context)

return Vue.extend({
vuetify,
components: { WrappedComponent },
template: `
<v-app>
<v-container fluid>
<wrapped-component />
</v-container>
</v-app>
`
})
}
})
3 changes: 3 additions & 0 deletions generator/template/_storybook/addon-vuetify/index.js
@@ -0,0 +1,3 @@
import withVuetify from './decorator'

export { withVuetify }
6 changes: 6 additions & 0 deletions generator/template/_storybook/addons.js
@@ -0,0 +1,6 @@
import './addon-show-vue-markup/register'
import '@storybook/addon-knobs/register'
import '@storybook/addon-actions/register'
import '@storybook/addon-notes/register'
import '@storybook/addon-a11y/register'
import '@storybook/addon-viewport/register'
13 changes: 13 additions & 0 deletions generator/template/_storybook/config.js
@@ -0,0 +1,13 @@
// Imports
import { configure, addDecorator } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import { withKnobs } from '@storybook/addon-knobs'
import { withTemplate } from '~storybook/addon-show-vue-markup'
import { withVuetify } from '~storybook/addon-vuetify'

addDecorator(withA11y)
addDecorator(withKnobs)
addDecorator(withTemplate)
addDecorator(withVuetify)

configure(require.context('./stories', true, /\.stories\.js$/), module)
60 changes: 60 additions & 0 deletions generator/template/_storybook/stories/example.stories.js
@@ -0,0 +1,60 @@
// Utilities
import { storyFactory } from '../util/helpers'
import { text, boolean } from '@storybook/addon-knobs'

export default { title: 'BaseCard' }

function genComponent (name) {
return {
name,

render (h) {
return h('div', this.$slots.default)
}
}
}

const story = storyFactory({
BaseBtn: genComponent('BaseBtn'),
BaseCard: genComponent('BaseCard'),
})

export const asDefault = () => story({
props: {
actions: {
default: boolean('Actions', false)
},
cardText: {
default: text('Card text', 'Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, dui. Donec sodales sagittis magna. Vestibulum dapibus nunc ac augue. Donec sodales sagittis magna. Duis vel nibh at velit scelerisque suscipit.')
},
divider: {
default: boolean('Divider', false)
},
text: {
default: boolean('Text', true)
},
title: {
default: boolean('Show title', true)
},
titleText: {
default: text('Title text', 'Card title')
}
},
template: `
<base-card>
<v-card-title v-if="title">{{ titleText }}</v-card-title>
<v-card-text v-if="text">{{ cardText }}</v-card-text>
<v-divider v-if="divider"></v-divider>
<v-card-actions v-if="actions">
<v-btn text>Cancel</v-btn>
<v-spacer></v-spacer>
<base-btn depressed>Accept</base-btn>
</v-card-actions>
</base-card>
`
})
15 changes: 15 additions & 0 deletions generator/template/_storybook/util/helpers.js
@@ -0,0 +1,15 @@
// Returns a function to generate stories
export const storyFactory = (components) => {
return opts => {
// If user supplied a string,
// create an object with it
if (typeof opts === 'string') {
opts = { template: opts }
}

return {
components,
...opts
}
}
}
18 changes: 18 additions & 0 deletions generator/template/_storybook/webpack.config.js
@@ -0,0 +1,18 @@
const path = require('path')

module.exports = async ({ config }) => {
config.resolve.alias['~storybook'] = path.resolve(__dirname)

config.module.rules.push({
resourceQuery: /blockType=story/,
loader: 'vue-storybook'
})

config.module.rules.push({
test: /\.s(a|c)ss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
})

return config
}
12 changes: 12 additions & 0 deletions generator/template/template.stories.js
@@ -0,0 +1,12 @@
// Utilities
import { storyFactory } from '../util/helpers'

export default { title: 'ComponentName' }

const story = storyFactory({
ComponentName: () => import('../../src/ComponentPath')
})

export const asDefault = () => story({
template: `<component-template></component-template>`
})
40 changes: 40 additions & 0 deletions index.js
@@ -0,0 +1,40 @@
const fs = require('fs')
const path = require('path')
const kebabCase = require('lodash/kebabCase')

module.exports = api => {
if (!api.hasPlugin('vuetify-cli')) return

const component = api.service.commands['make:component']

// If user removed or modified
// the script to a new name.
if (!component) return

const serveFn = component.fn

component.fn = async (...args) => {
const { name, rootDir, type } = await serveFn(...args)
let file

if (type.indexOf('../') === 0) {
const split = type.split('/')

split.shift()

file = `${split.join('/')}${name}`
} else {
file = `components/${type ? `${type}/` : ''}${name}/${name}`
}

const template =
fs.readFileSync(path.resolve(__dirname, './generator/template/template.stories.js'), 'utf-8')
.replace(/ComponentName/g, name)
.replace(/ComponentPath/g, file)
.replace(/component-template/g, kebabCase(name))

const dirName = kebabCase(name)

fs.writeFileSync(`${rootDir}/.storybook/stories/${dirName}.stories.js`, template, 'utf-8')
}
}
12 changes: 12 additions & 0 deletions package.json
@@ -0,0 +1,12 @@
{
"name": "vue-cli-plugin-vuetify-storybook",
"version": "0.0.0",
"description": "A Vue CLI 3 Plugin for bootstrapping Storybook with Vuetify",
"main": "index.js",
"author": "John Leider <john@vuetifyjs.com>",
"license": "MIT",
"devDependencies": {},
"dependencies": {
"lodash": "^4.17.15"
}
}
8 changes: 8 additions & 0 deletions yarn.lock
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==

0 comments on commit 3d86d59

Please sign in to comment.