rework post new and post edit forms to be collection-agnostic

This commit is contained in:
Sacha Greif 2016-02-26 10:42:57 +09:00
parent 50b0c57de3
commit 38e07737c3
17 changed files with 352 additions and 166 deletions

View file

@ -3,7 +3,7 @@ import NoSSR from 'react-no-ssr';
const Header = props => {
({Logo, ListContainer, CategoriesList, FlashContainer, NewPostButton, ModalButton, PostNewContainer, CurrentUserContainer, PostNew} = Telescope.components);
({Logo, ListContainer, CategoriesList, FlashContainer, NewPostButton, ModalButton, PostNewContainer, CurrentUserContainer, PostNew, NewDocContainer} = Telescope.components);
const logoUrl = Telescope.settings.get("logoUrl");
const siteTitle = Telescope.settings.get("title", "Telescope");
@ -16,7 +16,7 @@ const Header = props => {
{tagline ? <h2 className="tagline">{tagline}</h2> : "" }
</div>
<div className="nav">
{<ListContainer collection={Categories} component={CategoriesList} limit={0}/>}
<ListContainer collection={Categories} limit={0}><CategoriesList/></ListContainer>
</div>
<NoSSR onSSR={<p>Loading</p>}>
@ -25,7 +25,9 @@ const Header = props => {
{props.currentUser ? <p><a href={FlowRouter.path("account")}>My Account</a></p> : ""}
<ModalButton label="New Post" className="button button--primary"><CurrentUserContainer><PostNew /></CurrentUserContainer></ModalButton>
<ModalButton label="New Post" className="button button--primary">
<NewDocContainer collection={Posts} label="New Post" methodName="posts.new" callback={(post)=>{FlowRouter.go('posts.single', post);}}/>
</ModalButton>
<FlashContainer />

View file

@ -18,8 +18,8 @@ Telescope.registerComponent("PostList", require('./posts/list/PostList.jsx'));
Telescope.registerComponent("PostCategories", require('./posts/list/PostCategories.jsx'));
Telescope.registerComponent("PostCommenters", require('./posts/list/PostCommenters.jsx'));
Telescope.registerComponent("Post", require('./posts/Post.jsx'));
Telescope.registerComponent("PostEdit", require('./posts/PostEdit.jsx'));
Telescope.registerComponent("PostNew", require('./posts/PostNew.jsx'));
// Telescope.registerComponent("PostEdit", require('./posts/PostEdit.jsx'));
// Telescope.registerComponent("PostNew", require('./posts/PostNew.jsx'));
Telescope.registerComponent("NewPostButton", require('./posts/NewPostButton.jsx'));
// comments

View file

@ -21,10 +21,9 @@ const Post = (props) => {
publication="comments.list"
selector={{postId: post._id}}
terms={{postId: post._id, view: "postComments"}}
component={CommentList}
limit={0}
parentProperty="parentCommentId"
/>
><CommentList/></ListContainer>
<div className="post-new-comment">
<h4>New Comment:</h4>

View file

@ -1,52 +1,52 @@
import Core from "meteor/nova:core";
({Messages, NovaForms} = Core);
// import Core from "meteor/nova:core";
// ({Messages, NovaForms} = Core);
import Formsy from 'formsy-react';
// import Formsy from 'formsy-react';
const PostEdit = React.createClass({
// const PostEdit = React.createClass({
propTypes: {
document: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object.isRequired
},
// propTypes: {
// document: React.PropTypes.object.isRequired,
// currentUser: React.PropTypes.object.isRequired
// },
submitForm(data) {
const post = this.props.document;
const modifier = {$set: _.compactObject(data)};
// submitForm(data) {
// const post = this.props.document;
// const modifier = {$set: _.compactObject(data)};
event.preventDefault();
// event.preventDefault();
Meteor.call('posts.edit', post._id, modifier, (error, post) => {
if (error) {
console.log(error)
Messages.flash(error.message, "error")
} else {
Messages.flash("Post edited.", "success")
FlowRouter.go('posts.single', post);
}
});
},
// Meteor.call('posts.edit', post._id, modifier, (error, post) => {
// if (error) {
// console.log(error)
// Messages.flash(error.message, "error")
// } else {
// Messages.flash("Post edited.", "success")
// FlowRouter.go('posts.single', post);
// }
// });
// },
render() {
// render() {
({CanEditPost} = Telescope.components);
// ({CanEditPost} = Telescope.components);
const post = this.props.document;
// const post = this.props.document;
const fields = Posts.simpleSchema().getEditableFields(this.props.currentUser);
// const fields = Posts.simpleSchema().getEditableFields(this.props.currentUser);
return (
<CanEditPost user={this.props.currentUser} post={post}>
<div className="post-edit">
<h3>Edit Post {post.title}</h3>
<Formsy.Form onSubmit={this.submitForm}>
{fields.map(fieldName => NovaForms.getComponent(fieldName, Posts.simpleSchema()._schema[fieldName], post))}
<button type="submit" className="button button--primary">Submit</button>
</Formsy.Form>
</div>
</CanEditPost>
)
}
});
// return (
// <CanEditPost user={this.props.currentUser} post={post}>
// <div className="post-edit">
// <h3>Edit Post {post.title}</h3>
// <Formsy.Form onSubmit={this.submitForm}>
// {fields.map(fieldName => NovaForms.getComponent(fieldName, Posts.simpleSchema()._schema[fieldName], post))}
// <button type="submit" className="button button--primary">Submit</button>
// </Formsy.Form>
// </div>
// </CanEditPost>
// )
// }
// });
module.exports = PostEdit;
// module.exports = PostEdit;

View file

@ -1,60 +1,60 @@
import Core from "meteor/nova:core";
({Messages, NovaForms} = Core);
// import Core from "meteor/nova:core";
// ({Messages, NovaForms} = Core);
import Formsy from 'formsy-react';
// import Formsy from 'formsy-react';
const PostNew = React.createClass({
// const PostNew = React.createClass({
propTypes: {
currentUser: React.PropTypes.object,
postNewCallback: React.PropTypes.func,
closeModal: React.PropTypes.func
},
// propTypes: {
// currentUser: React.PropTypes.object,
// postNewCallback: React.PropTypes.func,
// closeModal: React.PropTypes.func
// },
getInitialState() {
return {
canSubmit: false
}
},
// getInitialState() {
// return {
// canSubmit: false
// }
// },
submitForm(data) {
// remove any empty properties
post = _.compactObject(data);
// submitForm(data) {
// // remove any empty properties
// post = _.compactObject(data);
post = Telescope.callbacks.run("posts.new.client", post);
// post = Telescope.callbacks.run("posts.new.client", post);
Meteor.call('posts.new', post, (error, post) => {
if (error) {
console.log(error)
Messages.flash(error.message, "error")
} else {
Messages.flash("Post created.", "success");
FlowRouter.go('posts.single', post);
if (this.props.closeModal) {
this.props.closeModal();
}
}
});
},
// Meteor.call('posts.new', post, (error, post) => {
// if (error) {
// console.log(error)
// Messages.flash(error.message, "error")
// } else {
// Messages.flash("Post created.", "success");
// FlowRouter.go('posts.single', post);
// if (this.props.closeModal) {
// this.props.closeModal();
// }
// }
// });
// },
render() {
// render() {
({CanCreatePost} = Telescope.components);
// ({CanCreatePost} = Telescope.components);
const fields = Posts.simpleSchema().getEditableFields(this.props.currentUser);
// const fields = Posts.simpleSchema().getEditableFields(this.props.currentUser);
return (
<CanCreatePost user={this.props.currentUser}>
<div className="post-new">
<h3>New Post</h3>
<Formsy.Form onSubmit={this.submitForm}>
{fields.map(fieldName => NovaForms.getComponent(fieldName, Posts.simpleSchema()._schema[fieldName]))}
<button type="submit" className="button button--primary">Submit</button>
</Formsy.Form>
</div>
</CanCreatePost>
)
}
});
// return (
// <CanCreatePost user={this.props.currentUser}>
// <div className="post-new">
// <h3>New Post</h3>
// <Formsy.Form onSubmit={this.submitForm}>
// {fields.map(fieldName => NovaForms.getComponent(fieldName, Posts.simpleSchema()._schema[fieldName]))}
// <button type="submit" className="button button--primary">Submit</button>
// </Formsy.Form>
// </div>
// </CanCreatePost>
// )
// }
// });
module.exports = PostNew;
// module.exports = PostNew;

View file

@ -21,7 +21,7 @@ const PostItem = React.createClass({
renderActions() {
({ModalButton, ItemContainer, PostEdit} = Telescope.components);
({ModalButton, ItemContainer, EditDocContainer} = Telescope.components);
const component = (
<ModalButton label="Edit" className="button button--secondary">
@ -29,8 +29,9 @@ const PostItem = React.createClass({
collection={Posts}
publication="posts.single"
terms={{_id: this.props.post._id}}
component={PostEdit}
/>
>
<EditDocContainer label="Edit Post" methodName="posts.edit"/>
</ItemContainer>
</ModalButton>
);

View file

@ -18,9 +18,8 @@ FlowRouter.route('/', {
selector={selector}
options={options}
terms={queryParams}
component={PostList}
joins={Posts.simpleSchema().getJoins()}
/>})
><PostList/></ListContainer>})
}
});
@ -41,9 +40,8 @@ FlowRouter.route('/posts/:_id', {
collection={Posts}
publication="posts.single"
terms={params}
component={Post}
joins={Posts.simpleSchema().getJoins()}
/>});
><Post/></ItemContainer>});
}
});
@ -56,7 +54,7 @@ FlowRouter.route('/posts/:_id/edit', {
publication="posts.single"
terms={{_id: params._id}}
component={PostEdit}
/>});
><PostEdit/></ItemContainer>});
}
});
@ -69,8 +67,7 @@ FlowRouter.route('/users/:slug', {
collection={Users}
publication="users.single"
terms={{'telescope.slug': params.slug}}
component={UsersSingle}
/>});
><UsersSingle/></ItemContainer>});
}
});
@ -84,7 +81,7 @@ FlowRouter.route('/account', {
publication="users.single"
terms={{_id: Meteor.userId()}}
component={UsersEdit}
/>});
><UsersEdit/></ItemContainer>});
}
});
@ -98,7 +95,7 @@ FlowRouter.route('/users/:slug/edit', {
publication="users.single"
terms={params}
component={UsersEdit}
/>});
><UsersEdit/></ItemContainer>});
}
});

View file

@ -3,5 +3,7 @@ Telescope.registerComponent("ItemContainer", require('./containers/ItemContainer
Telescope.registerComponent("ListContainer", require('./containers/ListContainer.jsx'));
Telescope.registerComponent("FlashContainer", require('./containers/FlashContainer.jsx'));
Telescope.registerComponent("CurrentUserContainer", require('./containers/CurrentUserContainer.jsx'));
Telescope.registerComponent("NewDocContainer", require('./containers/NewDocContainer.jsx'));
Telescope.registerComponent("EditDocContainer", require('./containers/EditDocContainer.jsx'));
Telescope.registerComponent("ModalButton", require('./components/ModalButton.jsx'));
Telescope.registerComponent("ModalButton", require('./components/ModalButton.jsx'));

View file

@ -0,0 +1,54 @@
// import Messages from "../messages.js";
// import NovaForms from "../forms.jsx";
// import Formsy from 'formsy-react';
// const EditDocumentForm = React.createClass({
// propTypes: {
// document: React.PropTypes.object.isRequired,
// currentUser: React.PropTypes.object.isRequired,
// collection: React.PropTypes.object.isRequired,
// label: React.PropTypes.string,
// callback: React.PropTypes.func,
// methodName: React.PropTypes.string
// },
// submitForm(data) {
// const document = this.props.document;
// const modifier = {$set: _.compactObject(data)};
// const collection = this.props.collection;
// const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
// Meteor.call(methodName, document._id, modifier, (error, document) => {
// if (error) {
// console.log(error)
// Messages.flash(error.message, "error")
// } else {
// Messages.flash("Document edited.", "success");
// if (this.props.callback) {
// this.props.callback(document);
// }
// }
// });
// },
// render() {
// const document = this.props.document;
// const collection = this.props.collection;
// const fields = collection.simpleSchema().getEditableFields(this.props.currentUser);
// return (
// <div className="document-edit">
// <h3>{this.props.label}</h3>
// <Formsy.Form onSubmit={this.submitForm}>
// {fields.map(fieldName => NovaForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], document))}
// <button type="submit" className="button button--primary">Submit</button>
// </Formsy.Form>
// </div>
// )
// }
// });
// module.exports = EditDocumentForm;

View file

@ -14,7 +14,6 @@ const customStyles = {
const ModalButton = React.createClass({
propTypes: {
propsToPass: React.PropTypes.object,
label: React.PropTypes.string.isRequired,
className: React.PropTypes.string
},
@ -39,7 +38,21 @@ const ModalButton = React.createClass({
// see http://stackoverflow.com/a/32371612/649299
const childrenWithProps = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, { ...this.props.propsToPass, closeModal: this.closeModal });
// if child component already has a callback, create new callback
// that both calls original callback and also closes modal
let callback;
if (child.props.callback) {
callback = (document) => {
child.props.callback(document);
this.closeModal();
}
} else {
callback = this.closeModal;
}
return React.cloneElement(child, { callback: callback });
});
return (
@ -50,7 +63,6 @@ const ModalButton = React.createClass({
onRequestClose={this.closeModal}
style={customStyles} >
{childrenWithProps}
</Modal>

View file

@ -1,54 +1,58 @@
import Core from "meteor/nova:core";
({Messages, NovaForms} = Core);
// import Messages from "../messages.js";
// import NovaForms from "../forms.jsx";
import Formsy from 'formsy-react';
// import Formsy from 'formsy-react';
const NewDocumentForm = React.createClass({
// const NewDocumentForm = React.createClass({
propTypes: {
currentUser: React.PropTypes.object,
categories: React.PropTypes.array,
collection: React.PropTypes.object,
postNewCallback: React.PropTypes.func
},
// propTypes: {
// currentUser: React.PropTypes.object.isRequired,
// collection: React.PropTypes.object.isRequired,
// label: React.PropTypes.string,
// callback: React.PropTypes.func,
// methodName: React.PropTypes.string
// },
getInitialState() {
return {
canSubmit: false
}
},
// getInitialState() {
// return {
// canSubmit: false
// }
// },
submitForm(data) {
// remove any empty properties
document = _.compactObject(data);
// submitForm(data) {
// // remove any empty properties
// const document = _.compactObject(data);
// const collection = this.props.collection;
// const methodName = this.props.methodName ? this.props.methodName : collection._name+'.create';
Meteor.call(this.props.collection._name+'.create', document, (error, result) => {
if (error) {
console.log(error)
Messages.flash(error.message, "error")
} else {
Messages.flash("Document created.", "success");
if (this.props.postNewCallback) {
this.props.postNewCallback(result);
}
}
});
},
// Meteor.call(methodName, document, (error, document) => {
// if (error) {
// console.log(error)
// Messages.flash(error.message, "error")
// } else {
// Messages.flash("Document created.", "success");
// if (this.props.callback) {
// this.props.callback(document);
// }
// }
// });
// },
render() {
// render() {
const fields = this.props.collection.simpleSchema().getEditableFields(this.props.currentUser);
// const collection = this.props.collection;
// const fields = collection.simpleSchema().getEditableFields(this.props.currentUser);
return (
<div className="new-document">
<h3>New </h3>
<Formsy.Form onSubmit={this.submitForm}>
{fields.map(fieldName => NovaForms.getComponent(fieldName, this.props.collection.simpleSchema()._schema[fieldName]))}
<button type="submit" className="button button--primary">Submit</button>
</Formsy.Form>
</div>
)
}
});
// return (
// <div className="new-document">
// <h3>{this.props.label}</h3>
// <Formsy.Form onSubmit={this.submitForm}>
// {fields.map(fieldName => NovaForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName]))}
// <button type="submit" className="button button--primary">Submit</button>
// </Formsy.Form>
// </div>
// )
// }
// });
module.exports = NewDocumentForm;
// module.exports = NewDocumentForm;

View file

@ -0,0 +1,63 @@
import Messages from "../messages.js";
import NovaForms from "../forms.jsx";
import Formsy from 'formsy-react';
const EditDocContainer = React.createClass({
propTypes: {
document: React.PropTypes.object, // required but might be passed later on
collection: React.PropTypes.object, // required but might be passed later on
label: React.PropTypes.string,
callback: React.PropTypes.func,
methodName: React.PropTypes.string
},
mixins: [ReactMeteorData],
getMeteorData() {
console.log(this)
return {
currentUser: Meteor.user()
};
},
submitForm(data) {
const document = this.props.document;
const modifier = {$set: _.compactObject(data)};
const collection = this.props.collection;
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
Meteor.call(methodName, document._id, modifier, (error, document) => {
if (error) {
console.log(error)
Messages.flash(error.message, "error")
} else {
Messages.flash("Document edited.", "success");
if (this.props.callback) {
this.props.callback(document);
}
}
});
},
render() {
const document = this.props.document;
const collection = this.props.collection;
const fields = collection.simpleSchema().getEditableFields(this.data.currentUser);
return (
<div className="document-edit">
<h3>{this.props.label}</h3>
<Formsy.Form onSubmit={this.submitForm}>
{fields.map(fieldName => NovaForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], document))}
<button type="submit" className="button button--primary">Submit</button>
</Formsy.Form>
</div>
)
}
});
module.exports = EditDocContainer;

View file

@ -4,11 +4,10 @@ const ItemContainer = React.createClass({
propTypes: {
collection: React.PropTypes.object.isRequired,
component: React.PropTypes.func.isRequired,
publication: React.PropTypes.string.isRequired,
terms: React.PropTypes.object,
propsToPass: React.PropTypes.object,
joins: React.PropTypes.array
joins: React.PropTypes.array,
callback: React.PropTypes.func // a callback function to pass through, for modals (note: use Redux?)
},
mixins: [ReactMeteorData],
@ -55,9 +54,7 @@ const ItemContainer = React.createClass({
const Component = this.props.component; // could be Post or PostEdit
if (this.data.document) {
return (
<Component {...this.props.propsToPass} {...this.data} />
)
return React.cloneElement(this.props.children, { ...this.data, callback: this.props.callback, collection: this.props.collection });
} else {
return <p>Loading</p>
}

View file

@ -29,7 +29,6 @@ const ListContainer = React.createClass({
propTypes: {
collection: React.PropTypes.object.isRequired, // the collection to paginate
component: React.PropTypes.func.isRequired, // the component results will be passed to
publication: React.PropTypes.string, // the publication to subscribe to
terms: React.PropTypes.object, // an object passed to the publication
selector: React.PropTypes.object, // the selector used in collection.find()
@ -134,10 +133,7 @@ const ListContainer = React.createClass({
},
render() {
const Component = this.props.component;
return (
<Component {...this.data} loadMore={this.loadMore}/>
)
return React.cloneElement(this.props.children, { ...this.data, loadMore: this.loadMore});
}
});

View file

@ -0,0 +1,59 @@
import Messages from "../messages.js";
import NovaForms from "../forms.jsx";
import Formsy from 'formsy-react';
const NewDocContainer = React.createClass({
propTypes: {
collection: React.PropTypes.object.isRequired,
label: React.PropTypes.string,
callback: React.PropTypes.func,
methodName: React.PropTypes.string
},
mixins: [ReactMeteorData],
getMeteorData() {
return {
currentUser: Meteor.user()
};
},
submitForm(data) {
// remove any empty properties
const document = _.compactObject(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)
Messages.flash(error.message, "error")
} else {
Messages.flash("Document created.", "success");
if (this.props.callback) {
this.props.callback(document);
}
}
});
},
render() {
const collection = this.props.collection;
const fields = collection.simpleSchema().getEditableFields(this.data.currentUser);
return (
<div className="new-document">
<h3>{this.props.label}</h3>
<Formsy.Form onSubmit={this.submitForm}>
{fields.map(fieldName => NovaForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName]))}
<button type="submit" className="button button--primary">Submit</button>
</Formsy.Form>
</div>
)
}
});
module.exports = NewDocContainer;