diff options
Diffstat (limited to 'web/src')
-rw-r--r-- | web/src/css/app.less | 1 | ||||
-rw-r--r-- | web/src/css/modal.less | 10 | ||||
-rw-r--r-- | web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap | 79 | ||||
-rw-r--r-- | web/src/js/__tests__/ducks/tutils.js | 30 | ||||
-rw-r--r-- | web/src/js/backends/websocket.js | 1 | ||||
-rw-r--r-- | web/src/js/components/Modal/OptionMaster.jsx | 119 | ||||
-rw-r--r-- | web/src/js/components/Modal/OptionModal.jsx | 27 | ||||
-rw-r--r-- | web/src/js/ducks/index.js | 2 |
8 files changed, 263 insertions, 6 deletions
diff --git a/web/src/css/app.less b/web/src/css/app.less index 353e412a..b9b5b310 100644 --- a/web/src/css/app.less +++ b/web/src/css/app.less @@ -19,3 +19,4 @@ html { @import (less) "footer.less"; @import (less) "codemirror.less"; @import (less) "contentview.less"; +@import (less) "modal.less"; diff --git a/web/src/css/modal.less b/web/src/css/modal.less index b08e309a..8d578f03 100644 --- a/web/src/css/modal.less +++ b/web/src/css/modal.less @@ -1,3 +1,13 @@ .modal-visible { display: block; } + + +.modal-dialog { + overflow-y: initial !important; +} + +.modal-body { + max-height: calc(100vh - 20px); + overflow-y: auto; +} diff --git a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap index 4fe163d1..af587ae4 100644 --- a/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap +++ b/web/src/js/__tests__/components/Modal/__snapshots__/ModalSpec.js.snap @@ -46,7 +46,84 @@ exports[`Modal Component should render correctly 2`] = ` <div className="modal-body" > - ... + <div + className="menu-entry" + > + <label> + booleanOption + <input + checked={false} + onChange={[Function]} + title="foo" + type="checkbox" + /> + </label> + </div> + <div + className="menu-entry" + > + <label + htmlFor="" + > + choiceOption + <select + name="choiceOption" + onChange={[Function]} + selected="b" + title="foo" + > + <option + value="a" + > + + a + + </option> + <option + value="b" + > + + b + + </option> + <option + value="c" + > + + c + + </option> + </select> + </label> + </div> + <div + className="menu-entry" + > + <label> + intOption + <input + onChange={[Function]} + onKeyDown={[Function]} + title="foo" + type="number" + value={1} + /> + </label> + </div> + <div + className="menu-entry" + > + <label> + strOption + <input + onChange={[Function]} + onKeyDown={[Function]} + title="foo" + type="text" + value="str content" + /> + </label> + </div> </div> <div className="modal-footer" diff --git a/web/src/js/__tests__/ducks/tutils.js b/web/src/js/__tests__/ducks/tutils.js index 9b92e676..a3e9c168 100644 --- a/web/src/js/__tests__/ducks/tutils.js +++ b/web/src/js/__tests__/ducks/tutils.js @@ -42,6 +42,36 @@ export function TStore(){ anticache: true, anticomp: false }, + options: { + booleanOption: { + choices: null, + default: false, + help: "foo", + type: "bool", + value: false + }, + strOption: { + choices: null, + default: null, + help: "foo", + type: "str", + value: "str content" + }, + intOption: { + choices: null, + default: 0, + help: "foo", + type: "int", + value: 1 + }, + choiceOption: { + choices: ['a', 'b', 'c'], + default: 'a', + help: "foo", + type: "str", + value: "b" + }, + }, flows: { selected: ["d91165be-ca1f-4612-88a9-c0f8696f3e29"], byId: {"d91165be-ca1f-4612-88a9-c0f8696f3e29": tflow}, diff --git a/web/src/js/backends/websocket.js b/web/src/js/backends/websocket.js index 01094ac4..d7e13bb2 100644 --- a/web/src/js/backends/websocket.js +++ b/web/src/js/backends/websocket.js @@ -27,6 +27,7 @@ export default class WebsocketBackend { this.fetchData("settings") this.fetchData("flows") this.fetchData("events") + this.fetchData("options") this.store.dispatch(connectionActions.startFetching()) } diff --git a/web/src/js/components/Modal/OptionMaster.jsx b/web/src/js/components/Modal/OptionMaster.jsx new file mode 100644 index 00000000..c25dda72 --- /dev/null +++ b/web/src/js/components/Modal/OptionMaster.jsx @@ -0,0 +1,119 @@ +import PropTypes from 'prop-types' + +PureBooleanOption.PropTypes = { + value: PropTypes.bool.isRequired, + onChange: PropTypes.func.isRequired, +} + +function PureBooleanOption({ value, onChange, name, help}) { + return ( + <label> + { name } + <input type="checkbox" + checked={value} + onChange={onChange} + title={help} + /> + </label> + ) +} + +PureStringOption.PropTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} + +function PureStringOption( { value, onChange, name, help }) { + let onKeyDown = (e) => {e.stopPropagation()} + return ( + <label> + { name } + <input type="text" + value={value} + onChange={onChange} + title={help} + onKeyDown={onKeyDown} + /> + </label> + ) +} + +PureNumberOption.PropTypes = { + value: PropTypes.number.isRequired, + onChange: PropTypes.func.isRequired, +} + +function PureNumberOption( {value, onChange, name, help }) { + let onKeyDown = (e) => {e.stopPropagation()} + return ( + <label> + { name } + <input type="number" + value={value} + onChange={onChange} + title={help} + onKeyDown={onKeyDown} + /> + </label> + ) +} + +PureChoicesOption.PropTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, +} + +function PureChoicesOption( { value, onChange, name, help, choices }) { + return ( + <label htmlFor=""> + { name } + <select name={name} onChange={onChange} title={help} selected={value}> + { choices.map((choice, index) => ( + <option key={index} value={choice}> {choice} </option> + ))} + </select> + </label> + ) +} + +const OptionTypes = { + bool: PureBooleanOption, + str: PureStringOption, + int: PureNumberOption, + "optional str": PureStringOption, + "sequence of str": PureStringOption, +} + +export default function OptionMaster({option, name, updateOptions, ...props}) { + let WrappedComponent = null + if (option.choices) { + WrappedComponent = PureChoicesOption + } else { + WrappedComponent = OptionTypes[option.type] + } + + let onChange = (e) => { + switch (option.type) { + case 'bool' : + updateOptions({[name]: !option.value}) + break + case 'int': + updateOptions({[name]: parseInt(e.target.value)}) + break + default: + updateOptions({[name]: e.target.value}) + } + } + return ( + <div className="menu-entry"> + <WrappedComponent + children={props.children} + value={option.value} + onChange={onChange} + name={name} + help={option.help} + choices={option.choices} + /> + </div> + ) +} diff --git a/web/src/js/components/Modal/OptionModal.jsx b/web/src/js/components/Modal/OptionModal.jsx index 500495c4..ef3a224a 100644 --- a/web/src/js/components/Modal/OptionModal.jsx +++ b/web/src/js/components/Modal/OptionModal.jsx @@ -1,16 +1,18 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import * as modalAction from '../../ducks/ui/modal' +import { update as updateOptions } from '../../ducks/options' +import Option from './OptionMaster' class PureOptionModal extends Component { constructor(props, context) { super(props, context) - this.state = { title: 'Options', } + this.state = { title: 'Options' } } render() { - const { hideModal } = this.props + const { hideModal, options } = this.props const { title } = this.state return ( <div> @@ -26,7 +28,19 @@ class PureOptionModal extends Component { </div> <div className="modal-body"> - ... + { + Object.keys(options).sort() + .map((key, index) => { + let option = options[key]; + return ( + <Option + key={index} + name={key} + updateOptions={updateOptions} + option={option} + />) + }) + } </div> <div className="modal-footer"> @@ -39,7 +53,10 @@ class PureOptionModal extends Component { export default connect( state => ({ - + options: state.options }), - { hideModal: modalAction.hideModal } + { + hideModal: modalAction.hideModal, + updateOptions: updateOptions, + } )(PureOptionModal) diff --git a/web/src/js/ducks/index.js b/web/src/js/ducks/index.js index 0f2426ec..be2f2885 100644 --- a/web/src/js/ducks/index.js +++ b/web/src/js/ducks/index.js @@ -4,6 +4,7 @@ import flows from "./flows" import settings from "./settings" import ui from "./ui/index" import connection from "./connection" +import options from './options' export default combineReducers({ eventLog, @@ -11,4 +12,5 @@ export default combineReducers({ settings, connection, ui, + options, }) |