From b23b7eea739b853431e99f301878bc903c58a8a8 Mon Sep 17 00:00:00 2001 From: Sacha Greif Date: Wed, 30 Mar 2016 19:08:06 +0900 Subject: [PATCH] include forms package in repo; add cache for FR-SSR; fix modal window callback issue; fix posts.single publication and subscription bug --- .meteor/packages | 1 + .meteor/versions | 3 +- .../lib/categories/list/CategoriesList.jsx | 1 - .../lib/common/NewPostButton.jsx | 3 +- .../lib/posts/PostEditForm.jsx | 2 +- packages/nova-base-components/lib/routes.jsx | 2 +- packages/nova-base-components/package.js | 3 +- .../nova-core/lib/components/ModalTrigger.jsx | 33 +++----- packages/nova-core/lib/router.jsx | 6 ++ packages/nova-demo/demo-components.jsx | 2 +- packages/nova-forms/README.md | 24 ++++++ packages/nova-forms/lib/EditDocument.jsx | 75 +++++++++++++++++++ packages/nova-forms/lib/NewDocument.jsx | 69 +++++++++++++++++ packages/nova-forms/lib/export.js | 13 ++++ packages/nova-forms/lib/smart-forms.jsx | 51 +++++++++++++ packages/nova-forms/lib/utils.js | 37 +++++++++ packages/nova-forms/package.js | 23 ++++++ packages/nova-lib/package.js | 1 + .../nova-posts/lib/server/publications.js | 2 +- 19 files changed, 321 insertions(+), 30 deletions(-) create mode 100644 packages/nova-forms/README.md create mode 100644 packages/nova-forms/lib/EditDocument.jsx create mode 100644 packages/nova-forms/lib/NewDocument.jsx create mode 100644 packages/nova-forms/lib/export.js create mode 100644 packages/nova-forms/lib/smart-forms.jsx create mode 100644 packages/nova-forms/lib/utils.js create mode 100644 packages/nova-forms/package.js diff --git a/.meteor/packages b/.meteor/packages index a800add39..6e87d0d31 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -23,6 +23,7 @@ nova:getting-started nova:categories nova:share nova:voting +nova:forms nova:embedly nova:base-components nova:base-styles diff --git a/.meteor/versions b/.meteor/versions index a69f24f33..2a41d6f75 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -88,6 +88,7 @@ nova:core@0.25.7 nova:email@0.25.7 nova:embedly@0.25.7 nova:events@0.25.7 +nova:forms@0.25.7 nova:getting-started@0.25.7 nova:i18n@0.25.7 nova:lib@0.25.7 @@ -136,6 +137,7 @@ tap:i18n@1.7.0 templating@1.1.7 templating-tools@1.0.2 tmeasday:check-npm-versions@0.1.1 +tmeasday:healthcheck-handler@0.0.2 tmeasday:publish-counts@0.7.3 tracker@1.0.11 twitter@1.1.7 @@ -143,7 +145,6 @@ ui@1.0.9 underscore@1.0.6 url@1.0.7 utilities:avatar@0.9.2 -utilities:react-form-containers@0.1.7 utilities:react-list-container@0.1.6 utilities:smart-methods@0.1.2 utilities:smart-publications@0.1.4 diff --git a/packages/nova-base-components/lib/categories/list/CategoriesList.jsx b/packages/nova-base-components/lib/categories/list/CategoriesList.jsx index a1b5fc8d2..e92ec38ff 100644 --- a/packages/nova-base-components/lib/categories/list/CategoriesList.jsx +++ b/packages/nova-base-components/lib/categories/list/CategoriesList.jsx @@ -30,7 +30,6 @@ const CategoriesList = ({categories}, context) => { }; - CategoriesList.contextTypes = { currentRoute: React.PropTypes.object }; diff --git a/packages/nova-base-components/lib/common/NewPostButton.jsx b/packages/nova-base-components/lib/common/NewPostButton.jsx index 989b928df..e8334129a 100644 --- a/packages/nova-base-components/lib/common/NewPostButton.jsx +++ b/packages/nova-base-components/lib/common/NewPostButton.jsx @@ -1,11 +1,12 @@ import React, { PropTypes, Component } from 'react'; import { Button } from 'react-bootstrap'; +import Router from '../router.js' import Core from "meteor/nova:core"; const Messages = Core.Messages; const ModalTrigger = Core.ModalTrigger; -import ReactForms from "meteor/utilities:react-form-containers"; +import ReactForms from "meteor/nova:forms"; const NewDocument = ReactForms.NewDocument; const NewPostButton = (props, context) => { diff --git a/packages/nova-base-components/lib/posts/PostEditForm.jsx b/packages/nova-base-components/lib/posts/PostEditForm.jsx index e84f97d94..92308abe5 100644 --- a/packages/nova-base-components/lib/posts/PostEditForm.jsx +++ b/packages/nova-base-components/lib/posts/PostEditForm.jsx @@ -1,5 +1,5 @@ import React, { PropTypes, Component } from 'react'; -import ReactForms from "meteor/utilities:react-form-containers"; +import ReactForms from "meteor/nova:forms"; const EditDocument = ReactForms.EditDocument; import Core from "meteor/nova:core"; diff --git a/packages/nova-base-components/lib/routes.jsx b/packages/nova-base-components/lib/routes.jsx index 03bf7dd80..38fce09eb 100644 --- a/packages/nova-base-components/lib/routes.jsx +++ b/packages/nova-base-components/lib/routes.jsx @@ -47,7 +47,7 @@ Router.route('/posts/:_id/:slug?', { { - - // if child component already has a successCallback, create new callback - // that both calls original callback and also closes modal - let successCallback; - if (child.props.successCallback) { - successCallback = (document) => { - child.props.successCallback(document); - this.closeModal(); - } - } else { - successCallback = this.closeModal; - } - - return React.cloneElement(child, { successCallback: successCallback }); - - }); const triggerComponent = React.cloneElement(this.props.component, { onClick: this.openModal }); @@ -70,7 +57,7 @@ class ModalTrigger extends Component { onRequestClose={this.closeModal} style={customStyles} > - {childrenWithProps} + {this.props.children} ) @@ -81,5 +68,9 @@ ModalTrigger.propTypes = { component: React.PropTypes.object.isRequired } +ModalTrigger.childContextTypes = { + closeCallback: React.PropTypes.func +} + module.exports = ModalTrigger; export default ModalTrigger; \ No newline at end of file diff --git a/packages/nova-core/lib/router.jsx b/packages/nova-core/lib/router.jsx index 64042aef7..b59eb79f0 100644 --- a/packages/nova-core/lib/router.jsx +++ b/packages/nova-core/lib/router.jsx @@ -24,6 +24,12 @@ FlowRouter.removeFromQueryArray = function (key, value) { FlowRouter.setQueryParams(params); } +if(Meteor.isServer) { + var timeInMillis = 1000 * 10; // 10 secs + FlowRouter.setPageCacheTimeout(timeInMillis); + FlowRouter.setDeferScriptLoading(true); +} + // FlowRouter.notFound = { // action: function() { // if (Meteor.isClient) { diff --git a/packages/nova-demo/demo-components.jsx b/packages/nova-demo/demo-components.jsx index 64db2da3c..e52f27972 100644 --- a/packages/nova-demo/demo-components.jsx +++ b/packages/nova-demo/demo-components.jsx @@ -4,7 +4,7 @@ import NoSSR from 'react-no-ssr'; import Core from 'meteor/nova:core'; import SmartContainers from "meteor/utilities:react-list-container"; -import FormContainers from "meteor/utilities:react-form-containers"; +import FormContainers from "meteor/nova:forms"; const ModalTrigger = Core.ModalTrigger; const NewDocument = FormContainers.NewDocument; diff --git a/packages/nova-forms/README.md b/packages/nova-forms/README.md new file mode 100644 index 000000000..292169b14 --- /dev/null +++ b/packages/nova-forms/README.md @@ -0,0 +1,24 @@ +# React Forms + +This package provides two components (`NewDocument` and `EditDocument`) that work with the schema extension defined in the [smart-methods](https://github.com/meteor-utilities/smart-methods) package to let you easily generate new document and edit document forms. + +### Install + +`meteor add utilities:react-form-containers` + +### `NewDocument` + +This component takes the following properties: + +- `collection`: the collection in which to insert the new document. +- `currentUser`: the current user. +- `errorCallback`: a function to call on error. +- `successCallback`: a function to call on success. +- `methodName`: the name of the method to submit the form to. +- `labelFunction`: a function that will be called on each field's name to get the label (for example, an internationalization function). + +### `EditDocument` + +This component takes the same properties as `NewDocument`, plus: + +- `document`: the document being edited. \ No newline at end of file diff --git a/packages/nova-forms/lib/EditDocument.jsx b/packages/nova-forms/lib/EditDocument.jsx new file mode 100644 index 000000000..f60bac4a1 --- /dev/null +++ b/packages/nova-forms/lib/EditDocument.jsx @@ -0,0 +1,75 @@ +import React, { PropTypes, Component } from 'react'; +import Formsy from 'formsy-react'; +import { Button } from 'react-bootstrap'; + +import SmartForms from "./smart-forms.jsx"; +import Utils from './utils.js'; + +const EditDocument = React.createClass({ + + propTypes: { + document: React.PropTypes.object.isRequired, + collection: React.PropTypes.object.isRequired, + currentUser: React.PropTypes.object, + successCallback: React.PropTypes.func, + errorCallback: React.PropTypes.func, + methodName: React.PropTypes.string, + labelFunction: React.PropTypes.func + }, + + submitForm(data) { + + console.log(data) + + const document = this.props.document; + const modifier = {$set: _.compactObject(Utils.flatten(data))}; + const collection = this.props.collection; + const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit'; + + console.log(modifier) + + Meteor.call(methodName, document._id, modifier, (error, document) => { + if (error) { + console.log(error) + if (this.props.errorCallback) { + this.props.errorCallback(document); + } + } else { + if (this.props.successCallback) { + this.props.successCallback(document); + } + if (this.context.closeCallback) { + this.context.closeCallback(); + } + } + }); + }, + + render() { + + const document = this.props.document; + const collection = this.props.collection; + const fields = collection.getInsertableFields(this.props.currentUser); + + const style = { + maxWidth: "800px", + width: "100%" + } + + return ( +
+ + {fields.map(fieldName =>
{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}
)} + +
+
+ ) + } +}); + +EditDocument.contextTypes = { + closeCallback: React.PropTypes.func +} + +module.exports = EditDocument; +export default EditDocument; \ No newline at end of file diff --git a/packages/nova-forms/lib/NewDocument.jsx b/packages/nova-forms/lib/NewDocument.jsx new file mode 100644 index 000000000..4251692da --- /dev/null +++ b/packages/nova-forms/lib/NewDocument.jsx @@ -0,0 +1,69 @@ +import React, { PropTypes, Component } from 'react'; +import Formsy from 'formsy-react'; +import { Button } from 'react-bootstrap'; + +import SmartForms from "./smart-forms.jsx"; +import Utils from './utils.js'; + +const NewDocument = React.createClass({ + + propTypes: { + collection: React.PropTypes.object.isRequired, + currentUser: React.PropTypes.object, + errorCallback: React.PropTypes.func, + successCallback: React.PropTypes.func, + methodName: React.PropTypes.string, + labelFunction: React.PropTypes.func + }, + + submitForm(data) { + + // remove any empty properties + const document = _.compactObject(Utils.flatten(data)); + const collection = this.props.collection; + const methodName = this.props.methodName ? this.props.methodName : collection._name+'.create'; + + Meteor.call(methodName, document, (error, document) => { + if (error) { + console.log(error) + if (this.props.errorCallback) { + this.props.errorCallback(document); + } + } else { + if (this.props.successCallback) { + this.props.successCallback(document); + } + if (this.context.closeCallback) { + this.context.closeCallback(); + } + } + }); + }, + + render() { + + const collection = this.props.collection; + const fields = collection.getInsertableFields(this.props.currentUser); + + const style = { + maxWidth: "800px", + width: "100%" + } + + return ( +
+ + {fields.map(fieldName =>
{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction)}
)} + +
+
+ ) + } +}); + +NewDocument.contextTypes = { + closeCallback: React.PropTypes.func +} + +module.exports = NewDocument; +export default NewDocument; \ No newline at end of file diff --git a/packages/nova-forms/lib/export.js b/packages/nova-forms/lib/export.js new file mode 100644 index 000000000..3d1491582 --- /dev/null +++ b/packages/nova-forms/lib/export.js @@ -0,0 +1,13 @@ +import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; +checkNpmVersions({ + "react": "^0.14.6", + "formsy-react": "^0.17.0", + "formsy-react-components": "^0.6.6", + "react-bootstrap": "^0.28.3" + // 'rebass': '^0.2.4', +}); + +import NewDocument from "./NewDocument.jsx"; +import EditDocument from "./EditDocument.jsx"; + +export default {NewDocument, EditDocument}; \ No newline at end of file diff --git a/packages/nova-forms/lib/smart-forms.jsx b/packages/nova-forms/lib/smart-forms.jsx new file mode 100644 index 000000000..f3aaf8ffa --- /dev/null +++ b/packages/nova-forms/lib/smart-forms.jsx @@ -0,0 +1,51 @@ +import React, { PropTypes, Component } from 'react'; +import Formsy from 'formsy-react'; +import FRC from 'formsy-react-components'; + +import Utils from './utils.js'; + +const Checkbox = FRC.Checkbox; +const CheckboxGroup = FRC.CheckboxGroup; +const Input = FRC.Input; +const RadioGroup = FRC.RadioGroup; +const Select = FRC.Select; +const Textarea = FRC.Textarea; + +const SmartForms = {}; + +SimpleSchema.extendOptions({ + insertableIf: Match.Optional(Function), + editableIf: Match.Optional(Function) +}); + +SmartForms.getComponent = (fieldName, field, labelFunction, document) => { + + let options = []; + if (field.autoform && field.autoform.options) { + options = typeof field.autoform.options === "function" ? field.autoform.options() : field.autoform.options; + } + + const value = document && Utils.deepValue(document, fieldName) ? Utils.deepValue(document, fieldName) : ""; + const label = typeof labelFunction === "function" ? labelFunction(fieldName) : fieldName; + + switch (field.control) { + + case "text": + return ; + case "textarea": + return