From a0280d86e9f568732a886e1b2e3fdc970d3a01b7 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Wed, 2 Jan 2019 18:35:36 +0100 Subject: [PATCH 01/35] Go back to using FormNestedFoot in Nested Array Fields --- .../vulcan-forms/lib/components/FormNestedArray.jsx | 13 +++++-------- .../vulcan-forms/lib/components/FormNestedFoot.jsx | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/vulcan-forms/lib/components/FormNestedArray.jsx b/packages/vulcan-forms/lib/components/FormNestedArray.jsx index 96ab98528..911309f6a 100644 --- a/packages/vulcan-forms/lib/components/FormNestedArray.jsx +++ b/packages/vulcan-forms/lib/components/FormNestedArray.jsx @@ -97,15 +97,12 @@ class FormNestedArray extends PureComponent { ) ), (!maxCount || arrayLength < maxCount) && ( - - - + addItem={this.addItem} + label={this.props.label} + className="form-nested-foot" + /> ), hasErrors ? ( ( - +const FormNestedFoot = ({ addItem }) => ( + ); From 9cb5a6c1d405ea7495f34310c5ddac9e4c26ac33 Mon Sep 17 00:00:00 2001 From: ochicf Date: Sat, 5 Jan 2019 11:48:52 +0100 Subject: [PATCH 02/35] add script to update package.json --- .vulcan/.gitignore | 2 + .vulcan/update_package.js | 88 +++++++++++++++++++++++++++++++++++++++ package.json | 3 ++ 3 files changed, 93 insertions(+) create mode 100644 .vulcan/.gitignore create mode 100644 .vulcan/update_package.js diff --git a/.vulcan/.gitignore b/.vulcan/.gitignore new file mode 100644 index 000000000..c87c6dcf5 --- /dev/null +++ b/.vulcan/.gitignore @@ -0,0 +1,2 @@ +bkp +package.json diff --git a/.vulcan/update_package.js b/.vulcan/update_package.js new file mode 100644 index 000000000..fa3138d18 --- /dev/null +++ b/.vulcan/update_package.js @@ -0,0 +1,88 @@ +#!/usr/bin/env node + + +var fs = require('fs'); +var mergePackages = require('@userfrosting/merge-package-dependencies'); +var jsdiff = require('diff'); +require('colors'); + +function diffPartReducer(accumulator, part) { + // green for additions, red for deletions + // grey for common parts + var color = part.added ? 'green' : (part.removed ? 'red' : 'grey'); + + return { + text: (accumulator.text || '') + part.value[color], + count: (accumulator.count || 0) + (part.added || part.removed ? 1 : 0), + }; +} + +// copied from sort-object-keys package +function sortObjectByKeyNameList(object, sortWith) { + var keys; + var sortFn; + + if (typeof sortWith === 'function') { + sortFn = sortWith; + } else { + keys = sortWith; + } + return (keys || []).concat(Object.keys(object).sort(sortFn)).reduce(function(total, key) { + total[key] = object[key]; + return total; + }, Object.create({})); +} + + +var appDirPath = './'; +var vulcanDirPath = './.vulcan/'; + +if (!fs.existsSync(vulcanDirPath + 'package.json')) { + console.log('Could not find \'' + vulcanDirPath + 'package.json\''); +} else if (!fs.existsSync(appDirPath + 'package.json')) { + console.log('Could not find \'' + appDirPath + 'package.json\''); +} else { + var appPackageFile = fs.readFileSync(appDirPath + '/package.json'); + var appPackageJson = JSON.parse(appPackageFile); + var vulcanPackageFile = fs.readFileSync(vulcanDirPath + 'package.json'); + var vulcanPackageJson = JSON.parse(vulcanPackageFile); + + console.log(appPackageJson.name + '@' + appPackageJson.version + ' \'package.json\' will be updated with Vulcan@' + vulcanPackageJson.version + ' dependencies.'); + + var backupDirPath = vulcanDirPath + 'bkp/'; + if (!fs.existsSync(backupDirPath)) { + fs.mkdirSync(backupDirPath); + } + var backupFilePath = backupDirPath + 'package-' + Date.now() + '.json'; + console.log('Saving a backup of \'' + appDirPath + 'package.json\' in \'' + backupFilePath + '\''); + fs.writeFileSync(backupFilePath, appPackageFile); + + var updatedAppPackageJson = mergePackages.npm( + // IMPORTANT: parse again because mergePackages.npm mutates json + JSON.parse(appPackageFile), + [vulcanDirPath] + ); + + [ + 'dependencies', + 'devDependencies', + 'peerDependencies' + ].forEach(function(key) { + if (updatedAppPackageJson[key]) { + updatedAppPackageJson[key] = sortObjectByKeyNameList(updatedAppPackageJson[key]); + } + + const diff = jsdiff.diffJson( + sortObjectByKeyNameList(appPackageJson[key] || {}), + updatedAppPackageJson[key] || {} + ).reduce(diffPartReducer, {}); + if (diff.count) { + console.log('Changes in "' + key + '":'); + console.log(diff.text); + } else { + console.log('No changes in "' + key + '".'); + } + }); + + fs.writeFileSync(appDirPath + 'package.json', JSON.stringify(updatedAppPackageJson, null, ' ')); +} diff --git a/package.json b/package.json index 60d470390..002f7373c 100644 --- a/package.json +++ b/package.json @@ -96,10 +96,13 @@ }, "private": true, "devDependencies": { + "@userfrosting/merge-package-dependencies": "^1.2.0", "autoprefixer": "^6.3.6", "babel-eslint": "^7.0.0", "babylon": "^6.18.0", + "colors": "^1.3.2", "chromedriver": "^2.40.0", + "diff": "^3.5.0", "enzyme": "^3.3.0", "enzyme-adapter-react-16.3": "^1.4.0", "eslint": "^3.19.0", From d948120e0798f5b16b23bb1bddb3c758286b9b84 Mon Sep 17 00:00:00 2001 From: ochicf Date: Sat, 5 Jan 2019 12:10:33 +0100 Subject: [PATCH 03/35] store current vulcanVersion in package.json --- .vulcan/update_package.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.vulcan/update_package.js b/.vulcan/update_package.js index fa3138d18..8038b636e 100644 --- a/.vulcan/update_package.js +++ b/.vulcan/update_package.js @@ -47,7 +47,16 @@ if (!fs.existsSync(vulcanDirPath + 'package.json')) { var vulcanPackageFile = fs.readFileSync(vulcanDirPath + 'package.json'); var vulcanPackageJson = JSON.parse(vulcanPackageFile); - console.log(appPackageJson.name + '@' + appPackageJson.version + ' \'package.json\' will be updated with Vulcan@' + vulcanPackageJson.version + ' dependencies.'); + if (appPackageJson.vulcanVersion) { + console.log(appPackageJson.name + '@' + appPackageJson.version + + ' \'package.json\' will be updated from Vulcan@' + appPackageJson.vulcanVersion + + ' to Vulcan@' + vulcanPackageJson.version + + ' dependencies.'); + } else { + console.log(appPackageJson.name + '@' + appPackageJson.version + + ' \'package.json\' will be updated with Vulcan@' + vulcanPackageJson.version + + ' dependencies.'); + } var backupDirPath = vulcanDirPath + 'bkp/'; if (!fs.existsSync(backupDirPath)) { @@ -63,6 +72,8 @@ if (!fs.existsSync(vulcanDirPath + 'package.json')) { [vulcanDirPath] ); + updatedAppPackageJson.vulcanVersion = vulcanPackageJson.version; + [ 'dependencies', 'devDependencies', From 6331f6b619bdbecaf1e4f5cb6890dc8111fb45e1 Mon Sep 17 00:00:00 2001 From: ochicf Date: Sat, 5 Jan 2019 12:21:13 +0100 Subject: [PATCH 04/35] add update-package-json script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 002f7373c..b5c58fa55 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "test-unit": "TEST_WATCH=1 meteor test-packages ./packages/* --port 3002 --driver-package meteortesting:mocha --raw-logs", "test": "npm run test-unit", "prettier": "node ./.vulcan/prettier/index.js write-changed", - "prettier-all": "node ./.vulcan/prettier/index.js write" + "prettier-all": "node ./.vulcan/prettier/index.js write", + "update-package-json": "node ./.vulcan/update_package.js" }, "husky": { "hooks": { From 5f81ade88c3d1017dc18297d182f250c90c994a5 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sat, 5 Jan 2019 21:32:03 +0900 Subject: [PATCH 05/35] Add support for apiKey header --- packages/vulcan-lib/lib/server/apollo_server.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vulcan-lib/lib/server/apollo_server.js b/packages/vulcan-lib/lib/server/apollo_server.js index 68ec55d74..c5cf65c07 100644 --- a/packages/vulcan-lib/lib/server/apollo_server.js +++ b/packages/vulcan-lib/lib/server/apollo_server.js @@ -203,7 +203,10 @@ const createApolloServer = (givenOptions = {}, givenConfig = {}) => { const headers = req.renderContext.originalHeaders || req.headers; options.context.locale = getHeaderLocale(headers, user && user.locale); - + + if (headers.apikey && (headers.apikey === getSetting('vulcan.apiKey'))) { + options.context.currentUser = { isAdmin: true, isApiUser: true }; + } // console.log('// apollo_server.js isSSR?', !!req.renderContext.originalHeaders ? 'yes' : 'no'); // console.log('// apollo_server.js headers:'); // console.log(headers); From 45e5e19861159f5016ae2919e1da0fce1cc907ff Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 8 Jan 2019 16:15:19 +0100 Subject: [PATCH 06/35] Add LayoutName to the Routes Debug dashboard --- .../vulcan-debug/lib/components/Routes.jsx | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/packages/vulcan-debug/lib/components/Routes.jsx b/packages/vulcan-debug/lib/components/Routes.jsx index a86b79e9a..ff538fef3 100644 --- a/packages/vulcan-debug/lib/components/Routes.jsx +++ b/packages/vulcan-debug/lib/components/Routes.jsx @@ -1,26 +1,31 @@ import React from 'react'; -import { registerComponent, Components, Routes } from 'meteor/vulcan:lib'; -import { Link } from 'react-router'; +import {registerComponent, Components, Routes} from 'meteor/vulcan:lib'; +import {Link} from 'react-router'; -const RoutePath = ({ document }) => - {document.path}; +const RoutePath = ({document}) => ( + {document.path} +); -const RoutesDashboard = props => -
- -
; +const RoutesDashboard = props => { + return ( +
+ +
+ ); +}; -registerComponent('Routes', RoutesDashboard); \ No newline at end of file +registerComponent('Routes', RoutesDashboard); From be42d6740f9916a63fcd7e90e3e0fd4059b56898 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 8 Jan 2019 16:21:26 +0100 Subject: [PATCH 07/35] rename AdminLayout to DebugLayout in the debug package --- .../vulcan-debug/lib/components/AdminLayout.jsx | 10 ---------- .../vulcan-debug/lib/components/DebugLayout.jsx | 10 ++++++++++ packages/vulcan-debug/lib/modules/components.js | 2 +- packages/vulcan-debug/lib/modules/routes.js | 16 ++++++++-------- 4 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 packages/vulcan-debug/lib/components/AdminLayout.jsx create mode 100644 packages/vulcan-debug/lib/components/DebugLayout.jsx diff --git a/packages/vulcan-debug/lib/components/AdminLayout.jsx b/packages/vulcan-debug/lib/components/AdminLayout.jsx deleted file mode 100644 index 238b7dfc4..000000000 --- a/packages/vulcan-debug/lib/components/AdminLayout.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { Components, registerComponent } from 'meteor/vulcan:lib'; - -const adminStyles = { - padding: '20px' -}; - -const AdminLayout = props =>
{props.children}
; - -registerComponent('AdminLayout', AdminLayout); \ No newline at end of file diff --git a/packages/vulcan-debug/lib/components/DebugLayout.jsx b/packages/vulcan-debug/lib/components/DebugLayout.jsx new file mode 100644 index 000000000..872619e53 --- /dev/null +++ b/packages/vulcan-debug/lib/components/DebugLayout.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; + +const debugStyles = { + padding: '20px' +}; + +const DebugLayout = props =>
{props.children}
; + +registerComponent('DebugLayout', DebugLayout); diff --git a/packages/vulcan-debug/lib/modules/components.js b/packages/vulcan-debug/lib/modules/components.js index 0dcc47be3..bd8da2a7e 100644 --- a/packages/vulcan-debug/lib/modules/components.js +++ b/packages/vulcan-debug/lib/modules/components.js @@ -1,4 +1,4 @@ -import '../components/AdminLayout.jsx'; +import '../components/DebugLayout.jsx'; import '../components/Emails.jsx'; import '../components/Groups.jsx'; diff --git a/packages/vulcan-debug/lib/modules/routes.js b/packages/vulcan-debug/lib/modules/routes.js index c4e7957af..b75f9e938 100644 --- a/packages/vulcan-debug/lib/modules/routes.js +++ b/packages/vulcan-debug/lib/modules/routes.js @@ -2,13 +2,13 @@ import { addRoute, getDynamicComponent } from 'meteor/vulcan:lib'; addRoute([ // {name: 'cheatsheet', path: '/cheatsheet', component: import('./components/Cheatsheet.jsx')}, - { name: 'debug', path: '/debug', componentName: 'DebugDashboard', layoutName: 'AdminLayout' }, - { name: 'debugGroups', path: '/debug/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'AdminLayout' }, - { name: 'debugSettings', path: '/debug/settings', componentName: 'Settings', layoutName: 'AdminLayout' }, - { name: 'debugCallbacks', path: '/debug/callbacks', componentName: 'Callbacks', layoutName: 'AdminLayout' }, + { name: 'debug', path: '/debug', componentName: 'DebugDashboard', layoutName: 'DebugLayout' }, + { name: 'debugGroups', path: '/debug/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'DebugLayout' }, + { name: 'debugSettings', path: '/debug/settings', componentName: 'Settings', layoutName: 'DebugLayout' }, + { name: 'debugCallbacks', path: '/debug/callbacks', componentName: 'Callbacks', layoutName: 'DebugLayout' }, // {name: 'emails', path: '/emails', component: () => getDynamicComponent(import('./components/Emails.jsx'))}, - { name: 'debugEmails', path: '/debug/emails', componentName: 'Emails', layoutName: 'AdminLayout' }, - { name: 'debugRoutes', path: '/debug/routes', componentName: 'Routes', layoutName: 'AdminLayout' }, - { name: 'debugComponents', path: '/debug/components', componentName: 'Components', layoutName: 'AdminLayout' }, - { name: 'debugI18n', path: '/debug/i18n', componentName: 'I18n', layoutName: 'AdminLayout' }, + { name: 'debugEmails', path: '/debug/emails', componentName: 'Emails', layoutName: 'DebugLayout' }, + { name: 'debugRoutes', path: '/debug/routes', componentName: 'Routes', layoutName: 'DebugLayout' }, + { name: 'debugComponents', path: '/debug/components', componentName: 'Components', layoutName: 'DebugLayout' }, + { name: 'debugI18n', path: '/debug/i18n', componentName: 'I18n', layoutName: 'DebugLayout' }, ]); From 1da5d5d4f238783acc6e00c44b360f6391f29d63 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 8 Jan 2019 16:22:14 +0100 Subject: [PATCH 08/35] create AdminLayout in vulcan:admin --- .../lib/components/AdminLayout.jsx | 44 +++++++++++++++++++ packages/vulcan-admin/lib/modules/index.js | 1 + packages/vulcan-admin/lib/modules/routes.js | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 packages/vulcan-admin/lib/components/AdminLayout.jsx diff --git a/packages/vulcan-admin/lib/components/AdminLayout.jsx b/packages/vulcan-admin/lib/components/AdminLayout.jsx new file mode 100644 index 000000000..f01785d96 --- /dev/null +++ b/packages/vulcan-admin/lib/components/AdminLayout.jsx @@ -0,0 +1,44 @@ +/** + * @Author: Apollinaire Lecocq + * @Date: 08-01-19 + * @Last modified by: apollinaire + * @Last modified time: 08-01-19 + */ +import React from 'react'; +import { + registerComponent, + Components, + withCurrentUser, +} from 'meteor/vulcan:core'; + +/** + * A simple component that renders the existing layout and checks wether the currentUser is an admin or not. + */ + +class AdminLayout extends React.PureComponent { + renderWithRestrictedAccess(children, currentUser) { + // while the currentUser is loading, don't render anything. + if (currentUser && currentUser.loading) { + return null; + } //if the currentUser is an admin, then render the children + else if (currentUser && currentUser.isAdmin) { + return children; + } // for every other case (just a member or not logged), render the 404 message + return ; + } + + render() { + const {currentUser, children} = this.props; + return ( + + {this.renderWithRestrictedAccess(children, currentUser)} + + ); + } +} + +registerComponent({ + name: 'AdminLayout', + component: AdminLayout, + hocs: [withCurrentUser], +}); diff --git a/packages/vulcan-admin/lib/modules/index.js b/packages/vulcan-admin/lib/modules/index.js index 9a851d3e1..895e5bc7c 100644 --- a/packages/vulcan-admin/lib/modules/index.js +++ b/packages/vulcan-admin/lib/modules/index.js @@ -1,3 +1,4 @@ import './fragments.js'; import './routes.js'; import './i18n.js'; +import '../components/AdminLayout'; diff --git a/packages/vulcan-admin/lib/modules/routes.js b/packages/vulcan-admin/lib/modules/routes.js index b71519605..ad80fae78 100644 --- a/packages/vulcan-admin/lib/modules/routes.js +++ b/packages/vulcan-admin/lib/modules/routes.js @@ -1,5 +1,5 @@ import { addRoute, getDynamicComponent } from 'meteor/vulcan:core'; import React from 'react'; -addRoute({ name: 'admin', path: '/admin', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))}); +addRoute({ name: 'admin', path: '/admin', component: () => getDynamicComponent(import('../components/AdminHome.jsx')), layoutName: 'AdminLayout'}); addRoute({ name: 'admin2', path: '/admin/users', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))}); From e0ed139215063a89e45dc155e532d3e4dad0f4f2 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 8 Jan 2019 18:38:20 +0100 Subject: [PATCH 09/35] currentUser.loading -> currentUserLoading --- packages/vulcan-admin/lib/components/AdminLayout.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-admin/lib/components/AdminLayout.jsx b/packages/vulcan-admin/lib/components/AdminLayout.jsx index f01785d96..b3685f5a8 100644 --- a/packages/vulcan-admin/lib/components/AdminLayout.jsx +++ b/packages/vulcan-admin/lib/components/AdminLayout.jsx @@ -18,7 +18,7 @@ import { class AdminLayout extends React.PureComponent { renderWithRestrictedAccess(children, currentUser) { // while the currentUser is loading, don't render anything. - if (currentUser && currentUser.loading) { + if (currentUserLoading) { return null; } //if the currentUser is an admin, then render the children else if (currentUser && currentUser.isAdmin) { From 70581a103608fad46fbc953ee0fe070f5cc5fa80 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 8 Jan 2019 18:40:02 +0100 Subject: [PATCH 10/35] automatic prettier --- packages/vulcan-admin/lib/modules/routes.js | 15 ++++-- packages/vulcan-debug/lib/modules/routes.js | 58 +++++++++++++++++---- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/vulcan-admin/lib/modules/routes.js b/packages/vulcan-admin/lib/modules/routes.js index ad80fae78..725b61ff0 100644 --- a/packages/vulcan-admin/lib/modules/routes.js +++ b/packages/vulcan-admin/lib/modules/routes.js @@ -1,5 +1,14 @@ -import { addRoute, getDynamicComponent } from 'meteor/vulcan:core'; +import {addRoute, getDynamicComponent} from 'meteor/vulcan:core'; import React from 'react'; -addRoute({ name: 'admin', path: '/admin', component: () => getDynamicComponent(import('../components/AdminHome.jsx')), layoutName: 'AdminLayout'}); -addRoute({ name: 'admin2', path: '/admin/users', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))}); +addRoute({ + name: 'admin', + path: '/admin', + component: () => getDynamicComponent(import('../components/AdminHome.jsx')), + layoutName: 'AdminLayout', +}); +addRoute({ + name: 'admin2', + path: '/admin/users', + component: () => getDynamicComponent(import('../components/AdminHome.jsx')), +}); diff --git a/packages/vulcan-debug/lib/modules/routes.js b/packages/vulcan-debug/lib/modules/routes.js index b75f9e938..d5ef99c88 100644 --- a/packages/vulcan-debug/lib/modules/routes.js +++ b/packages/vulcan-debug/lib/modules/routes.js @@ -1,14 +1,54 @@ -import { addRoute, getDynamicComponent } from 'meteor/vulcan:lib'; +import {addRoute, getDynamicComponent} from 'meteor/vulcan:lib'; addRoute([ // {name: 'cheatsheet', path: '/cheatsheet', component: import('./components/Cheatsheet.jsx')}, - { name: 'debug', path: '/debug', componentName: 'DebugDashboard', layoutName: 'DebugLayout' }, - { name: 'debugGroups', path: '/debug/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'DebugLayout' }, - { name: 'debugSettings', path: '/debug/settings', componentName: 'Settings', layoutName: 'DebugLayout' }, - { name: 'debugCallbacks', path: '/debug/callbacks', componentName: 'Callbacks', layoutName: 'DebugLayout' }, + { + name: 'debug', + path: '/debug', + componentName: 'DebugDashboard', + layoutName: 'DebugLayout', + }, + { + name: 'debugGroups', + path: '/debug/groups', + component: () => getDynamicComponent(import('../components/Groups.jsx')), + layoutName: 'DebugLayout', + }, + { + name: 'debugSettings', + path: '/debug/settings', + componentName: 'Settings', + layoutName: 'DebugLayout', + }, + { + name: 'debugCallbacks', + path: '/debug/callbacks', + componentName: 'Callbacks', + layoutName: 'DebugLayout', + }, // {name: 'emails', path: '/emails', component: () => getDynamicComponent(import('./components/Emails.jsx'))}, - { name: 'debugEmails', path: '/debug/emails', componentName: 'Emails', layoutName: 'DebugLayout' }, - { name: 'debugRoutes', path: '/debug/routes', componentName: 'Routes', layoutName: 'DebugLayout' }, - { name: 'debugComponents', path: '/debug/components', componentName: 'Components', layoutName: 'DebugLayout' }, - { name: 'debugI18n', path: '/debug/i18n', componentName: 'I18n', layoutName: 'DebugLayout' }, + { + name: 'debugEmails', + path: '/debug/emails', + componentName: 'Emails', + layoutName: 'DebugLayout', + }, + { + name: 'debugRoutes', + path: '/debug/routes', + componentName: 'Routes', + layoutName: 'DebugLayout', + }, + { + name: 'debugComponents', + path: '/debug/components', + componentName: 'Components', + layoutName: 'DebugLayout', + }, + { + name: 'debugI18n', + path: '/debug/i18n', + componentName: 'I18n', + layoutName: 'DebugLayout', + }, ]); From 74e41dd97dd250ea3aa2d70d8c1a799e65c5e025 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Wed, 9 Jan 2019 11:29:25 +0100 Subject: [PATCH 11/35] use withAccess in AdminLayout --- .../lib/components/AdminLayout.jsx | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/packages/vulcan-admin/lib/components/AdminLayout.jsx b/packages/vulcan-admin/lib/components/AdminLayout.jsx index b3685f5a8..3062f17d5 100644 --- a/packages/vulcan-admin/lib/components/AdminLayout.jsx +++ b/packages/vulcan-admin/lib/components/AdminLayout.jsx @@ -2,43 +2,32 @@ * @Author: Apollinaire Lecocq * @Date: 08-01-19 * @Last modified by: apollinaire - * @Last modified time: 08-01-19 + * @Last modified time: 09-01-19 */ import React from 'react'; -import { - registerComponent, - Components, - withCurrentUser, -} from 'meteor/vulcan:core'; +import {registerComponent, Components, withAccess} from 'meteor/vulcan:core'; + +// we need a component to wrap the `withAccess` hoc around something +function EmptyComponent({children}) { + return children; +} +EmptyComponent.displayName = 'EmptyComponent'; + +const RestrictToAdmins = withAccess({groups: ['admins']})(EmptyComponent); /** * A simple component that renders the existing layout and checks wether the currentUser is an admin or not. */ -class AdminLayout extends React.PureComponent { - renderWithRestrictedAccess(children, currentUser) { - // while the currentUser is loading, don't render anything. - if (currentUserLoading) { - return null; - } //if the currentUser is an admin, then render the children - else if (currentUser && currentUser.isAdmin) { - return children; - } // for every other case (just a member or not logged), render the 404 message - return ; - } - - render() { - const {currentUser, children} = this.props; - return ( - - {this.renderWithRestrictedAccess(children, currentUser)} - - ); - } +function AdminLayout({children}) { + return ( + + {children} + + ); } registerComponent({ name: 'AdminLayout', component: AdminLayout, - hocs: [withCurrentUser], }); From 1e6e6f88f3bce3bdcfb60c3fbafe7458e1a08c57 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Thu, 10 Jan 2019 14:23:50 +0100 Subject: [PATCH 12/35] Wrap ErrorCatcher with `withSiteData` This way you can add multiple error catchers in your react tree. My use case is adding one in the Modal component, so that when you add a buggy thing in the modal it's caught here and the user can exit the modal and the rest of the app is still rendered. --- packages/vulcan-core/lib/modules/components/App.jsx | 6 ++---- packages/vulcan-core/lib/modules/components/Dummy.jsx | 10 ++++++++++ packages/vulcan-core/lib/modules/index.js | 2 ++ packages/vulcan-errors/lib/components/ErrorCatcher.jsx | 4 ++-- 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 packages/vulcan-core/lib/modules/components/Dummy.jsx diff --git a/packages/vulcan-core/lib/modules/components/App.jsx b/packages/vulcan-core/lib/modules/components/App.jsx index 5bc556d68..e256bcb3e 100644 --- a/packages/vulcan-core/lib/modules/components/App.jsx +++ b/packages/vulcan-core/lib/modules/components/App.jsx @@ -17,8 +17,6 @@ import { withApollo } from 'react-apollo'; import { withCookies } from 'react-cookie'; import moment from 'moment'; -const DummyErrorCatcher = ({ children }) => children; - class App extends PureComponent { constructor(props) { super(props); @@ -114,7 +112,7 @@ class App extends PureComponent { const LayoutComponent = currentRoute.layoutName ? Components[currentRoute.layoutName] : Components.Layout; // if defined, use ErrorCatcher component to wrap layout contents - const ErrorCatcher = Components.ErrorCatcher ? Components.ErrorCatcher : DummyErrorCatcher; + const ErrorCatcher = Components.ErrorCatcher ? Components.ErrorCatcher : Components.Dummy; return ( @@ -125,7 +123,7 @@ class App extends PureComponent { {this.props.currentUserLoading ? ( ) : this.props.children ? ( - {this.props.children} + {this.props.children} ) : ( )} diff --git a/packages/vulcan-core/lib/modules/components/Dummy.jsx b/packages/vulcan-core/lib/modules/components/Dummy.jsx new file mode 100644 index 000000000..65f64de54 --- /dev/null +++ b/packages/vulcan-core/lib/modules/components/Dummy.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import {registerComponent} from 'meteor/vulcan:lib'; + +function Dummy({children}) { + return children; +} +Dummy.displayName = 'Dummy'; + +registerComponent({name: 'Dummy', component: Dummy}); +export default Dummy; diff --git a/packages/vulcan-core/lib/modules/index.js b/packages/vulcan-core/lib/modules/index.js index abc5165a6..e6e0cd47a 100644 --- a/packages/vulcan-core/lib/modules/index.js +++ b/packages/vulcan-core/lib/modules/index.js @@ -24,6 +24,7 @@ export { default as Flash } from './components/Flash.jsx'; export { default as HelloWorld } from './components/HelloWorld.jsx'; export { default as Welcome } from './components/Welcome.jsx'; export { default as RouterHook } from './components/RouterHook.jsx'; +export { default as Dummy } from './components/Dummy.jsx'; export { default as withAccess } from './containers/withAccess.js'; export { default as withMessages } from './containers/withMessages.js'; @@ -35,6 +36,7 @@ export { default as withDelete } from './containers/withDelete.js'; export { default as withCurrentUser } from './containers/withCurrentUser.js'; export { default as withMutation } from './containers/withMutation.js'; export { default as withUpsert } from './containers/withUpsert.js'; +export { default as withSiteData } from './containers/withSiteData.js'; export { default as withComponents } from './containers/withComponents'; diff --git a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx index b8be2f322..c542571bc 100644 --- a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx +++ b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx @@ -10,7 +10,7 @@ Usage: */ -import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import { Components, registerComponent, withCurrentUser, withSiteData } from 'meteor/vulcan:core'; import React, { Component } from 'react'; import { Errors } from '../modules/errors.js'; @@ -43,4 +43,4 @@ class ErrorCatcher extends Component { } } -registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser); +registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser, withSiteData); From ce8e29fc5b1f10dcabee50280cf42c0dfb8a3014 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Thu, 10 Jan 2019 14:28:23 +0100 Subject: [PATCH 13/35] Rewrite AdminLayout to use new Dummy component --- packages/vulcan-admin/lib/components/AdminLayout.jsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/vulcan-admin/lib/components/AdminLayout.jsx b/packages/vulcan-admin/lib/components/AdminLayout.jsx index 3062f17d5..98d7cf0bf 100644 --- a/packages/vulcan-admin/lib/components/AdminLayout.jsx +++ b/packages/vulcan-admin/lib/components/AdminLayout.jsx @@ -2,18 +2,12 @@ * @Author: Apollinaire Lecocq * @Date: 08-01-19 * @Last modified by: apollinaire - * @Last modified time: 09-01-19 + * @Last modified time: 10-01-19 */ import React from 'react'; -import {registerComponent, Components, withAccess} from 'meteor/vulcan:core'; +import {registerComponent, Components, withAccess, Dummy} from 'meteor/vulcan:core'; -// we need a component to wrap the `withAccess` hoc around something -function EmptyComponent({children}) { - return children; -} -EmptyComponent.displayName = 'EmptyComponent'; - -const RestrictToAdmins = withAccess({groups: ['admins']})(EmptyComponent); +const RestrictToAdmins = withAccess({groups: ['admins']})(Dummy); /** * A simple component that renders the existing layout and checks wether the currentUser is an admin or not. From ac2349f2ff9e6b116898e4f7d5e945ed23041f00 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Thu, 10 Jan 2019 15:12:19 +0100 Subject: [PATCH 14/35] Update validation callbacks' debug page The API for validation callbacks changed some time ago and the debug page was not updated to reflect this. --- .../lib/modules/default_mutations.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/vulcan-core/lib/modules/default_mutations.js b/packages/vulcan-core/lib/modules/default_mutations.js index 1f0950993..50a43b840 100644 --- a/packages/vulcan-core/lib/modules/default_mutations.js +++ b/packages/vulcan-core/lib/modules/default_mutations.js @@ -207,11 +207,12 @@ const registerCollectionCallbacks = (typeName, options) => { if (options.create) { registerCallback({ name: `${typeName}.create.validate`, - iterator: { document: 'The document being inserted' }, + iterator: { validationErrors: 'An array that can be used to accumulate validation errors' }, properties: [ { document: 'The document being inserted' }, { currentUser: 'The current user' }, - { validationErrors: 'An object that can be used to accumulate validation errors' }, + { collection: 'The collection the document belongs to' }, + { context: 'The context of the mutation'}, ], runs: 'sync', returns: 'document', @@ -248,11 +249,13 @@ const registerCollectionCallbacks = (typeName, options) => { if (options.update) { registerCallback({ name: `${typeName}.update.validate`, - iterator: { data: 'The client data' }, + iterator: { validationErrors: 'An object that can be used to accumulate validation errors' }, properties: [ { document: 'The document being edited' }, + { data: 'The client data' }, { currentUser: 'The current user' }, - { validationErrors: 'An object that can be used to accumulate validation errors' }, + { collection: 'The collection the document belongs to' }, + { context: 'The context of the mutation'}, ], runs: 'sync', returns: 'modifier', @@ -296,10 +299,12 @@ const registerCollectionCallbacks = (typeName, options) => { if (options.delete) { registerCallback({ name: `${typeName}.delete.validate`, - iterator: { document: 'The document being removed' }, + iterator: { validationErrors: 'An object that can be used to accumulate validation errors' }, properties: [ { currentUser: 'The current user' }, - { validationErrors: 'An object that can be used to accumulate validation errors' }, + { document: 'The document being removed' }, + { collection: 'The collection the document belongs to'}, + { context: 'The context of this mutation'} ], runs: 'sync', returns: 'document', From d912e4a2241dc0c32d5352984903ff2f10f825d0 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 15 Jan 2019 17:34:51 +0100 Subject: [PATCH 15/35] Add failureComponent and failureComponentName to `withAccess` --- .../lib/modules/containers/withAccess.js | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/vulcan-core/lib/modules/containers/withAccess.js b/packages/vulcan-core/lib/modules/containers/withAccess.js index a778e5f82..b48ba6b01 100644 --- a/packages/vulcan-core/lib/modules/containers/withAccess.js +++ b/packages/vulcan-core/lib/modules/containers/withAccess.js @@ -1,31 +1,63 @@ -import React, { PureComponent } from 'react'; -import { withCurrentUser } from 'meteor/vulcan:core'; -import { withRouter } from 'react-router'; +import React, {PureComponent} from 'react'; +import {Components} from 'meteor/vulcan:lib'; +import withCurrentUser from './withCurrentUser'; +import {withRouter} from 'react-router'; import Users from 'meteor/vulcan:users'; -export default function withAccess (options) { +/** + * withAccess - description + * + * @param {Object} options the options that define the hoc + * @param {string[]} options.groups the groups that have access to this component + * @param {string} options.redirect the link to redirect to in case the access is not granted (optional) + * @param {string} options.failureComponentName the name of a component to display if access is not granted (optional) + * @param {Component} options.failureComponent the component to display if access is not granted (optional) + * @return {PureComponent} a React component that will display only if the acces is granted + */ - const { groups, redirect } = options; +export default function withAccess(options) { + const { + groups = [], + redirect = null, + failureComponent = null, + failureComponentName = null, + } = options; // we return a function that takes a component and itself returns a component return WrappedComponent => { class AccessComponent extends PureComponent { - // if there are any groups defined check if user belongs, else just check if user exists canAccess = currentUser => { return groups ? Users.isMemberOf(currentUser, groups) : currentUser; - } + }; // redirect on constructor if user cannot access constructor(props) { super(props); - if(!this.canAccess(props.currentUser) && typeof redirect === 'string') { + if ( + !this.canAccess(props.currentUser) && + typeof redirect === 'string' + ) { props.router.push(redirect); } } + renderFailureComponent() { + if (failureComponentName) { + const FailureComponent = Components[failureComponentName]; + return ; + } else if (failureComponent) { + const FailureComponent = failureComponent; // necesary because jsx components must be uppercase + return ; + } else return null; + } + render() { - return this.canAccess(this.props.currentUser) ? : null; + return this.canAccess(this.props.currentUser) ? ( + + ) : ( + this.renderFailureComponent() + ); } } From 5b5017888ba69e28a45fc910b3de04be0f312945 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sat, 19 Jan 2019 21:32:36 +0900 Subject: [PATCH 16/35] Add support for attachments to email --- packages/vulcan-email/lib/server/email.js | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/vulcan-email/lib/server/email.js b/packages/vulcan-email/lib/server/email.js index 61d26d2af..dc77c7841 100644 --- a/packages/vulcan-email/lib/server/email.js +++ b/packages/vulcan-email/lib/server/email.js @@ -4,6 +4,7 @@ import Juice from 'juice'; import htmlToText from 'html-to-text'; import Handlebars from 'handlebars'; import { Utils, getSetting, registerSetting, runQuery, Strings, getString } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core is not loaded yet +import { Email } from 'meteor/email'; /* @@ -82,13 +83,13 @@ VulcanEmail.generateTextVersion = html => { }); }; -VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo, headers) => { +VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo, headers, attachments) => { // TODO: limit who can send emails // TODO: fix this error: Error: getaddrinfo ENOTFOUND if (typeof to === 'object') { // eslint-disable-next-line no-redeclare - var { to, cc, bcc, replyTo, subject, html, text, throwErrors, headers } = to; + var { to, cc, bcc, replyTo, subject, html, text, throwErrors, headers, attachments } = to; } const from = getSetting('defaultEmail', 'noreply@example.com'); @@ -102,14 +103,15 @@ VulcanEmail.send = (to, subject, html, text, throwErrors, cc, bcc, replyTo, head const email = { from: from, - to: to, - cc: cc, - bcc: bcc, - replyTo: replyTo, - subject: subject, - headers: headers, - text: text, - html: html, + to, + cc, + bcc, + replyTo, + subject, + headers, + text, + html, + attachments, }; const shouldSendEmail = process.env.NODE_ENV === 'production' || getSetting('enableDevelopmentEmails', false); @@ -153,9 +155,9 @@ VulcanEmail.build = async ({ emailName, variables, locale }) => { return { data, subject, html }; }; -VulcanEmail.buildAndSend = async ({ to, cc, bcc, replyTo, emailName, variables, locale = getSetting('locale'), headers }) => { +VulcanEmail.buildAndSend = async ({ to, cc, bcc, replyTo, emailName, variables, locale = getSetting('locale'), headers, attachments }) => { const email = await VulcanEmail.build({ to, emailName, variables, locale }); - return VulcanEmail.send({ to, cc, bcc, replyTo, subject: email.subject, html: email.html, headers }); + return VulcanEmail.send({ to, cc, bcc, replyTo, subject: email.subject, html: email.html, headers, attachments }); }; VulcanEmail.buildAndSendHTML = (to, subject, html) => VulcanEmail.send(to, subject, VulcanEmail.buildTemplate(html)); From f152a5b8c689ae75f7f50450c534798e18e369f3 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sun, 20 Jan 2019 17:25:07 +0900 Subject: [PATCH 17/35] clean up/refactor mutators --- packages/vulcan-lib/lib/server/mutators.js | 69 +++++++++++----------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index 6a2cda8ce..a0e067275 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -53,7 +53,7 @@ export const createMutator = async ({ collection, document, data, currentUser, v const schema = collection.simpleSchema()._schema; - const callbackProperties = { currentUser, collection, context }; + const properties = { currentUser, collection, context, document: newDocument }; if (validate) { @@ -62,8 +62,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v validationErrors = validationErrors.concat(validateDocument(newDocument, collection, context)); // run validation callbacks - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: validationErrors, properties: { ...callbackProperties, document: newDocument } }); - validationErrors = await runCallbacks({ name: '*.create.validate', iterator: validationErrors, properties: { ...callbackProperties, document: newDocument } }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.create.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.validate`, newDocument, currentUser, validationErrors); @@ -112,8 +112,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v // } // run sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties: callbackProperties }); - newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.before`, newDocument, currentUser); newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.sync`, newDocument, currentUser); @@ -122,21 +122,20 @@ export const createMutator = async ({ collection, document, data, currentUser, v newDocument._id = await Connectors.create(collection, newDocument); // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties: callbackProperties }); - newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.after`, newDocument, currentUser); // get fresh copy of document from db - // TODO: not needed? - const insertedDocument = await Connectors.get(collection, newDocument._id); - + newDocument = await Connectors.get(collection, newDocument._id); + // run async callbacks // note: query for document to get fresh document with collection-hooks effects applied - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties: { insertedDocument, document: insertedDocument, ...callbackProperties }}); - await runCallbacksAsync({ name: '*.create.async', properties: { insertedDocument, document: insertedDocument, ...callbackProperties }}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties, }); + await runCallbacksAsync({ name: '*.create.async', properties, }); // OpenCRUD backwards compatibility - await runCallbacksAsync(`${collectionName.toLowerCase()}.new.async`, insertedDocument, currentUser, collection); + await runCallbacksAsync(`${collectionName.toLowerCase()}.new.async`, newDocument, currentUser, collection); debug('\x1b[33m=> created new document: \x1b[0m'); debug(newDocument); @@ -169,7 +168,14 @@ export const updateMutator = async ({ collection, documentId, selector, data, se throw new Error(`Could not find document to update for selector: ${JSON.stringify(selector)}`); } - const callbackProperties = { data, document, currentUser, collection, context }; + // get a "preview" of the new document + let newDocument = { ...document, ...data}; + newDocument = pickBy(newDocument, f => f !== null); + + const properties = { data, oldDocument: document, newDocument, currentUser, collection, context }; + + // OpenCRUD backwards compatibility + properties.document = newDocument; debug(''); debugGroup(`--------------- start \x1b[36m${collectionName} Update Mutator\x1b[0m ---------------`); @@ -177,14 +183,15 @@ export const updateMutator = async ({ collection, documentId, selector, data, se debug('// selector: ', selector); debug('// data: ', data); + if (validate) { let validationErrors = []; validationErrors = validationErrors.concat(validateData(data, document, collection, context)); - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: validationErrors, properties: callbackProperties }); - validationErrors = await runCallbacks({ name: '*.update.validate', iterator: validationErrors, properties: callbackProperties }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.update.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.validate`, dataToModifier(data), document, currentUser, validationErrors)); @@ -198,10 +205,6 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } - // get a "preview" of the new document - let newDocument = { ...document, ...data}; - newDocument = pickBy(newDocument, f => f !== null); - // run onUpdate step for(let fieldName of Object.keys(schema)) { let autoValue; @@ -219,8 +222,8 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run sync callbacks - data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties: { newDocument, ...callbackProperties }}); - data = await runCallbacks({ name: '*.update.before', iterator: data, properties: { newDocument, ...callbackProperties }}); + data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties,}); + data = await runCallbacks({ name: '*.update.before', iterator: data, properties,}); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.before`, dataToModifier(data), document, currentUser, newDocument)); data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.sync`, dataToModifier(data), document, currentUser, newDocument)); @@ -252,14 +255,14 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties: callbackProperties }); - newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties: callbackProperties }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.edit.after`, newDocument, document, currentUser); // run async callbacks - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties: { ...callbackProperties,newDocument, document: newDocument, oldDocument: document }}); - await runCallbacksAsync({ name: '*.update.async', properties: { ...callbackProperties, newDocument, document: newDocument, oldDocument: document }}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties,}); + await runCallbacksAsync({ name: '*.update.async', properties,}); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.edit.async`, newDocument, document, currentUser, collection); @@ -296,14 +299,14 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU throw new Error(`Could not find document to delete for selector: ${JSON.stringify(selector)}`); } - const callbackProperties = { document, currentUser, collection, context }; + const properties = { document, currentUser, collection, context }; if (validate) { let validationErrors = []; - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: validationErrors, properties: callbackProperties }); - validationErrors = await runCallbacks({ name: '*.delete.validate', iterator: validationErrors, properties: callbackProperties }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.delete.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility document = await runCallbacks(`${collectionName.toLowerCase()}.remove.validate`, document, currentUser); @@ -329,8 +332,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU } } - await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties: callbackProperties }); - await runCallbacks({ name: '*.delete.before', iterator: document, properties: callbackProperties }); + await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties, }); + await runCallbacks({ name: '*.delete.before', iterator: document, properties, }); // OpenCRUD backwards compatibility await runCallbacks(`${collectionName.toLowerCase()}.remove.before`, document, currentUser); await runCallbacks(`${collectionName.toLowerCase()}.remove.sync`, document, currentUser); @@ -343,8 +346,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU collection.loader.clear(selector.documentId); } - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties: callbackProperties }); - await runCallbacksAsync({ name: '*.delete.async', properties: callbackProperties }); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties, }); + await runCallbacksAsync({ name: '*.delete.async', properties, }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.remove.async`, document, currentUser, collection); From ffb2ed555b453c1587a6279950d849c860f4af25 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sun, 20 Jan 2019 17:28:52 +0900 Subject: [PATCH 18/35] Add support for running multiple callback hooks at the same time --- packages/vulcan-lib/lib/modules/callbacks.js | 9 +++++ packages/vulcan-lib/lib/server/mutators.js | 42 +++++++------------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/callbacks.js b/packages/vulcan-lib/lib/modules/callbacks.js index 871b54ec9..8d3ea9d9e 100644 --- a/packages/vulcan-lib/lib/modules/callbacks.js +++ b/packages/vulcan-lib/lib/modules/callbacks.js @@ -77,6 +77,15 @@ export const runCallbacks = function () { if (typeof arguments[0] === 'object' && arguments.length === 1) { const singleArgument = arguments[0]; hook = singleArgument.name; + + // if an array of callback hook names is passed, call `runCallbacks` for each hook + if (Array.isArray(hook)) { + hook.forEach(name => { + runCallbacks({ ...singleArgument, name }); + }); + return; + } + formattedHook = formatHookName(hook); item = singleArgument.iterator; args = singleArgument.properties; diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index a0e067275..9b0843170 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -62,8 +62,7 @@ export const createMutator = async ({ collection, document, data, currentUser, v validationErrors = validationErrors.concat(validateDocument(newDocument, collection, context)); // run validation callbacks - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: validationErrors, properties, }); - validationErrors = await runCallbacks({ name: '*.create.validate', iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.validate`, '*.create.validate'], iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.validate`, newDocument, currentUser, validationErrors); @@ -112,18 +111,15 @@ export const createMutator = async ({ collection, document, data, currentUser, v // } // run sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties, }); - newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.before`, '*.create.before'], iterator: newDocument, properties, }); // OpenCRUD backwards compatibility - newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.before`, newDocument, currentUser); - newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.sync`, newDocument, currentUser); + newDocument = await runCallbacks([`${collectionName.toLowerCase()}.new.before`, `${collectionName.toLowerCase()}.new.sync`], newDocument, currentUser); // add _id to document newDocument._id = await Connectors.create(collection, newDocument); // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties, }); - newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.after`, '*.create.after'], iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.after`, newDocument, currentUser); @@ -132,8 +128,7 @@ export const createMutator = async ({ collection, document, data, currentUser, v // run async callbacks // note: query for document to get fresh document with collection-hooks effects applied - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties, }); - await runCallbacksAsync({ name: '*.create.async', properties, }); + await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.create.async`, '*.create.async'], properties, }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.new.async`, newDocument, currentUser, collection); @@ -190,8 +185,7 @@ export const updateMutator = async ({ collection, documentId, selector, data, se validationErrors = validationErrors.concat(validateData(data, document, collection, context)); - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: validationErrors, properties, }); - validationErrors = await runCallbacks({ name: '*.update.validate', iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.update.validate`, '*.update.validate'], iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.validate`, dataToModifier(data), document, currentUser, validationErrors)); @@ -222,11 +216,9 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run sync callbacks - data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties,}); - data = await runCallbacks({ name: '*.update.before', iterator: data, properties,}); + data = await runCallbacks({ name:[ `${typeName.toLowerCase()}.update.before`, '*.update.before'], iterator: data, properties,}); // OpenCRUD backwards compatibility - data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.before`, dataToModifier(data), document, currentUser, newDocument)); - data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.sync`, dataToModifier(data), document, currentUser, newDocument)); + data = modifierToData(await runCallbacks([`${collectionName.toLowerCase()}.edit.before`, `${collectionName.toLowerCase()}.edit.sync`], dataToModifier(data), document, currentUser, newDocument)); // update connector requires a modifier, so get it from data const modifier = dataToModifier(data); @@ -255,14 +247,12 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties, }); - newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.update.after`, '*.update.after'], iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.edit.after`, newDocument, document, currentUser); // run async callbacks - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties,}); - await runCallbacksAsync({ name: '*.update.async', properties,}); + await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.update.async`, '*.update.async'], properties,}); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.edit.async`, newDocument, document, currentUser, collection); @@ -305,8 +295,7 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU let validationErrors = []; - validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: validationErrors, properties, }); - validationErrors = await runCallbacks({ name: '*.delete.validate', iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.delete.validate`, '*.delete.validate'], iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility document = await runCallbacks(`${collectionName.toLowerCase()}.remove.validate`, document, currentUser); @@ -332,11 +321,9 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU } } - await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties, }); - await runCallbacks({ name: '*.delete.before', iterator: document, properties, }); + await runCallbacks({ name: [`${typeName.toLowerCase()}.delete.before`, '*.delete.before'], iterator: document, properties, }); // OpenCRUD backwards compatibility - await runCallbacks(`${collectionName.toLowerCase()}.remove.before`, document, currentUser); - await runCallbacks(`${collectionName.toLowerCase()}.remove.sync`, document, currentUser); + await runCallbacks([`${collectionName.toLowerCase()}.remove.before`, `${collectionName.toLowerCase()}.remove.sync`], document, currentUser); await Connectors.delete(collection, selector); @@ -346,8 +333,7 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU collection.loader.clear(selector.documentId); } - await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties, }); - await runCallbacksAsync({ name: '*.delete.async', properties, }); + await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.delete.async`, '*.delete.async'], properties, }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.remove.async`, document, currentUser, collection); From 744a02be321e3aa0fe24791232b6b6777b7278af Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Fri, 18 Jan 2019 11:43:01 +0100 Subject: [PATCH 19/35] rework arrow function with default value --- packages/vulcan-forms/lib/components/Form.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-forms/lib/components/Form.jsx b/packages/vulcan-forms/lib/components/Form.jsx index e4dc8ee2a..ae887c572 100644 --- a/packages/vulcan-forms/lib/components/Form.jsx +++ b/packages/vulcan-forms/lib/components/Form.jsx @@ -319,13 +319,15 @@ class SmartForm extends Component { Note: when submitting the form (getData()), do not include any extra fields. */ - getFieldNames = (args = {}) => { + getFieldNames = (args) => { + // we do this to avoid having default values in arrow functions, which breaks MS Edge support. See https://github.com/meteor/meteor/issues/10171 + let args0 = args || {}; const { schema = this.state.schema, excludeHiddenFields = true, replaceIntlFields = false, addExtraFields = true - } = args; + } = args0; const { fields, addFields } = this.props; From 026bffb5c3e3537267a19816d2594f86c0b46725 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Mon, 21 Jan 2019 18:06:59 +0100 Subject: [PATCH 20/35] update lint and prettier rules --- .eslintrc | 5 +++-- .prettierrc.js | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.eslintrc b/.eslintrc index 1b0df8be3..41c33c109 100644 --- a/.eslintrc +++ b/.eslintrc @@ -27,8 +27,9 @@ ] } ], - "babel/array-bracket-spacing": 0, + "babel/array-bracket-spacing": 1, "babel/object-curly-spacing": 0, + # "babel/object-curly-spacing": [1, "always", { "objectsInObjects": false, "arraysInObjects": false }], "babel/object-shorthand": 0, "babel/arrow-parens": 0, "no-await-in-loop": 1, @@ -78,4 +79,4 @@ "param": true, "returns": true } -} \ No newline at end of file +} diff --git a/.prettierrc.js b/.prettierrc.js index 80ae81bc0..47202179a 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -3,11 +3,11 @@ const {esNextPaths} = require('./.vulcan/shared/pathsByLanguageVersion'); module.exports = { - bracketSpacing: false, + bracketSpacing: true, singleQuote: true, jsxBracketSameLine: true, trailingComma: 'es5', - printWidth: 80, + printWidth: 100, parser: 'babylon', overrides: [ @@ -18,4 +18,4 @@ module.exports = { }, }, ], -}; \ No newline at end of file +}; From b2e3aaaeb54a266a060c539f45fbf0f9fd208e96 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Mon, 21 Jan 2019 18:10:34 +0100 Subject: [PATCH 21/35] fix lint errors --- .../vulcan-accounts/imports/ui/components/FormMessage.jsx | 4 ++-- packages/vulcan-core/lib/modules/components/Card.jsx | 4 ++-- packages/vulcan-lib/lib/server/intl.js | 4 ++-- packages/vulcan-lib/lib/server/mutators.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx b/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx index f97823a50..43b13c66b 100755 --- a/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx +++ b/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx @@ -6,9 +6,9 @@ export class AccountsFormMessage extends React.Component { let { message, type, className = 'message', style = {} } = this.props; message = _.isObject(message) ? message.message : message; // If message is object, then try to get message from it return message ? ( -
{ message }
+
{ message }
) : null; } } -registerComponent('AccountsFormMessage', AccountsFormMessage); \ No newline at end of file +registerComponent('AccountsFormMessage', AccountsFormMessage); diff --git a/packages/vulcan-core/lib/modules/components/Card.jsx b/packages/vulcan-core/lib/modules/components/Card.jsx index ebac5dc14..32d03ec90 100644 --- a/packages/vulcan-core/lib/modules/components/Card.jsx +++ b/packages/vulcan-core/lib/modules/components/Card.jsx @@ -29,7 +29,7 @@ const getTypeName = (field, fieldName, collection) => { }; const parseImageUrl = value => { - const isImage = ['.png', '.jpg', '.gif'].indexOf(value.substr(-4)) !== -1 || ['.webp', '.jpeg' ].indexOf(value.substr(-5)) !== -1; + const isImage = ['.png', '.jpg', '.gif'].indexOf(value.substr(-4)) !== -1 || ['.webp', '.jpeg'].indexOf(value.substr(-5)) !== -1; return isImage ? {value}/ : parseUrl(value); @@ -187,4 +187,4 @@ Card.contextTypes = { intl: intlShape }; -registerComponent('Card', Card); \ No newline at end of file +registerComponent('Card', Card); diff --git a/packages/vulcan-lib/lib/server/intl.js b/packages/vulcan-lib/lib/server/intl.js index d1d947451..7a871dd32 100644 --- a/packages/vulcan-lib/lib/server/intl.js +++ b/packages/vulcan-lib/lib/server/intl.js @@ -47,7 +47,7 @@ class IntlDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field, details) { const { resolve = defaultFieldResolver, name } = field; field.resolve = async function (...args) { - const [ doc, graphQLArguments, context ] = args; + const [doc, graphQLArguments, context] = args; const fieldValue = await resolve.apply(this, args); const locale = graphQLArguments.locale || context.locale; const defaultLocale = getSetting('locale'); @@ -178,4 +178,4 @@ export const getHeaderLocale = (headers, userLocale) => { return locale; -}; \ No newline at end of file +}; diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index 9b0843170..ce76be14b 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -216,7 +216,7 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run sync callbacks - data = await runCallbacks({ name:[ `${typeName.toLowerCase()}.update.before`, '*.update.before'], iterator: data, properties,}); + data = await runCallbacks({ name:[`${typeName.toLowerCase()}.update.before`, '*.update.before'], iterator: data, properties,}); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks([`${collectionName.toLowerCase()}.edit.before`, `${collectionName.toLowerCase()}.edit.sync`], dataToModifier(data), document, currentUser, newDocument)); From 12df83e23f50c3d8f8abee53d311bfcdea0246bc Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Tue, 22 Jan 2019 09:52:16 +0900 Subject: [PATCH 22/35] Revert "Add support for running multiple callback hooks at the same time" This reverts commit ffb2ed555b453c1587a6279950d849c860f4af25. --- packages/vulcan-lib/lib/modules/callbacks.js | 9 ----- packages/vulcan-lib/lib/server/mutators.js | 42 +++++++++++++------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/callbacks.js b/packages/vulcan-lib/lib/modules/callbacks.js index 8d3ea9d9e..871b54ec9 100644 --- a/packages/vulcan-lib/lib/modules/callbacks.js +++ b/packages/vulcan-lib/lib/modules/callbacks.js @@ -77,15 +77,6 @@ export const runCallbacks = function () { if (typeof arguments[0] === 'object' && arguments.length === 1) { const singleArgument = arguments[0]; hook = singleArgument.name; - - // if an array of callback hook names is passed, call `runCallbacks` for each hook - if (Array.isArray(hook)) { - hook.forEach(name => { - runCallbacks({ ...singleArgument, name }); - }); - return; - } - formattedHook = formatHookName(hook); item = singleArgument.iterator; args = singleArgument.properties; diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index 9b0843170..a0e067275 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -62,7 +62,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v validationErrors = validationErrors.concat(validateDocument(newDocument, collection, context)); // run validation callbacks - validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.validate`, '*.create.validate'], iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.create.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.create.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.validate`, newDocument, currentUser, validationErrors); @@ -111,15 +112,18 @@ export const createMutator = async ({ collection, document, data, currentUser, v // } // run sync callbacks - newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.before`, '*.create.before'], iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.before`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.create.before', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility - newDocument = await runCallbacks([`${collectionName.toLowerCase()}.new.before`, `${collectionName.toLowerCase()}.new.sync`], newDocument, currentUser); + newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.before`, newDocument, currentUser); + newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.sync`, newDocument, currentUser); // add _id to document newDocument._id = await Connectors.create(collection, newDocument); // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.create.after`, '*.create.after'], iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.create.after`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.create.after', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.new.after`, newDocument, currentUser); @@ -128,7 +132,8 @@ export const createMutator = async ({ collection, document, data, currentUser, v // run async callbacks // note: query for document to get fresh document with collection-hooks effects applied - await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.create.async`, '*.create.async'], properties, }); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.create.async`, properties, }); + await runCallbacksAsync({ name: '*.create.async', properties, }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.new.async`, newDocument, currentUser, collection); @@ -185,7 +190,8 @@ export const updateMutator = async ({ collection, documentId, selector, data, se validationErrors = validationErrors.concat(validateData(data, document, collection, context)); - validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.update.validate`, '*.update.validate'], iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.update.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.update.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.validate`, dataToModifier(data), document, currentUser, validationErrors)); @@ -216,9 +222,11 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run sync callbacks - data = await runCallbacks({ name:[ `${typeName.toLowerCase()}.update.before`, '*.update.before'], iterator: data, properties,}); + data = await runCallbacks({ name: `${typeName.toLowerCase()}.update.before`, iterator: data, properties,}); + data = await runCallbacks({ name: '*.update.before', iterator: data, properties,}); // OpenCRUD backwards compatibility - data = modifierToData(await runCallbacks([`${collectionName.toLowerCase()}.edit.before`, `${collectionName.toLowerCase()}.edit.sync`], dataToModifier(data), document, currentUser, newDocument)); + data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.before`, dataToModifier(data), document, currentUser, newDocument)); + data = modifierToData(await runCallbacks(`${collectionName.toLowerCase()}.edit.sync`, dataToModifier(data), document, currentUser, newDocument)); // update connector requires a modifier, so get it from data const modifier = dataToModifier(data); @@ -247,12 +255,14 @@ export const updateMutator = async ({ collection, documentId, selector, data, se } // run any post-operation sync callbacks - newDocument = await runCallbacks({ name: [`${typeName.toLowerCase()}.update.after`, '*.update.after'], iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: `${typeName.toLowerCase()}.update.after`, iterator: newDocument, properties, }); + newDocument = await runCallbacks({ name: '*.update.after', iterator: newDocument, properties, }); // OpenCRUD backwards compatibility newDocument = await runCallbacks(`${collectionName.toLowerCase()}.edit.after`, newDocument, document, currentUser); // run async callbacks - await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.update.async`, '*.update.async'], properties,}); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.update.async`, properties,}); + await runCallbacksAsync({ name: '*.update.async', properties,}); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.edit.async`, newDocument, document, currentUser, collection); @@ -295,7 +305,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU let validationErrors = []; - validationErrors = await runCallbacks({ name: [`${typeName.toLowerCase()}.delete.validate`, '*.delete.validate'], iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: `${typeName.toLowerCase()}.delete.validate`, iterator: validationErrors, properties, }); + validationErrors = await runCallbacks({ name: '*.delete.validate', iterator: validationErrors, properties, }); // OpenCRUD backwards compatibility document = await runCallbacks(`${collectionName.toLowerCase()}.remove.validate`, document, currentUser); @@ -321,9 +332,11 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU } } - await runCallbacks({ name: [`${typeName.toLowerCase()}.delete.before`, '*.delete.before'], iterator: document, properties, }); + await runCallbacks({ name: `${typeName.toLowerCase()}.delete.before`, iterator: document, properties, }); + await runCallbacks({ name: '*.delete.before', iterator: document, properties, }); // OpenCRUD backwards compatibility - await runCallbacks([`${collectionName.toLowerCase()}.remove.before`, `${collectionName.toLowerCase()}.remove.sync`], document, currentUser); + await runCallbacks(`${collectionName.toLowerCase()}.remove.before`, document, currentUser); + await runCallbacks(`${collectionName.toLowerCase()}.remove.sync`, document, currentUser); await Connectors.delete(collection, selector); @@ -333,7 +346,8 @@ export const deleteMutator = async ({ collection, documentId, selector, currentU collection.loader.clear(selector.documentId); } - await runCallbacksAsync({ name: [`${typeName.toLowerCase()}.delete.async`, '*.delete.async'], properties, }); + await runCallbacksAsync({ name: `${typeName.toLowerCase()}.delete.async`, properties, }); + await runCallbacksAsync({ name: '*.delete.async', properties, }); // OpenCRUD backwards compatibility await runCallbacksAsync(`${collectionName.toLowerCase()}.remove.async`, document, currentUser, collection); From 4597fefa223693a576c803a3e33c5298205a5347 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 22 Jan 2019 14:52:38 +0100 Subject: [PATCH 23/35] Add error message for wrong registered hoc --- packages/vulcan-lib/lib/modules/components.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/components.js b/packages/vulcan-lib/lib/modules/components.js index a4e5430f7..3163c9b2b 100644 --- a/packages/vulcan-lib/lib/modules/components.js +++ b/packages/vulcan-lib/lib/modules/components.js @@ -56,8 +56,16 @@ export const getComponent = (name) => { } if (component.hocs) { const hocs = component.hocs.map(hoc => { - if(!Array.isArray(hoc)) return hoc; + if(!Array.isArray(hoc)) { + if (typeof hoc !== 'function') { + throw new Error(`In registered component ${name}, an hoc is of type ${typeof hoc}`) + } + return hoc; + } const [actualHoc, ...args] = hoc; + if (typeof actualHoc !== 'function') { + throw new Error(`In registered component ${name}, an hoc is of type ${typeof actualHoc}`) + } return actualHoc(...args); }); return compose(...hocs)(component.rawComponent); @@ -202,4 +210,3 @@ export const delayedComponent = name => { // return proxy; //}; export const mergeWithComponents = myComponents => (myComponents ? { ...Components, ...myComponents } : Components); - From e128cd76509536c30a97d94512b059554e4afcaf Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Tue, 22 Jan 2019 14:58:16 +0100 Subject: [PATCH 24/35] prettier --- packages/vulcan-lib/lib/modules/components.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/components.js b/packages/vulcan-lib/lib/modules/components.js index 3163c9b2b..947ae8213 100644 --- a/packages/vulcan-lib/lib/modules/components.js +++ b/packages/vulcan-lib/lib/modules/components.js @@ -49,22 +49,22 @@ export function registerComponent(name, rawComponent, ...hocs) { * @param {String} name The name of the component to get. * @returns {Function|React Component} A (wrapped) React component */ -export const getComponent = (name) => { +export const getComponent = name => { const component = ComponentsTable[name]; if (!component) { throw new Error(`Component ${name} not registered.`); } if (component.hocs) { const hocs = component.hocs.map(hoc => { - if(!Array.isArray(hoc)) { + if (!Array.isArray(hoc)) { if (typeof hoc !== 'function') { - throw new Error(`In registered component ${name}, an hoc is of type ${typeof hoc}`) + throw new Error(`In registered component ${name}, an hoc is of type ${typeof hoc}`); } return hoc; } const [actualHoc, ...args] = hoc; if (typeof actualHoc !== 'function') { - throw new Error(`In registered component ${name}, an hoc is of type ${typeof actualHoc}`) + throw new Error(`In registered component ${name}, an hoc is of type ${typeof actualHoc}`); } return actualHoc(...args); }); From 12ec3190ffefdb13b2e2605109e5ac50a08060ef Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Wed, 23 Jan 2019 09:56:15 +0900 Subject: [PATCH 25/35] Clean up Form component's render function --- packages/vulcan-forms/lib/components/Form.jsx | 108 ++++++++++-------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/packages/vulcan-forms/lib/components/Form.jsx b/packages/vulcan-forms/lib/components/Form.jsx index ae887c572..1437c26e1 100644 --- a/packages/vulcan-forms/lib/components/Form.jsx +++ b/packages/vulcan-forms/lib/components/Form.jsx @@ -990,65 +990,81 @@ class SmartForm extends Component { } }; + + // --------------------------------------------------------------------- // + // ------------------------- Props to Pass ----------------------------- // + // --------------------------------------------------------------------- // + + getWrapperProps = () => ({ + className: 'document-' + this.getFormType(), + }); + + getFormProps = () => ({ + id: this.props.id, + onSubmit: this.submitForm, + onKeyDown: this.formKeyDown, + ref: e => { + this.form = e; + }, + }); + + getFormErrorsProps = () => ({ + errors: this.state.errors + }); + + getFormGroupProps = group => ({ + key: group.name, + ...group, + errors: this.state.errors, + throwError: this.throwError, + currentValues: this.state.currentValues, + updateCurrentValues: this.updateCurrentValues, + deletedValues: this.state.deletedValues, + addToDeletedValues: this.addToDeletedValues, + clearFieldErrors: this.clearFieldErrors, + formType: this.getFormType(), + currentUser: this.props.currentUser, + disabled: this.state.disabled, + formComponents: mergeWithComponents(this.props.formComponents), + }); + + getFormSubmitProps = () => ({ + submitLabel: this.props.submitLabel, + cancelLabel: this.props.cancelLabel, + revertLabel: this.props.revertLabel, + cancelCallback: this.props.cancelCallback, + revertCallback: this.props.revertCallback, + document: this.getDocument(), + deleteDocument: + (this.getFormType() === 'edit' && + this.props.showRemove && + this.deleteDocument) || + null, + collectionName:this.props.collectionName, + currentValues:this.state.currentValues, + deletedValues:this.state.deletedValues, + errors:this.state.errors, + }); + // --------------------------------------------------------------------- // // ----------------------------- Render -------------------------------- // // --------------------------------------------------------------------- // render() { - const fieldGroups = this.getFieldGroups(); - const collectionName = this.props.collectionName; const FormComponents = mergeWithComponents(this.props.formComponents); return ( -
- { - this.form = e; - }} - > - +
+ + - {fieldGroups.map(group => ( - + {this.getFieldGroups().map(group => ( + ))} {this.props.repeatErrors && this.renderErrors()} - +
); From e2f58c54bdd8a2fe017fe85dd40867b0eacc1d51 Mon Sep 17 00:00:00 2001 From: Apollinaire Date: Wed, 23 Jan 2019 14:29:59 +0100 Subject: [PATCH 26/35] Reset error catcher on route change --- .../lib/components/ErrorCatcher.jsx | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx index c542571bc..fccf84717 100644 --- a/packages/vulcan-errors/lib/components/ErrorCatcher.jsx +++ b/packages/vulcan-errors/lib/components/ErrorCatcher.jsx @@ -11,6 +11,7 @@ Usage: */ import { Components, registerComponent, withCurrentUser, withSiteData } from 'meteor/vulcan:core'; +import { withRouter } from 'react-router'; import React, { Component } from 'react'; import { Errors } from '../modules/errors.js'; @@ -31,11 +32,26 @@ class ErrorCatcher extends Component { }); }; + componentDidUpdate(prevProps) { + if ( + this.props.location && + prevProps.location && + this.props.location.pathname && + prevProps.location.pathname && + prevProps.location.pathname !== this.props.location.pathname + ) { + // reset the component state when the route changes to re-render the app and avodi blocking the navigation + this.setState({ error: null }); + } + } + render() { const { error } = this.state; return error ? (
- +
) : ( this.props.children @@ -43,4 +59,4 @@ class ErrorCatcher extends Component { } } -registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser, withSiteData); +registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser, withSiteData, withRouter); From 5f941d7d76b36cd3b356325b0497aa87b3de063d Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Thu, 24 Jan 2019 18:01:53 +0900 Subject: [PATCH 27/35] Remove Formsy dependency --- .../lib/modules/components/Flash.jsx | 2 +- .../vulcan-embed/lib/components/EmbedURL.jsx | 6 +-- packages/vulcan-forms/lib/components/Form.jsx | 14 +++--- .../lib/components/FormComponent.jsx | 3 +- .../lib/components/forms/Checkbox.jsx | 11 +++-- .../lib/components/forms/Checkboxgroup.jsx | 19 ++++---- .../lib/components/forms/Date.jsx | 42 +++++++----------- .../lib/components/forms/Datetime.jsx | 42 +++++++----------- .../lib/components/forms/Default.jsx | 15 ++++--- .../lib/components/forms/Email.jsx | 11 +++-- .../components/forms/FormComponentInner.jsx | 9 +++- .../lib/components/forms/FormItem.jsx | 18 ++++++++ .../lib/components/forms/Number.jsx | 11 +++-- .../lib/components/forms/Radiogroup.jsx | 4 +- .../lib/components/forms/Select.jsx | 21 ++++++--- .../lib/components/forms/SelectMultiple.jsx | 6 +-- .../lib/components/forms/Textarea.jsx | 12 +++-- .../lib/components/forms/Time.jsx | 44 +++++++------------ .../lib/components/forms/Url.jsx | 12 +++-- .../lib/modules/components.js | 1 + 20 files changed, 166 insertions(+), 137 deletions(-) create mode 100644 packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx diff --git a/packages/vulcan-core/lib/modules/components/Flash.jsx b/packages/vulcan-core/lib/modules/components/Flash.jsx index 2381976d9..359b4ebe2 100644 --- a/packages/vulcan-core/lib/modules/components/Flash.jsx +++ b/packages/vulcan-core/lib/modules/components/Flash.jsx @@ -45,7 +45,7 @@ class Flash extends PureComponent { const flashType = type === 'error' ? 'danger' : type; // if flashType is "error", use "danger" instead return ( - + ); diff --git a/packages/vulcan-embed/lib/components/EmbedURL.jsx b/packages/vulcan-embed/lib/components/EmbedURL.jsx index f28be3253..b06534a71 100644 --- a/packages/vulcan-embed/lib/components/EmbedURL.jsx +++ b/packages/vulcan-embed/lib/components/EmbedURL.jsx @@ -3,9 +3,7 @@ import { withMutation } from 'meteor/vulcan:core'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import FRC from 'formsy-react-components'; - -const Input = FRC.Input; +import { Form } from 'react-bootstrap'; class EmbedURL extends Component { constructor(props) { @@ -161,7 +159,7 @@ class EmbedURL extends Component {
- { - // note: we can discard the data collected by Formsy because all the data we need is already available via getDocument() + submitForm = event => { + event.preventDefault(); + // if form is disabled (there is already a submit handler running) don't do anything if (this.state.disabled) { return; @@ -933,9 +933,9 @@ class SmartForm extends Component { // clear errors and disable form while it's submitting this.setState(prevState => ({ errors: [], disabled: true })); - // complete the data with values from custom components which are not being catched by Formsy mixin + // complete the data with values from custom components // note: it follows the same logic as SmartForm's getDocument method - data = this.getData({ replaceIntlFields: true, addExtraFields: false }); + let data = this.getData({ replaceIntlFields: true, addExtraFields: false }); // if there's a submit callback, run it if (this.props.submitCallback) { @@ -1055,7 +1055,7 @@ class SmartForm extends Component { return (
- +
{this.getFieldGroups().map(group => ( @@ -1065,7 +1065,7 @@ class SmartForm extends Component { {this.props.repeatErrors && this.renderErrors()} - +
); } diff --git a/packages/vulcan-forms/lib/components/FormComponent.jsx b/packages/vulcan-forms/lib/components/FormComponent.jsx index 26e428bb8..de1254250 100644 --- a/packages/vulcan-forms/lib/components/FormComponent.jsx +++ b/packages/vulcan-forms/lib/components/FormComponent.jsx @@ -93,7 +93,8 @@ class FormComponent extends Component { Function passed to form controls (always controlled) to update their value */ - handleChange = (name, value) => { + handleChange = value => { + // if value is an empty string, delete the field if (value === '') { value = null; diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Checkbox.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Checkbox.jsx index 67ee01be6..282cc7057 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Checkbox.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Checkbox.jsx @@ -1,8 +1,11 @@ import React from 'react'; -import { Checkbox } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; -const CheckboxComponent = ({refFunction, inputProperties}) => - ; +const CheckboxComponent = ({ refFunction, path, inputProperties }) => ( + + + +); registerComponent('FormComponentCheckbox', CheckboxComponent); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Checkboxgroup.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Checkboxgroup.jsx index 0c89027f7..022db53ed 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Checkboxgroup.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Checkboxgroup.jsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Checkbox } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; import without from 'lodash/without'; import uniq from 'lodash/uniq'; import intersection from 'lodash/intersection'; @@ -22,25 +22,28 @@ const CheckboxGroupComponent = ({ refFunction, label, path, value, formType, upd } return ( -
- -
+ +
{options.map((option, i) => ( - { + onChange={event => { + const isChecked = event.target.checked; const newValue = isChecked ? [...value, option.value] : without(value, option.value); updateCurrentValues({ [path]: newValue }); }} /> ))}
-
+ ); }; diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Date.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Date.jsx index 64d5e8f6d..01e41a3f3 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Date.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Date.jsx @@ -1,43 +1,35 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import DateTimePicker from 'react-datetime'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Components, registerComponent } from 'meteor/vulcan:core'; class DateComponent extends PureComponent { - constructor(props) { super(props); this.updateDate = this.updateDate.bind(this); } - // when the datetime picker has mounted, SmartForm will catch the date value (no formsy mixin in this component) - // componentDidMount() { - // if (this.props.value) { - // this.updateDate(this.props.value); - // } - // } - updateDate(date) { - this.context.updateCurrentValues({[this.props.path]: date}); + this.context.updateCurrentValues({ [this.props.path]: date }); } render() { - - const date = this.props.value ? (typeof this.props.value === 'string' ? new Date(this.props.value) : this.props.value) : null; + const date = this.props.value + ? typeof this.props.value === 'string' + ? new Date(this.props.value) + : this.props.value + : null; return ( -
- -
- this.updateDate(newDate)} - inputProps={{name: this.props.name}} - /> -
-
+ + this.updateDate(newDate)} + inputProps={{ name: this.props.name }} + /> + ); } } @@ -57,4 +49,4 @@ DateComponent.contextTypes = { export default DateComponent; -registerComponent('FormComponentDate', DateComponent); \ No newline at end of file +registerComponent('FormComponentDate', DateComponent); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Datetime.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Datetime.jsx index dece5d99c..dc01f9aad 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Datetime.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Datetime.jsx @@ -1,43 +1,35 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import DateTimePicker from 'react-datetime'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Components, registerComponent } from 'meteor/vulcan:core'; class DateTime extends PureComponent { - constructor(props) { super(props); this.updateDate = this.updateDate.bind(this); } - // when the datetime picker has mounted, SmartForm will catch the date value (no formsy mixin in this component) - // componentDidMount() { - // if (this.props.value) { - // this.updateDate(this.props.value); - // } - // } - updateDate(date) { - this.context.updateCurrentValues({[this.props.path]: date}); + this.context.updateCurrentValues({ [this.props.path]: date }); } render() { + const date = this.props.value + ? typeof this.props.value === 'string' + ? new Date(this.props.value) + : this.props.value + : null; - const date = this.props.value ? (typeof this.props.value === 'string' ? new Date(this.props.value) : this.props.value) : null; - return ( -
- -
- this.updateDate(newDate._d)} - format={'x'} - inputProps={{name: this.props.name}} - /> -
-
+ + this.updateDate(newDate._d)} + format={'x'} + inputProps={{ name: this.props.name }} + /> + ); } } @@ -57,4 +49,4 @@ DateTime.contextTypes = { export default DateTime; -registerComponent('FormComponentDateTime', DateTime); \ No newline at end of file +registerComponent('FormComponentDateTime', DateTime); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Default.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Default.jsx index f4b319b15..6760c46ba 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Default.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Default.jsx @@ -1,9 +1,12 @@ import React from 'react'; -import { Input } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; -const Default = ({refFunction, inputProperties}) => - ; +const Default = ({ refFunction, inputProperties }) => ( + + + +); - registerComponent('FormComponentDefault', Default); - registerComponent('FormComponentText', Default); +registerComponent('FormComponentDefault', Default); +registerComponent('FormComponentText', Default); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Email.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Email.jsx index 4050a6be7..16aff7033 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Email.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Email.jsx @@ -1,8 +1,11 @@ import React from 'react'; -import { Input } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; -const EmailComponent = ({refFunction, inputProperties}) => - ; +const EmailComponent = ({ refFunction, inputProperties }) => ( + + + +); registerComponent('FormComponentEmail', EmailComponent); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx index 4447ef31e..10bb45fd2 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormComponentInner.jsx @@ -21,15 +21,20 @@ class FormComponentInner extends PureComponent { }; getProperties = () => { - const { name, options, label, onChange, value, disabled } = this.props; + const { name, path, options, label, onChange, value, disabled } = this.props; // these properties are whitelisted so that they can be safely passed to the actual form input // and avoid https://facebook.github.io/react/warnings/unknown-prop.html warnings const inputProperties = { name, + path, options, label, - onChange, + onChange: event => { + // FormComponent's handleChange expects value as argument; look in target.checked and target.value + const inputValue = typeof event.target.checked === 'undefined' ? event.target.value : event.target.checked; + onChange(inputValue); + }, value, disabled, ...this.props.inputProperties, diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx new file mode 100644 index 000000000..a052492c1 --- /dev/null +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormItem.jsx @@ -0,0 +1,18 @@ +/* + +Layout for a single form item + +*/ + +import React from 'react'; +import { Form } from 'react-bootstrap'; +import { registerComponent } from 'meteor/vulcan:core'; + +const FormItem = ({ path, label, children }) => ( + + {label} + {children} + +); + +registerComponent('FormItem', FormItem); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Number.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Number.jsx index 2e28ef06d..d7a340339 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Number.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Number.jsx @@ -1,8 +1,11 @@ import React from 'react'; -import { Input } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; -const NumberComponent = ({refFunction, inputProperties}) => - ; +const NumberComponent = ({ refFunction, inputProperties }) => ( + + + +); registerComponent('FormComponentNumber', NumberComponent); diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx index 9d2c88e1a..f3e6fc580 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Radiogroup.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import { RadioGroup } from 'formsy-react-components'; +import { Form } from 'react-bootstrap'; import { registerComponent } from 'meteor/vulcan:core'; -const RadioGroupComponent = ({refFunction, inputProperties, ...properties}) => ; +const RadioGroupComponent = ({refFunction, inputProperties, ...properties}) => ; registerComponent('FormComponentRadioGroup', RadioGroupComponent); \ No newline at end of file diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx index 0c7c7be0b..ef70ba968 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Select.jsx @@ -1,20 +1,31 @@ import React from 'react'; import { intlShape } from 'meteor/vulcan:i18n'; -import { Select } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; // copied from vulcan:forms/utils.js to avoid extra dependency const getFieldType = datatype => datatype && datatype[0].type; -const SelectComponent = ({refFunction, inputProperties, datatype, ...properties}, { intl }) => { +const SelectComponent = ({ refFunction, inputProperties, datatype, ...properties }, { intl }) => { const noneOption = { label: intl.formatMessage({ id: 'forms.select_option' }), value: getFieldType(datatype) === String || getFieldType(datatype) === Number ? '' : null, // depending on field type, empty value can be '' or null disabled: true, }; - let otherOptions = Array.isArray(inputProperties.options) && inputProperties.options.length ? inputProperties.options : []; + let otherOptions = + Array.isArray(inputProperties.options) && inputProperties.options.length + ? inputProperties.options + : []; const options = [noneOption, ...otherOptions]; - return ; + return ; }; SelectMultipleComponent.contextTypes = { diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx index 33272d70f..8048c488b 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/Textarea.jsx @@ -1,7 +1,11 @@ import React from 'react'; -import { Textarea } from 'formsy-react-components'; -import { registerComponent } from 'meteor/vulcan:core'; +import { Form } from 'react-bootstrap'; +import { Components, registerComponent } from 'meteor/vulcan:core'; -const TextareaComponent = ({refFunction, inputProperties, ...properties}) =>