Use prettier

This commit is contained in:
nndio 2019-01-10 20:51:14 +02:00
parent 458f9019aa
commit 6907c48147
302 changed files with 5352 additions and 3638 deletions

File diff suppressed because one or more lines are too long

View File

@ -6,3 +6,4 @@ umd
build build
coverage coverage
node_modules node_modules
__snapshots__

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ umd
build build
coverage coverage
.idea .idea
.eslintcache

View File

@ -8,5 +8,5 @@ cache:
- "node_modules" - "node_modules"
script: script:
- yarn build:all - yarn build:all
- yarn lint - yarn lint:all
- yarn test - yarn test

View File

@ -11,4 +11,3 @@ Project maintainers have the right and responsibility to remove, edit, or reject
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)

View File

@ -6,7 +6,7 @@ By installing [`redux-devtools-cli`](https://github.com/reduxjs/redux-devtools/t
We're using [SocketCluster](http://socketcluster.io/) for realtime communication, which provides a fast and scalable webSocket layer and a minimal pub/sub system. You need to include one of [its clients](https://github.com/SocketCluster/client-drivers) in your app to communicate with RemotedevServer. Currently there are clients for [JavaScript (NodeJS)](https://github.com/SocketCluster/socketcluster-client), [Java](https://github.com/sacOO7/socketcluster-client-java), [Python](https://github.com/sacOO7/socketcluster-client-python), [C](https://github.com/sacOO7/socketcluster-client-C), [Objective-C](https://github.com/abpopov/SocketCluster-ios-client) and [.NET/C#](https://github.com/sacOO7/SocketclusterClientDotNet). We're using [SocketCluster](http://socketcluster.io/) for realtime communication, which provides a fast and scalable webSocket layer and a minimal pub/sub system. You need to include one of [its clients](https://github.com/SocketCluster/client-drivers) in your app to communicate with RemotedevServer. Currently there are clients for [JavaScript (NodeJS)](https://github.com/SocketCluster/socketcluster-client), [Java](https://github.com/sacOO7/socketcluster-client-java), [Python](https://github.com/sacOO7/socketcluster-client-python), [C](https://github.com/sacOO7/socketcluster-client-C), [Objective-C](https://github.com/abpopov/SocketCluster-ios-client) and [.NET/C#](https://github.com/sacOO7/SocketclusterClientDotNet).
By default, the websocket server is running on `ws://localhost:8000/socketcluster/`. By default, the websocket server is running on `ws://localhost:8000/socketcluster/`.
### Messaging lifecycle ### Messaging lifecycle
@ -15,6 +15,7 @@ By default, the websocket server is running on `ws://localhost:8000/socketcluste
The client driver provides a way to connect to the server via websockets (see the docs for the selected client). The client driver provides a way to connect to the server via websockets (see the docs for the selected client).
##### JavaScript ##### JavaScript
```js ```js
var socket = socketCluster.connect({ var socket = socketCluster.connect({
hostname: 'localhost', hostname: 'localhost',
@ -23,18 +24,20 @@ var socket = socketCluster.connect({
``` ```
##### Python ##### Python
```py ```py
socket = Socketcluster.socket("ws://localhost:8000/socketcluster/") socket = Socketcluster.socket("ws://localhost:8000/socketcluster/")
socket.connect() socket.connect()
``` ```
> Note that JavaScript client composes the url from `hostname` and `port`, adding `/socketcluster/` path automatically. For other clients, you should specify that path. For example, for `ObjectiveC` it would be `self.client.initWithHost("localhost/socketcluster/", onPort: 8000, securely: false)`. > Note that JavaScript client composes the url from `hostname` and `port`, adding `/socketcluster/` path automatically. For other clients, you should specify that path. For example, for `ObjectiveC` it would be `self.client.initWithHost("localhost/socketcluster/", onPort: 8000, securely: false)`.
#### 2. Disconnecting and reconnecting #### 2. Disconnecting and reconnecting
SocketCluster client handles reconnecting for you, but you still might want to know when the connection is established, or when it failed to connect. SocketCluster client handles reconnecting for you, but you still might want to know when the connection is established, or when it failed to connect.
##### JavaScript ##### JavaScript
```js ```js
socket.on('connect', status => { socket.on('connect', status => {
// Here will come the next step // Here will come the next step
@ -48,6 +51,7 @@ socket.on('error', error => {
``` ```
##### Python ##### Python
```py ```py
def onconnect(socket): def onconnect(socket):
// Here will call the next step // Here will call the next step
@ -66,9 +70,13 @@ socket.setBasicListener(onconnect, ondisconnect, onConnectError)
We're not providing an authorizing mechanism yet. All you have to do is to emit a `login` event, and you'll get a `channelName` you should subscribe for, and watch for messages and events. Make sure to pass the `master` event, otherwise it should be a monitor, not a client app. We're not providing an authorizing mechanism yet. All you have to do is to emit a `login` event, and you'll get a `channelName` you should subscribe for, and watch for messages and events. Make sure to pass the `master` event, otherwise it should be a monitor, not a client app.
##### JavaScript ##### JavaScript
```js ```js
socket.emit('login', 'master', (error, channelName) => { socket.emit('login', 'master', (error, channelName) => {
if (error) { console.log(error); return; } if (error) {
console.log(error);
return;
}
channel = socket.subscribe(channelName); channel = socket.subscribe(channelName);
channel.watch(handleMessages); channel.watch(handleMessages);
socket.on(channelName, handleMessages); socket.on(channelName, handleMessages);
@ -80,6 +88,7 @@ function handleMessages(message) {
``` ```
##### Python ##### Python
```py ```py
socket.emitack("login", "master", login) socket.emitack("login", "master", login)
@ -99,6 +108,7 @@ You could just emit the `login` event, and omit subscribing (and point `5` bello
To send your data to the monitor use `log` or `log-noid` channel. The latter will add the socket id to the message from the server side (useful when the message was sent before the connection was established). To send your data to the monitor use `log` or `log-noid` channel. The latter will add the socket id to the message from the server side (useful when the message was sent before the connection was established).
The message object includes the following: The message object includes the following:
- `type` - usually should be `ACTION`. If you want to indicate that we're starting a new log (clear all actions emitted before and add `@@INIT`), use `INIT`. In case you have a lifted state similar to one provided by [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument), use `STATE`. - `type` - usually should be `ACTION`. If you want to indicate that we're starting a new log (clear all actions emitted before and add `@@INIT`), use `INIT`. In case you have a lifted state similar to one provided by [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument), use `STATE`.
- `action` - the action object. It is recommended to lift it in another object, and add `timestamp` to show when the action was fired off: `{ timestamp: Date.now(), action: { type: 'SOME_ACTION' } }`. - `action` - the action object. It is recommended to lift it in another object, and add `timestamp` to show when the action was fired off: `{ timestamp: Date.now(), action: { type: 'SOME_ACTION' } }`.
- `payload` - usually the state or lifted state object. - `payload` - usually the state or lifted state object.
@ -107,6 +117,7 @@ The message object includes the following:
- `id` - socket connection id, which should be either `socket.id` or should not provided and use `log-noid` channel. - `id` - socket connection id, which should be either `socket.id` or should not provided and use `log-noid` channel.
##### JavaScript ##### JavaScript
```js ```js
const message = { const message = {
type: 'ACTION', type: 'ACTION',
@ -120,6 +131,7 @@ socket.emit(socket.id ? 'log' : 'log-noid', message);
``` ```
##### Python ##### Python
```py ```py
class Message: class Message:
def __init__(self, action, state): def __init__(self, action, state):
@ -133,14 +145,16 @@ socket.emit(socket.id if "log" else "log-noid", Message(action, state));
#### 5. Listening for monitor events #### 5. Listening for monitor events
When a monitor action is emitted, you'll get an event on the subscribed function. The argument object includes a `type` key, which can be: When a monitor action is emitted, you'll get an event on the subscribed function. The argument object includes a `type` key, which can be:
- `DISPATCH` - a monitor action dispatched on Redux DevTools monitor, like `{ type: 'DISPATCH', payload: { type: 'JUMP_TO_STATE', 'index': 2 }`. See [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js) for details. Additionally to that API, you'll get also a stringified `state` object when needed. So, for example, for time travelling (`JUMP_TO_STATE`) you can just parse and set the state (see the example). Usually implementing this type of actions would be enough. - `DISPATCH` - a monitor action dispatched on Redux DevTools monitor, like `{ type: 'DISPATCH', payload: { type: 'JUMP_TO_STATE', 'index': 2 }`. See [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js) for details. Additionally to that API, you'll get also a stringified `state` object when needed. So, for example, for time travelling (`JUMP_TO_STATE`) you can just parse and set the state (see the example). Usually implementing this type of actions would be enough.
- `ACTION` - the user requested to dispatch an action remotely like `{ type: 'ACTION', action: '{ type: \'INCREMENT_COUNTER\' }' }`. The `action` can be either a stringified javascript object which should be evalled or a function which arguments should be evalled like [here](https://github.com/zalmoxisus/remotedev-utils/blob/master/src/index.js#L62-L70). - `ACTION` - the user requested to dispatch an action remotely like `{ type: 'ACTION', action: '{ type: \'INCREMENT_COUNTER\' }' }`. The `action` can be either a stringified javascript object which should be evalled or a function which arguments should be evalled like [here](https://github.com/zalmoxisus/remotedev-utils/blob/master/src/index.js#L62-L70).
- `START` - a monitor was opened. You could handle this event in order not to do extra tasks when the app is not monitored. - `START` - a monitor was opened. You could handle this event in order not to do extra tasks when the app is not monitored.
- `STOP` - a monitor was closed. You can take this as no need to send data to the monitor. I there are several monitors and one was closed, all others will send `START` event to acknowledge that we still have to send data. - `STOP` - a monitor was closed. You can take this as no need to send data to the monitor. I there are several monitors and one was closed, all others will send `START` event to acknowledge that we still have to send data.
See [`mobx-remotedev`](https://github.com/zalmoxisus/mobx-remotedev/blob/master/src/monitorActions.js) for an example of implementation without [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js). See [`mobx-remotedev`](https://github.com/zalmoxisus/mobx-remotedev/blob/master/src/monitorActions.js) for an example of implementation without [`redux-devtools-instrument`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js).
##### JavaScript ##### JavaScript
```js ```js
function handleMessages(message) { function handleMessages(message) {
if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') { if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') {
@ -150,6 +164,7 @@ function handleMessages(message) {
``` ```
##### Python ##### Python
```py ```py
def handleMessages(key, message): def handleMessages(key, message):
if message.type === "DISPATCH" and message.payload.type === "JUMP_TO_STATE": if message.type === "DISPATCH" and message.payload.type === "JUMP_TO_STATE":

View File

@ -46,10 +46,12 @@ const DevTools = createDevTools(
// Consult their repositories to learn about those props. // Consult their repositories to learn about those props.
// Here, we put LogMonitor inside a DockMonitor. // Here, we put LogMonitor inside a DockMonitor.
// Note: DockMonitor is visible by default. // Note: DockMonitor is visible by default.
<DockMonitor toggleVisibilityKey='ctrl-h' <DockMonitor
changePositionKey='ctrl-q' toggleVisibilityKey="ctrl-h"
defaultIsVisible={true}> changePositionKey="ctrl-q"
<LogMonitor theme='tomorrow' /> defaultIsVisible={true}
>
<LogMonitor theme="tomorrow" />
</DockMonitor> </DockMonitor>
); );
@ -60,9 +62,7 @@ Note that you can use `LogMonitor` directly without wrapping it in `DockMonitor`
```js ```js
// If you'd rather not use docking UI, use <LogMonitor> directly // If you'd rather not use docking UI, use <LogMonitor> directly
const DevTools = createDevTools( const DevTools = createDevTools(<LogMonitor theme="solarized" />);
<LogMonitor theme='solarized' />
);
``` ```
#### Use `DevTools.instrument()` Store Enhancer #### Use `DevTools.instrument()` Store Enhancer
@ -75,7 +75,7 @@ The easiest way to apply several store enhancers in a row is to use the [`compos
You can add additional options to it: `DevTools.instrument({ maxAge: 50, shouldCatchErrors: true })`. See [`redux-devtools-instrument`'s API](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-instrument#api) for more details. You can add additional options to it: `DevTools.instrument({ maxAge: 50, shouldCatchErrors: true })`. See [`redux-devtools-instrument`'s API](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-instrument#api) for more details.
Its important that you should add `DevTools.instrument()` *after* `applyMiddleware` in your `compose()` function arguments. This is because `applyMiddleware` is potentially asynchronous, but `DevTools.instrument()` expects all actions to be plain objects rather than actions interpreted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk). So make sure `applyMiddleware()` goes first in the `compose()` call, and `DevTools.instrument()` goes after it. Its important that you should add `DevTools.instrument()` _after_ `applyMiddleware` in your `compose()` function arguments. This is because `applyMiddleware` is potentially asynchronous, but `DevTools.instrument()` expects all actions to be plain objects rather than actions interpreted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk). So make sure `applyMiddleware()` goes first in the `compose()` call, and `DevTools.instrument()` goes after it.
##### `store/configureStore.js` ##### `store/configureStore.js`
@ -99,7 +99,9 @@ export default function configureStore(initialState) {
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled) // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) { if (module.hot) {
module.hot.accept('../reducers', () => module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')/*.default if you use Babel 6+ */) store.replaceReducer(
require('../reducers') /*.default if you use Babel 6+ */
)
); );
} }
@ -126,7 +128,7 @@ function getDebugSessionKey() {
// You can write custom logic here! // You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar // By default we try to read the key from ?debug_session=<key> in the address bar
const matches = window.location.href.match(/[?&]debug_session=([^&#]+)\b/); const matches = window.location.href.match(/[?&]debug_session=([^&#]+)\b/);
return (matches && matches.length > 0)? matches[1] : null; return matches && matches.length > 0 ? matches[1] : null;
} }
export default function configureStore(initialState) { export default function configureStore(initialState) {
@ -181,7 +183,7 @@ export default function configureStore(initialState) {
// Note: only Redux >= 3.1.0 supports passing enhancer as third argument. // Note: only Redux >= 3.1.0 supports passing enhancer as third argument.
// See https://github.com/rackt/redux/releases/tag/v3.1.0 // See https://github.com/rackt/redux/releases/tag/v3.1.0
return createStore(rootReducer, initialState, enhancer); return createStore(rootReducer, initialState, enhancer);
}; }
``` ```
##### `store/configureStore.dev.js` ##### `store/configureStore.dev.js`
@ -205,7 +207,7 @@ function getDebugSessionKey() {
// You can write custom logic here! // You can write custom logic here!
// By default we try to read the key from ?debug_session=<key> in the address bar // By default we try to read the key from ?debug_session=<key> in the address bar
const matches = window.location.href.match(/[?&]debug_session=([^&]+)\b/); const matches = window.location.href.match(/[?&]debug_session=([^&]+)\b/);
return (matches && matches.length > 0)? matches[1] : null; return matches && matches.length > 0 ? matches[1] : null;
} }
export default function configureStore(initialState) { export default function configureStore(initialState) {
@ -216,7 +218,9 @@ export default function configureStore(initialState) {
// Hot reload reducers (requires Webpack or Browserify HMR to be enabled) // Hot reload reducers (requires Webpack or Browserify HMR to be enabled)
if (module.hot) { if (module.hot) {
module.hot.accept('../reducers', () => module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')/*.default if you use Babel 6+ */) store.replaceReducer(
require('../reducers') /*.default if you use Babel 6+ */
)
); );
} }
@ -346,7 +350,11 @@ import { render } from 'react-dom';
import DevTools from './containers/DevTools'; import DevTools from './containers/DevTools';
export default function showDevTools(store) { export default function showDevTools(store) {
const popup = window.open(null, 'Redux DevTools', 'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'); const popup = window.open(
null,
'Redux DevTools',
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'
);
// Reload in case it already exists // Reload in case it already exists
popup.location.reload(); popup.location.reload();
@ -366,11 +374,11 @@ Note that there are no useful props you can pass to the `DevTools` component oth
### Gotchas ### Gotchas
* **Your reducers have to be pure and free of side effects to work correctly with DevTools.** For example, even generating a random ID in reducer makes it impure and non-deterministic. Instead, do this in action creators. - **Your reducers have to be pure and free of side effects to work correctly with DevTools.** For example, even generating a random ID in reducer makes it impure and non-deterministic. Instead, do this in action creators.
* **Make sure to only apply `DevTools.instrument()` and render `<DevTools>` in development!** In production, this will be terribly slow because actions just accumulate forever. As described above, you need to use conditional `require`s and use `DefinePlugin` (Webpack) or `loose-envify` (Browserify) together with Uglify to remove the dead code. Here is [an example](https://github.com/erikras/react-redux-universal-hot-example/) that adds Redux DevTools handling the production case correctly. - **Make sure to only apply `DevTools.instrument()` and render `<DevTools>` in development!** In production, this will be terribly slow because actions just accumulate forever. As described above, you need to use conditional `require`s and use `DefinePlugin` (Webpack) or `loose-envify` (Browserify) together with Uglify to remove the dead code. Here is [an example](https://github.com/erikras/react-redux-universal-hot-example/) that adds Redux DevTools handling the production case correctly.
* **It is important that `DevTools.instrument()` store enhancer should be added to your middleware stack *after* `applyMiddleware` in the `compose`d functions, as `applyMiddleware` is potentially asynchronous.** Otherwise, DevTools wont see the raw actions emitted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk). - **It is important that `DevTools.instrument()` store enhancer should be added to your middleware stack _after_ `applyMiddleware` in the `compose`d functions, as `applyMiddleware` is potentially asynchronous.** Otherwise, DevTools wont see the raw actions emitted by asynchronous middleware such as [redux-promise](https://github.com/acdlite/redux-promise) or [redux-thunk](https://github.com/gaearon/redux-thunk).
### What Next? ### What Next?

View File

@ -8,9 +8,5 @@
"allowBranch": "master" "allowBranch": "master"
} }
}, },
"ignoreChanges": [ "ignoreChanges": ["**/test/**", "**/examples/**", "**/*.md"]
"**/test/**",
"**/examples/**",
"**/*.md"
]
} }

View File

@ -9,7 +9,8 @@
"eslint-plugin-react": "7.12.3", "eslint-plugin-react": "7.12.3",
"jest": "^23.6.0", "jest": "^23.6.0",
"lerna": "3.9.0", "lerna": "3.9.0",
"pre-commit": "^1.1.3" "lint-staged": "^8.1.0",
"prettier": "^1.15.3"
}, },
"scripts": { "scripts": {
"lerna": "lerna", "lerna": "lerna",
@ -21,13 +22,23 @@
"lint": "eslint '**/*.{js,jsx}' --cache", "lint": "eslint '**/*.{js,jsx}' --cache",
"lint:fix": "eslint '**/*.{js,jsx}' --fix --cache", "lint:fix": "eslint '**/*.{js,jsx}' --fix --cache",
"lint:all": "eslint '**/*.{js,jsx}'", "lint:all": "eslint '**/*.{js,jsx}'",
"prettify": "prettier '**/*.{js,jsx,json,css,html,md}' --ignore-path .eslintignore --single-quote --write",
"precommit": "lint-staged",
"test": "jest --onlyChanged", "test": "jest --onlyChanged",
"test:all": "jest" "test:all": "jest"
}, },
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"pre-commit": [ "lint-staged": {
"lint" "*.{js,jsx}": [
] "prettier --single-quote --write",
"yarn lint:fix",
"git add"
],
"*.{json,css,html,md}": [
"prettier --single-quote --write",
"git add"
]
}
} }

View File

@ -1,5 +1,4 @@
d3tooltip # d3tooltip
=========================
This tooltip aims for a minimal yet highly configurable API. It has a long way to go, but the essentials are there. This tooltip aims for a minimal yet highly configurable API. It has a long way to go, but the essentials are there.
It was created by [@romseguy](https://github.com/romseguy) and merged from [`romseguy/d3tooltip`](https://github.com/romseguy/d3tooltip). It was created by [@romseguy](https://github.com/romseguy) and merged from [`romseguy/d3tooltip`](https://github.com/romseguy/d3tooltip).
@ -47,9 +46,9 @@ vis.selectAll('circle').data(someData).enter()
## API ## API
Option | Type | Default | Description | Option | Type | Default | Description |
--------------------------|--------------|---------------------|-------------------------------------------------------------- | -------- | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
`root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select) | `root` | DOM.Element | `body` | The tooltip will be added as a child of that element. You can also use a D3 [selection](https://github.com/mbostock/d3/wiki/Selections#d3_select) |
`left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element | `left` | Number | `undefined` | Sets the tooltip `x` absolute position instead of the mouse `x` position, relative to the `root` element |
`top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element | `top` | Number | `undefined` | Sets the tooltip `y` absolute position instead of the mouse `y` position, relative to the `root` element |
`offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option | `offset` | Object | `{left: 0, top: 0}` | Sets the distance, starting from the cursor position, until the tooltip is rendered. **Warning**: only applicable if you don't provide a `left` or `top` option |

View File

@ -5,19 +5,14 @@ const { prependClass, functor } = utils.default || utils;
const defaultOptions = { const defaultOptions = {
left: undefined, // mouseX left: undefined, // mouseX
top: undefined, // mouseY top: undefined, // mouseY
offset: {left: 0, top: 0}, offset: { left: 0, top: 0 },
root: undefined root: undefined
}; };
export default function tooltip(d3, className = 'tooltip', options = {}) { export default function tooltip(d3, className = 'tooltip', options = {}) {
const { const { left, top, offset, root } = { ...defaultOptions, ...options };
left,
top,
offset,
root
} = {...defaultOptions, ...options};
let attrs = {'class': className}; let attrs = { class: className };
let text = () => ''; let text = () => '';
let styles = {}; let styles = {};
@ -33,7 +28,8 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
anchor.selectAll(`div.${className}`).remove(); anchor.selectAll(`div.${className}`).remove();
el = anchor.append('div') el = anchor
.append('div')
.attr(prependClass(className)(attrs)) .attr(prependClass(className)(attrs))
.style({ .style({
position: 'absolute', position: 'absolute',
@ -49,12 +45,10 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
let [mouseX, mouseY] = d3.mouse(rootNode); let [mouseX, mouseY] = d3.mouse(rootNode);
let [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top]; let [x, y] = [left || mouseX + offset.left, top || mouseY - offset.top];
el el.style({
.style({ left: x + 'px',
left: x + 'px', top: y + 'px'
top: y + 'px' }).html(() => text(node));
})
.html(() => text(node));
}, },
'mouseout.tip': () => el.remove() 'mouseout.tip': () => el.remove()
@ -63,14 +57,14 @@ export default function tooltip(d3, className = 'tooltip', options = {}) {
tip.attr = function setAttr(d) { tip.attr = function setAttr(d) {
if (is(Object, d)) { if (is(Object, d)) {
attrs = {...attrs, ...d}; attrs = { ...attrs, ...d };
} }
return this; return this;
}; };
tip.style = function setStyle(d) { tip.style = function setStyle(d) {
if (is(Object, d)) { if (is(Object, d)) {
styles = {...styles, ...d}; styles = { ...styles, ...d };
} }
return this; return this;
}; };

View File

@ -1,31 +1,29 @@
const path = require('path'); const path = require('path');
module.exports = (env = {}) => ( module.exports = (env = {}) => ({
{ mode: 'production',
mode: 'production', entry: {
entry: { app: ['./src/index.js']
app: ['./src/index.js'] },
}, output: {
output: { library: 'd3tooltip',
library: 'd3tooltip', libraryTarget: 'umd',
libraryTarget: 'umd', path: path.resolve(__dirname, 'dist'),
path: path.resolve(__dirname, 'dist'), filename: env.minimize ? 'd3tooltip.min.js' : 'd3tooltip.js'
filename: env.minimize ? 'd3tooltip.min.js' : 'd3tooltip.js' },
}, module: {
module: { rules: [
rules: [ {
{ test: /\.js$/,
test: /\.js$/, loader: 'babel-loader',
loader: 'babel-loader', exclude: /node_modules/
exclude: /node_modules/ }
} ]
] },
}, optimization: {
optimization: { minimize: !!env.minimize
minimize: !!env.minimize },
}, performance: {
performance: { hints: false
hints: false
}
} }
); });

View File

@ -7,5 +7,6 @@ const parse = require('git-url-parse');
var ghUrl = process.argv[2]; var ghUrl = process.argv[2];
const parsedUrl = parse(ghUrl); const parsedUrl = parse(ghUrl);
const ghPagesUrl = 'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name; const ghPagesUrl =
'https://' + parsedUrl.owner + '.github.io/' + parsedUrl.name;
console.log(ghPagesUrl); console.log(ghPagesUrl);

View File

@ -14,7 +14,7 @@ var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom(''); global.document = jsdom('');
global.window = document.defaultView; global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => { Object.keys(document.defaultView).forEach(property => {
if (typeof global[property] === 'undefined') { if (typeof global[property] === 'undefined') {
exposedProperties.push(property); exposedProperties.push(property);
global[property] = document.defaultView[property]; global[property] = document.defaultView[property];
@ -25,9 +25,9 @@ global.navigator = {
userAgent: 'node.js' userAgent: 'node.js'
}; };
process.on('unhandledRejection', function (error) { process.on('unhandledRejection', function(error) {
console.error('Unhandled Promise Rejection:'); console.error('Unhandled Promise Rejection:');
console.error(error && error.stack || error); console.error((error && error.stack) || error);
}); });
require('./user/pretest.js'); require('./user/pretest.js');

View File

@ -5,15 +5,17 @@ import { withKnobs } from '@storybook/addon-knobs';
import { withTheme } from './themeAddon/theme'; import { withTheme } from './themeAddon/theme';
import '../src/presets.js'; import '../src/presets.js';
addDecorator(withOptions({ addDecorator(
name: 'DevUI', withOptions({
url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui', name: 'DevUI',
goFullScreen: false, url: 'https://github.com/reduxjs/redux-devtools/tree/master/packages/devui',
showStoriesPanel: true, goFullScreen: false,
showAddonPanel: true, showStoriesPanel: true,
showSearchBox: false, showAddonPanel: true,
addonPanelInRight: true showSearchBox: false,
})); addonPanelInRight: true
})
);
addDecorator(withTheme); addDecorator(withTheme);
addDecorator(withKnobs); addDecorator(withKnobs);

View File

@ -11,10 +11,11 @@
min-height: 400px; min-height: 400px;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: "Helvetica Neue", "Lucida Grande", sans-serif; font-family: 'Helvetica Neue', 'Lucida Grande', sans-serif;
font-size: 11px; font-size: 11px;
} }
#root, #root > div { #root,
#root > div {
height: 100%; height: 100%;
} }
#root > div > div { #root > div > div {

View File

@ -7,10 +7,7 @@ addons.register(ADDON_ID, api => {
const channel = addons.getChannel(); const channel = addons.getChannel();
addons.addPanel(PANEL_ID, { addons.addPanel(PANEL_ID, {
title: 'Theme', title: 'Theme',
render: ({ active }) => ( render: ({ active }) =>
active ? active ? <Panel channel={channel} api={api} /> : null
<Panel channel={channel} api={api} />
: null
)
}); });
}); });

View File

@ -4,7 +4,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Code Pro'), local('SourceCodePro-Regular'), src: local('Source Code Pro'), local('SourceCodePro-Regular'),
url('./source-code-pro-v6-latin/source-code-pro-v6-latin-regular.woff2') format('woff2'); url('./source-code-pro-v6-latin/source-code-pro-v6-latin-regular.woff2')
format('woff2');
} }
/* source-sans-pro-regular - latin */ /* source-sans-pro-regular - latin */
@font-face { @font-face {
@ -12,7 +13,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), src: local('Source Sans Pro'), local('SourceSansPro-Regular'),
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-regular.woff2') format('woff2'); url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-regular.woff2')
format('woff2');
} }
/* source-sans-pro-600 - latin */ /* source-sans-pro-600 - latin */
@font-face { @font-face {
@ -20,7 +22,8 @@
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'),
url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-600.woff2') format('woff2'); url('./source-sans-pro-v9-latin/source-sans-pro-v9-latin-600.woff2')
format('woff2');
} }
/* roboto-regular - latin */ /* roboto-regular - latin */
@ -29,7 +32,7 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), src: local('Roboto'), local('Roboto-Regular'),
url('./roboto-v15-latin/roboto-v15-latin-regular.woff2') format('woff2'); url('./roboto-v15-latin/roboto-v15-latin-regular.woff2') format('woff2');
} }
/* roboto-mono-regular - latin */ /* roboto-mono-regular - latin */
@font-face { @font-face {
@ -37,7 +40,8 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Roboto Mono'), local('RobotoMono-Regular'), src: local('Roboto Mono'), local('RobotoMono-Regular'),
url('./roboto-mono-v4-latin/roboto-mono-v4-latin-regular.woff2') format('woff2'); url('./roboto-mono-v4-latin/roboto-mono-v4-latin-regular.woff2')
format('woff2');
} }
/* Generated with https://google-webfonts-helper.herokuapp.com */ /* Generated with https://google-webfonts-helper.herokuapp.com */

View File

@ -1,15 +1,16 @@
{ {
"name": "devui", "name": "devui",
"version": "1.0.0-3", "version": "1.0.0-3",
"description": "description": "Reusable React components for building DevTools monitors and apps.",
"Reusable React components for building DevTools monitors and apps.", "files": [
"files": ["lib", "fonts"], "lib",
"fonts"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git" "url": "https://github.com/reduxjs/redux-devtools.git"
}, },
"author": "author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"start": "npm run storybook", "start": "npm run storybook",

View File

@ -10,13 +10,15 @@ const CommonWrapper = createStyledComponent(commonStyle);
export default class Button extends Component { export default class Button extends Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return nextProps.children !== this.props.children || return (
nextProps.children !== this.props.children ||
nextProps.disabled !== this.props.disabled || nextProps.disabled !== this.props.disabled ||
nextProps.mark !== this.props.mark || nextProps.mark !== this.props.mark ||
nextProps.size !== this.props.size || nextProps.size !== this.props.size ||
nextProps.primary !== this.props.primary || nextProps.primary !== this.props.primary ||
nextProps.tooltipPosition !== this.props.tooltipPosition || nextProps.tooltipPosition !== this.props.tooltipPosition ||
nextProps.title !== this.props.title; nextProps.title !== this.props.title
);
} }
onMouseUp = e => { onMouseUp = e => {
@ -56,15 +58,32 @@ export default class Button extends Component {
Button.propTypes = { Button.propTypes = {
children: PropTypes.any.isRequired, children: PropTypes.any.isRequired,
title: PropTypes.string, title: PropTypes.string,
tooltipPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right', tooltipPosition: PropTypes.oneOf([
'bottom-left', 'bottom-right', 'top-left', 'top-right']), 'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
]),
onClick: PropTypes.func, onClick: PropTypes.func,
type: PropTypes.string, type: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
primary: PropTypes.bool, primary: PropTypes.bool,
size: PropTypes.oneOf(['big', 'normal', 'small']), size: PropTypes.oneOf(['big', 'normal', 'small']),
mark: PropTypes.oneOf([false, 'base08', 'base09', 'base0A', 'base0B', mark: PropTypes.oneOf([
'base0C', 'base0D', 'base0E', 'base0F']), false,
'base08',
'base09',
'base0A',
'base0B',
'base0C',
'base0D',
'base0E',
'base0F'
]),
theme: PropTypes.object theme: PropTypes.object
}; };

View File

@ -16,44 +16,62 @@ export const Container = styled.div`
storiesOf('Button', module) storiesOf('Button', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <Button
<Container> title={text('Title', 'Hello Tooltip! \\a And from new line hello!')}
<Button tooltipPosition={select('tooltipPosition', [
title={text('Title', 'Hello Tooltip! \\a And from new line hello!')} 'top',
tooltipPosition={ 'bottom',
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'left',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'right',
} 'bottom-left',
primary={boolean('primary', true)} 'bottom-right',
size={select('size', ['big', 'normal', 'small'], 'normal')} 'top-left',
disabled={boolean('Disabled', false)} 'top-right'
onClick={action('button clicked')} ])}
> primary={boolean('primary', true)}
{text('Label', 'Hello Button')} size={select('size', ['big', 'normal', 'small'], 'normal')}
</Button> disabled={boolean('Disabled', false)}
</Container> onClick={action('button clicked')}
) >
) {text('Label', 'Hello Button')}
.add( </Button>
'mark', </Container>
() => ( ))
<Container> .add('mark', () => (
<Button <Container>
mark={select('mark', ['base08', 'base09', 'base0A', 'base0B', <Button
'base0C', 'base0D', 'base0E', 'base0F'], 'base08')} mark={select(
title={text('Title', 'Hello Tooltip')} 'mark',
tooltipPosition={ [
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'base08',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'base09',
} 'base0A',
size={select('size', ['big', 'normal', 'small'], 'normal')} 'base0B',
disabled={boolean('Disabled', false)} 'base0C',
onClick={action('button clicked')} 'base0D',
> 'base0E',
<MdFiberManualRecord /> 'base0F'
</Button> ],
</Container> 'base08'
) )}
); title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
size={select('size', ['big', 'normal', 'small'], 'normal')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<MdFiberManualRecord />
</Button>
</Container>
));

View File

@ -2,7 +2,7 @@ import { css } from 'styled-components';
import { fadeIn } from '../../utils/animations'; import { fadeIn } from '../../utils/animations';
import colorEffect from '../../utils/color'; import colorEffect from '../../utils/color';
const both = (tooltipPosition) => { const both = tooltipPosition => {
switch (tooltipPosition) { switch (tooltipPosition) {
case 'bottom': case 'bottom':
return ` return `
@ -46,7 +46,7 @@ const both = (tooltipPosition) => {
} }
}; };
const before = (tooltipPosition) => { const before = tooltipPosition => {
switch (tooltipPosition) { switch (tooltipPosition) {
case 'bottom-left': case 'bottom-left':
return ` return `
@ -110,12 +110,13 @@ const after = (tooltipPosition, color) => {
} }
}; };
const getDirection = (tooltipPosition) => { const getDirection = tooltipPosition => {
return (tooltipPosition.indexOf('-') > 0) ? return tooltipPosition.indexOf('-') > 0
tooltipPosition.substring(0, tooltipPosition.indexOf('-')) : tooltipPosition; ? tooltipPosition.substring(0, tooltipPosition.indexOf('-'))
: tooltipPosition;
}; };
const getSize = (size) => { const getSize = size => {
switch (size) { switch (size) {
case 'big': case 'big':
return 'min-height: 34px; padding: 2px 12px;'; return 'min-height: 34px; padding: 2px 12px;';
@ -144,8 +145,13 @@ export const commonStyle = ({ theme, mark, size }) => css`
pointer-events: none; pointer-events: none;
} }
${mark && ` ${mark &&
background-color: ${colorEffect(theme[mark], 'fade', theme.light ? 0.92 : 0.82)}; `
background-color: ${colorEffect(
theme[mark],
'fade',
theme.light ? 0.92 : 0.82
)};
> svg { > svg {
color: ${theme[mark]}; color: ${theme[mark]};
@ -158,7 +164,13 @@ export const commonStyle = ({ theme, mark, size }) => css`
} }
`; `;
export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size }) => css` export const tooltipStyle = ({
theme,
tooltipTitle,
tooltipPosition,
mark,
size
}) => css`
${commonStyle({ theme, mark, size })} ${commonStyle({ theme, mark, size })}
&:before { &:before {
@ -170,7 +182,9 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
border-radius: 3px; border-radius: 3px;
background: ${theme.base01}; background: ${theme.base01};
border: 1px solid ${theme.base02}; border: 1px solid ${theme.base02};
box-shadow: 1px 1px 2px -1px ${theme.base02}, 1px 1px 2px 0px ${theme.base02}; box-shadow: 1px 1px 2px -1px ${theme.base02}, 1px 1px 2px 0px ${
theme.base02
};
} }
&:after, &:after,
@ -194,7 +208,8 @@ export const tooltipStyle = ({ theme, tooltipTitle, tooltipPosition, mark, size
${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''} ${theme.type === 'material' ? `animation: ${fadeIn} 500ms;` : ''}
} }
${theme.type !== 'material' && ` ${theme.type !== 'material' &&
`
&:after { &:after {
content: ""; content: "";
border-style: solid; border-style: solid;

View File

@ -11,21 +11,30 @@ export const style = ({ theme, primary, disabled }) => css`
margin: auto 0; margin: auto 0;
border: 1px solid ${theme.base02}; border: 1px solid ${theme.base02};
border-radius: 4px; border-radius: 4px;
${primary ? ` ${
primary
? `
background-color: ${theme.base05}; background-color: ${theme.base05};
color: ${theme.base00}; color: ${theme.base00};
` : ` `
: `
background-color: ${theme.base01}; background-color: ${theme.base01};
color: ${theme.base05}; color: ${theme.base05};
`} `
${disabled ? ` }
${
disabled
? `
cursor: not-allowed; cursor: not-allowed;
opacity: 0.6; opacity: 0.6;
` : ` `
: `
cursor: pointer; cursor: pointer;
`} `
}
${!disabled && ` ${!disabled &&
`
&:hover, &:hover,
&:focus { &:focus {
background-color: ${primary ? theme.base07 : theme.base02}; background-color: ${primary ? theme.base07 : theme.base02};

View File

@ -13,20 +13,24 @@ export const style = ({ theme, primary, disabled }) => css`
text-transform: uppercase; text-transform: uppercase;
margin: auto 0; margin: auto 0;
background-color: ${primary ? theme.base05 : theme.base01}; background-color: ${primary ? theme.base05 : theme.base01};
${disabled ? ` ${disabled
? `
cursor: not-allowed; cursor: not-allowed;
color: ${theme.base04}; color: ${theme.base04};
opacity: 0.6; opacity: 0.6;
` : ` `
: `
cursor: pointer; cursor: pointer;
color: ${primary ? theme.base00 : theme.base05}; color: ${primary ? theme.base00 : theme.base05};
`} `}
${!disabled ? ` ${!disabled
? `
box-shadow: box-shadow:
0 2px 2px 0 ${theme.base03}, 0 2px 2px 0 ${theme.base03},
0 3px 1px -2px ${theme.base02}, 0 3px 1px -2px ${theme.base02},
0 1px 5px 0 ${theme.base02}; 0 1px 5px 0 ${theme.base02};
` : ''} `
: ''}
&:hover, &:focus:not(:active) { &:hover, &:focus:not(:active) {

View File

@ -10,15 +10,23 @@ export const MainContainerWrapper = styled.div`
color: ${props => props.theme.base07}; color: ${props => props.theme.base07};
font-size: 12px; font-size: 12px;
div, input, textarea, keygen, select, button { div,
font-family: ${props => props.theme.fontFamily || 'monaco, monospace'}; input,
} textarea,
keygen,
select,
button {
font-family: ${props => props.theme.fontFamily || 'monaco, monospace'};
}
.CodeMirror div, pre, .monitor-LogMonitor * { .CodeMirror div,
font-family: ${props => props.theme.codeFontFamily || props.theme.fontFamily || 'monospace'}; pre,
} .monitor-LogMonitor * {
font-family: ${props =>
props.theme.codeFontFamily || props.theme.fontFamily || 'monospace'};
}
.monitor { .monitor {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;

View File

@ -12,8 +12,10 @@ export default class ContextMenu extends Component {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.items !== this.props.items || if (
nextProps.visible !== this.props.visible) { nextProps.items !== this.props.items ||
nextProps.visible !== this.props.visible
) {
this.updateItems(nextProps.items); this.updateItems(nextProps.items);
} }
} }
@ -32,7 +34,7 @@ export default class ContextMenu extends Component {
e.target.blur(); e.target.blur();
}; };
onClick = (e) => { onClick = e => {
this.props.onClick(e.target.value); this.props.onClick(e.target.value);
}; };
@ -78,7 +80,7 @@ export default class ContextMenu extends Component {
}); });
} }
menuRef = (c) => { menuRef = c => {
this.menu = c; this.menu = c;
}; };

View File

@ -16,17 +16,14 @@ export const Container = styled.div`
storiesOf('ContextMenu', module) storiesOf('ContextMenu', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <ContextMenu
<Container> visible
<ContextMenu onClick={action('menu item clicked')}
visible x={number('x', 100)}
onClick={action('menu item clicked')} y={number('y', 100)}
x={number('x', 100)} items={items}
y={number('y', 100)} />
items={items} </Container>
/> ));
</Container>
)
);

View File

@ -1,11 +1,13 @@
import { css } from 'styled-components'; import { css } from 'styled-components';
export default ({ theme, left, top, visible }) => css` export default ({ theme, left, top, visible }) => css`
${visible ? ` ${visible
? `
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
transition: opacity 0.2s linear; transition: opacity 0.2s linear;
` : ` `
: `
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
transition: visibility 0s 0.2s, opacity 0.2s linear; transition: visibility 0s 0.2s, opacity 0.2s linear;
@ -36,7 +38,7 @@ export default ({ theme, left, top, visible }) => css`
color: ${theme.base07}; color: ${theme.base07};
} }
&:focus { &:focus {
outline:0; outline: 0;
} }
} }
`; `;

View File

@ -52,47 +52,47 @@ export default class Dialog extends (PureComponent || Component) {
{!noHeader && ( {!noHeader && (
<div className="mc-dialog--header"> <div className="mc-dialog--header">
<div>{schema ? schema.title || title : title}</div> <div>{schema ? schema.title || title : title}</div>
{!modal && <button onClick={onDismiss}>×</button>} {!modal && <button onClick={onDismiss}>×</button>}
</div> </div>
)} )}
<div className="mc-dialog--body"> <div className="mc-dialog--body">
{children} {children}
{schema && ( {schema && (
<Form {...rest}> <Form {...rest}>
{!noFooter && {!noFooter && (
(
<input <input
type="submit" type="submit"
ref={this.getFormButtonRef} ref={this.getFormButtonRef}
className="mc-dialog--hidden" className="mc-dialog--hidden"
/> />
) )}
}
</Form> </Form>
)} )}
</div> </div>
{ {!noFooter &&
!noFooter && (actions ? (
(actions ? <div className="mc-dialog--footer">
<div className="mc-dialog--footer"> {submitText
{submitText ? ? [
[...actions, ...actions,
<Button key="default-submit" primary onClick={this.onSubmit}> <Button
key="default-submit"
primary
onClick={this.onSubmit}
>
{submitText} {submitText}
</Button> </Button>
] ]
: actions : actions}
} </div>
</div> ) : (
: <div className="mc-dialog--footer">
<div className="mc-dialog--footer"> <Button onClick={onDismiss}>Cancel</Button>
<Button onClick={onDismiss}>Cancel</Button> <Button primary onClick={this.onSubmit}>
<Button primary onClick={this.onSubmit}> {submitText || 'Submit'}
{submitText || 'Submit'} </Button>
</Button> </div>
</div> ))}
)
}
</div> </div>
</DialogWrapper> </DialogWrapper>
); );

View File

@ -7,39 +7,33 @@ import { schema, uiSchema, formData } from '../../Form/stories/schema';
storiesOf('Dialog', module) storiesOf('Dialog', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Dialog
() => ( title={text('title', 'Dialog Title')}
<Dialog submitText={text('submitText', 'Submit!')}
title={text('title', 'Dialog Title')} open={boolean('open', true)}
submitText={text('submitText', 'Submit!')} noHeader={boolean('noHeader', false)}
open={boolean('open', true)} noFooter={boolean('noFooter', false)}
noHeader={boolean('noHeader', false)} modal={boolean('modal', false)}
noFooter={boolean('noFooter', false)} fullWidth={boolean('fullWidth', false)}
modal={boolean('modal', false)} onDismiss={action('dialog dismissed')}
fullWidth={boolean('fullWidth', false)} onSubmit={action('dialog submitted')}
onDismiss={action('dialog dismissed')} >
onSubmit={action('dialog submitted')} {text('children', 'Hello Dialog!')}
> </Dialog>
{text('children', 'Hello Dialog!')} ))
</Dialog> .add('with form', () => (
) <Dialog
) open={boolean('open', true)}
.add( noHeader={boolean('noHeader', false)}
'with form', noFooter={boolean('noFooter', false)}
() => ( fullWidth={boolean('fullWidth', false)}
<Dialog submitText={text('submitText', 'Submit!')}
open={boolean('open', true)} formData={object('formData', formData)}
noHeader={boolean('noHeader', false)} schema={object('schema', schema)}
noFooter={boolean('noFooter', false)} uiSchema={object('uiSchema', uiSchema)}
fullWidth={boolean('fullWidth', false)} onChange={action('form changed')}
submitText={text('submitText', 'Submit!')} onSubmit={action('form submitted')}
formData={object('formData', formData)} onDismiss={action('dialog dismissed')}
schema={object('schema', schema)} />
uiSchema={object('uiSchema', uiSchema)} ));
onChange={action('form changed')}
onSubmit={action('form submitted')}
onDismiss={action('dialog dismissed')}
/>
)
);

View File

@ -36,10 +36,8 @@ export const style = ({ theme, open, fullWidth }) => css`
border: 1px outset ${theme.base01}; border: 1px outset ${theme.base01};
border-radius: 2px; border-radius: 2px;
background-color: ${theme.base00}; background-color: ${theme.base00};
box-shadow: box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header { > div.mc-dialog--header {
display: flex; display: flex;
@ -76,15 +74,23 @@ export const style = ({ theme, open, fullWidth }) => css`
> form { > form {
padding: 0; padding: 0;
> .form-group { margin-bottom: 0; }
> div > fieldset { > .form-group {
legend { display: none; } margin-bottom: 0;
#root__description { margin-top: 0; }
} }
.mc-dialog--hidden { display: none; } > div > fieldset {
legend {
display: none;
}
#root__description {
margin-top: 0;
}
}
.mc-dialog--hidden {
display: none;
}
} }
} }
@ -102,7 +108,7 @@ export const style = ({ theme, open, fullWidth }) => css`
margin: 0 -16px -16px; margin: 0 -16px -16px;
padding: 2px 10px; padding: 2px 10px;
border-top: 1px solid ${theme.base03}; border-top: 1px solid ${theme.base03};
> button { > button {
margin-left: 5px; margin-left: 5px;
} }

View File

@ -35,10 +35,8 @@ export const style = ({ theme, open, fullWidth }) => css`
margin-bottom: 16px; margin-bottom: 16px;
border: none; border: none;
background-color: ${theme.base00}; background-color: ${theme.base00};
box-shadow: box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14),
0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
0 11px 15px -7px rgba(0, 0, 0, 0.12),
0 24px 38px 3px rgba(0, 0, 0, 0.2);
> div.mc-dialog--header { > div.mc-dialog--header {
display: flex; display: flex;
@ -73,15 +71,23 @@ export const style = ({ theme, open, fullWidth }) => css`
> form { > form {
padding: 0; padding: 0;
> .form-group { margin-bottom: 0; }
> div > fieldset { > .form-group {
legend { display: none; } margin-bottom: 0;
#root__description { margin-top: 0; }
} }
.mc-dialog--hidden { display: none; } > div > fieldset {
legend {
display: none;
}
#root__description {
margin-top: 0;
}
}
.mc-dialog--hidden {
display: none;
}
} }
} }

View File

@ -4,28 +4,27 @@ import styled from 'styled-components';
import CodeMirror from 'codemirror'; import CodeMirror from 'codemirror';
import { defaultStyle, themedStyle } from './styles'; import { defaultStyle, themedStyle } from './styles';
const EditorContainer = styled.div('', const EditorContainer = styled.div('', ({ theme }) =>
({ theme }) => (theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)) theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme)
); );
export default class Editor extends Component { export default class Editor extends Component {
componentDidMount() { componentDidMount() {
this.cm = CodeMirror( // eslint-disable-line new-cap this.cm = CodeMirror(this.node, {
this.node, value: this.props.value,
{ mode: this.props.mode,
value: this.props.value, lineNumbers: this.props.lineNumbers,
mode: this.props.mode, lineWrapping: this.props.lineWrapping,
lineNumbers: this.props.lineNumbers, readOnly: this.props.readOnly,
lineWrapping: this.props.lineWrapping, autofocus: this.props.autofocus,
readOnly: this.props.readOnly, foldGutter: this.props.foldGutter,
autofocus: this.props.autofocus, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
foldGutter: this.props.foldGutter, });
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
}
);
if (this.props.onChange) { if (this.props.onChange) {
this.cm.on('change', (doc, change) => { this.props.onChange(doc.getValue(), change); }); this.cm.on('change', (doc, change) => {
this.props.onChange(doc.getValue(), change);
});
} }
} }

View File

@ -34,7 +34,9 @@ export default class WithTabs extends Component {
} }
]} ]}
selected={this.state.selected} selected={this.state.selected}
onClick={selected => { this.setState({ selected }); }} onClick={selected => {
this.setState({ selected });
}}
align={select('align', ['left', 'right', 'center'], 'left')} align={select('align', ['left', 'right', 'center'], 'left')}
/> />
); );

View File

@ -30,11 +30,6 @@ storiesOf('Editor', module)
), ),
{ info: 'Based on [CodeMirror](http://codemirror.net/).' } { info: 'Based on [CodeMirror](http://codemirror.net/).' }
) )
.add( .add('with tabs', () => (
'with tabs', <WithTabs lineNumbers={boolean('lineNumbers', true)} />
() => ( ));
<WithTabs
lineNumbers={boolean('lineNumbers', true)}
/>
)
);

View File

@ -19,37 +19,79 @@ export const themedStyle = theme => css`
background-color: ${theme.base00}; background-color: ${theme.base00};
color: ${theme.base04}; color: ${theme.base04};
.cm-header { color: ${theme.base05}; } .cm-header {
.cm-quote { color: ${theme.base09}; } color: ${theme.base05};
}
.cm-quote {
color: ${theme.base09};
}
.cm-keyword { color: ${theme.base0F}; } .cm-keyword {
.cm-atom { color: ${theme.base0F}; } color: ${theme.base0F};
.cm-number { color: ${theme.base0F}; } }
.cm-def { color: ${theme.base0D}; } .cm-atom {
color: ${theme.base0F};
}
.cm-number {
color: ${theme.base0F};
}
.cm-def {
color: ${theme.base0D};
}
.cm-variable { color: ${theme.base05}; } .cm-variable {
.cm-variable-2 { color: ${theme.base0A}; } color: ${theme.base05};
.cm-variable-3 { color: ${theme.base0E}; } }
.cm-variable-2 {
color: ${theme.base0A};
}
.cm-variable-3 {
color: ${theme.base0E};
}
.cm-property { color: ${theme.base0C}; } .cm-property {
.cm-operator { color: ${theme.base0E}; } color: ${theme.base0C};
}
.cm-operator {
color: ${theme.base0E};
}
.cm-comment { .cm-comment {
color: ${theme.base05}; color: ${theme.base05};
font-style: italic; font-style: italic;
} }
.cm-string { color: ${theme.base0B}; } .cm-string {
.cm-string-2 { color: ${theme.base0A}; } color: ${theme.base0B};
}
.cm-string-2 {
color: ${theme.base0A};
}
.cm-meta { color: ${theme.base0B}; } .cm-meta {
.cm-qualifier { color: ${theme.base0A}; } color: ${theme.base0B};
.cm-builtin { color: ${theme.base0F}; } }
.cm-bracket { color: ${theme.base09}; } .cm-qualifier {
.CodeMirror-matchingbracket { color: ${theme.base0B}; } color: ${theme.base0A};
.CodeMirror-nonmatchingbracket { color: ${theme.base08}; } }
.cm-tag { color: ${theme.base02}; } .cm-builtin {
.cm-attribute { color: ${theme.base0C}; } color: ${theme.base0F};
}
.cm-bracket {
color: ${theme.base09};
}
.CodeMirror-matchingbracket {
color: ${theme.base0B};
}
.CodeMirror-nonmatchingbracket {
color: ${theme.base08};
}
.cm-tag {
color: ${theme.base02};
}
.cm-attribute {
color: ${theme.base0C};
}
.cm-hr { .cm-hr {
color: transparent; color: transparent;
@ -62,7 +104,9 @@ export const themedStyle = theme => css`
cursor: pointer; cursor: pointer;
} }
.cm-special { color: ${theme.base0E}; } .cm-special {
color: ${theme.base0E};
}
.cm-em { .cm-em {
color: #999; color: #999;
@ -70,7 +114,9 @@ export const themedStyle = theme => css`
text-decoration-style: dotted; text-decoration-style: dotted;
} }
.cm-strong { color: ${theme.base01}; } .cm-strong {
color: ${theme.base01};
}
.cm-error, .cm-error,
.cm-invalidchar { .cm-invalidchar {
@ -78,7 +124,9 @@ export const themedStyle = theme => css`
border-bottom: 1px dotted ${theme.base08}; border-bottom: 1px dotted ${theme.base08};
} }
div.CodeMirror-selected { background: ${theme.base01}; } div.CodeMirror-selected {
background: ${theme.base01};
}
.CodeMirror-line::selection, .CodeMirror-line::selection,
.CodeMirror-line > span::selection, .CodeMirror-line > span::selection,
@ -106,17 +154,27 @@ export const themedStyle = theme => css`
padding: 0 5px; padding: 0 5px;
} }
.CodeMirror-guttermarker-subtle { color: ${theme.base05}; } .CodeMirror-guttermarker-subtle {
.CodeMirror-guttermarker { color: ${theme.base09}; } color: ${theme.base05};
}
.CodeMirror-guttermarker {
color: ${theme.base09};
}
.CodeMirror-gutter .CodeMirror-gutter-text { .CodeMirror-gutter .CodeMirror-gutter-text {
color: ${theme.base05}; color: ${theme.base05};
} }
.CodeMirror-cursor { border-left: 1px solid #819090; } .CodeMirror-cursor {
border-left: 1px solid #819090;
}
.cm-fat-cursor .CodeMirror-cursor { background: ${theme.base02}; } .cm-fat-cursor .CodeMirror-cursor {
.cm-animate-fat-cursor { background-color: ${theme.base02}; } background: ${theme.base02};
}
.cm-animate-fat-cursor {
background-color: ${theme.base02};
}
.CodeMirror-activeline-background { .CodeMirror-activeline-background {
background: ${theme.base07}; background: ${theme.base07};

View File

@ -10,16 +10,30 @@ const FormContainer = createStyledComponent(styles, JSONSchemaForm);
export default class Form extends (PureComponent || Component) { export default class Form extends (PureComponent || Component) {
render() { render() {
const { widgets, children, submitText, primaryButton, noSubmit, ...rest } = this.props; const {
widgets,
children,
submitText,
primaryButton,
noSubmit,
...rest
} = this.props;
return ( return (
<FormContainer {...rest} widgets={{ ...customWidgets, ...widgets }}> <FormContainer {...rest} widgets={{ ...customWidgets, ...widgets }}>
{ {noSubmit ? (
noSubmit ? <noscript /> : <noscript />
children || ) : (
<Button size="big" primary={primaryButton} theme={rest.theme} type="submit"> children || (
{submitText || 'Submit'} <Button
</Button> size="big"
} primary={primaryButton}
theme={rest.theme}
type="submit"
>
{submitText || 'Submit'}
</Button>
)
)}
</FormContainer> </FormContainer>
); );
} }
@ -33,5 +47,7 @@ Form.propTypes = {
schema: PropTypes.object.isRequired, schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object, uiSchema: PropTypes.object,
formData: PropTypes.any, formData: PropTypes.any,
widgets: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object])) widgets: PropTypes.objectOf(
PropTypes.oneOfType([PropTypes.func, PropTypes.object])
)
}; };

View File

@ -19,8 +19,9 @@ storiesOf('Form', module)
onSubmit={action('form submitted')} onSubmit={action('form submitted')}
/> />
), ),
{ info: {
'Wrapper around [`react-jsonschema-form`](https://github.com/mozilla-services/react-jsonschema-form)' info:
+ ' with custom widgets.' 'Wrapper around [`react-jsonschema-form`](https://github.com/mozilla-services/react-jsonschema-form)' +
' with custom widgets.'
} }
); );

View File

@ -27,31 +27,19 @@ module.exports = {
title: 'A multiple choices list', title: 'A multiple choices list',
items: { items: {
type: 'string', type: 'string',
enum: [ enum: ['foo', 'bar', 'fuzz']
'foo',
'bar',
'fuzz'
]
}, },
uniqueItems: true uniqueItems: true
}, },
numberEnum: { numberEnum: {
type: 'number', type: 'number',
title: 'Number enum', title: 'Number enum',
enum: [ enum: [1, 2, 3]
1,
2,
3
]
}, },
numberEnumRadio: { numberEnumRadio: {
type: 'number', type: 'number',
title: 'Number enum', title: 'Number enum',
enum: [ enum: [1, 2, 3]
1,
2,
3
]
}, },
integerRange: { integerRange: {
title: 'Integer range', title: 'Integer range',

View File

@ -1,368 +1,368 @@
import { css } from 'styled-components'; import { css } from 'styled-components';
export default ({ theme }) => css` export default ({ theme }) => css`
padding: 10px; padding: 10px;
line-height: 1.846; line-height: 1.846;
font-size: 14px; font-size: 14px;
color: ${theme.base06}; color: ${theme.base06};
fieldset { fieldset {
padding: 0; padding: 0;
margin: 0; margin: 0;
border: 0; border: 0;
min-width: 0; min-width: 0;
} }
legend { legend {
display: block; display: block;
width: 100%; width: 100%;
padding: 0; padding: 0;
font-size: 20px; font-size: 20px;
color: ${theme.base04}; color: ${theme.base04};
border: 0; border: 0;
} }
label { label {
display: inline-block; display: inline-block;
max-width: 100%; max-width: 100%;
font-weight: bold; font-weight: bold;
} }
.form-control { .form-control {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
font-size: 12px; font-size: 12px;
width: 100%; width: 100%;
color: ${theme.base05}; color: ${theme.base05};
background-color: transparent; background-color: transparent;
background-image: none; background-image: none;
line-height: ${theme.inputInternalHeight}px; line-height: ${theme.inputInternalHeight}px;
padding: 0 ${theme.inputPadding}px; padding: 0 ${theme.inputPadding}px;
border-style: solid; border-style: solid;
border-width: ${theme.inputBorderWidth}px; border-width: ${theme.inputBorderWidth}px;
border-color: ${theme.inputBorderColor}; border-color: ${theme.inputBorderColor};
border-radius: ${theme.inputBorderRadius}px; border-radius: ${theme.inputBorderRadius}px;
} }
.form-control:focus, .form-control:focus,
input.form-control:focus { input.form-control:focus {
outline: 0; outline: 0;
${theme.inputFocusedStyle} ${theme.inputFocusedStyle}
} }
.form-control[disabled], .form-control[disabled],
.form-control[readonly], .form-control[readonly],
fieldset[disabled] .form-control { fieldset[disabled] .form-control {
background-color: transparent; background-color: transparent;
opacity: 1; opacity: 1;
} }
.form-control[disabled], .form-control[disabled],
fieldset[disabled] .form-control { fieldset[disabled] .form-control {
cursor: not-allowed; cursor: not-allowed;
} }
textarea.form-control { textarea.form-control {
height: auto; height: auto;
} }
.form-group { .form-group {
margin-bottom: 5px; margin-bottom: 5px;
} }
.radio, .radio,
.checkbox { .checkbox {
position: relative; position: relative;
display: block; display: block;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.radio label, .radio label,
.checkbox label { .checkbox label {
min-height: 23px; min-height: 23px;
padding-left: 20px; padding-left: 20px;
margin-bottom: 0; margin-bottom: 0;
font-weight: normal; font-weight: normal;
cursor: pointer; cursor: pointer;
} }
.radio input[type="radio"], .radio input[type='radio'],
.radio-inline input[type="radio"], .radio-inline input[type='radio'],
.checkbox input[type="checkbox"], .checkbox input[type='checkbox'],
.checkbox-inline input[type="checkbox"] { .checkbox-inline input[type='checkbox'] {
position: absolute; position: absolute;
margin-left: -20px; margin-left: -20px;
margin-top: 4px \\9; margin-top: 4px \\9;
} }
.radio + .radio, .radio + .radio,
.checkbox + .checkbox { .checkbox + .checkbox {
margin-top: -5px; margin-top: -5px;
} }
.radio-inline, .radio-inline,
.checkbox-inline { .checkbox-inline {
position: relative; position: relative;
display: inline-block; display: inline-block;
padding-left: 25px; padding-left: 25px;
margin-bottom: 0; margin-bottom: 0;
vertical-align: middle; vertical-align: middle;
font-weight: normal; font-weight: normal;
cursor: pointer; cursor: pointer;
} }
.radio-inline + .radio-inline, .radio-inline + .radio-inline,
.checkbox-inline + .checkbox-inline { .checkbox-inline + .checkbox-inline {
margin-top: 0; margin-top: 0;
margin-left: 10px; margin-left: 10px;
} }
.radio label, .radio label,
.radio-inline label, .radio-inline label,
.checkbox label, .checkbox label,
.checkbox-inline label { .checkbox-inline label {
padding-left: 25px; padding-left: 25px;
} }
.radio input[type="radio"], .radio input[type='radio'],
.radio-inline input[type="radio"], .radio-inline input[type='radio'],
.checkbox input[type="radio"], .checkbox input[type='radio'],
.checkbox-inline input[type="radio"], .checkbox-inline input[type='radio'],
.radio input[type="checkbox"], .radio input[type='checkbox'],
.radio-inline input[type="checkbox"], .radio-inline input[type='checkbox'],
.checkbox input[type="checkbox"], .checkbox input[type='checkbox'],
.checkbox-inline input[type="checkbox"] { .checkbox-inline input[type='checkbox'] {
margin-left: -25px; margin-left: -25px;
} }
input[type="radio"], input[type='radio'],
.radio input[type="radio"], .radio input[type='radio'],
.radio-inline input[type="radio"] { .radio-inline input[type='radio'] {
position: relative; position: relative;
margin-top: 6px; margin-top: 6px;
margin-right: 4px; margin-right: 4px;
vertical-align: top; vertical-align: top;
border: none; border: none;
background-color: transparent; background-color: transparent;
appearance: none; appearance: none;
cursor: pointer; cursor: pointer;
} }
input[type="radio"]:focus, input[type='radio']:focus,
.radio input[type="radio"]:focus, .radio input[type='radio']:focus,
.radio-inline input[type="radio"]:focus { .radio-inline input[type='radio']:focus {
outline: none; outline: none;
} }
input[type="radio"]:before, input[type='radio']:before,
.radio input[type="radio"]:before, .radio input[type='radio']:before,
.radio-inline input[type="radio"]:before, .radio-inline input[type='radio']:before,
input[type="radio"]:after, input[type='radio']:after,
.radio input[type="radio"]:after, .radio input[type='radio']:after,
.radio-inline input[type="radio"]:after { .radio-inline input[type='radio']:after {
content: ""; content: '';
display: block; display: block;
width: 18px; width: 18px;
height: 18px; height: 18px;
border-radius: 50%; border-radius: 50%;
transition: 240ms; transition: 240ms;
box-sizing: border-box; box-sizing: border-box;
} }
input[type="radio"]:before, input[type='radio']:before,
.radio input[type="radio"]:before, .radio input[type='radio']:before,
.radio-inline input[type="radio"]:before { .radio-inline input[type='radio']:before {
position: absolute; position: absolute;
left: 0; left: 0;
top: -3px; top: -3px;
background-color: ${theme.base0D}; background-color: ${theme.base0D};
transform: scale(0); transform: scale(0);
} }
input[type="radio"]:after, input[type='radio']:after,
.radio input[type="radio"]:after, .radio input[type='radio']:after,
.radio-inline input[type="radio"]:after { .radio-inline input[type='radio']:after {
position: relative; position: relative;
top: -3px; top: -3px;
border: 2px solid ${theme.base03}; border: 2px solid ${theme.base03};
} }
input[type="radio"]:checked:before, input[type='radio']:checked:before,
.radio input[type="radio"]:checked:before, .radio input[type='radio']:checked:before,
.radio-inline input[type="radio"]:checked:before { .radio-inline input[type='radio']:checked:before {
transform: scale(0.5); transform: scale(0.5);
} }
input[type="radio"]:disabled:checked:before, input[type='radio']:disabled:checked:before,
.radio input[type="radio"]:disabled:checked:before, .radio input[type='radio']:disabled:checked:before,
.radio-inline input[type="radio"]:disabled:checked:before { .radio-inline input[type='radio']:disabled:checked:before {
background-color: ${theme.base03}; background-color: ${theme.base03};
} }
input[type="radio"]:checked:after, input[type='radio']:checked:after,
.radio input[type="radio"]:checked:after, .radio input[type='radio']:checked:after,
.radio-inline input[type="radio"]:checked:after { .radio-inline input[type='radio']:checked:after {
border-color: ${theme.base0D}; border-color: ${theme.base0D};
} }
input[type="radio"]:disabled:after, input[type='radio']:disabled:after,
.radio input[type="radio"]:disabled:after, .radio input[type='radio']:disabled:after,
.radio-inline input[type="radio"]:disabled:after, .radio-inline input[type='radio']:disabled:after,
input[type="radio"]:disabled:checked:after, input[type='radio']:disabled:checked:after,
.radio input[type="radio"]:disabled:checked:after, .radio input[type='radio']:disabled:checked:after,
.radio-inline input[type="radio"]:disabled:checked:after { .radio-inline input[type='radio']:disabled:checked:after {
border-color: ${theme.base03}; border-color: ${theme.base03};
} }
input[type="checkbox"], input[type='checkbox'],
.checkbox input[type="checkbox"], .checkbox input[type='checkbox'],
.checkbox-inline input[type="checkbox"] { .checkbox-inline input[type='checkbox'] {
position: relative; position: relative;
border: none; border: none;
margin-bottom: -4px; margin-bottom: -4px;
appearance: none; appearance: none;
cursor: pointer; cursor: pointer;
} }
input[type="checkbox"]:focus, input[type='checkbox']:focus,
.checkbox input[type="checkbox"]:focus, .checkbox input[type='checkbox']:focus,
.checkbox-inline input[type="checkbox"]:focus { .checkbox-inline input[type='checkbox']:focus {
outline: none; outline: none;
} }
input[type="checkbox"]:focus:after, input[type='checkbox']:focus:after,
.checkbox input[type="checkbox"]:focus:after, .checkbox input[type='checkbox']:focus:after,
.checkbox-inline input[type="checkbox"]:focus:after { .checkbox-inline input[type='checkbox']:focus:after {
border-color: ${theme.base0D}; border-color: ${theme.base0D};
} }
input[type="checkbox"]:after, input[type='checkbox']:after,
.checkbox input[type="checkbox"]:after, .checkbox input[type='checkbox']:after,
.checkbox-inline input[type="checkbox"]:after { .checkbox-inline input[type='checkbox']:after {
content: ""; content: '';
display: block; display: block;
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-top: -2px; margin-top: -2px;
margin-right: 5px; margin-right: 5px;
border: 2px solid ${theme.base03}; border: 2px solid ${theme.base03};
border-radius: 4px; border-radius: 4px;
transition: 240ms; transition: 240ms;
box-sizing: border-box; box-sizing: border-box;
} }
input[type="checkbox"]:checked:before, input[type='checkbox']:checked:before,
.checkbox input[type="checkbox"]:checked:before, .checkbox input[type='checkbox']:checked:before,
.checkbox-inline input[type="checkbox"]:checked:before { .checkbox-inline input[type='checkbox']:checked:before {
content: ""; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 6px; left: 6px;
display: table; display: table;
width: 6px; width: 6px;
height: 12px; height: 12px;
border: 2px solid #fff; border: 2px solid #fff;
border-top-width: 0; border-top-width: 0;
border-left-width: 0; border-left-width: 0;
transform: rotate(45deg); transform: rotate(45deg);
box-sizing: border-box; box-sizing: border-box;
} }
input[type="checkbox"]:checked:after, input[type='checkbox']:checked:after,
.checkbox input[type="checkbox"]:checked:after, .checkbox input[type='checkbox']:checked:after,
.checkbox-inline input[type="checkbox"]:checked:after { .checkbox-inline input[type='checkbox']:checked:after {
background-color: ${theme.base0D}; background-color: ${theme.base0D};
border-color: ${theme.base0D}; border-color: ${theme.base0D};
} }
input[type="checkbox"]:disabled:after, input[type='checkbox']:disabled:after,
.checkbox input[type="checkbox"]:disabled:after, .checkbox input[type='checkbox']:disabled:after,
.checkbox-inline input[type="checkbox"]:disabled:after { .checkbox-inline input[type='checkbox']:disabled:after {
border-color: ${theme.base03}; border-color: ${theme.base03};
} }
input[type="checkbox"]:disabled:checked:after, input[type='checkbox']:disabled:checked:after,
.checkbox input[type="checkbox"]:disabled:checked:after, .checkbox input[type='checkbox']:disabled:checked:after,
.checkbox-inline input[type="checkbox"]:disabled:checked:after { .checkbox-inline input[type='checkbox']:disabled:checked:after {
background-color: ${theme.base03}; background-color: ${theme.base03};
border-color: transparent; border-color: transparent;
} }
input[type="radio"][disabled], input[type='radio'][disabled],
input[type="checkbox"][disabled], input[type='checkbox'][disabled],
input[type="radio"].disabled, input[type='radio'].disabled,
input[type="checkbox"].disabled, input[type='checkbox'].disabled,
fieldset[disabled] input[type="radio"], fieldset[disabled] input[type='radio'],
fieldset[disabled] input[type="checkbox"] { fieldset[disabled] input[type='checkbox'] {
cursor: not-allowed; cursor: not-allowed;
} }
.radio-inline.disabled, .radio-inline.disabled,
.checkbox-inline.disabled, .checkbox-inline.disabled,
fieldset[disabled] .radio-inline, fieldset[disabled] .radio-inline,
fieldset[disabled] .checkbox-inline { fieldset[disabled] .checkbox-inline {
cursor: not-allowed; cursor: not-allowed;
} }
.radio.disabled label, .radio.disabled label,
.checkbox.disabled label, .checkbox.disabled label,
fieldset[disabled] .radio label, fieldset[disabled] .radio label,
fieldset[disabled] .checkbox label { fieldset[disabled] .checkbox label {
cursor: not-allowed; cursor: not-allowed;
} }
.has-error .help-block, .has-error .help-block,
.has-error .control-label, .has-error .control-label,
.has-error .radio, .has-error .radio,
.has-error .checkbox, .has-error .checkbox,
.has-error .radio-inline, .has-error .radio-inline,
.has-error .checkbox-inline, .has-error .checkbox-inline,
.has-error.radio label, .has-error.radio label,
.has-error.checkbox label, .has-error.checkbox label,
.has-error.radio-inline label, .has-error.radio-inline label,
.has-error.checkbox-inline label { .has-error.checkbox-inline label {
color: ${theme.base08}; color: ${theme.base08};
} }
.panel { .panel {
border: none; border: none;
border-radius: 2px; border-radius: 2px;
box-shadow: 0 1px 4px ${theme.base03}; box-shadow: 0 1px 4px ${theme.base03};
margin-bottom: 23px; margin-bottom: 23px;
} }
.panel-heading { .panel-heading {
padding: 5px 15px; padding: 5px 15px;
} }
.panel-title { .panel-title {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
font-size: 15px; font-size: 15px;
} }
.panel-danger { .panel-danger {
box-shadow: 0 1px 4px ${theme.base08}; box-shadow: 0 1px 4px ${theme.base08};
} }
.panel-danger>.panel-heading { .panel-danger > .panel-heading {
color: ${theme.base00}; color: ${theme.base00};
background-color: ${theme.base08}; background-color: ${theme.base08};
} }
.text-danger { .text-danger {
color: ${theme.base08}; color: ${theme.base08};
} }
.list-group { .list-group {
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
.list-group-item { .list-group-item {
position: relative; position: relative;
display: block; display: block;
padding: 10px 15px; padding: 10px 15px;
} }
`; `;

View File

@ -8,7 +8,9 @@ const SelectWidget = ({ options, multi, ...rest }) => (
); );
const RangeWidget = ({ const RangeWidget = ({
schema, readonly, autofocus, schema,
readonly,
autofocus,
label, // eslint-disable-line label, // eslint-disable-line
options, // eslint-disable-line options, // eslint-disable-line
formContext, // eslint-disable-line formContext, // eslint-disable-line

View File

@ -11,16 +11,22 @@ const NotificationWrapper = createStyledComponent(styles);
export default class Notification extends Component { export default class Notification extends Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return nextProps.children !== this.props.children || return (
nextProps.type !== this.props.type; nextProps.children !== this.props.children ||
nextProps.type !== this.props.type
);
} }
getIcon = () => { getIcon = () => {
switch (this.props.type) { switch (this.props.type) {
case 'warning': return <WarningIcon />; case 'warning':
case 'error': return <ErrorIcon />; return <WarningIcon />;
case 'success': return <SuccessIcon />; case 'error':
default: return null; return <ErrorIcon />;
case 'success':
return <SuccessIcon />;
default:
return null;
} }
}; };
@ -29,9 +35,11 @@ export default class Notification extends Component {
<NotificationWrapper type={this.props.type} theme={this.props.theme}> <NotificationWrapper type={this.props.type} theme={this.props.theme}>
{this.getIcon()} {this.getIcon()}
<span>{this.props.children}</span> <span>{this.props.children}</span>
{this.props.onClose && {this.props.onClose && (
<button onClick={this.props.onClose}><CloseIcon /></button> <button onClick={this.props.onClose}>
} <CloseIcon />
</button>
)}
</NotificationWrapper> </NotificationWrapper>
); );
} }

View File

@ -15,18 +15,17 @@ export const Container = styled.div`
storiesOf('Notification', module) storiesOf('Notification', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <Notification
<Container> type={select(
<Notification 'type',
type={ ['info', 'success', 'warning', 'error'],
select('type', ['info', 'success', 'warning', 'error'], 'warning') 'warning'
} )}
onClose={action('notification closed')} onClose={action('notification closed')}
> >
{text('Message', 'Hello Notification')} {text('Message', 'Hello Notification')}
</Notification> </Notification>
</Container> </Container>
) ));
);

View File

@ -49,7 +49,8 @@ export default ({ theme, type }) => css`
opacity: 0.8; opacity: 0.8;
} }
& > button:hover, & > button:active { & > button:hover,
& > button:active {
opacity: 1; opacity: 1;
} }

View File

@ -7,8 +7,10 @@ const SegmentedWrapper = createStyledComponent(styles);
export default class SegmentedControl extends Component { export default class SegmentedControl extends Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return nextProps.disabled !== this.props.disabled || return (
nextProps.selected !== this.props.selected; nextProps.disabled !== this.props.disabled ||
nextProps.selected !== this.props.selected
);
} }
onClick = e => { onClick = e => {

View File

@ -15,16 +15,13 @@ export const Container = styled.div`
storiesOf('SegmentedControl', module) storiesOf('SegmentedControl', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <SegmentedControl
<Container> values={['Button1', 'Button2', 'Button3']}
<SegmentedControl selected={text('selected', 'Button1')}
values={['Button1', 'Button2', 'Button3']} onClick={action('button selected')}
selected={text('selected', 'Button1')} disabled={boolean('Disabled', false)}
onClick={action('button selected')} />
disabled={boolean('Disabled', false)} </Container>
/> ));
</Container>
)
);

View File

@ -5,7 +5,8 @@ export default ({ theme, disabled }) => css`
display: flex; display: flex;
flex-shrink: 0; flex-shrink: 0;
> [data-selected], > [data-selected]:hover { > [data-selected],
> [data-selected]:hover {
background-color: ${theme.base04}; background-color: ${theme.base04};
color: ${theme.base00}; color: ${theme.base00};
} }
@ -19,10 +20,12 @@ export default ({ theme, disabled }) => css`
border: 1px solid ${color(theme.base03, 'alpha', 0.4)}; border: 1px solid ${color(theme.base03, 'alpha', 0.4)};
border-left-width: 0; border-left-width: 0;
padding: 5px 10px; padding: 5px 10px;
${disabled ? ` ${disabled
? `
cursor: not-allowed; cursor: not-allowed;
opacity: 0.6; opacity: 0.6;
` : ` `
: `
cursor: pointer; cursor: pointer;
color: ${theme.base05}; color: ${theme.base05};
background-color: ${theme.base01}; background-color: ${theme.base01};

View File

@ -13,17 +13,22 @@ export default class Select extends (PureComponent || Component) {
} }
Select.propTypes = { Select.propTypes = {
autosize: PropTypes.bool, // whether to enable autosizing or not autosize: PropTypes.bool, // whether to enable autosizing or not
clearable: PropTypes.bool, // should it be possible to reset value clearable: PropTypes.bool, // should it be possible to reset value
disabled: PropTypes.bool, // whether the Select is disabled or not disabled: PropTypes.bool, // whether the Select is disabled or not
isLoading: PropTypes.bool, // whether the Select is loading externally or not isLoading: PropTypes.bool, // whether the Select is loading externally or not
menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options menuMaxHeight: PropTypes.number, // maximum css height for the opened menu of options
multi: PropTypes.bool, // multi-value input multi: PropTypes.bool, // multi-value input
searchable: PropTypes.bool, // whether to enable searching feature or not searchable: PropTypes.bool, // whether to enable searching feature or not
simpleValue: PropTypes.bool, // pass the value with label to onChange simpleValue: PropTypes.bool, // pass the value with label to onChange
value: PropTypes.any, // initial field value value: PropTypes.any, // initial field value
valueKey: PropTypes.string, // path of the label value in option objects valueKey: PropTypes.string, // path of the label value in option objects
openOuterUp: PropTypes.bool // value to control the opening direction openOuterUp: PropTypes.bool // value to control the opening direction
}; };
Select.defaultProps = { autosize: true, clearable: false, simpleValue: true, menuMaxHeight: 200 }; Select.defaultProps = {
autosize: true,
clearable: false,
simpleValue: true,
menuMaxHeight: 200
};

View File

@ -12,7 +12,7 @@ export const Container = styled.div`
width: 100%; width: 100%;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
> div { > div {
width: 90%; width: 90%;
} }
@ -39,8 +39,9 @@ storiesOf('Select', module)
/> />
</Container> </Container>
), ),
{ info: {
'Wrapper around [React Select](https://github.com/JedWatson/react-select) with themes ' info:
+ 'and new props like `openOuterUp` and `menuMaxHeight`.' 'Wrapper around [React Select](https://github.com/JedWatson/react-select) with themes ' +
'and new props like `openOuterUp` and `menuMaxHeight`.'
} }
); );

View File

@ -59,10 +59,9 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
} }
&.is-open > .Select-control { &.is-open > .Select-control {
border-radius: ${openOuterUp ? border-radius: ${openOuterUp
`0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px` : ? `0 0 ${theme.inputBorderRadius}px ${theme.inputBorderRadius}px`
`${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0` : `${theme.inputBorderRadius}px ${theme.inputBorderRadius}px 0 0`};
};
} }
&.is-searchable { &.is-searchable {
@ -212,9 +211,7 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
.Select-arrow { .Select-arrow {
border-color: ${theme.base03} transparent transparent; border-color: ${theme.base03} transparent transparent;
border-style: solid; border-style: solid;
border-width: border-width: ${theme.selectArrowWidth}px ${theme.selectArrowWidth}px
${theme.selectArrowWidth}px
${theme.selectArrowWidth}px
${theme.selectArrowWidth / 2}px; ${theme.selectArrowWidth / 2}px;
display: inline-block; display: inline-block;
height: 0; height: 0;
@ -317,7 +314,8 @@ export default ({ theme, openOuterUp, menuMaxHeight }) => css`
border-bottom-right-radius: ${theme.inputBorderRadius}px; border-bottom-right-radius: ${theme.inputBorderRadius}px;
border-top-right-radius: ${theme.inputBorderRadius}px; border-top-right-radius: ${theme.inputBorderRadius}px;
cursor: default; cursor: default;
padding: ${Math.floor(theme.inputPadding / 4)}px ${Math.floor(theme.inputPadding / 2)}px; padding: ${Math.floor(theme.inputPadding / 4)}px
${Math.floor(theme.inputPadding / 2)}px;
} }
a.Select-value-label { a.Select-value-label {

View File

@ -9,12 +9,14 @@ const ContainerWithValue = createStyledComponent(containerStyle);
export default class Slider extends Component { export default class Slider extends Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return nextProps.label !== this.props.label || return (
nextProps.label !== this.props.label ||
nextProps.value !== this.props.value || nextProps.value !== this.props.value ||
nextProps.max !== this.props.max || nextProps.max !== this.props.max ||
nextProps.min !== this.props.min || nextProps.min !== this.props.min ||
nextProps.withValue !== this.props.withValue || nextProps.withValue !== this.props.withValue ||
nextProps.disabled !== this.props.disabled; nextProps.disabled !== this.props.disabled
);
} }
onChange = e => { onChange = e => {
@ -25,7 +27,7 @@ export default class Slider extends Component {
const { label, sublabel, withValue, theme, ...rest } = this.props; const { label, sublabel, withValue, theme, ...rest } = this.props;
const { value, max, min, disabled } = rest; const { value, max, min, disabled } = rest;
const absMax = max - min; const absMax = max - min;
const percent = (value - min) / absMax * 100; const percent = ((value - min) / absMax) * 100;
const slider = <input {...rest} onChange={this.onChange} type="range" />; const slider = <input {...rest} onChange={this.onChange} type="range" />;
return ( return (
@ -35,13 +37,19 @@ export default class Slider extends Component {
withLabel={!!label} withLabel={!!label}
theme={theme} theme={theme}
> >
{label && <label>{label} {sublabel && <span>{sublabel}</span>}</label>} {label && (
{!withValue ? slider : <label>
{label} {sublabel && <span>{sublabel}</span>}
</label>
)}
{!withValue ? (
slider
) : (
<ContainerWithValue theme={theme}> <ContainerWithValue theme={theme}>
{slider} {slider}
<div>{value}</div> <div>{value}</div>
</ContainerWithValue> </ContainerWithValue>
} )}
</SliderWrapper> </SliderWrapper>
); );
} }

View File

@ -15,20 +15,17 @@ export const Container = styled.div`
storiesOf('Slider', module) storiesOf('Slider', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <Slider
<Container> value={number('value', 0)}
<Slider min={number('min', 0)}
value={number('value', 0)} max={number('max', 100)}
min={number('min', 0)} label={text('label', 'Slider label')}
max={number('max', 100)} sublabel={text('sublabel', '(sublabel)')}
label={text('label', 'Slider label')} withValue={boolean('withValue', false)}
sublabel={text('sublabel', '(sublabel)')} disabled={boolean('disabled', false)}
withValue={boolean('withValue', false)} onChange={action('slider changed')}
disabled={boolean('disabled', false)} />
onChange={action('slider changed')} </Container>
/> ));
</Container>
)
);

View File

@ -42,21 +42,32 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
border-radius: 0.8em/1.1em; border-radius: 0.8em/1.1em;
font-size: 1em; font-size: 1em;
cursor: pointer; cursor: pointer;
background: linear-gradient(${theme.base02}, ${theme.base00}) padding-box, 50% 50% border-box; background: linear-gradient(${theme.base02}, ${
theme.base00
}) padding-box, 50% 50% border-box;
background-size: 100% 100%; background-size: 100% 100%;
} }
${prefixSelectors('input', ['webkit-slider-runnable-track', 'moz-range-track', 'ms-track'], `{ ${prefixSelectors(
'input',
['webkit-slider-runnable-track', 'moz-range-track', 'ms-track'],
`{
position: relative; position: relative;
height: 0.8em; height: 0.8em;
border-radius: 0.5em; border-radius: 0.5em;
box-shadow: 0 0 .125em ${theme.base04}; box-shadow: 0 0 .125em ${theme.base04};
background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${theme.base01}) background: linear-gradient(${theme.base01}, ${theme.base02} 40%, ${
theme.base01
})
no-repeat ${theme.base00}; no-repeat ${theme.base00};
background-size: ${percent}% 100%; background-size: ${percent}% 100%;
}`)} }`
)}
${prefixSelectors('input', ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], `{ ${prefixSelectors(
'input',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
position: relative; position: relative;
appearance: none; appearance: none;
cursor: ew-resize; cursor: ew-resize;
@ -68,13 +79,16 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
height: 1.5em; height: 1.5em;
border-radius: 50%; border-radius: 50%;
cursor: pointer; cursor: pointer;
}`)} }`
)}
${prefixSelectors('input:focus:not(:active)', ${prefixSelectors(
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], 'input:focus:not(:active)',
`{ ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
box-shadow: 0 0 1px 2px ${theme.base0D}; box-shadow: 0 0 1px 2px ${theme.base0D};
}`)} }`
)}
input::-moz-focus-outer { input::-moz-focus-outer {
border: 0; border: 0;

View File

@ -19,7 +19,9 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
width: 100%; width: 100%;
color: ${theme.base06}; color: ${theme.base06};
> span { color: ${theme.base04}; } > span {
color: ${theme.base04};
}
} }
input { input {
@ -32,8 +34,12 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
cursor: pointer; cursor: pointer;
color: inherit; color: inherit;
background-color: ${theme.base02}; background-color: ${theme.base02};
background-image: background-image: linear-gradient(
linear-gradient(90deg, currentcolor, currentcolor ${percent}%, transparent ${percent}%); 90deg,
currentcolor,
currentcolor ${percent}%,
transparent ${percent}%
);
background-clip: content-box; background-clip: content-box;
height: 0.5em; height: 0.5em;
border-radius: 999px; border-radius: 999px;
@ -41,7 +47,10 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
font-size: 1em; font-size: 1em;
} }
${prefixSelectors('input', ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], `{ ${prefixSelectors(
'input',
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
background-image: none; background-image: none;
@ -53,14 +62,17 @@ export const style = ({ theme, percent, disabled, withLabel }) => css`
border 0.18s ${animationCurve}, border 0.18s ${animationCurve},
box-shadow 0.18s ${animationCurve}, box-shadow 0.18s ${animationCurve},
background 0.28s ${animationCurve}; background 0.28s ${animationCurve};
}`)} }`
)}
${prefixSelectors('input:focus:not(:active)', ${prefixSelectors(
['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'], 'input:focus:not(:active)',
`{ ['webkit-slider-thumb', 'moz-range-thumb', 'ms-thumb'],
`{
box-shadow: 0 0 0 8px ${color(theme.base0D, 'alpha', 0.5)}; box-shadow: 0 0 0 8px ${color(theme.base0D, 'alpha', 0.5)};
transform: scale(1.2); transform: scale(1.2);
}`)} }`
)}
input::-moz-focus-outer { input::-moz-focus-outer {
border: 0; border: 0;

View File

@ -75,7 +75,9 @@ export default class Tabs extends Component {
return ( return (
<TabsContainer position={this.props.position}> <TabsContainer position={this.props.position}>
{tabsHeader} {tabsHeader}
<div><this.SelectedComponent {...(this.selector && this.selector())} /></div> <div>
<this.SelectedComponent {...this.selector && this.selector()} />
</div>
</TabsContainer> </TabsContainer>
); );
} }

View File

@ -22,9 +22,11 @@ export default class TabsHeader extends Component {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.tabs !== this.props.tabs || if (
nextProps.tabs !== this.props.tabs ||
nextProps.selected !== this.props.selected || nextProps.selected !== this.props.selected ||
nextProps.collapsible !== this.props.collapsible) { nextProps.collapsible !== this.props.collapsible
) {
this.setState({ hiddenTabs: [], visibleTabs: nextProps.tabs.slice() }); this.setState({ hiddenTabs: [], visibleTabs: nextProps.tabs.slice() });
} }
} }
@ -47,7 +49,9 @@ export default class TabsHeader extends Component {
if (this.iconWidth === 0) { if (this.iconWidth === 0) {
const tabButtons = this.tabsRef.children; const tabButtons = this.tabsRef.children;
if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') { if (this.tabsRef.children[tabButtons.length - 1].value === 'expandIcon') {
this.iconWidth = tabButtons[tabButtons.length - 1].getBoundingClientRect().width; this.iconWidth = tabButtons[
tabButtons.length - 1
].getBoundingClientRect().width;
shouldCollapse = true; shouldCollapse = true;
} }
} else if (this.state.hiddenTabs.length === 0) { } else if (this.state.hiddenTabs.length === 0) {
@ -96,14 +100,15 @@ export default class TabsHeader extends Component {
if (tabsRefRight >= tabsWrapperRight - this.iconWidth) { if (tabsRefRight >= tabsWrapperRight - this.iconWidth) {
if ( if (
this.props.position === 'right' && hiddenTabs.length > 0 && this.props.position === 'right' &&
hiddenTabs.length > 0 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width tabsWrapperRef.getBoundingClientRect().width
) { ) {
while ( while (
i < tabs.length - 1 && i < tabs.length - 1 &&
tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] < tabsRef.getBoundingClientRect().width + this.hiddenTabsWidth[0] <
tabsWrapperRef.getBoundingClientRect().width tabsWrapperRef.getBoundingClientRect().width
) { ) {
hiddenTab = hiddenTabs.shift(); hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
@ -111,12 +116,16 @@ export default class TabsHeader extends Component {
} }
} else { } else {
while ( while (
i > 0 && tabButtons[i] && i > 0 &&
tabButtons[i].getBoundingClientRect().right >= tabsWrapperRight - this.iconWidth tabButtons[i] &&
tabButtons[i].getBoundingClientRect().right >=
tabsWrapperRight - this.iconWidth
) { ) {
if (tabButtons[i].value !== selected) { if (tabButtons[i].value !== selected) {
hiddenTabs.unshift(...visibleTabs.splice(i, 1)); hiddenTabs.unshift(...visibleTabs.splice(i, 1));
this.hiddenTabsWidth.unshift(tabButtons[i].getBoundingClientRect().width); this.hiddenTabsWidth.unshift(
tabButtons[i].getBoundingClientRect().width
);
} else { } else {
tabsWrapperRight -= tabButtons[i].getBoundingClientRect().width; tabsWrapperRight -= tabButtons[i].getBoundingClientRect().width;
} }
@ -125,9 +134,10 @@ export default class TabsHeader extends Component {
} }
} else { } else {
while ( while (
i < tabs.length - 1 && tabButtons[i] && i < tabs.length - 1 &&
tabButtons[i].getBoundingClientRect().right + tabButtons[i] &&
this.hiddenTabsWidth[0] < tabsWrapperRight - this.iconWidth tabButtons[i].getBoundingClientRect().right + this.hiddenTabsWidth[0] <
tabsWrapperRight - this.iconWidth
) { ) {
hiddenTab = hiddenTabs.shift(); hiddenTab = hiddenTabs.shift();
visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab); visibleTabs.splice(Number(hiddenTab.key), 0, hiddenTab);
@ -150,7 +160,7 @@ export default class TabsHeader extends Component {
this.tabsRef = node; this.tabsRef = node;
}; };
expandMenu = (e) => { expandMenu = e => {
const rect = e.currentTarget.children[0].getBoundingClientRect(); const rect = e.currentTarget.children[0].getBoundingClientRect();
this.setState({ this.setState({
contextMenu: { contextMenu: {
@ -171,11 +181,14 @@ export default class TabsHeader extends Component {
> >
<div ref={this.getTabsRef}> <div ref={this.getTabsRef}>
{visibleTabs} {visibleTabs}
{this.props.collapsible && visibleTabs.length < this.props.items.length && {this.props.collapsible &&
<button onClick={this.expandMenu} value="expandIcon"><CollapseIcon /></button> visibleTabs.length < this.props.items.length && (
} <button onClick={this.expandMenu} value="expandIcon">
<CollapseIcon />
</button>
)}
</div> </div>
{this.props.collapsible && contextMenu && {this.props.collapsible && contextMenu && (
<ContextMenu <ContextMenu
items={hiddenTabs} items={hiddenTabs}
onClick={this.props.onClick} onClick={this.props.onClick}
@ -183,7 +196,7 @@ export default class TabsHeader extends Component {
y={contextMenu.top} y={contextMenu.top}
visible={this.state.subMenuOpened} visible={this.state.subMenuOpened}
/> />
} )}
</TabsWrapper> </TabsWrapper>
); );
} }
@ -198,4 +211,3 @@ TabsHeader.propTypes = {
collapsible: PropTypes.bool, collapsible: PropTypes.bool,
selected: PropTypes.string selected: PropTypes.string
}; };

View File

@ -38,4 +38,5 @@ export const tabs = [
]; ];
export const simple10Tabs = []; export const simple10Tabs = [];
for (let i = 1; i <= 10; i++) simple10Tabs.push({ name: `Tab${i}`, value: `${i}` }); for (let i = 1; i <= 10; i++)
simple10Tabs.push({ name: `Tab${i}`, value: `${i}` });

View File

@ -16,29 +16,25 @@ const Container = styled.div`
storiesOf('Tabs', module) storiesOf('Tabs', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <Tabs
<Container><Tabs
tabs={simple10Tabs} tabs={simple10Tabs}
selected={text('selected', '2')} selected={text('selected', '2')}
main={boolean('main', true)} main={boolean('main', true)}
onClick={action('tab selected')} onClick={action('tab selected')}
collapsible={boolean('collapsible', true)} collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'left')} position={select('position', ['left', 'right', 'center'], 'left')}
/></Container>
)
)
.add(
'with content',
() => (
<Tabs
tabs={tabs}
selected={text('selected', 'Tab2')}
main={boolean('main', false)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', false)}
position={select('position', ['left', 'right', 'center'], 'left')}
/> />
) </Container>
); ))
.add('with content', () => (
<Tabs
tabs={tabs}
selected={text('selected', 'Tab2')}
main={boolean('main', false)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', false)}
position={select('position', ['left', 'right', 'center'], 'left')}
/>
));

View File

@ -11,10 +11,14 @@ export const TabsContainer = styled.div`
height: 100%; height: 100%;
> div > div:first-child { > div > div:first-child {
${props => props.position !== 'left' && ` ${props =>
props.position !== 'left' &&
`
margin-left: auto !important; margin-left: auto !important;
`} `}
${props => props.position === 'center' && ` ${props =>
props.position === 'center' &&
`
margin-right: auto !important; margin-right: auto !important;
`} `}
} }

View File

@ -7,7 +7,8 @@ export const style = ({ theme, main }) => css`
background-color: ${theme.base01}; background-color: ${theme.base01};
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
${!main && ` ${!main &&
`
border-top: 1px solid ${theme.base01}; border-top: 1px solid ${theme.base01};
border-bottom: 1px solid ${theme.base02}; border-bottom: 1px solid ${theme.base02};
`} `}
@ -39,9 +40,10 @@ export const style = ({ theme, main }) => css`
} }
> [data-selected] { > [data-selected] {
${main ? ${
`border-bottom: 2px solid ${theme.base0D};` : main
` ? `border-bottom: 2px solid ${theme.base0D};`
: `
background-color: ${theme.base00}; background-color: ${theme.base00};
border: 1px solid ${theme.base02}; border: 1px solid ${theme.base02};
border-bottom: 1px solid ${theme.base00}; border-bottom: 1px solid ${theme.base00};

View File

@ -8,7 +8,8 @@ export const style = ({ theme, main }) => css`
background-color: ${theme.base01}; background-color: ${theme.base01};
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
${!main && ` ${!main &&
`
border-top: 1px solid ${theme.base01}; border-top: 1px solid ${theme.base01};
border-bottom: 1px solid ${theme.base02}; border-bottom: 1px solid ${theme.base02};
`} `}
@ -37,7 +38,7 @@ export const style = ({ theme, main }) => css`
border-bottom: 2px solid ${theme.base03}; border-bottom: 2px solid ${theme.base03};
color: ${theme.base04}; color: ${theme.base04};
} }
&.collapsed{ &.collapsed {
display: none; display: none;
} }

View File

@ -2,12 +2,27 @@ import React from 'react';
import { storiesOf } from '@storybook/react'; import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import styled from 'styled-components'; import styled from 'styled-components';
import { withKnobs, text, number, boolean, select } from '@storybook/addon-knobs'; import {
withKnobs,
text,
number,
boolean,
select
} from '@storybook/addon-knobs';
import PlayIcon from 'react-icons/lib/md/play-arrow'; import PlayIcon from 'react-icons/lib/md/play-arrow';
import RecordIcon from 'react-icons/lib/md/fiber-manual-record'; import RecordIcon from 'react-icons/lib/md/fiber-manual-record';
import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left'; import LeftIcon from 'react-icons/lib/md/keyboard-arrow-left';
import RightIcon from 'react-icons/lib/md/keyboard-arrow-right'; import RightIcon from 'react-icons/lib/md/keyboard-arrow-right';
import { Toolbar, Divider, Spacer, Button, Select, Slider, SegmentedControl, Tabs } from '../../'; import {
Toolbar,
Divider,
Spacer,
Button,
Select,
Slider,
SegmentedControl,
Tabs
} from '../../';
import { options } from '../../Select/stories/options'; import { options } from '../../Select/stories/options';
import { simple10Tabs } from '../../Tabs/stories/data'; import { simple10Tabs } from '../../Tabs/stories/data';
@ -26,132 +41,165 @@ export const SliderContainer = styled.div`
storiesOf('Toolbar', module) storiesOf('Toolbar', module)
.addDecorator(withKnobs) .addDecorator(withKnobs)
.add( .add('default', () => (
'default', <Container>
() => ( <Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}>
<Container> <Button
<Toolbar borderPosition={select('borderPosition', ['top', 'bottom'])}> title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Divider />
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<RecordIcon />
</Button>
<Divider />
<Spacer />
<Select options={options} />
</Toolbar>
</Container>
))
.add('tabs', () => (
<Container>
<Toolbar>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Tabs
tabs={simple10Tabs}
selected={text('selected', '2')}
main={boolean('main', true)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'center')}
/>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
</Toolbar>
</Container>
))
.add('with slider', () => (
<Container>
<SliderContainer>
<Toolbar noBorder fullHeight compact>
<Button <Button
title={text('Title', 'Hello Tooltip')} title={text('play title', 'Play')}
tooltipPosition={ tooltipPosition={select('tooltipPosition', [
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'top',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'bottom',
} 'left',
disabled={boolean('Disabled', false)} 'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
onClick={action('button clicked')} onClick={action('button clicked')}
> >
{text('Label', 'Hello Button')} <PlayIcon />
</Button> </Button>
<Divider /> <Slider
<Button value={number('value', 80)}
title={text('Title', 'Hello Tooltip')} min={number('min', 0)}
tooltipPosition={ max={number('max', 100)}
select('tooltipPosition', ['top', 'bottom', 'left', 'right', label={text('label', 'Slider label')}
'bottom-left', 'bottom-right', 'top-left', 'top-right']) withValue={boolean('withValue', false)}
} onChange={action('slider changed')}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
<RecordIcon />
</Button>
<Divider />
<Spacer />
<Select options={options} />
</Toolbar>
</Container>
)
)
.add(
'tabs',
() => (
<Container>
<Toolbar>
<Button
title={text('Title', 'Hello Tooltip')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled={boolean('Disabled', false)}
onClick={action('button clicked')}
>
{text('Label', 'Hello Button')}
</Button>
<Tabs
tabs={simple10Tabs}
selected={text('selected', '2')}
main={boolean('main', true)}
onClick={action('tab selected')}
collapsible={boolean('collapsible', true)}
position={select('position', ['left', 'right', 'center'], 'center')}
/> />
<Button <Button
title={text('Title', 'Hello Tooltip')} title="Previous state"
tooltipPosition={ tooltipPosition={select('tooltipPosition', [
select('tooltipPosition', ['top', 'bottom', 'left', 'right', 'top',
'bottom-left', 'bottom-right', 'top-left', 'top-right']) 'bottom',
} 'left',
disabled={boolean('Disabled', false)} 'right',
onClick={action('button clicked')} 'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
disabled
onClick={action('previous state clicked')}
> >
{text('Label', 'Hello Button')} <LeftIcon />
</Button> </Button>
<Button
title="Next state"
tooltipPosition={select('tooltipPosition', [
'top',
'bottom',
'left',
'right',
'bottom-left',
'bottom-right',
'top-left',
'top-right'
])}
onClick={action('next state clicked')}
>
<RightIcon />
</Button>
<SegmentedControl
values={['live', '1x']}
selected={select('selected', ['live', '1x'], 'live')}
onClick={action('button selected')}
/>
</Toolbar> </Toolbar>
</Container> </SliderContainer>
) </Container>
) ));
.add(
'with slider',
() => (
<Container>
<SliderContainer>
<Toolbar noBorder fullHeight compact>
<Button
title={text('play title', 'Play')}
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
onClick={action('button clicked')}
>
<PlayIcon />
</Button>
<Slider
value={number('value', 80)}
min={number('min', 0)}
max={number('max', 100)}
label={text('label', 'Slider label')}
withValue={boolean('withValue', false)}
onChange={action('slider changed')}
/>
<Button
title="Previous state"
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
disabled
onClick={action('previous state clicked')}
>
<LeftIcon />
</Button>
<Button
title="Next state"
tooltipPosition={
select('tooltipPosition', ['top', 'bottom', 'left', 'right',
'bottom-left', 'bottom-right', 'top-left', 'top-right'])
}
onClick={action('next state clicked')}
>
<RightIcon />
</Button>
<SegmentedControl
values={['live', '1x']}
selected={select('selected', ['live', '1x'], 'live')}
onClick={action('button selected')}
/>
</Toolbar>
</SliderContainer>
</Container>
)
);

View File

@ -14,8 +14,7 @@ const Toolbar = styled.div`
text-align: center; text-align: center;
position: relative; position: relative;
${({ borderPosition, theme }) => ${({ borderPosition, theme }) =>
borderPosition && `border-${borderPosition}: 1px solid ${theme.base02};` borderPosition && `border-${borderPosition}: 1px solid ${theme.base02};`}
}
& > div { & > div {
margin: auto ${props => (props.noBorder ? '0' : '1px;')}; margin: auto ${props => (props.noBorder ? '0' : '1px;')};

View File

@ -1,4 +1,4 @@
export default (colors) => ({ export default colors => ({
...colors, ...colors,
fontFamily: "'Source Sans Pro', sans-serif", fontFamily: "'Source Sans Pro', sans-serif",
codeFontFamily: "'Source Code Pro', monospace", codeFontFamily: "'Source Code Pro', monospace",

View File

@ -1,4 +1,4 @@
export default (colors) => ({ export default colors => ({
fontFamily: "'Roboto', sans-serif", fontFamily: "'Roboto', sans-serif",
codeFontFamily: "'Roboto Mono', monospace", codeFontFamily: "'Roboto Mono', monospace",
inputPadding: 5, inputPadding: 5,

View File

@ -36,7 +36,9 @@ export const ripple = theme => `
top: 0; top: 0;
left: 0; left: 0;
pointer-events: none; pointer-events: none;
background-image: radial-gradient(circle, ${theme.base07} 11%, transparent 11%); background-image: radial-gradient(circle, ${
theme.base07
} 11%, transparent 11%);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 50%; background-position: 50%;
transform: scale(10, 10); transform: scale(10, 10);

View File

@ -7,6 +7,10 @@ import Color from 'color';
effect('#000000', 'alpha', 0.5); effect('#000000', 'alpha', 0.5);
*/ */
export default (color, effect, val) => new Color(color)[effect](val).hsl().string(); export default (color, effect, val) =>
new Color(color)
[effect](val)
.hsl()
.string();
// TODO: memoize it // TODO: memoize it

View File

@ -1,17 +1,19 @@
import styled from 'styled-components'; import styled from 'styled-components';
import getDefaultTheme from '../themes/default'; import getDefaultTheme from '../themes/default';
const getStyle = (styles, type) => ( const getStyle = (styles, type) =>
typeof styles === 'object' ? styles[type] || styles.default : styles typeof styles === 'object' ? styles[type] || styles.default : styles;
);
export default (styles, component) => export default (styles, component) =>
styled(component || 'div')`${ styled(component || 'div')`
props => ( ${props =>
props.theme.type ? getStyle(styles, props.theme.type) : props.theme.type
// used outside of container (theme provider) ? getStyle(styles, props.theme.type)
getStyle(styles, 'default')({ ...props, theme: getDefaultTheme(props.theme) }) : // used outside of container (theme provider)
) getStyle(styles, 'default')({
}`; ...props,
theme: getDefaultTheme(props.theme)
})}
`;
// TODO: memoize it? // TODO: memoize it?

View File

@ -5,7 +5,10 @@ import * as additionalSchemes from '../colorSchemes';
import invertColors from '../utils/invertColors'; import invertColors from '../utils/invertColors';
export const schemes = { ...baseSchemes, ...additionalSchemes }; export const schemes = { ...baseSchemes, ...additionalSchemes };
export const listSchemes = () => Object.keys(schemes).slice(1).sort(); // remove `__esModule` export const listSchemes = () =>
Object.keys(schemes)
.slice(1)
.sort(); // remove `__esModule`
export const listThemes = () => Object.keys(themes); export const listThemes = () => Object.keys(themes);
export const getTheme = ({ theme: type, scheme, light }) => { export const getTheme = ({ theme: type, scheme, light }) => {

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Button } from '../src'; import { Button } from '../src';
describe('Button', function () { describe('Button', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Button>Text</Button>); const wrapper = render(<Button>Text</Button>);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -3,10 +3,12 @@ import { render } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Container } from '../src'; import { Container } from '../src';
describe('Container', function () { describe('Container', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render( const wrapper = render(
<Container themeData={{ theme: 'default', scheme: 'default', invert: false }}> <Container
themeData={{ theme: 'default', scheme: 'default', invert: false }}
>
Text Text
</Container> </Container>
); );

View File

@ -4,29 +4,23 @@ import { renderToJson } from 'enzyme-to-json';
import { ContextMenu } from '../src'; import { ContextMenu } from '../src';
import { items } from '../src/ContextMenu/stories/data'; import { items } from '../src/ContextMenu/stories/data';
describe('ContextMenu', function () { describe('ContextMenu', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render( const wrapper = render(
<ContextMenu <ContextMenu items={items} onClick={() => {}} x={100} y={100} />
items={items}
onClick={() => {}}
x={100}
y={100}
/>
); );
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
it('should handle the click event', () => { it('should handle the click event', () => {
const onClick = jest.fn(); const onClick = jest.fn();
const wrapper = mount( const wrapper = mount(
<ContextMenu <ContextMenu items={items} onClick={onClick} x={100} y={100} />
items={items} );
onClick={onClick}
x={100}
y={100}
/>);
wrapper.find('button').first().simulate('click'); wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled(); expect(onClick).toBeCalled();
}); });
}); });

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Dialog } from '../src'; import { Dialog } from '../src';
describe('Dialog', function () { describe('Dialog', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Dialog />); const wrapper = render(<Dialog />);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
@ -11,11 +11,7 @@ describe('Dialog', function () {
it('renders with props', () => { it('renders with props', () => {
const wrapper = render( const wrapper = render(
<Dialog <Dialog title="Dialog Title" open fullWidth>
title="Dialog Title"
open
fullWidth
>
Hello Dialog! Hello Dialog!
</Dialog> </Dialog>
); );
@ -31,7 +27,10 @@ describe('Dialog', function () {
const onDismiss = jest.fn(); const onDismiss = jest.fn();
const wrapper = mount(<Dialog open onDismiss={onDismiss} />); const wrapper = mount(<Dialog open onDismiss={onDismiss} />);
wrapper.find('button').first().simulate('click'); wrapper
.find('button')
.first()
.simulate('click');
expect(onDismiss).toBeCalled(); expect(onDismiss).toBeCalled();
}); });
@ -39,7 +38,10 @@ describe('Dialog', function () {
const onSubmit = jest.fn(); const onSubmit = jest.fn();
const wrapper = mount(<Dialog open onSubmit={onSubmit} />); const wrapper = mount(<Dialog open onSubmit={onSubmit} />);
wrapper.find('button').last().simulate('click'); wrapper
.find('button')
.last()
.simulate('click');
expect(onSubmit).toBeCalled(); expect(onSubmit).toBeCalled();
}); });
}); });

View File

@ -4,10 +4,10 @@ import { mountToJson } from 'enzyme-to-json';
import { Editor } from '../src'; import { Editor } from '../src';
import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/javascript/javascript';
describe('Editor', function () { describe('Editor', function() {
const getBoundingClientRect = jest.fn(); const getBoundingClientRect = jest.fn();
const getClientRects = jest.fn(); const getClientRects = jest.fn();
document.body.createTextRange = function () { document.body.createTextRange = function() {
return { return {
getBoundingClientRect() { getBoundingClientRect() {
getBoundingClientRect(); getBoundingClientRect();

View File

@ -4,14 +4,11 @@ import { shallowToJson } from 'enzyme-to-json';
import { Form } from '../src'; import { Form } from '../src';
import { schema, uiSchema, formData } from '../src/Form/stories/schema'; import { schema, uiSchema, formData } from '../src/Form/stories/schema';
describe('Form', function () { describe('Form', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = shallow( const wrapper = shallow(
<Form <Form formData={formData} schema={schema} uiSchema={uiSchema} />
formData={formData} );
schema={schema}
uiSchema={uiSchema}
/>);
expect(shallowToJson(wrapper)).toMatchSnapshot(); expect(shallowToJson(wrapper)).toMatchSnapshot();
}); });
@ -23,18 +20,15 @@ describe('Form', function () {
formData={formData} formData={formData}
schema={schema} schema={schema}
uiSchema={uiSchema} uiSchema={uiSchema}
/>); />
);
expect(shallowToJson(wrapper)).toMatchSnapshot(); expect(shallowToJson(wrapper)).toMatchSnapshot();
}); });
it('renders with no button', () => { it('renders with no button', () => {
const wrapper = shallow( const wrapper = shallow(
<Form <Form formData={formData} schema={schema} uiSchema={uiSchema} noSubmit />
formData={formData} );
schema={schema}
uiSchema={uiSchema}
noSubmit
/>);
expect(shallowToJson(wrapper)).toMatchSnapshot(); expect(shallowToJson(wrapper)).toMatchSnapshot();
}); });

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Notification } from '../src'; import { Notification } from '../src';
describe('Notification', function () { describe('Notification', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Notification>Message</Notification>); const wrapper = render(<Notification>Message</Notification>);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
@ -11,14 +11,18 @@ describe('Notification', function () {
it('renders with props', () => { it('renders with props', () => {
const wrapper = render( const wrapper = render(
<Notification type="error" onClose={() => {}}>Message</Notification> <Notification type="error" onClose={() => {}}>
Message
</Notification>
); );
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
it('should handle the click event', () => { it('should handle the click event', () => {
const onClose = jest.fn(); const onClose = jest.fn();
const wrapper = mount(<Notification onClose={onClose}>Message</Notification>); const wrapper = mount(
<Notification onClose={onClose}>Message</Notification>
);
wrapper.find('button').simulate('click'); wrapper.find('button').simulate('click');
expect(onClose).toBeCalled(); expect(onClose).toBeCalled();

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { SegmentedControl } from '../src'; import { SegmentedControl } from '../src';
describe('SegmentedControl', function () { describe('SegmentedControl', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render( const wrapper = render(
<SegmentedControl <SegmentedControl
@ -17,15 +17,19 @@ describe('SegmentedControl', function () {
}); });
it('should handle the click event', () => { it('should handle the click event', () => {
const onClick = jest.fn(); const onClick = jest.fn();
const wrapper = mount(<SegmentedControl const wrapper = mount(
values={['Button1', 'Button2', 'Button3']} <SegmentedControl
selected="Button1" values={['Button1', 'Button2', 'Button3']}
disabled={false} selected="Button1"
onClick={onClick} disabled={false}
/> onClick={onClick}
/>
); );
wrapper.find('button').first().simulate('click'); wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled(); expect(onClick).toBeCalled();
}); });
}); });

View File

@ -4,7 +4,7 @@ import { renderToJson, mountToJson } from 'enzyme-to-json';
import { Select } from '../src'; import { Select } from '../src';
import { options } from '../src/Select/stories/options'; import { options } from '../src/Select/stories/options';
describe('Select', function () { describe('Select', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Select options={options} onChange={() => {}} />); const wrapper = render(<Select options={options} onChange={() => {}} />);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -3,7 +3,7 @@ import { render, mount } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Slider } from '../src'; import { Slider } from '../src';
describe('Slider', function () { describe('Slider', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Slider />); const wrapper = render(<Slider />);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();

View File

@ -4,7 +4,7 @@ import { renderToJson } from 'enzyme-to-json';
import { Tabs } from '../src'; import { Tabs } from '../src';
import { tabs, simple10Tabs } from '../src/Tabs/stories/data'; import { tabs, simple10Tabs } from '../src/Tabs/stories/data';
describe('Tabs', function () { describe('Tabs', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render(<Tabs tabs={tabs} onClick={() => {}} />); const wrapper = render(<Tabs tabs={tabs} onClick={() => {}} />);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
@ -12,22 +12,14 @@ describe('Tabs', function () {
it('renders with props', () => { it('renders with props', () => {
const wrapper = render( const wrapper = render(
<Tabs <Tabs tabs={tabs} onClick={() => {}} selected="Tab2" />
tabs={tabs}
onClick={() => {}}
selected="Tab2"
/>
); );
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
it('renders tabs without inner components', () => { it('renders tabs without inner components', () => {
const wrapper = render( const wrapper = render(
<Tabs <Tabs tabs={simple10Tabs} onClick={() => {}} selected="5" />
tabs={simple10Tabs}
onClick={() => {}}
selected="5"
/>
); );
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
@ -36,7 +28,10 @@ describe('Tabs', function () {
const onClick = jest.fn(); const onClick = jest.fn();
const wrapper = mount(<Tabs tabs={tabs} onClick={onClick} />); const wrapper = mount(<Tabs tabs={tabs} onClick={onClick} />);
wrapper.find('button').first().simulate('click'); wrapper
.find('button')
.first()
.simulate('click');
expect(onClick).toBeCalled(); expect(onClick).toBeCalled();
}); });
}); });

View File

@ -3,7 +3,7 @@ import { render } from 'enzyme';
import { renderToJson } from 'enzyme-to-json'; import { renderToJson } from 'enzyme-to-json';
import { Toolbar, Divider, Spacer, Button } from '../src'; import { Toolbar, Divider, Spacer, Button } from '../src';
describe('Toolbar', function () { describe('Toolbar', function() {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render( const wrapper = render(
<Toolbar> <Toolbar>
@ -17,9 +17,7 @@ describe('Toolbar', function () {
}); });
it('renders with props', () => { it('renders with props', () => {
const wrapper = render( const wrapper = render(<Toolbar borderPosition="top" />);
<Toolbar borderPosition="top" />
);
expect(renderToJson(wrapper)).toMatchSnapshot(); expect(renderToJson(wrapper)).toMatchSnapshot();
}); });
}); });

View File

@ -2,10 +2,10 @@
exports[`Button renders correctly 1`] = ` exports[`Button renders correctly 1`] = `
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
Text Text
</button> </button>

View File

@ -2,7 +2,7 @@
exports[`Container renders correctly 1`] = ` exports[`Container renders correctly 1`] = `
<div <div
class="sc-bdVaJa dRsjcb" class="sc-bdVaJa ODaHo"
> >
Text Text
</div> </div>

View File

@ -2,7 +2,7 @@
exports[`ContextMenu renders correctly 1`] = ` exports[`ContextMenu renders correctly 1`] = `
<div <div
class="sc-EHOje dIpPFG" class="sc-EHOje cfLLnh"
> >
<button <button
value="Menu Item 1" value="Menu Item 1"

View File

@ -2,7 +2,7 @@
exports[`Dialog renders correctly 1`] = ` exports[`Dialog renders correctly 1`] = `
<div <div
class="sc-iwsKbI fOBkrR" class="sc-iwsKbI islPis"
> >
<div /> <div />
<div> <div>
@ -21,19 +21,19 @@ exports[`Dialog renders correctly 1`] = `
class="mc-dialog--footer" class="mc-dialog--footer"
> >
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
Cancel Cancel
</button> </button>
</div> </div>
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat iMkoYt" class="sc-htpNat cvNnmn"
> >
Submit Submit
</button> </button>
@ -45,7 +45,7 @@ exports[`Dialog renders correctly 1`] = `
exports[`Dialog renders modal 1`] = ` exports[`Dialog renders modal 1`] = `
<div <div
class="sc-iwsKbI fOBkrR" class="sc-iwsKbI islPis"
> >
<div /> <div />
<div> <div>
@ -61,19 +61,19 @@ exports[`Dialog renders modal 1`] = `
class="mc-dialog--footer" class="mc-dialog--footer"
> >
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
Cancel Cancel
</button> </button>
</div> </div>
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat iMkoYt" class="sc-htpNat cvNnmn"
> >
Submit Submit
</button> </button>
@ -85,7 +85,7 @@ exports[`Dialog renders modal 1`] = `
exports[`Dialog renders with props 1`] = ` exports[`Dialog renders with props 1`] = `
<div <div
class="sc-iwsKbI fpUYbC" class="sc-iwsKbI hRSLqU"
open="" open=""
> >
<div /> <div />
@ -109,19 +109,19 @@ exports[`Dialog renders with props 1`] = `
class="mc-dialog--footer" class="mc-dialog--footer"
> >
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
Cancel Cancel
</button> </button>
</div> </div>
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat iMkoYt" class="sc-htpNat cvNnmn"
> >
Submit Submit
</button> </button>

View File

@ -14,7 +14,7 @@ exports[`Editor renders correctly 1`] = `
innerRef={[Function]} innerRef={[Function]}
> >
<div <div
className="sc-gZMcBi eROHUl" className="sc-gZMcBi bFOJgt"
/> />
</styled.div> </styled.div>
</Editor> </Editor>

View File

@ -2,7 +2,7 @@
exports[`Notification renders correctly 1`] = ` exports[`Notification renders correctly 1`] = `
<div <div
class="sc-fjdhpX fmuodm" class="sc-fjdhpX gcvrGp"
type="info" type="info"
> >
<span> <span>
@ -13,7 +13,7 @@ exports[`Notification renders correctly 1`] = `
exports[`Notification renders with props 1`] = ` exports[`Notification renders with props 1`] = `
<div <div
class="sc-fjdhpX fmuodm" class="sc-fjdhpX gcvrGp"
type="error" type="error"
> >
<svg <svg

View File

@ -2,7 +2,7 @@
exports[`SegmentedControl renders correctly 1`] = ` exports[`SegmentedControl renders correctly 1`] = `
<div <div
class="sc-jTzLTM jHNWjD" class="sc-jTzLTM bwMlok"
> >
<button <button
data-selected="true" data-selected="true"

View File

@ -2,7 +2,7 @@
exports[`Select renders correctly 1`] = ` exports[`Select renders correctly 1`] = `
<div <div
class="Select sc-bZQynM lpCNM is-searchable Select--single" class="Select sc-bZQynM eKUTFA is-searchable Select--single"
> >
<div <div
class="Select-control" class="Select-control"
@ -47,7 +47,7 @@ exports[`Select renders correctly 1`] = `
exports[`Select renders with props 1`] = ` exports[`Select renders with props 1`] = `
<div <div
class="Select sc-bZQynM dVPEwd has-value is-clearable is-disabled is-loading Select--multi" class="Select sc-bZQynM lbesTc has-value is-clearable is-disabled is-loading Select--multi"
> >
<div <div
class="Select-control" class="Select-control"
@ -155,7 +155,7 @@ exports[`Select should select another option 1`] = `
autosize={true} autosize={true}
backspaceRemoves={true} backspaceRemoves={true}
backspaceToRemoveMessage="Press backspace to remove {label}" backspaceToRemoveMessage="Press backspace to remove {label}"
className="sc-bZQynM lpCNM" className="sc-bZQynM eKUTFA"
clearAllText="Clear all" clearAllText="Clear all"
clearRenderer={[Function]} clearRenderer={[Function]}
clearValueText="Clear value" clearValueText="Clear value"
@ -215,7 +215,7 @@ exports[`Select should select another option 1`] = `
valueKey="value" valueKey="value"
> >
<div <div
className="Select sc-bZQynM lpCNM is-open is-searchable Select--single" className="Select sc-bZQynM eKUTFA is-open is-searchable Select--single"
> >
<div <div
className="Select-control" className="Select-control"
@ -404,7 +404,7 @@ exports[`Select shouldn't find any results 1`] = `
autosize={true} autosize={true}
backspaceRemoves={true} backspaceRemoves={true}
backspaceToRemoveMessage="Press backspace to remove {label}" backspaceToRemoveMessage="Press backspace to remove {label}"
className="sc-bZQynM lpCNM" className="sc-bZQynM eKUTFA"
clearAllText="Clear all" clearAllText="Clear all"
clearRenderer={[Function]} clearRenderer={[Function]}
clearValueText="Clear value" clearValueText="Clear value"
@ -464,7 +464,7 @@ exports[`Select shouldn't find any results 1`] = `
valueKey="value" valueKey="value"
> >
<div <div
className="Select sc-bZQynM lpCNM is-open is-searchable Select--single" className="Select sc-bZQynM eKUTFA is-open is-searchable Select--single"
> >
<div <div
className="Select-control" className="Select-control"

View File

@ -2,7 +2,7 @@
exports[`Slider renders correctly 1`] = ` exports[`Slider renders correctly 1`] = `
<div <div
class="sc-gzVnrw gTqmBD" class="sc-gzVnrw hKBSWW"
> >
<input <input
max="100" max="100"
@ -15,7 +15,7 @@ exports[`Slider renders correctly 1`] = `
exports[`Slider renders with props 1`] = ` exports[`Slider renders with props 1`] = `
<div <div
class="sc-gzVnrw csqRof" class="sc-gzVnrw gnJNaZ"
disabled="" disabled=""
> >
<label> <label>

View File

@ -5,7 +5,7 @@ exports[`Tabs renders correctly 1`] = `
class="sc-VigVT fmiisu" class="sc-VigVT fmiisu"
> >
<div <div
class="sc-gqjmRU gEvsQs" class="sc-gqjmRU kpDKzc"
> >
<div> <div>
<button <button
@ -33,7 +33,7 @@ exports[`Tabs renders tabs without inner components 1`] = `
class="sc-VigVT fmiisu" class="sc-VigVT fmiisu"
> >
<div <div
class="sc-gqjmRU gEvsQs" class="sc-gqjmRU kpDKzc"
> >
<div> <div>
<button <button
@ -97,7 +97,7 @@ exports[`Tabs renders with props 1`] = `
class="sc-VigVT fmiisu" class="sc-VigVT fmiisu"
> >
<div <div
class="sc-gqjmRU gEvsQs" class="sc-gqjmRU kpDKzc"
> >
<div> <div>
<button <button

View File

@ -5,10 +5,10 @@ exports[`Toolbar renders correctly 1`] = `
class="sc-jzJRlG bBrbNn" class="sc-jzJRlG bBrbNn"
> >
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
1 1
</button> </button>
@ -20,10 +20,10 @@ exports[`Toolbar renders correctly 1`] = `
class="sc-kAzzGY lcEaIs" class="sc-kAzzGY lcEaIs"
/> />
<div <div
class="sc-ifAKCX jTewnV" class="sc-ifAKCX evScRP"
> >
<button <button
class="sc-htpNat fWcQZ" class="sc-htpNat ldLqpm"
> >
2 2
</button> </button>

View File

@ -5,15 +5,16 @@ The following opinions must be taken into account since the primary use case of
- Objects and arrays deeply nested within collections are not converted into a tree structure. See `someNestedObject` and `someNestedArray` in the [output](https://github.com/romseguy/map2tree#output) below, or the [corresponding test](https://github.com/romseguy/map2tree/blob/master/test/map2tree.js#L140). - Objects and arrays deeply nested within collections are not converted into a tree structure. See `someNestedObject` and `someNestedArray` in the [output](https://github.com/romseguy/map2tree#output) below, or the [corresponding test](https://github.com/romseguy/map2tree/blob/master/test/map2tree.js#L140).
- Provides support for [Immutable.js](https://github.com/facebook/immutable-js) data structures (only List and Map though). - Provides support for [Immutable.js](https://github.com/facebook/immutable-js) data structures (only List and Map though).
# Usage # Usage
```javascript ```javascript
map2tree(someMap, options = { map2tree(
key: 'state', // the name you want for as the root node of the output tree someMap,
pushMethod: 'push' // use 'unshift' to change the order children nodes are added (options = {
}) key: 'state', // the name you want for as the root node of the output tree
pushMethod: 'push' // use 'unshift' to change the order children nodes are added
})
);
``` ```
# Input # Input
@ -22,16 +23,16 @@ map2tree(someMap, options = {
const someMap = { const someMap = {
someReducer: { someReducer: {
todos: [ todos: [
{title: 'map', someNestedObject: {foo: 'bar'}}, { title: 'map', someNestedObject: { foo: 'bar' } },
{title: 'to', someNestedArray: ['foo', 'bar']}, { title: 'to', someNestedArray: ['foo', 'bar'] },
{title: 'tree'}, { title: 'tree' },
{title: 'map2tree'} { title: 'map2tree' }
], ],
completedCount: 1 completedCount: 1
}, },
otherReducer: { otherReducer: {
foo: 0, foo: 0,
bar:{key: 'value'} bar: { key: 'value' }
} }
}; };
``` ```

View File

@ -11,7 +11,7 @@
"test": "jest", "test": "jest",
"prepare": "npm run build && npm run build:umd", "prepare": "npm run build && npm run build:umd",
"prepublishOnly": "npm run test && npm run clean && npm run build && npm run build:umd && npm run build:umd:min" "prepublishOnly": "npm run test && npm run clean && npm run build && npm run build:umd && npm run build:umd:min"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/reduxjs/redux-devtools.git" "url": "https://github.com/reduxjs/redux-devtools.git"

View File

@ -19,16 +19,24 @@ function visit(parent, visitFn, childrenFn) {
function getNode(tree, key) { function getNode(tree, key) {
let node = null; let node = null;
visit(tree, d => { visit(
if (d.name === key) { tree,
node = d; d => {
} if (d.name === key) {
}, d => d.children); node = d;
}
},
d => d.children
);
return node; return node;
} }
export default function map2tree(root, options = {}, tree = {name: options.key || 'state', children: []}) { export default function map2tree(
root,
options = {},
tree = { name: options.key || 'state', children: [] }
) {
if (!isPlainObject(root) && root && !root.toJS) { if (!isPlainObject(root) && root && !root.toJS) {
return {}; return {};
} }
@ -41,8 +49,11 @@ export default function map2tree(root, options = {}, tree = {name: options.key |
} }
mapValues(root && root.toJS ? root.toJS() : root, (maybeImmutable, key) => { mapValues(root && root.toJS ? root.toJS() : root, (maybeImmutable, key) => {
const value = maybeImmutable && maybeImmutable.toJS ? maybeImmutable.toJS() : maybeImmutable; const value =
let newNode = {name: key}; maybeImmutable && maybeImmutable.toJS
? maybeImmutable.toJS()
: maybeImmutable;
let newNode = { name: key };
if (isArray(value)) { if (isArray(value)) {
newNode.children = []; newNode.children = [];
@ -61,7 +72,7 @@ export default function map2tree(root, options = {}, tree = {name: options.key |
currentNode.children[pushMethod](newNode); currentNode.children[pushMethod](newNode);
map2tree(value, {key, pushMethod}, tree); map2tree(value, { key, pushMethod }, tree);
}); });
return tree; return tree;

View File

@ -3,7 +3,7 @@ import immutable from 'immutable';
test('# rootNodeKey', () => { test('# rootNodeKey', () => {
const map = {}; const map = {};
const options = {key: 'foo'}; const options = { key: 'foo' };
expect(map2tree(map, options).name).toBe('foo'); expect(map2tree(map, options).name).toBe('foo');
}); });
@ -16,9 +16,7 @@ describe('# shallow map', () => {
const expected = { const expected = {
name: 'state', name: 'state',
children: [ children: [{ name: 'a', value: null }]
{name: 'a', value: null}
]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
@ -33,10 +31,7 @@ describe('# shallow map', () => {
const expected = { const expected = {
name: 'state', name: 'state',
children: [ children: [{ name: 'a', value: 'foo' }, { name: 'b', value: 'bar' }]
{name: 'a', value: 'foo'},
{name: 'b', value: 'bar'}
]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
@ -45,14 +40,12 @@ describe('# shallow map', () => {
test('## object', () => { test('## object', () => {
const map = { const map = {
a: {aa: 'foo'} a: { aa: 'foo' }
}; };
const expected = { const expected = {
name: 'state', name: 'state',
children: [ children: [{ name: 'a', children: [{ name: 'aa', value: 'foo' }] }]
{name: 'a', children: [{name: 'aa', value: 'foo'}]}
]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
@ -61,24 +54,27 @@ describe('# shallow map', () => {
test('## immutable Map', () => { test('## immutable Map', () => {
const map = { const map = {
a: immutable.fromJS({aa: 'foo', ab: 'bar'}) a: immutable.fromJS({ aa: 'foo', ab: 'bar' })
}; };
const expected = { const expected = {
name: 'state', name: 'state',
children: [ children: [
{name: 'a', children: [{name: 'aa', value: 'foo'}, {name: 'ab', value: 'bar'}]} {
name: 'a',
children: [{ name: 'aa', value: 'foo' }, { name: 'ab', value: 'bar' }]
}
] ]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
}) });
}); });
describe('# deep map', () => { describe('# deep map', () => {
test('## null', () => { test('## null', () => {
const map = { const map = {
a: {aa: null} a: { aa: null }
}; };
const expected = { const expected = {
@ -102,7 +98,7 @@ describe('# deep map', () => {
test('## object', () => { test('## object', () => {
const map = { const map = {
a: {aa: {aaa: 'foo'}} a: { aa: { aaa: 'foo' } }
}; };
const expected = { const expected = {
@ -113,9 +109,7 @@ describe('# deep map', () => {
children: [ children: [
{ {
name: 'aa', name: 'aa',
children: [ children: [{ name: 'aaa', value: 'foo' }]
{name: 'aaa', value: 'foo'}
]
} }
] ]
} }
@ -129,21 +123,18 @@ describe('# deep map', () => {
describe('# array map', () => { describe('# array map', () => {
const map = { const map = {
a: [ a: [1, 2]
1,
2
]
}; };
test('## push', () => { test('## push', () => {
const expected = { const expected = {
name: 'state', name: 'state',
children: [{ children: [
name: 'a', {
children: [ name: 'a',
{name: 'a[0]', value: 1}, children: [{ name: 'a[0]', value: 1 }, { name: 'a[1]', value: 2 }]
{name: 'a[1]', value: 2}] }
}] ]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
@ -151,16 +142,15 @@ describe('# array map', () => {
}); });
test('## unshift', () => { test('## unshift', () => {
const options = {pushMethod: 'unshift'}; const options = { pushMethod: 'unshift' };
const expected = { const expected = {
name: 'state', name: 'state',
children: [{ children: [
name: 'a', {
children: [ name: 'a',
{name: 'a[1]', value: 2}, children: [{ name: 'a[1]', value: 2 }, { name: 'a[0]', value: 1 }]
{name: 'a[0]', value: 1} }
] ]
}]
}; };
expect(map2tree(map, options)).toEqual(expected); expect(map2tree(map, options)).toEqual(expected);
@ -169,33 +159,28 @@ describe('# array map', () => {
test('## null', () => { test('## null', () => {
const map = { const map = {
a: [ a: [null]
null
]
}; };
const expected = { const expected = {
name: 'state', name: 'state',
children: [{ children: [
name: 'a', {
children: [ name: 'a',
{name: 'a[0]', value: null} children: [{ name: 'a[0]', value: null }]
] }
}] ]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
expect(map2tree(immutable.fromJS(map))).toEqual(expected); expect(map2tree(immutable.fromJS(map))).toEqual(expected);
}) });
}); });
describe('# collection map', () => { describe('# collection map', () => {
test('## value', () => { test('## value', () => {
const map = { const map = {
a: [ a: [{ aa: 1 }, { aa: 2 }]
{aa: 1},
{aa: 2}
]
}; };
const expected = { const expected = {
@ -204,8 +189,8 @@ describe('# collection map', () => {
{ {
name: 'a', name: 'a',
children: [ children: [
{name: 'a[0]', object: {aa: 1}}, { name: 'a[0]', object: { aa: 1 } },
{name: 'a[1]', object: {aa: 2}} { name: 'a[1]', object: { aa: 2 } }
] ]
} }
] ]
@ -217,9 +202,7 @@ describe('# collection map', () => {
test('## object', () => { test('## object', () => {
const map = { const map = {
a: [ a: [{ aa: { aaa: 'foo' } }]
{aa: {aaa: 'foo'}}
]
}; };
const expected = { const expected = {
@ -227,14 +210,12 @@ describe('# collection map', () => {
children: [ children: [
{ {
name: 'a', name: 'a',
children: [ children: [{ name: 'a[0]', object: { aa: { aaa: 'foo' } } }]
{name: 'a[0]', object: {aa: {aaa: 'foo'}}}
]
} }
] ]
}; };
expect(map2tree(map)).toEqual(expected); expect(map2tree(map)).toEqual(expected);
expect(map2tree(immutable.fromJS(map))).toEqual(expected); expect(map2tree(immutable.fromJS(map))).toEqual(expected);
}) });
}); });

View File

@ -1,31 +1,29 @@
const path = require('path'); const path = require('path');
module.exports = (env = {}) => ( module.exports = (env = {}) => ({
{ mode: 'production',
mode: 'production', entry: {
entry: { app: ['./src/index.js']
app: ['./src/index.js'] },
}, output: {
output: { library: 'd3tooltip',
library: 'd3tooltip', libraryTarget: 'umd',
libraryTarget: 'umd', path: path.resolve(__dirname, 'dist'),
path: path.resolve(__dirname, 'dist'), filename: env.minimize ? 'map2tree.min.js' : 'map2tree.js'
filename: env.minimize ? 'map2tree.min.js' : 'map2tree.js' },
}, module: {
module: { rules: [
rules: [ {
{ test: /\.js$/,
test: /\.js$/, loader: 'babel-loader',
loader: 'babel-loader', exclude: /node_modules/
exclude: /node_modules/ }
} ]
] },
}, optimization: {
optimization: { minimize: !!env.minimize
minimize: !!env.minimize },
}, performance: {
performance: { hints: false
hints: false
}
} }
); });

View File

@ -2,7 +2,6 @@ The MIT License (MIT)
Copyright (c) 2015 Shusaku Uesugi, (c) 2016-present Alexander Kuznetsov Copyright (c) 2015 Shusaku Uesugi, (c) 2016-present Alexander Kuznetsov
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights

View File

@ -33,6 +33,7 @@ Check out [examples](examples) directory for more details.
### Theming ### Theming
This component now uses [react-base16-styling](https://github.com/alexkuz/react-base16-styling) module, which allows to customize component via `theme` property, which can be the following: This component now uses [react-base16-styling](https://github.com/alexkuz/react-base16-styling) module, which allows to customize component via `theme` property, which can be the following:
- [base16](http://chriskempson.github.io/base16) theme data. [The example theme data can be found here](https://github.com/gaearon/redux-devtools/tree/75322b15ee7ba03fddf10ac3399881e302848874/src/react/themes). - [base16](http://chriskempson.github.io/base16) theme data. [The example theme data can be found here](https://github.com/gaearon/redux-devtools/tree/75322b15ee7ba03fddf10ac3399881e302848874/src/react/themes).
- object that contains style objects, strings (that treated as classnames) or functions. A function is used to extend its first argument `{ style, className }` and should return an object with the same structure. Other arguments depend on particular context (and should be described here). See [createStylingFromTheme.js](https://github.com/alexkuz/react-json-tree/blob/feature-refactor-styling/src/createStylingFromTheme.js) for the list of styling object keys. Also, this object can extend `base16` theme via `extend` property. - object that contains style objects, strings (that treated as classnames) or functions. A function is used to extend its first argument `{ style, className }` and should return an object with the same structure. Other arguments depend on particular context (and should be described here). See [createStylingFromTheme.js](https://github.com/alexkuz/react-json-tree/blob/feature-refactor-styling/src/createStylingFromTheme.js) for the list of styling object keys. Also, this object can extend `base16` theme via `extend` property.
@ -62,8 +63,7 @@ const theme = {
<div> <div>
<JSONTree data={data} theme={theme} invertTheme={false} /> <JSONTree data={data} theme={theme} invertTheme={false} />
</div> </div>;
``` ```
#### Result (Monokai theme, dark background): #### Result (Monokai theme, dark background):
@ -74,21 +74,24 @@ const theme = {
```jsx ```jsx
<div> <div>
<JSONTree data={data} theme={{ <JSONTree
extend: theme, data={data}
// underline keys for literal values theme={{
valueLabel: { extend: theme,
textDecoration: 'underline' // underline keys for literal values
}, valueLabel: {
// switch key for objects to uppercase when object is expanded. textDecoration: 'underline'
// `nestedNodeLabel` receives additional arguments `expanded` and `keyPath` },
nestedNodeLabel: ({ style }, nodeType, expanded) => ({ // switch key for objects to uppercase when object is expanded.
style: { // `nestedNodeLabel` receives additional arguments `expanded` and `keyPath`
...style, nestedNodeLabel: ({ style }, nodeType, expanded) => ({
textTransform: expanded ? 'uppercase' : style.textTransform style: {
} ...style,
}) textTransform: expanded ? 'uppercase' : style.textTransform
}} /> }
})
}}
/>
</div> </div>
``` ```
@ -120,8 +123,8 @@ You can pass the following properties to customize rendered labels and values:
```jsx ```jsx
<JSONTree <JSONTree
labelRenderer={raw => <strong>{raw}</strong>} labelRenderer={raw => <strong>{raw}</strong>}
valueRenderer={raw => <em>{raw}</em>} valueRenderer={raw => <em>{raw}</em>}
/> />
``` ```

View File

@ -1,5 +1,4 @@
react-hot-boilerplate # react-hot-boilerplate
=====================
The minimal dev environment to enable live-editing React components. The minimal dev environment to enable live-editing React components.
@ -32,16 +31,16 @@ This boilerplate is purposefully simple to show the minimal configuration for Re
### Dependencies ### Dependencies
* React - React
* Webpack - Webpack
* [webpack-dev-server](https://github.com/webpack/webpack-dev-server) - [webpack-dev-server](https://github.com/webpack/webpack-dev-server)
* [babel-loader](https://github.com/babel/babel-loader) - [babel-loader](https://github.com/babel/babel-loader)
* [react-hot-loader](https://github.com/gaearon/react-hot-loader) - [react-hot-loader](https://github.com/gaearon/react-hot-loader)
### Resources ### Resources
* [Demo video](http://vimeo.com/100010922) - [Demo video](http://vimeo.com/100010922)
* [react-hot-loader on Github](https://github.com/gaearon/react-hot-loader) - [react-hot-loader on Github](https://github.com/gaearon/react-hot-loader)
* [Integrating JSX live reload into your workflow](http://gaearon.github.io/react-hot-loader/getstarted/) - [Integrating JSX live reload into your workflow](http://gaearon.github.io/react-hot-loader/getstarted/)
* [Troubleshooting guide](https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md) - [Troubleshooting guide](https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md)
* Ping dan_abramov on Twitter or #reactjs IRC - Ping dan_abramov on Twitter or #reactjs IRC

Some files were not shown because too many files have changed in this diff Show More