diff --git a/.gitignore b/.gitignore index 865cf5f1d1..eadb05ef36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store node_modules/ -public/ +bundles/ PIG/bundles/ Parse-Dashboard/bundles/ +Parse-Dashboard/parse-dashboard-config.json diff --git a/PIG.config.js b/PIG.config.js index 5a40aed4df..a2f0e12764 100644 --- a/PIG.config.js +++ b/PIG.config.js @@ -1,10 +1,6 @@ -// Import the main configuration file -var configuration = require('./webpack.config.js'); +var configuration = require('./base.config.js'); -// Remove the dashboard configuration, we're only building the PIG -delete configuration.entry.dashboard; - -// Remove SVG plugin -configuration.plugins = []; +configuration.entry = {PIG: './parse-interface-guide/index.js'}; +configuration.output.path = './PIG/bundles'; module.exports = configuration; diff --git a/README.md b/README.md index c6726233cc..7b6abaa079 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ -# parse-dashboard -An open source dashboard for managing your Parse apps that aren't hosted on Parse.com +# Parse Dashboard -Currently this is just a separate repo containing all the dashboard code, with a few modifications to make it actually build. +An open source dashboard for managing your Parse apps. + +To run it: + +``` +git clone git@github.com:ParsePlatform/parse-dashboard.git +cd parse-dashboard +npm install +npm run-script dashboard +``` + + Next add your app into parse-dashboard/Parse-Dashboard/parse-dashboard-config.json. Ask Drew how to do this. It is currently difficult, but it will get better. + + Then visit http://localhost:4040 and you will be able to manage your parse apps. diff --git a/base.config.js b/base.config.js new file mode 100644 index 0000000000..4590e88156 --- /dev/null +++ b/base.config.js @@ -0,0 +1,40 @@ +//This file should be imported by another config file, like dashboard.config.js + +var path = require('path'); +var SvgPrepPlugin = require('./plugins/svg-prep'); + +module.exports = { + output: { + filename: '[name].bundle.js' + }, + resolve: { + root: [__dirname, path.join(__dirname, 'node_modules')] + }, + resolveLoader: { + root: path.join(__dirname, 'node_modules') + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader', + query: { + optional: ['runtime', 'es7.decorators'] + } + }, { + test: /\.scss$/, + loader: "style-loader!css-loader?modules&localIdentName=[local]__[hash:base64:5]!sass-loader?includePaths[]=" + + encodeURIComponent(path.resolve(__dirname, "../../app/webpack")) + }, { + test: /\.css$/, + loader: 'style-loader!css-loader' + } + ] + }, + plugins: [ + new SvgPrepPlugin({ + source: path.join(__dirname, 'icons') + }) + ] +}; diff --git a/components/PlatformCard/PlatformCard.test.js b/components/PlatformCard/PlatformCard.test.js deleted file mode 100644 index 31eac826fb..0000000000 --- a/components/PlatformCard/PlatformCard.test.js +++ /dev/null @@ -1,17 +0,0 @@ -jest.dontMock('./PlatformCard.react'); - -import React from 'react'; -import ReactDOM from 'react-dom'; -import TestUtils from 'react-addons-test-utils'; - -const PlatformCard = require('./PlatformCard.react'); - -describe('PlatformCard', () => { - it('can render examples', () => { - jest.dontMock('./PlatformCard.example'); - const example = require('./PlatformCard.example'); - example.demos.forEach((example, i) => { - example.render(); - }); - }); -}); diff --git a/dashboard.config.js b/dashboard.config.js index 86b7150810..5cdc3f99ed 100644 --- a/dashboard.config.js +++ b/dashboard.config.js @@ -1,14 +1,6 @@ -var path = require('path'); +var configuration = require('./base.config.js'); -// Import the main configuration file -var configuration = require('./webpack.config.js'); - -// Remove the dashboard configuration, we're only building the PIG -delete configuration.entry.PIG; - -configuration.output.path = path.join(__dirname, 'Parse-Dashboard', 'bundles'); - -// Remove SVG plugin -configuration.plugins = []; +configuration.entry = {dashboard: './dashboard/index.js'}; +configuration.output.path = './Parse-Dashboard/bundles'; module.exports = configuration; diff --git a/dashboard/index.js b/dashboard/index.js index 4b1c9845ce..2e17d626cb 100644 --- a/dashboard/index.js +++ b/dashboard/index.js @@ -1,8 +1,9 @@ +import AppsManager from 'lib/AppsManager'; +import Immutable from 'immutable'; +import installDevTools from 'immutable-devtools'; import Parse from 'parse'; import ReactDOM from 'react-dom'; import Routes from './routes'; -import Immutable from 'immutable'; -import installDevTools from 'immutable-devtools'; require('stylesheets/fonts.scss'); installDevTools(Immutable); @@ -16,5 +17,6 @@ if (window.DEVELOPMENT) { Parse.CoreManager.set('VERSION', 'browser'); // App entry point - -ReactDOM.render(Routes, document.getElementById('browser_mount')); +AppsManager.seed().then(() => { + ReactDOM.render(Routes, document.getElementById('browser_mount')); +}); diff --git a/let b/let new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/AppsManager.js b/lib/AppsManager.js index 2f39c21252..d32f52f260 100644 --- a/lib/AppsManager.js +++ b/lib/AppsManager.js @@ -1,19 +1,23 @@ -import ParseApp from 'lib/ParseApp'; +import ParseApp from 'lib/ParseApp'; import { get, post, del } from 'lib/AJAX'; -import { unescape } from 'lib/StringEscaping'; +import { unescape } from 'lib/StringEscaping'; let appsStore = null; const AppsManager = { seed() { + let promise = get('/parse-dashboard-config.json'); + promise.then(({ apps }) => { + appsStore = appsStore.concat(apps.map(app => new ParseApp(app))); + }); let appsData = document.getElementById('appsData'); - if (!appsData) { - return; + if (appsData) { + let rawApps = JSON.parse(unescape(appsData.innerHTML || '[]')); + appsStore = rawApps.map(raw => new ParseApp(raw)); + } else { + appsStore = []; } - let rawApps = JSON.parse(unescape(appsData.innerHTML)); - appsStore = rawApps.map((raw) => { - return new ParseApp(raw); - }); + return promise; }, apps() { diff --git a/components/Button/Button.test.js b/lib/tests/Button.test.js similarity index 96% rename from components/Button/Button.test.js rename to lib/tests/Button.test.js index 41daca013d..2faa8c0bb2 100644 --- a/components/Button/Button.test.js +++ b/lib/tests/Button.test.js @@ -1,10 +1,10 @@ -jest.dontMock('./Button.react'); +jest.dontMock('../../components/Button/Button.react'); import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; -const Button = require('./Button.react'); +const Button = require('../../components/Button/Button.react'); describe('Button', () => { it('has a default state', () => { diff --git a/components/Markdown/Markdown.test.js b/lib/tests/Markdown.test.js similarity index 52% rename from components/Markdown/Markdown.test.js rename to lib/tests/Markdown.test.js index 5c7c73d486..6713d53a16 100644 --- a/components/Markdown/Markdown.test.js +++ b/lib/tests/Markdown.test.js @@ -1,15 +1,15 @@ -jest.dontMock('./Markdown.react'); +jest.dontMock('../../components/Markdown/Markdown.react'); import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; -const Markdown = require('./Markdown.react'); +const Markdown = require('../../components/Markdown/Markdown.react'); describe('Markdown', () => { it('can render examples', () => { - jest.dontMock('./Markdown.example'); - const example = require('./Markdown.example'); + jest.dontMock('../../components/Markdown/Markdown.example'); + const example = require('../../components/Markdown/Markdown.example'); example.demos.forEach((example, i) => { example.render(); }); diff --git a/lib/tests/PlatformCard.test.js b/lib/tests/PlatformCard.test.js new file mode 100644 index 0000000000..c3055e0115 --- /dev/null +++ b/lib/tests/PlatformCard.test.js @@ -0,0 +1,17 @@ +jest.dontMock('../../components/PlatformCard/PlatformCard.react'); + +import React from 'react'; +import ReactDOM from 'react-dom'; +import TestUtils from 'react-addons-test-utils'; + +const PlatformCard = require('../../components/PlatformCard/PlatformCard.react'); + +describe('PlatformCard', () => { + it('can render examples', () => { + jest.dontMock('../../components/PlatformCard/PlatformCard.example'); + const example = require('../../components/PlatformCard/PlatformCard.example'); + example.demos.forEach((example, i) => { + example.render(); + }); + }); +}); diff --git a/components/Tooltip/Tooltip.test.js b/lib/tests/Tooltip.test.js similarity index 58% rename from components/Tooltip/Tooltip.test.js rename to lib/tests/Tooltip.test.js index 0064c6df4e..cb9d8e7291 100644 --- a/components/Tooltip/Tooltip.test.js +++ b/lib/tests/Tooltip.test.js @@ -1,10 +1,10 @@ -jest.dontMock('./Tooltip.react'); +jest.dontMock('../../components/Tooltip/Tooltip.react'); import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; -const Tooltip = require('./Tooltip.react'); +const Tooltip = require('../../components/Tooltip/Tooltip.react'); describe('Tooltip', () => { // test suite goes here diff --git a/package.json b/package.json index 2d90580f05..892a7b4621 100644 --- a/package.json +++ b/package.json @@ -38,16 +38,11 @@ "generate": "node scripts/generate.js" }, "jest": { - "rootDir": "../../app/webpack", "testPathDirs": [ - "components", "lib" ], - "testPathIgnorePatterns": [ - "/node_modules/" - ], - "scriptPreprocessor": "/../../script/webpack/testing/preprocessor.js", - "testDirectoryName": "webpack", + "scriptPreprocessor": "/testing/preprocessor.js", + "testDirectoryName": "tests", "testFileExtensions": [ "test.js" ], diff --git a/parse-interface-guide/ComponentsMap.js b/parse-interface-guide/ComponentsMap.js index dfb0cff77d..1582077e47 100644 --- a/parse-interface-guide/ComponentsMap.js +++ b/parse-interface-guide/ComponentsMap.js @@ -61,4 +61,4 @@ export let SliderWrap = require('components/SliderWrap/SliderWrap export let StatusIndicator = require('components/StatusIndicator/StatusIndicator.example'); export let TextInput = require('components/TextInput/TextInput.example'); export let Toggle = require('components/Toggle/Toggle.example'); -export let Tooltip = require('components/Tooltip/Tooltip.example'); \ No newline at end of file +export let Tooltip = require('components/Tooltip/Tooltip.example'); diff --git a/production.config.js b/production.config.js index ef57e72887..069dd1d0cc 100644 --- a/production.config.js +++ b/production.config.js @@ -1,12 +1,15 @@ -// Production build configuration for dashboard.parse.com +var configuration = require('./base.config.js'); -var webpack = require('webpack'); - -// Import the main configuration file -var configuration = require('./webpack.config.js'); +configuration.entry = { + dashboard: './dashboard/index.js', + login: './login/index.js', + signup: './signup/index.js', + PIG: './parse-interface-guide/index.js', + quickstart: './quickstart/index.js', +}; +configuration.output.path = './production/bundles'; -// Remove the PIG configuration, we're only building the dashboard -delete configuration.entry.PIG; +var webpack = require('webpack'); // Add propType removal to Babel var loaders = configuration.module.loaders; diff --git a/scripts/generate.js b/scripts/generate.js index 35b03d8fbc..4af5de8794 100755 --- a/scripts/generate.js +++ b/scripts/generate.js @@ -5,8 +5,9 @@ const fs = require('fs'); const path = require('path'); -const rootDir = path.join(__dirname, '..', '..', '..', 'app', 'webpack', 'components'); -const pigDir = path.join(__dirname, '..', '..', '..', 'app', 'webpack', 'PIG'); +const rootDir = path.join(__dirname, '..', 'components'); +const pigDir = path.join(__dirname, '..', 'parse-interface-guide'); +const testDir = path.join(__dirname, '..', 'lib', 'tests'); function padding(length) { let space = []; @@ -18,17 +19,16 @@ function padding(length) { function generateReact(name) { return ( -`import React from 'react'; -import styles from 'components/${name}/${name}.scss'; - -export default class ${name} extends React.Component { - constructor() { - super(); - } +`import PropTypes from 'lib/PropTypes'; +import React from 'react'; +import styles from 'components/${name}/${name}.scss'; - render() { +let ${name} = ({prop1}) => { + return
; +} - } +${name}.propTypes = { + prop1: PropTypes.string.isRequired.describe('Replace me with the actual props'), } `); } @@ -36,7 +36,7 @@ export default class ${name} extends React.Component { function generateExample(name) { return ( `import React${padding(name.length - 5)} from 'react'; -import ${name}${padding(5 - name.length)} from 'components/${name}/${name}.react'; +import ${name}${padding(5 - name.length)} f`+ 'rom' +` 'components/${name}/${name}.react'; export const component = ${name}; @@ -53,18 +53,18 @@ export const demos = [ function generateTest(name) { return ( -`jest.dontMock('./${name}.react'); +`jest.dontMock('../../components/${name}/${name}.react'); import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; -const ${name} = require('./${name}.react'); +const ${name} = require('../../components/${name}/${name}.react'); describe('${name}', () => { it('can render examples', () => { - jest.dontMock('./${name}.example'); - const example = require('./${name}.example'); + jest.dontMock('../../components/${name}/${name}.example'); + const example = require('../../components/${name}/${name}.example'); example.demos.forEach((example, i) => { example.render(); }); @@ -74,6 +74,7 @@ describe('${name}', () => { `); } + function updateComponentMap(name) { let numSpace = 1; if (name.length < 26) { @@ -86,7 +87,7 @@ function updateComponentMap(name) { } return ( -`export let ${name}${spaces}= require('components/${name}/${name}.example');` +`export let ${name}${spaces}= require('components/${name}/${name}.example');\n` ); } @@ -122,7 +123,7 @@ try { fs.writeFileSync(path.join(rootDir, name, `${name}.react.js`), generateReact(name)); fs.writeFileSync(path.join(rootDir, name, `${name}.scss`), ''); fs.writeFileSync(path.join(rootDir, name, `${name}.example.js`), generateExample(name)); - fs.writeFileSync(path.join(rootDir, name, `${name}.test.js`), generateTest(name)); + fs.writeFileSync(path.join(testDir, `${name}.test.js`), generateTest(name)); fs.appendFileSync(path.join(pigDir, 'ComponentsMap.js'), updateComponentMap(name)); console.log(`Component ${name} created at ${path.join(rootDir, name)}.`); diff --git a/testing/preprocessor.js b/testing/preprocessor.js index 2c915a7547..8bb2343c9c 100644 --- a/testing/preprocessor.js +++ b/testing/preprocessor.js @@ -1,8 +1,5 @@ var babel = require('babel-core'); var extractClassnames = require('./extractClassnames'); -var path = require('path'); - -var rootDir = path.join(process.cwd(), '..', '..', 'app', 'webpack'); module.exports = { process: function (src, filename) { @@ -12,9 +9,9 @@ module.exports = { } // Let Jest handle our custom module resolution - src = src.replace(/from \'stylesheets/g, "from '" + path.join(rootDir, 'stylesheets')); - src = src.replace(/from \'lib/g, "from '" + path.join(rootDir, 'lib')); - src = src.replace(/from \'components/g, "from '" + path.join(rootDir, 'components')); + src = src.replace(/from \'stylesheets/g, "from '../../stylesheets"); + src = src.replace(/from \'lib/g, "from '../../lib"); + src = src.replace(/from \'components/g, "from '../../components"); // Ignore all files within node_modules // babel files can be .js, .es, .jsx or .es6 diff --git a/webpack.config.js b/webpack.config.js index 242c592760..bfa433fe9c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,48 +1,12 @@ -var path = require('path'); -var SvgPrepPlugin = require('./plugins/svg-prep'); +var configuration = require('./base.config.js'); -var entries = { - dashboard: path.join(__dirname, 'dashboard', 'index.js'), - login: path.join(__dirname, 'login', 'index.js'), - signup: path.join(__dirname, 'signup', 'index.js'), - PIG: path.join(__dirname, 'parse-interface-guide', 'index.js'), - quickstart: path.join(__dirname, 'quickstart', 'index.js') +configuration.entry = { + dashboard: './dashboard/index.js', + login: './login/index.js', + signup: './signup/index.js', + PIG: './parse-interface-guide/index.js', + quickstart: './quickstart/index.js', }; +configuration.output.path = './bundles'; -module.exports = { - entry: entries, - output: { - path: path.join(__dirname, 'PIG', 'bundles'), - filename: '[name].bundle.js' - }, - resolve: { - root: [__dirname, path.join(__dirname, 'node_modules')] - }, - resolveLoader: { - root: path.join(__dirname, 'node_modules') - }, - module: { - loaders: [ - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel-loader', - query: { - optional: ['runtime', 'es7.decorators'] - } - }, { - test: /\.scss$/, - loader: "style-loader!css-loader?modules&localIdentName=[local]__[hash:base64:5]!sass-loader?includePaths[]=" + - encodeURIComponent(path.resolve(__dirname, "../../app/webpack")) - }, { - test: /\.css$/, - loader: 'style-loader!css-loader' - } - ] - }, - plugins: [ - new SvgPrepPlugin({ - source: path.join(__dirname, 'icons') - }) - ] -}; +module.exports = configuration;