Skip to content

Commit

Permalink
Using d2-ui <Form /> generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Markionium committed Dec 21, 2015
1 parent 30775bc commit 71814ba
Show file tree
Hide file tree
Showing 100 changed files with 5,947 additions and 98 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ coverage/*

dist/*

#TODO: Jshint is a dependency of the git hook which installs these files on install...
# TODO: Jshint is a dependency of the git hook which installs these files on install...
.jshintignore
.jshintrc

build/*

npm-debug.log
stats.json

# TODO: Remove when using proper jQuery loader
src/dev_jquery.js
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"babel-loader": "^5.3.2",
"classnames": "^2.1.3",
"d2": "0.0.16",
"d2-flux": "^0.4.0",
"d2-ui": "0.0.1",
"d2-flux": "^0.5.0",
"d2-ui": "0.0.7",
"d2-ui-basicfields": "^0.4.1",
"d2-ui-button": "0.0.2",
"d2-ui-datatable": "0.0.4",
Expand All @@ -58,6 +58,7 @@
"d2-ui-pagination": "0.0.4",
"d2-utils": "0.0.4",
"jquery": "^2.1.4",
"lodash.isnumber": "^3.0.1",
"loglevel": "^1.4.0",
"material-ui": "^0.12.3",
"react-router": "^0.13.3",
Expand Down
1 change: 1 addition & 0 deletions scss/App/App.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.app {
color: inherit;
padding-top: 3rem;
}
12 changes: 10 additions & 2 deletions scss/SideBar/SideBar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ $sidebar--border-color: #E1E1E1;
$sidebar--border-style: 1px solid;
$sidebar--item--text-color: #303030;
$sidebar--item--hover-color: #EEE;
$left-bar-width: 256px;

.sidebar {
padding: 1rem;
width: $left-bar-width;
float: left;
position: fixed;
margin-top: 16px;
bottom: 0;
top: 0;
left: 0;
padding-top: 2rem;
overflow-y: auto;

ul {
list-style: none;
Expand All @@ -23,7 +32,6 @@ $sidebar--item--hover-color: #EEE;
a {
color: $sidebar--item--text-color;
display: block;
padding: 1rem;
text-decoration: none;
}
}
3 changes: 2 additions & 1 deletion scss/maintenance.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ $sidebar-border-style: 1px solid;
}

html {
background: #ffffff;
font-family: 'Roboto', sans-serif;
font-size: 14px;
}

//Components
@import './SideBar/sidebar';

Expand Down
2 changes: 1 addition & 1 deletion src/App/App.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import HeaderBar from 'd2-ui/lib/header-bar/HeaderBar.component';
import MainContent from '../MainContent/MainContent.component';
import SideBar from '../SideBar/SideBarContainer.component';
import SnackbarContainer from '../Snackbar/SnackbarContainer.component';
import {getInstance} from 'd2';
import {getInstance} from 'd2/lib/d2';
import AppWithD2 from 'd2-ui/lib/app/AppWithD2.component';
import log from 'loglevel';
import appTheme from './app.theme';
Expand Down
40 changes: 28 additions & 12 deletions src/App/app.theme.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
import ThemeManager from 'material-ui/lib/styles/theme-manager';
import Colors from 'material-ui/lib/styles/colors';
import ColorManipulator from 'material-ui/lib/utils/color-manipulator';
import Spacing from 'material-ui/lib/styles/spacing';
import ThemeManager from 'material-ui/lib/styles/theme-manager';

const themeConfig = {
const theme = {
spacing: Spacing,
fontFamily: 'Roboto, sans-serif',
palette: {
primary1Color: Colors.blue500,
primary2Color: Colors.blue700,
primary3Color: Colors.lightBlack,
accent1Color: Colors.blueA200,
accent2Color: Colors.grey100,
primary3Color: Colors.grey300,
accent1Color: '#276696',
accent2Color: '#E9E9E9',
accent3Color: Colors.grey500,
accent4Color: Colors.blueGrey50,
textColor: Colors.darkBlack,
alternateTextColor: Colors.white,
canvasColor: Colors.white,
borderColor: Colors.grey300,
disabledColor: ColorManipulator.fade(Colors.darkBlack, 0.3),
},

};

const appTheme = ThemeManager.getMuiTheme(themeConfig);
function createAppTheme(style) {
return {
sideBar: {
backgroundColor: '#F3F3F3',
backgroundColorItem: 'transparent',
backgroundColorItemActive: style.palette.accent2Color,
textColor: style.palette.textColor,
textColorActive: style.palette.primary1Color,
borderStyle: '1px solid #e1e1e1',
},
forms: {
minWidth: 350,
maxWidth: 900,
},
formFields: {
secondaryColor: style.palette.accent4Color,
},
};
}

appTheme.formFields = {
secondaryColor: themeConfig.palette.accent4Color,
};
const muiTheme = ThemeManager.getMuiTheme(theme);
const appTheme = createAppTheme(theme);

export default appTheme;
export default Object.assign({}, muiTheme, appTheme);
74 changes: 74 additions & 0 deletions src/BasicFields/AttributeFields.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import InputField from './InputField.component';
import Input from './Input.component';
import Select from './Select.component';
import FormFields from './FormFields.component';
import LabelWrapper from './wrappers/LabelWrapper.component';

const AttributeFields = React.createClass({
propTypes: {
model: React.PropTypes.shape({
modelDefinition: React.PropTypes.shape({
attributeProperties: React.PropTypes.object,
}).isRequired,
attributes: React.PropTypes.object,
}).isRequired,
style: React.PropTypes.object,
},

render() {
if (!this.props.model || !this.props.model.modelDefinition.attributeProperties) {
return null;
}

return (
<FormFields style={this.props.style}>
{Object.keys(this.props.model.modelDefinition.attributeProperties).map(attributeName => {
const attributeDefinition = this.props.model.modelDefinition.attributeProperties[attributeName];
const field = {
key: 'attributes.' + attributeName,
type: attributeDefinition.optionSet ? Select : Input,
templateOptions: {
label: attributeName,
required: Boolean(attributeDefinition.mandatory),
translateLabel: false,
},
validators: {
required: () => {
// Not required, or required and has a value
return !Boolean(attributeDefinition.mandatory) || !!this.props.model.attributes[attributeName];
},
},
};

if (attributeDefinition.optionSet) {
field.wrapper = LabelWrapper;
}

if (attributeDefinition.optionSet) {
field.templateOptions.options = attributeDefinition.optionSet.options;
field.toModelTransformer = valueOnModel => {
if (valueOnModel && valueOnModel.code) {
return valueOnModel.code;
}
return undefined;
};
field.fromModelTransformer = function transformAttribute(valueOnModel) {
return this.templateOptions.options.reduce((result, option) => {
if (!result && option.code === valueOnModel.attributes[attributeName]) {
return option;
}
return result;
}, undefined);
}.bind(field);
}

return (<InputField formName={'userForm'} key={attributeName} model={this.props.model}
fieldConfig={field}/>);
})}
</FormFields>
);
},
});

export default AttributeFields;
39 changes: 39 additions & 0 deletions src/BasicFields/CheckBox.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import FormFieldMixin from './FormField.mixin';
import CheckBox from 'material-ui/lib/checkbox';
import Translate from 'd2-ui/lib/i18n/Translate.mixin';

export default React.createClass({
propTypes: {
fieldConfig: React.PropTypes.shape({
key: React.PropTypes.string,
}).isRequired,
model: React.PropTypes.object.isRequired,
},

mixins: [FormFieldMixin, Translate],

render() {
const fc = this.props.fieldConfig;
const to = fc.templateOptions || {};
const getLabelText = () => {
return this.getTranslation(to.label || fc.key);
};

return (
<div className="check-box">
<CheckBox id={fc.key}
defaultChecked={this.props.model[fc.key]}
onClick={this.toggleCheckBox}
label={getLabelText()}
/>
</div>
);
},

toggleCheckBox() {
const fc = this.props.fieldConfig;

this.handleChange({target: {value: !this.props.model[fc.key]}});
},
});
103 changes: 103 additions & 0 deletions src/BasicFields/Form.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from 'react';
import classes from 'classnames';

import log from 'loglevel';

const Form = React.createClass({
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.node,
React.PropTypes.arrayOf(React.PropTypes.node),
]),
headerFields: React.PropTypes.arrayOf(React.PropTypes.string),
model: React.PropTypes.object.isRequired,
name: React.PropTypes.string.isRequired,
style: React.PropTypes.object,
},

childContextTypes: {
updateForm: React.PropTypes.func.isRequired,
setStatus: React.PropTypes.func.isRequired,
},

getChildContext() {
return {
updateForm: this.updateForm,
setStatus: this.setStatus,
};
},

getInitialState() {
return {
formStatus: false,
};
},

render() {
const classList = classes('d2-form');

const children = React.Children.map(this.props.children, child => {
if (child) {
return React.addons.cloneWithProps(child, {isFormValid: this.isValid, style: this.props.style});
}
});

return (
<form className={classList} name={this.props.name}>
{children}
</form>
);
},

formFieldStates: {},

updateForm(fieldName, newValue, oldValue) {
// TODO: Ideally we would like the model to be immutable. It would be better if we could emit a model change here
// on some model observer that would propagate down through the props.
this.setValue(fieldName, newValue);

// Force an update of the component as there might be fields that depend on values of others
// skip logic etc.
this.forceUpdate();

log.debug(`Form updated because of change to ${fieldName} from '${oldValue}' to '${newValue}'`);
},

setStatus(fieldName, isValid) {
this.formFieldStates[fieldName] = isValid;
// log.debug(`${fieldName} is now ${isValid ? 'Valid' : 'Invalid'}`);

const formStatus = Object.keys(this.formFieldStates)
.reduce((collector, formFieldName) => {
return collector && this.formFieldStates[formFieldName];
}, true);

// log.debug(`FormStatus: ${formStatus}`);

if (formStatus !== this.state.formStatus) {
this.setState({
formStatus: formStatus,
}, () => {
// log.debug(`This current form status is: ${formStatus ? 'Valid' : 'Invalid'}`);
});
}
},

isValid() {
return this.state.formStatus;
},

setValue(fieldName, newValue) {
if (!fieldName || !this.props.model) { return undefined; }

const keyParts = fieldName.split('.');
let value = this.props.model;

while (keyParts.length > 1) {
value = value[keyParts.shift()];
}
value[keyParts[0]] = newValue;
},
});

export default Form;
Loading

0 comments on commit 71814ba

Please sign in to comment.