From 83e5d560656093a843fd874eb61bcef67c41e936 Mon Sep 17 00:00:00 2001 From: Sean Adkinson Date: Wed, 29 Jul 2015 15:20:20 -0700 Subject: [PATCH 1/6] Support static field rendering --- src/index.jsx | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/index.jsx b/src/index.jsx index 3ef2bdc..4347cb6 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -5,7 +5,8 @@ var React = require('react') var { BooleanField, BoundField, CheckboxChoiceInput, CheckboxFieldRenderer, CheckboxSelectMultiple, ChoiceFieldRenderer, FileField, Form, - MultiValueField, MultiWidget, RadioChoiceInput, RadioFieldRenderer, RadioSelect + MultiValueField, MultiWidget, RadioChoiceInput, RadioFieldRenderer, + RadioSelect, Widget } = require('newforms') var SPINNER = 'data:image/gif;base64,R0lGODlhDgAOANU%2FAJ2rtf39%2FfL09a65wvX2993i5qq2v9Ta35CgrLjCyuTo6%2Bfq7aGvub3Hzs7V2vX3%2BI6eq9rf47rEzOvu8NLZ3ens7u7w8sDJ0ODl6MfP1aazvYqbqNDX3Pr7%2FLW%2Fx4iZpomap%2BPn6vHz9Y2dqqSxu%2FT19%2Bjr7tfd4dvg5KOwuvj5%2BeLm6ae0vd%2Fk5%2Fj5%2BvHz9Nbc4Nbc4Y2dqff4%2Bebp7NXb3%2FDy9Iqbp%2BXp7Pv8%2FL%2FIz%2Fn6%2B7nDy%2FDy84%2Bfq%2F%2F%2F%2FyH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FwtYTVAgRGF0YVhNUDw%2FeHBhY2tldCBiZWdpbj0i77u%2FIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8%2BIDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyNzA4MjZFM0EyRUExMUUzQjE2OUQwNUQ1MzZBQ0M2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyNzA4MjZFNEEyRUExMUUzQjE2OUQwNUQ1MzZBQ0M2NyI%2BIDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk2NDkzOTlDQTJBOTExRTNCMTY5RDA1RDUzNkFDQzY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjI3MDgyNkUyQTJFQTExRTNCMTY5RDA1RDUzNkFDQzY3Ii8%2BIDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY%2BIDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8%2BAf%2F%2B%2Ffz7%2Bvn49%2Fb19PPy8fDv7u3s6%2Brp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M%2FOzczLysnIx8bFxMPCwcC%2Fvr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ%2BenZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8%2BPTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEBQMAPwAsAAAAAA4ADgAABhTAn3BILBqPyKRyyWw6n9CodGoMAgAh%2BQQFAwA%2FACwHAAAAAQADAAAGBcCOrRMEACH5BAUDAD8ALAcAAAABAAMAAAYFwNKhFAQAIfkEBQMAPwAsBwAAAAEAAwAABgXABQkXBAAh%2BQQFAwA%2FACwHAAAAAgADAAAGB8DQ7FOLPYIAIfkEBQMAPwAsBwAAAAMAAwAABgrAX%2Bn3%2B0xOmV8QACH5BAUDAD8ALAcAAAAEAAMAAAYLQMxvOCSJfjpNIAgAIfkEBQMAPwAsBwAAAAUABAAABg%2FA0G9I%2FCmGDR%2BoMiRQfkEAIfkEBQMAPwAsBwAAAAYABQAABhNAzG9IHIaGNcnQQXwwPotm7RcEACH5BAUDAD8ALAcAAAAHAAYAAAYVwNVvSCwSTw3ExzgECYkEBMOYMXSCACH5BAUDAD8ALAcAAAAHAAgAAAYcwNBvSCQqij8fiFMkDIXIFPLyERRRn1axl1gEAQAh%2BQQFAwA%2FACwLAAcAAwADAAAGCsDIB3P5CFCeXxAAIfkEBQMAPwAsCgAHAAQABQAABhHAn7Al%2FIkeiNTP8An9MA5hEAAh%2BQQFAwA%2FACwIAAMABgAKAAAGHMCf8LcaGo9II%2BpXOL6MDCGBASrWEKBhjRQaBgEAIfkEBQMAPwAsBgAAAAgADgAABirA3%2BRHLP4YxJCxYGw6i4%2BndEpsPQVGwi%2F1VE5ODd%2BPQxx8Pj9FsRIqNYMAIfkECQMAPwAsAwAAAAkADgAABiLAn%2FA3Gxp%2FjuNw8kMgldAhIUqtWq%2FKC692DLA%2BHyhhdQwCACH5BAkDAD8ALAAAAAAOAA4AAAZGwJ9wKOwQj0QGKYQ8XnwgR5NIYHymxAeCgR1efqLuDyUWkstfYgBJQBAdgPCwCiLWQBAJ7NSAco4VBh%2BDHyQKUw8KISVHQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGUcCfcEgsGn%2BBQehItCBADubwwQCtpMIHgoEVXj6vLupTEH9aP1OE%2BRX8DCORkYBICU0bgHtIqC6FNRsQEicnDT4gHEULGh%2BOHyQKTA8hISVFQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGVsCfcEgsGoe9Y1EBciiHDwYI8xSWEIyqUPexBVQBZeRTWHwoStSn5QIllJeP4GeQvYwEREpY2QBERARSIUMwGyMSMScNPiAcRSYsH5MfJApKDwohJUVBACH5BAkDAD8ALAAAAAAOAA4AAAZRwJ9wSCwaj8ghLTl0gFbMHwGR%2Bs0GCuTlI8B9DkjUp7X4UMJjFyih5f4MspdxWv1VNgARkcAAhYYwGyMSMScNPiAcRSYsH44fJFlHDwohJUVBACH5BAkDAD8ALAAAAAAOAA4AAAZVwJ9wSCwaj8gjIZBk%2FlgaZCb1m30kSN3HhvvUkJFPYfGhIFGflguUQF4%2Bgp9B9jISENRfZQMQEQkMICFDMBsjEjEnDT4gHEUmLB%2BSHyQKSA8KISVFQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGUcCfcEgsGo%2FCCZJo2nCWQsNIBHWBeEvLjvY5IAuf1uJDQaLC1gTy8hH8DLKXkYBICSsbAHVIYIBCQzAbIxIxJw0%2BIE9MLB%2BOHyQKSA8KISVFQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGU8CfcEgsCnNGYw3gSg5NG0DJKWSNetTf7JPI%2FhQfincRdgoUOReom7x8BD%2BD7GV8IBjCSlREJDA%2BIUMwGyMSMScNPiAORSYsH5AfKYFJDwohU0RBACH5BAkDAD8ALAAAAAAOAA4AAAZPwJ9wSCwKFyhjsXYDKIemDUDwFLJG1Orsw6sKcZ%2BD97f4UMYuUGL8M8hexkemI6xIRcQHA7QawjYjEjE1Ej4gDkUmLB%2BMHyQhTw8KGCVFQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGSsCfcEgsChcajJFY20BOS6FpAxBEhYaR6PqbfXjcH%2B5zCC8%2BlLALlAj%2FDLJXuELdDh%2BBImwzksRODQgNRiYsH4cfJCFRDworJUVBACH5BAkDAD8ALAAAAAAOAA4AAAZGwJ9Q2BkajQsN4nisbUaSAFNougEE06FhJMoKZyCeV0j7HMa%2FxYeCdoES6J9B9kJXNoDuGPaUxGA2WSYsH4UZYw8KGARHQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGPMCfUPhQDY%2FDBetzQB5rN4hk4hRWNgBBdWgYibZCFYgHFtKY5d%2B5WRaT091v%2BQqQg6HSV1n5MaV%2FDwFVQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGPMCfUPiwDI%2FDBetjQB5rG4ik5RSaNgBRdWgYabc%2FF4gHFtI%2Bh%2FIP96GoZ5%2BE%2Bsca9dQLrEBdA6HmRnNqQQAh%2BQQJAwA%2FACwAAAAADgAOAAAGN8CfUPgwDY9DE%2BvjQx5jm5Ek4hSaNgBRdWiQvbZCF4gHFtI%2Bh%2FIPh1bPPmS1YURQmxzqvH4%2FDAIAIfkECQMAPwAsAAAAAA4ADgAABjXAn1D4UASGSKGJ9fmokkPYZiSJHaGmDUAERRpkr%2B7QBeKJh4sP5SzEfWrs38yziNvv%2BLw%2BCAAh%2BQQJAwA%2FACwAAAAADgAOAAAGL8CfUPhQBIZIoYn1%2BaiSQ9hmJIkdoaYNQARFGmTcrlAF4omHFhLqzG673%2FC4%2FBwEACH5BAkDAD8ALAAAAAAOAA4AAAYqwJ9Q%2BAgFhkjhQvP5qJLD2gYiOR2hpg1AAEUaRqIu8rESm8%2FotHrNbrODACH5BAkDAD8ALAAAAAAOAA4AAAYowJ9QSFgFhkghTfP5qJLD2g3Cqx2hOQDABk3uSt2weEwum8%2FotBoZBAAh%2BQQJAwA%2FACwAAAAADgAOAAAGI8CfUEgIBYZI4ULz%2BaiSwx1iJDkdoUKTCMvter%2FgsHhMLpeDACH5BAkDAD8ALAAAAAAOAA4AAAYgwJ9QSFgFhkihSvP5qJLJAe9whFqv2Kx2y%2B16v%2BDwMAgAIfkECQMAPwAsAAAAAA4ADgAABh7An1BICAWGyKHl81Eln5nT8UmtWq%2FYrHbL7Xq%2FwyAAIfkECQMAPwAsAAAAAA4ADgAABh3An1D4WAWGSCTno0o6S7Wjc0qtWq%2FYrHbL7XqHQQAh%2BQQFAwA%2FACwAAAAADgAOAAAGGsCfcIgLDI9IgArJ%2FBWb0Kh0Sq1ar9isVhoEACH5BAUDAD8ALAYAAAABAAMAAAYFQAFHEAQAIfkECQMAPwAsBgAAAAEAAwAABgXAnK0TBAAh%2BQQJAwA%2FACwAAAAADgAOAAAGFMCfcEgsGo%2FIpHLJbDqf0Kh0agwCACH5BAUDAD8ALAAAAAAOAA4AAAYUwJ9wSCwaj8ikcslsOp%2FQqHRqDAIAIfkEBQMAPwAsAAAAAAEAAQAABgPAXxAAIfkEBQMAPwAsAAAAAAEAAQAABgPAXxAAIfkEBQMAPwAsAAAAAAEAAQAABgPAXxAAIfkEBQMAPwAsAAAAAAEAAQAABgPAXxAAOw%3D%3D' @@ -149,6 +150,19 @@ var BootstrapRadioInlineRenderer = RadioFieldRenderer.extend({ } }) +var BootstrapStaticWidget = Widget.extend({ + constructor: function BootstrapStaticWidget(kwargs) { + if (!(this instanceof BootstrapStaticWidget)) { return new BootstrapStaticWidget(kwargs) } + this.convertStatic = kwargs && kwargs.convertStatic || ((value) => value); + Widget.call(this, kwargs) + }, + render(name, value, kwargs) { + return ( +

{this.convertStatic(value)}

+ ) + } +}) + // ========================================================= Form Components === var BootstrapForm = React.createClass({ @@ -185,7 +199,8 @@ var BootstrapForm = React.createClass({ propTypes: { form: React.PropTypes.instanceOf(Form).isRequired, - spinner: React.PropTypes.string + spinner: React.PropTypes.string, + static: React.PropTypes.bool }, getDefaultProps() { @@ -211,7 +226,7 @@ var BootstrapForm = React.createClass({ ) } rows.push.apply(rows, form.visibleFields().map(field => - + )) var hiddenFields = form.hiddenFields() if (hiddenFields.length > 0) { @@ -232,6 +247,7 @@ var BootstrapField = React.createClass({ propTypes: { field: React.PropTypes.instanceOf(BoundField).isRequired , spinner: React.PropTypes.string + , static: React.PropTypes.bool }, getDefaultProps() { @@ -257,6 +273,9 @@ var BootstrapField = React.createClass({ !(field.field.widget instanceof RadioSelect) && !(field.field.widget instanceof CheckboxSelectMultiple) })}} + if (this.props.static) { + widgetAttrs.widget = field.field.widgetAttrs.staticWidget || BootstrapStaticWidget(field.field.widgetAttrs) + } // Always show help text for empty fields, regardless of status var showHelpText = field.helpText && (field.isEmpty() || status == 'default') @@ -413,13 +432,12 @@ var Container = React.createClass({ , className: React.PropTypes.string , fluid: React.PropTypes.bool , spinner: React.PropTypes.string + , static: React.PropTypes.bool }, getDefaultProps() { return { - autoColumns: null - , fluid: false - , spinner: SPINNER + spinner: SPINNER } }, @@ -432,10 +450,9 @@ var Container = React.createClass({ {formErrors.messages().map(errorMessage)} } {React.Children.map(this.props.children, (row, index) => React.cloneElement(row, { - autoColumns: this.props.autoColumns - , form: this.props.form + ...this.props , index: index - , spinner: this.props.spinner + , className: null // Don't propagate className }))} {form.nonFieldPending() && Validating… @@ -448,6 +465,7 @@ var Row = React.createClass({ propTypes: { autoColumns: React.PropTypes.oneOf(BOOTSTRAP_COLUMN_SIZES) , className: React.PropTypes.string + , static: React.PropTypes.bool }, render() { @@ -465,9 +483,8 @@ var Row = React.createClass({ return
{React.Children.map(this.props.children, (child, index) => { return React.cloneElement(child, extend({ - form: this.props.form - , spinner: this.props.spinner - }, columnProps[index])) + ...this.props + , className: null // Don't propagate className })}
} @@ -488,12 +505,13 @@ var Field = React.createClass({ propTypes: { name: React.PropTypes.string.isRequired + , static: React.PropTypes.bool }, render() { var field = this.props.form.boundField(this.props.name) return
- +
} }) @@ -513,4 +531,4 @@ extend(BootstrapForm, { , Row }) -module.exports = BootstrapForm \ No newline at end of file +module.exports = BootstrapForm From 6cb0ab52be5927b5897a89570a7805a8dc1866b0 Mon Sep 17 00:00:00 2001 From: Sean Adkinson Date: Wed, 29 Jul 2015 15:28:04 -0700 Subject: [PATCH 2/6] Docs --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 1747eb3..e640298 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,21 @@ first grid layout example above: ![spinner](https://github.com/insin/newforms-bootstrap/raw/master/spinner.gif "Default async validation spinner") +* `static` - set to `true` to render fields using a static display control: `

{value}

` + + Provide a `widgetAttrs.convertStatic` function on a field to convert the value when displaying statically. The following + would render a cost value with a dollar sign in front when `static=true`, but would otherwise just render an integer field: + + ```javascript + var ProductForm = forms.Form.extend({ + cost: forms.IntegerField({ + widgetAttrs: { + convertStatic: (value) => '$' + value + } + }) + }) + ``` + ### Bootstrap-compatible choice field renderers The following custom renderers are available for use. Note that the non-inline From 85ece499ac2d9a4ef0b27aeed1391b269bfa7b4c Mon Sep 17 00:00:00 2001 From: Sean Adkinson Date: Wed, 29 Jul 2015 18:09:56 -0700 Subject: [PATCH 3/6] Horizontal forms --- README.md | 35 +++++++ newforms-bootstrap.iml | 8 ++ npm-debug.log | 27 ++++++ src/index.jsx | 214 +++++++++++++++++++++++++++++++++-------- 4 files changed, 244 insertions(+), 40 deletions(-) create mode 100644 newforms-bootstrap.iml create mode 100644 npm-debug.log diff --git a/README.md b/README.md index e640298..f608355 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,12 @@ first grid layout example above: ![spinner](https://github.com/insin/newforms-bootstrap/raw/master/spinner.gif "Default async validation spinner") * `static` - set to `true` to render fields using a static display control: `

{value}

` + + This is useful if you want to have readonly forms, and switch to editable given a flag: + + ```html + + ``` Provide a `widgetAttrs.convertStatic` function on a field to convert the value when displaying statically. The following would render a cost value with a dollar sign in front when `static=true`, but would otherwise just render an integer field: @@ -197,6 +203,35 @@ first grid layout example above: }) ``` +* `horizontal` - An object for specifying `form-horizontal` classes. Keys are one of the bootstrap + sizes (xs, sm, md, lg), and values are the number given to the form control. + + So for example: + + ```html + + ``` + + This would produce fields such as: + + ```html +
+ +
+ +
+
+
+
+
+ +
+
+
+ ``` + ### Bootstrap-compatible choice field renderers The following custom renderers are available for use. Note that the non-inline diff --git a/newforms-bootstrap.iml b/newforms-bootstrap.iml new file mode 100644 index 0000000..80cc739 --- /dev/null +++ b/newforms-bootstrap.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000..e25bbfd --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,27 @@ +0 info it worked if it ends with ok +1 verbose cli [ 'node', '/usr/local/bin/npm', 'run', 'dist' ] +2 info using npm@1.4.28 +3 info using node@v0.10.38 +4 verbose node symlink /usr/local/bin/node +5 verbose run-script [ 'predist', 'dist', 'postdist' ] +6 info predist newforms-bootstrap@1.1.1 +7 info dist newforms-bootstrap@1.1.1 +8 verbose unsafe-perm in lifecycle true +9 info newforms-bootstrap@1.1.1 Failed to exec dist script +10 error newforms-bootstrap@1.1.1 dist: `gulp bundle-js --production --release && gulp bundle-js --development --release` +10 error Exit status 127 +11 error Failed at the newforms-bootstrap@1.1.1 dist script. +11 error This is most likely a problem with the newforms-bootstrap package, +11 error not with npm itself. +11 error Tell the author that this fails on your system: +11 error gulp bundle-js --production --release && gulp bundle-js --development --release +11 error You can get their info via: +11 error npm owner ls newforms-bootstrap +11 error There is likely additional logging output above. +12 error System Darwin 14.4.0 +13 error command "node" "/usr/local/bin/npm" "run" "dist" +14 error cwd /Users/seanadkinson/code/newforms-bootstrap +15 error node -v v0.10.38 +16 error npm -v 1.4.28 +17 error code ELIFECYCLE +18 verbose exit [ 1, true ] diff --git a/src/index.jsx b/src/index.jsx index 4347cb6..0928c42 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -165,6 +165,13 @@ var BootstrapStaticWidget = Widget.extend({ // ========================================================= Form Components === +var HorizontalPropType = React.PropTypes.shape({ + xs: React.PropTypes.number, + sm: React.PropTypes.number, + md: React.PropTypes.number, + lg: React.PropTypes.number +}); + var BootstrapForm = React.createClass({ statics: { patchFields(form) { @@ -200,7 +207,8 @@ var BootstrapForm = React.createClass({ propTypes: { form: React.PropTypes.instanceOf(Form).isRequired, spinner: React.PropTypes.string, - static: React.PropTypes.bool + static: React.PropTypes.bool, + horizontal: HorizontalPropType }, getDefaultProps() { @@ -245,57 +253,180 @@ var BootstrapForm = React.createClass({ var BootstrapField = React.createClass({ propTypes: { - field: React.PropTypes.instanceOf(BoundField).isRequired - , spinner: React.PropTypes.string - , static: React.PropTypes.bool + field: React.PropTypes.instanceOf(BoundField).isRequired, + spinner: React.PropTypes.string, + static: React.PropTypes.bool, + horizontal: HorizontalPropType }, getDefaultProps() { return { - spinner: SPINNER - } + spinner: SPINNER, + horizontal: {} + }; }, render() { - var field = this.props.field - var status = field.status() - var isBooleanField = field.field.constructor === BooleanField - var isFileField = field.field instanceof FileField - var isSpecialCaseWidget = isBooleanField || isFileField - var containerClasses = cx({ - 'checkbox': isBooleanField - , 'form-group': !isBooleanField - , 'has-error': status == 'error' - , 'has-success': status == 'valid' - }) - var widgetAttrs = {attrs: {className: cx({ - 'form-control': !isFileField && - !(field.field.widget instanceof RadioSelect) && - !(field.field.widget instanceof CheckboxSelectMultiple) - })}} + var field = this.props.field; + var status = field.status(); + + return ( +
+ {this.getControlWithLabel(field, status)} + {this.getHelpText(field, status)} + {this.getSpinner(status)} + {this.getError(status)} +
+ ); + }, + + getContainerClasses(field, status) { + var isBooleanField = this.isBooleanField(field); + var isHorizontal = this.isHorizontalForm(); + return cx({ + 'checkbox': !isHorizontal && isBooleanField, + 'form-group': isHorizontal || !isBooleanField, + 'has-error': status == 'error', + 'has-success': status == 'valid' + }); + }, + + isBooleanField(field) { + return field.field.constructor === BooleanField; + }, + + isFileField(field) { + return field.field instanceof FileField; + }, + + isRadioSelect(field) { + return field.field.widget instanceof RadioSelect; + }, + + isCheckboxSelectMultipleField(field) { + return field.field.widget instanceof CheckboxSelectMultiple; + }, + + getControlWithLabel(field) { + + if (this.isBooleanField(field)) { + var checkbox = ( + + ); + + if (!this.isHorizontalForm()) { + return checkbox; + } + + return ( +
+
+ {checkbox} +
+
+ ); + } + + if (this.isFileField(field)) { + return ( +
+ {field.labelTag({attrs: {className: 'control-label ' + this.getHorizontalLabelClasses()}})} +
+ {field.asWidget(this.getWidgetAttrs(field))} +
+
+ ); + } + + return ( +
+ {field.labelTag({attrs: {className: 'control-label ' + this.getHorizontalLabelClasses()}})} +
+ {field.asWidget(this.getWidgetAttrs(field))} +
+
+ ); + }, + + getWidgetAttrs(field) { + var widgetAttrs = { + attrs: { + className: cx({ + 'form-control': !this.isFileField(field) + && !this.isRadioSelect(field) + && !this.isCheckboxSelectMultipleField(field) + }) + } + }; + if (this.props.static) { widgetAttrs.widget = field.field.widgetAttrs.staticWidget || BootstrapStaticWidget(field.field.widgetAttrs) } + + return widgetAttrs; + }, + + getHelpText(field, status) { + // Always show help text for empty fields, regardless of status - var showHelpText = field.helpText && (field.isEmpty() || status == 'default') - - return
- {!isBooleanField && field.labelTag({attrs: {className: 'control-label'}})} - {!isSpecialCaseWidget && field.asWidget(widgetAttrs)} - {isBooleanField && } - {isFileField &&
- {field.asWidget(widgetAttrs)} -
} - {showHelpText && field.helpTextTag({attrs: {className: 'help-block'}})} - {status == 'pending' && - Validating… - } - {status == 'error' && field.errors().messages().map(errorMessage)} -
+ var showHelpText = field.helpText && (field.isEmpty() || status == 'default'); + + return showHelpText && field.helpTextTag({attrs: {className: 'help-block'}}); + }, + + getSpinner(status) { + if (status == 'pending') { + return ( + + Validating… + + ); + } + }, + + getError(status) { + if (status == 'error') { + return field.errors().messages().map(errorMessage); + } + }, + + isHorizontalForm() { + return this.props.horizontal.length > 0; + }, + + getHorizontalLabelClasses() { + var classes = []; + var h = this.props.horizontal; + + for (var size in h) { + if (h.hasOwnProperty(size)) { + classes.push(`col-${size}-${12-h[size]}`); + } + } + + return classes.join(' '); + }, + + getHorizontalControlClasses(field) { + var classes = []; + var isBooleanField = this.isBooleanField(field); + var h = this.props.horizontal; + + for (var size in h) { + if (h.hasOwnProperty(size)) { + classes.push(`col-${size}-${h[size]}`); + if (isBooleanField) { + classes.push(`col-${size}-offset-${12-h[size]}`); + } + } + } + + return classes.join(' '); } -}) + +}); // ========================================================= Grid Components === @@ -433,6 +564,7 @@ var Container = React.createClass({ , fluid: React.PropTypes.bool , spinner: React.PropTypes.string , static: React.PropTypes.bool + , horizontal: HorizontalPropType }, getDefaultProps() { @@ -466,6 +598,7 @@ var Row = React.createClass({ autoColumns: React.PropTypes.oneOf(BOOTSTRAP_COLUMN_SIZES) , className: React.PropTypes.string , static: React.PropTypes.bool + , horizontal: HorizontalPropType }, render() { @@ -506,6 +639,7 @@ var Field = React.createClass({ propTypes: { name: React.PropTypes.string.isRequired , static: React.PropTypes.bool + , horizontal: HorizontalPropType }, render() { From 5711772bb70c6bcee7fac7f665dac3d41df0f766 Mon Sep 17 00:00:00 2001 From: Sean Adkinson Date: Wed, 29 Jul 2015 18:11:15 -0700 Subject: [PATCH 4/6] Deleting files that shouldnt be there --- newforms-bootstrap.iml | 8 -------- npm-debug.log | 27 --------------------------- 2 files changed, 35 deletions(-) delete mode 100644 newforms-bootstrap.iml delete mode 100644 npm-debug.log diff --git a/newforms-bootstrap.iml b/newforms-bootstrap.iml deleted file mode 100644 index 80cc739..0000000 --- a/newforms-bootstrap.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index e25bbfd..0000000 --- a/npm-debug.log +++ /dev/null @@ -1,27 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ 'node', '/usr/local/bin/npm', 'run', 'dist' ] -2 info using npm@1.4.28 -3 info using node@v0.10.38 -4 verbose node symlink /usr/local/bin/node -5 verbose run-script [ 'predist', 'dist', 'postdist' ] -6 info predist newforms-bootstrap@1.1.1 -7 info dist newforms-bootstrap@1.1.1 -8 verbose unsafe-perm in lifecycle true -9 info newforms-bootstrap@1.1.1 Failed to exec dist script -10 error newforms-bootstrap@1.1.1 dist: `gulp bundle-js --production --release && gulp bundle-js --development --release` -10 error Exit status 127 -11 error Failed at the newforms-bootstrap@1.1.1 dist script. -11 error This is most likely a problem with the newforms-bootstrap package, -11 error not with npm itself. -11 error Tell the author that this fails on your system: -11 error gulp bundle-js --production --release && gulp bundle-js --development --release -11 error You can get their info via: -11 error npm owner ls newforms-bootstrap -11 error There is likely additional logging output above. -12 error System Darwin 14.4.0 -13 error command "node" "/usr/local/bin/npm" "run" "dist" -14 error cwd /Users/seanadkinson/code/newforms-bootstrap -15 error node -v v0.10.38 -16 error npm -v 1.4.28 -17 error code ELIFECYCLE -18 verbose exit [ 1, true ] From d16a1cdfa80735043c3e3717e33b7bc812cdd4a8 Mon Sep 17 00:00:00 2001 From: Andrew Somerville Date: Fri, 25 Sep 2015 23:07:55 -0400 Subject: [PATCH 5/6] fix merge/rebase mistake --- src/index.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.jsx b/src/index.jsx index 0928c42..ee411a4 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -618,6 +618,7 @@ var Row = React.createClass({ return React.cloneElement(child, extend({ ...this.props , className: null // Don't propagate className + }, columnProps[index])) })} } From bce87c5044620fc68e1656475af3ab76eb81e1c9 Mon Sep 17 00:00:00 2001 From: Andrew Somerville Date: Fri, 25 Sep 2015 23:23:46 -0400 Subject: [PATCH 6/6] fix missing 'field' parameter on getError --- src/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.jsx b/src/index.jsx index ee411a4..340177a 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -275,7 +275,7 @@ var BootstrapField = React.createClass({ {this.getControlWithLabel(field, status)} {this.getHelpText(field, status)} {this.getSpinner(status)} - {this.getError(status)} + {this.getError(field, status)} ); }, @@ -386,7 +386,7 @@ var BootstrapField = React.createClass({ } }, - getError(status) { + getError(field, status) { if (status == 'error') { return field.errors().messages().map(errorMessage); }