-
Notifications
You must be signed in to change notification settings - Fork 33
POC - Client-Side Dash Apps #7
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
'use strict'; | ||
|
||
var partial = require('webpack-partial').default; | ||
|
||
module.exports = function (config) { | ||
return partial(config, { | ||
entry: {bundle: [ | ||
'whatwg-fetch', | ||
'./local.js' | ||
]} | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
'use strict'; | ||
|
||
var path = require('path'); | ||
var partial = require('webpack-partial').default; | ||
|
||
var ROOT = process.cwd(); | ||
var BUILD = path.join(ROOT, 'dash_renderer'); | ||
|
||
module.exports = function (config) { | ||
return partial(config, { | ||
output: { | ||
path: BUILD, | ||
publicPath: '/dash_renderer/', | ||
// TODO: Bundle filename should be hashed (#10) | ||
filename: 'local.js' | ||
} | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
'use strict'; | ||
|
||
var compose = require('ramda').compose; | ||
|
||
var babel = require('./partials/babel'); | ||
var defineEnv = require('./partials/defineEnv'); | ||
var entryLocal = require('./partials/entryLocal'); | ||
var outputLocal = require('./partials/outputLocal'); | ||
var baseConfig = require('./webpack.config'); | ||
|
||
// TODO: support locally served source maps in production (#11) | ||
module.exports = compose( | ||
babel, | ||
defineEnv, | ||
entryLocal, | ||
outputLocal | ||
)(baseConfig); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,19 @@ | |
<head> | ||
<meta charset="UTF-8" /> | ||
</head> | ||
<body> | ||
<body class="container" style="margin-top: 50px;"> | ||
<div id="react-entry-point"></div> | ||
</body> | ||
|
||
<script type="text/javascript"> | ||
<script type="text/javascript" src="dash_renderer/[email protected]"></script> | ||
<script type="text/javascript" src="dash_renderer/[email protected]"></script> | ||
|
||
</script> | ||
<script type="text/javascript" src="//unpkg.com/dash-html-components@latest/dash_html_components/bundle.js"></script> | ||
<script type="text/javascript" src="//unpkg.com/dash-core-components@latest/dash_core_components/bundle.js"></script> | ||
|
||
<script type="text/javascript" src="build/bundle.js"></script> | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/react-select.min.css"/> | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/assets/index.css"/> | ||
<link rel="stylesheet" href="https://codepen.io/chriddyp/pen/bWLwgP.css"/> | ||
|
||
<script type="text/javascript" src="dash_renderer/local.js"></script> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/*eslint-env browser */ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
require('es6-promise').polyfill(); | ||
import {combineReducers} from 'redux'; | ||
import {connect, Provider} from 'react-redux' | ||
import {createStore, applyMiddleware} from 'redux'; | ||
import thunk from 'redux-thunk'; | ||
import createLogger from 'redux-logger'; | ||
import {pluck, reduce} from 'ramda'; | ||
|
||
// Actions | ||
import { | ||
computeGraphs, | ||
computePaths, | ||
setLayout, | ||
triggerDefaultState | ||
} from './actions/index'; | ||
|
||
// Render the app | ||
import renderTree from './renderTree'; | ||
|
||
// Stores | ||
import layout from './reducers/layout'; | ||
import graphs from './reducers/dependencyGraph'; | ||
import paths from './reducers/paths'; | ||
import requestQueue from './reducers/requestQueue'; | ||
import * as API from './reducers/api'; | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/* | ||
* Change the path here to try out different examples. | ||
* Examples need to export appLayout and mapInputsToOutputs | ||
* objects. | ||
*/ | ||
import {appLayout, mapInputsToOutputs} from './local_examples/example_1_input_to_div'; | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/* | ||
* You shouldn't have to edit anything below here | ||
* This block below does a few things: | ||
* - it transforms the API calls into | ||
* some promises that call the `transform` method | ||
* for the appropriate input-output pair. | ||
* - it initializes the app without the API controller | ||
*/ | ||
|
||
const dependenciesRequest = { | ||
content: mapInputsToOutputs.map(object => { | ||
object.events = []; | ||
object.state = []; | ||
return object; | ||
}), | ||
status: 200 | ||
}; | ||
|
||
window.fetch = function(url, options) { | ||
const payload = JSON.parse(options.body); | ||
return new Promise(resolveResonse => { | ||
const inputOutputPair = mapInputsToOutputs.find(pair => | ||
(pair.output.id === payload.output.id) && | ||
(pair.output.property === payload.output.property) | ||
); | ||
resolveResonse({ | ||
status: 200, | ||
json: () => new Promise(resolveJson => { | ||
resolveJson({ | ||
response: { | ||
props: { | ||
'id': inputOutputPair.output.id, | ||
[inputOutputPair.output.property]: ( | ||
inputOutputPair.output.transform( | ||
reduce( | ||
(acc, input) => { | ||
acc[`${input.id}.${input.property}`] = input.value; | ||
return acc | ||
}, | ||
{}, | ||
payload.inputs | ||
) | ||
) | ||
) | ||
} | ||
} | ||
}) | ||
}) | ||
}) | ||
}); | ||
} | ||
|
||
|
||
// Initialize a store | ||
const reducer = combineReducers({ | ||
layout, | ||
graphs, | ||
paths, | ||
requestQueue, | ||
dependenciesRequest: API.dependenciesRequest | ||
}); | ||
const logger = createLogger() | ||
let store; | ||
const initializeStore = () => { | ||
if (store) { | ||
return store; | ||
} | ||
|
||
store = createStore( | ||
reducer, | ||
applyMiddleware(thunk, logger) | ||
); | ||
|
||
window.store = store; /* global window:true */ | ||
|
||
return store; | ||
}; | ||
|
||
store = initializeStore(); | ||
|
||
store.dispatch({ | ||
type: 'dependenciesRequest', | ||
payload: dependenciesRequest | ||
}); | ||
store.dispatch(setLayout(appLayout)); | ||
store.dispatch(computePaths({subTree: appLayout, startingPath: []})); | ||
store.dispatch(computeGraphs(dependenciesRequest.content)); | ||
store.dispatch(triggerDefaultState); | ||
|
||
const ConnectedApp = connect(state => ({ | ||
layout: state.layout, | ||
dependenciesRequest: state.dependenciesRequest | ||
}))( | ||
props => { | ||
return ( | ||
<div> | ||
{renderTree(props.layout, props.dependenciesRequest.content)} | ||
</div> | ||
) | ||
} | ||
); | ||
|
||
ReactDOM.render( | ||
<Provider store={store}> | ||
<ConnectedApp/> | ||
</Provider>, | ||
document.getElementById('react-entry-point') | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
export const appLayout = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the super simple example to get started. |
||
'type': 'Div', | ||
'namespace': 'dash_html_components', | ||
'props': { | ||
'content': [ | ||
|
||
{ | ||
'type': 'Input', | ||
'namespace': 'dash_core_components', | ||
'props': { | ||
'id': 'my-input', | ||
'value': 'Initial value' | ||
} | ||
}, | ||
|
||
{ | ||
'type': 'Div', | ||
'namespace': 'dash_html_components', | ||
'props': { | ||
'id': 'my-div' | ||
} | ||
} | ||
|
||
] | ||
} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This
|
||
|
||
export const mapInputsToOutputs = [ | ||
{ | ||
inputs: [ | ||
{ | ||
id: 'my-input', | ||
property: 'value' | ||
} | ||
], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
output: { | ||
id: 'my-div', | ||
property: 'content', | ||
|
||
/* | ||
* Of course, we can't have functions in the actual spec, | ||
* but we could introduce a lightweight transformation | ||
* language for modifying strings, accessing values in | ||
* objects or arrays, arithmetic, etc. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Important point here! ^^ |
||
*/ | ||
transform: inputArguments => { | ||
return `You've entered: ${inputArguments['my-input.value']}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
} | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To create a new example, just change this import. And make sure that the examples that you write export an
appLayout
and amapInputsToOutputs
object.