mirror of
https://github.com/vale981/Vulcan
synced 2025-03-05 09:31:43 -05:00
enable $unset of empty properties in edit form; bring in CheckboxGroup code to work around refs bug
This commit is contained in:
parent
b23b7eea73
commit
deb48b5a97
6 changed files with 385 additions and 13 deletions
|
@ -137,7 +137,6 @@ tap:i18n@1.7.0
|
||||||
templating@1.1.7
|
templating@1.1.7
|
||||||
templating-tools@1.0.2
|
templating-tools@1.0.2
|
||||||
tmeasday:check-npm-versions@0.1.1
|
tmeasday:check-npm-versions@0.1.1
|
||||||
tmeasday:healthcheck-handler@0.0.2
|
|
||||||
tmeasday:publish-counts@0.7.3
|
tmeasday:publish-counts@0.7.3
|
||||||
tracker@1.0.11
|
tracker@1.0.11
|
||||||
twitter@1.1.7
|
twitter@1.1.7
|
||||||
|
|
|
@ -17,17 +17,25 @@ const EditDocument = React.createClass({
|
||||||
labelFunction: React.PropTypes.func
|
labelFunction: React.PropTypes.func
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFields() {
|
||||||
|
const collection = this.props.collection;
|
||||||
|
const fields = collection.getInsertableFields(this.props.currentUser);
|
||||||
|
return fields;
|
||||||
|
},
|
||||||
|
|
||||||
submitForm(data) {
|
submitForm(data) {
|
||||||
|
|
||||||
console.log(data)
|
const fields = this.getFields();
|
||||||
|
|
||||||
const document = this.props.document;
|
const document = this.props.document;
|
||||||
const modifier = {$set: _.compactObject(Utils.flatten(data))};
|
// put all keys with data on $set
|
||||||
|
const set = _.compactObject(Utils.flatten(data));
|
||||||
|
// put all keys without data on $unset
|
||||||
|
const unsetKeys = _.difference(fields, _.keys(set));
|
||||||
|
const unset = _.object(unsetKeys, unsetKeys.map(()=>true));
|
||||||
|
const modifier = {$set: set, $unset: unset};
|
||||||
const collection = this.props.collection;
|
const collection = this.props.collection;
|
||||||
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
|
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
|
||||||
|
|
||||||
console.log(modifier)
|
|
||||||
|
|
||||||
Meteor.call(methodName, document._id, modifier, (error, document) => {
|
Meteor.call(methodName, document._id, modifier, (error, document) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
@ -49,7 +57,7 @@ const EditDocument = React.createClass({
|
||||||
|
|
||||||
const document = this.props.document;
|
const document = this.props.document;
|
||||||
const collection = this.props.collection;
|
const collection = this.props.collection;
|
||||||
const fields = collection.getInsertableFields(this.props.currentUser);
|
const fields = this.getFields();
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
maxWidth: "800px",
|
maxWidth: "800px",
|
||||||
|
@ -59,7 +67,11 @@ const EditDocument = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="edit-document" style={style}>
|
<div className="edit-document" style={style}>
|
||||||
<Formsy.Form onSubmit={this.submitForm}>
|
<Formsy.Form onSubmit={this.submitForm}>
|
||||||
{fields.map(fieldName => <div key={fieldName} className={"input-"+fieldName}>{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}</div>)}
|
{fields.map(fieldName =>
|
||||||
|
<div key={fieldName} className={"input-"+fieldName}>
|
||||||
|
{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Button type="submit" bsStyle="primary">Submit</Button>
|
<Button type="submit" bsStyle="primary">Submit</Button>
|
||||||
</Formsy.Form>
|
</Formsy.Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
159
packages/nova-forms/lib/component.js
Normal file
159
packages/nova-forms/lib/component.js
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
layout: React.PropTypes.string,
|
||||||
|
validatePristine: React.PropTypes.bool,
|
||||||
|
rowClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
labelClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
elementWrapperClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
layout: React.PropTypes.string,
|
||||||
|
validatePristine: React.PropTypes.bool,
|
||||||
|
rowClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
labelClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
elementWrapperClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
])
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
disabled: false,
|
||||||
|
validatePristine: false,
|
||||||
|
onChange: function() {},
|
||||||
|
onFocus: function() {},
|
||||||
|
onBlur: function() {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessors for "special" properties.
|
||||||
|
*
|
||||||
|
* The following methods are used to merge master default properties that
|
||||||
|
* are optionally set on the parent form. This to to allow customising these
|
||||||
|
* properties 'as a whole' for the form, while retaining the ability to
|
||||||
|
* override the properties on a component basis.
|
||||||
|
*
|
||||||
|
* Also see the parent-context mixin.
|
||||||
|
*/
|
||||||
|
getLayout: function() {
|
||||||
|
var defaultProperty = this.context.layout || 'horizontal';
|
||||||
|
return this.props.layout ? this.props.layout : defaultProperty;
|
||||||
|
},
|
||||||
|
|
||||||
|
getValidatePristine: function() {
|
||||||
|
var defaultProperty = this.context.validatePristine || false;
|
||||||
|
return this.props.validatePristine ? this.props.validatePristine : defaultProperty;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowClassName: function() {
|
||||||
|
return [this.context.rowClassName, this.props.rowClassName];
|
||||||
|
},
|
||||||
|
|
||||||
|
getLabelClassName: function() {
|
||||||
|
return [this.context.labelClassName, this.props.labelClassName];
|
||||||
|
},
|
||||||
|
|
||||||
|
getElementWrapperClassName: function() {
|
||||||
|
return [this.context.elementWrapperClassName, this.props.elementWrapperClassName];
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowProperties: function() {
|
||||||
|
return {
|
||||||
|
label: this.props.label,
|
||||||
|
rowClassName: this.getRowClassName(),
|
||||||
|
labelClassName: this.getLabelClassName(),
|
||||||
|
elementWrapperClassName: this.getElementWrapperClassName(),
|
||||||
|
layout: this.getLayout(),
|
||||||
|
required: this.isRequired(),
|
||||||
|
hasErrors: this.showErrors()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
hashString: function(string) {
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < string.length; i++) {
|
||||||
|
hash = (((hash << 5) - hash) + string.charCodeAt(i)) & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getId
|
||||||
|
*
|
||||||
|
* The ID is used as an attribute on the form control, and is used to allow
|
||||||
|
* associating the label element with the form control.
|
||||||
|
*
|
||||||
|
* If we don't explicitly pass an `id` prop, we generate one based on the
|
||||||
|
* `name` and `label` properties.
|
||||||
|
*/
|
||||||
|
getId: function() {
|
||||||
|
if (this.props.id) {
|
||||||
|
return this.props.id;
|
||||||
|
}
|
||||||
|
var label = (typeof this.props.label === 'undefined' ? '' : this.props.label);
|
||||||
|
return [
|
||||||
|
'frc',
|
||||||
|
this.props.name.split('[').join('_').replace(']', ''),
|
||||||
|
this.hashString(JSON.stringify(label))
|
||||||
|
].join('-');
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHelp: function() {
|
||||||
|
if (!this.props.help) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className="help-block">{this.props.help}</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderErrorMessage: function() {
|
||||||
|
if (!this.showErrors()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var errorMessages = this.getErrorMessages() || [];
|
||||||
|
return errorMessages.map((message, key) => {
|
||||||
|
return (
|
||||||
|
<span key={key} className="help-block validation-message">{message}</span>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showErrors: function() {
|
||||||
|
if (this.isPristine() === true) {
|
||||||
|
if (this.getValidatePristine() === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (this.isValid() === false);
|
||||||
|
}
|
||||||
|
};
|
126
packages/nova-forms/lib/row.js
Normal file
126
packages/nova-forms/lib/row.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*jshint node:true */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var classNames = require('classnames/dedupe');
|
||||||
|
|
||||||
|
var Row = React.createClass({
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
label: React.PropTypes.node,
|
||||||
|
rowClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
labelClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
elementWrapperClassName: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.string,
|
||||||
|
React.PropTypes.array,
|
||||||
|
React.PropTypes.object
|
||||||
|
]),
|
||||||
|
required: React.PropTypes.bool,
|
||||||
|
hasErrors: React.PropTypes.bool,
|
||||||
|
fakeLabel: React.PropTypes.bool,
|
||||||
|
layout: React.PropTypes.oneOf(['horizontal', 'vertical', 'elementOnly']),
|
||||||
|
htmlFor: React.PropTypes.string
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
label: '',
|
||||||
|
rowClassName: '',
|
||||||
|
labelClassName: '',
|
||||||
|
elementWrapperClassName: '',
|
||||||
|
required: false,
|
||||||
|
hasErrors: false,
|
||||||
|
fakeLabel: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderLabel: function() {
|
||||||
|
|
||||||
|
if (this.props.layout === 'elementOnly') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelClassNames = [];
|
||||||
|
labelClassNames.push('control-label');
|
||||||
|
|
||||||
|
if (this.props.layout === 'horizontal') {
|
||||||
|
labelClassNames.push('col-sm-3');
|
||||||
|
}
|
||||||
|
|
||||||
|
labelClassNames.push(this.props.labelClassName);
|
||||||
|
|
||||||
|
if (this.props.fakeLabel) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(labelClassNames)}>
|
||||||
|
<strong>
|
||||||
|
{this.props.label}
|
||||||
|
{this.props.required ? ' *' : null}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<label className={classNames(labelClassNames)} htmlFor={this.props.htmlFor}>
|
||||||
|
{this.props.label}
|
||||||
|
{this.props.required ? ' *' : null}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
if (this.props.layout === 'elementOnly') {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{this.props.children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cssClasses = {
|
||||||
|
row: ['form-group'],
|
||||||
|
elementWrapper: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.props.hasErrors) {
|
||||||
|
cssClasses.row.push('has-error');
|
||||||
|
cssClasses.row.push('has-feedback');
|
||||||
|
}
|
||||||
|
|
||||||
|
var element = this.props.children;
|
||||||
|
if (this.props.layout === 'horizontal') {
|
||||||
|
|
||||||
|
// Horizontal layout needs a 'row' class for Bootstrap 4
|
||||||
|
cssClasses.row.push('row');
|
||||||
|
|
||||||
|
cssClasses.elementWrapper.push('col-sm-9');
|
||||||
|
cssClasses.elementWrapper.push(this.props.elementWrapperClassName);
|
||||||
|
|
||||||
|
element = (
|
||||||
|
<div className={classNames(cssClasses.elementWrapper)}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cssClasses.row.push(this.props.rowClassName);
|
||||||
|
return (
|
||||||
|
<div className={classNames(cssClasses.row)}>
|
||||||
|
{this.renderLabel()}
|
||||||
|
{element}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Row;
|
|
@ -5,7 +5,7 @@ import FRC from 'formsy-react-components';
|
||||||
import Utils from './utils.js';
|
import Utils from './utils.js';
|
||||||
|
|
||||||
const Checkbox = FRC.Checkbox;
|
const Checkbox = FRC.Checkbox;
|
||||||
const CheckboxGroup = FRC.CheckboxGroup;
|
// const CheckboxGroup = FRC.CheckboxGroup;
|
||||||
const Input = FRC.Input;
|
const Input = FRC.Input;
|
||||||
const RadioGroup = FRC.RadioGroup;
|
const RadioGroup = FRC.RadioGroup;
|
||||||
const Select = FRC.Select;
|
const Select = FRC.Select;
|
||||||
|
@ -38,7 +38,7 @@ SmartForms.getComponent = (fieldName, field, labelFunction, document) => {
|
||||||
return <Checkbox key={fieldName} name={fieldName} value={value} label={label}/>;
|
return <Checkbox key={fieldName} name={fieldName} value={value} label={label}/>;
|
||||||
// note: checkboxgroup cause React refs error, so use RadioGroup for now
|
// note: checkboxgroup cause React refs error, so use RadioGroup for now
|
||||||
case "checkboxgroup":
|
case "checkboxgroup":
|
||||||
return <RadioGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
return <CheckboxGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
||||||
case "radiogroup":
|
case "radiogroup":
|
||||||
return <RadioGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
return <RadioGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
||||||
case "select":
|
case "select":
|
||||||
|
@ -48,4 +48,80 @@ SmartForms.getComponent = (fieldName, field, labelFunction, document) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SmartForms;
|
export default SmartForms;
|
||||||
|
|
||||||
|
|
||||||
|
import ComponentMixin from './component';
|
||||||
|
import Row from './row';
|
||||||
|
|
||||||
|
const CheckboxGroup = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Formsy.Mixin, ComponentMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
name: React.PropTypes.string.isRequired,
|
||||||
|
options: React.PropTypes.array.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
label: '',
|
||||||
|
help: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
changeCheckbox: function() {
|
||||||
|
var value = [];
|
||||||
|
this.props.options.forEach(function(option, key) {
|
||||||
|
if (this.refs['element-' + key].checked) {
|
||||||
|
value.push(option.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
this.setValue(value);
|
||||||
|
this.props.onChange(this.props.name, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderElement: function() {
|
||||||
|
var _this = this;
|
||||||
|
var controls = this.props.options.map(function(checkbox, key) {
|
||||||
|
var checked = (_this.getValue().indexOf(checkbox.value) !== -1);
|
||||||
|
var disabled = _this.isFormDisabled() || checkbox.disabled || _this.props.disabled;
|
||||||
|
return (
|
||||||
|
<div className="checkbox" key={key}>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
ref={'element-' + key}
|
||||||
|
checked={checked}
|
||||||
|
type="checkbox"
|
||||||
|
value={checkbox.value}
|
||||||
|
onChange={_this.changeCheckbox}
|
||||||
|
disabled={disabled}
|
||||||
|
/> {checkbox.label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return controls;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
|
||||||
|
if (this.getLayout() === 'elementOnly') {
|
||||||
|
return (
|
||||||
|
<div>{this.renderElement()}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row
|
||||||
|
{...this.getRowProperties()}
|
||||||
|
fakeLabel={true}
|
||||||
|
>
|
||||||
|
{this.renderElement()}
|
||||||
|
{this.renderHelp()}
|
||||||
|
{this.renderErrorMessage()}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"postInterval": 20,
|
"postInterval": 20,
|
||||||
"RSSLinksPointTo": "link",
|
"RSSLinksPointTo": "link",
|
||||||
"commentInterval": 20,
|
"commentInterval": 20,
|
||||||
"maxPostsPerDay": 10
|
"maxPostsPerDay": 10,
|
||||||
"startInvitesCount": 5,
|
"startInvitesCount": 5,
|
||||||
"postsPerPage": 10,
|
"postsPerPage": 10,
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
"facebookPage": "http://facebook.com/foo",
|
"facebookPage": "http://facebook.com/foo",
|
||||||
|
|
||||||
"googleAnalyticsId":"123foo",
|
"googleAnalyticsId":"123foo",
|
||||||
"embedlyKey":"123foo",
|
"embedlyKey":"123foo"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue