From c97856c71de92103fcc69c600cb64be60eb892ff Mon Sep 17 00:00:00 2001 From: Aquib Master Date: Mon, 9 Oct 2017 22:50:43 +1300 Subject: [PATCH] Flash an error on save when required fields are missed - Add error type constants - Flash an error message when the user saves without completing all required fields - Surface presence errors --- .all-contributorsrc | 9 ++++++ README.md | 4 +-- src/actions/entries.js | 17 ++++++++++- src/components/ControlPanel/ControlPane.js | 8 +++-- src/components/Widgets/ControlHOC.js | 35 ++++++++++++++++++---- src/constants/validationErrorTypes.js | 5 ++++ 6 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 src/constants/validationErrorTypes.js diff --git a/.all-contributorsrc b/.all-contributorsrc index 002cfc77..3156264d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -373,6 +373,15 @@ "contributions": [ "doc" ] + }, + { + "login": "aquibm", + "name": "Aquib Master", + "avatar_url": "https://avatars0.githubusercontent.com/u/2936813?v=4", + "profile": "http://aquibm.com/", + "contributions": [ + "code" + ] } ] } diff --git a/README.md b/README.md index e98d91e8..2bbd8be1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Netlify CMS -[![All Contributors](https://img.shields.io/badge/all_contributors-47-orange.svg?style=flat-square)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-48-orange.svg?style=flat-square)](#contributors) A CMS for static site generators. Give non-technical users a simple way to edit and add content to any site built with a static site generator. @@ -54,7 +54,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds | [
Kalin Chernev](https://github.com/kalinchernev)
| [
tortilaman](https://github.com/tortilaman)
| [
Václav Klecanda](http://www.vxk.cz)
| [
DrianHillman](http://drianhillman.me)
| [
Sean Crawford](https://github.com/seantcoyote)
| [
Robert Riemann](http://blog.riemann.cc)
| [
Ben Berman](http://jygabyte.com)
| | [
Benjamin Kniffler](https://github.com/bkniffler)
| [
Mike Wickett](http://www.wickett.ca)
| [
Rory Claasen](http://roryclaasen.me)
| [
Frederic Brodbeck](http://www.freder.io/)
| [
Stuart Dum](https://github.com/simplystuart)
| [
Ryan Watters](https://github.com/rdwatters)
| [
Helder S Ribeiro](https://twitter.com/hsribei)
| | [
Artem Govorov](http://dm.gl)
| [
Cédric Delpoux](http://xuopled.netlify.com/)
| [
imorente](https://github.com/imorente)
| [
David Francoeur](http://davidfrancoeur.com)
| [
Rusta](https://github.com/Rusta)
| [
Henrik Lau Eriksson](http://henrik.laueriksson.com)
| [
Kraig Walker](https://www.kraigwalker.com)
| -| [
Rich Cook](http://www.TalesofMurder.com)
| [
Damien Van Der Windt](https://github.com/damienvdw)
| [
Matt Jared](http://mattjared.github.io/)
| [
bruce-one](https://github.com/bruce-one)
| [
Frank Taillandier](https://frank.taillandier.me)
[📖](https://github.com/netlify/netlify-cms/commits?author=DirtyF "Documentation") | +| [
Rich Cook](http://www.TalesofMurder.com)
| [
Damien Van Der Windt](https://github.com/damienvdw)
| [
Matt Jared](http://mattjared.github.io/)
| [
bruce-one](https://github.com/bruce-one)
| [
Frank Taillandier](https://frank.taillandier.me)
[📖](https://github.com/netlify/netlify-cms/commits?author=DirtyF "Documentation") | [
Aquib Master](http://aquibm.com/)
[💻](https://github.com/netlify/netlify-cms/commits?author=aquibm "Code") | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/src/actions/entries.js b/src/actions/entries.js index 6efe6cfc..72df4a40 100644 --- a/src/actions/entries.js +++ b/src/actions/entries.js @@ -7,6 +7,7 @@ import { getIntegrationProvider } from '../integrations'; import { getAsset, selectIntegration } from '../reducers'; import { selectFields } from '../reducers/collections'; import { createEntry } from '../valueObjects/Entry'; +import ValidationErrorTypes from '../constants/validationErrorTypes'; const { notifSend } = notifActions; @@ -265,9 +266,23 @@ export function persistEntry(collection) { return (dispatch, getState) => { const state = getState(); const entryDraft = state.entryDraft; + const fieldsErrors = entryDraft.get('fieldsErrors'); // Early return if draft contains validation errors - if (!entryDraft.get('fieldsErrors').isEmpty()) return Promise.reject(); + if (!fieldsErrors.isEmpty()) { + const hasPresenceErrors = fieldsErrors + .some(errors => errors.some(error => error.type && error.type === ValidationErrorTypes.PRESENCE)); + + if (hasPresenceErrors) { + dispatch(notifSend({ + message: 'Oops, you\'ve missed a required field. Please complete before saving.', + kind: 'danger', + dismissAfter: 8000, + })); + } + + return Promise.reject(); + } const backend = currentBackend(state.config); const assetProxies = entryDraft.get('mediaFiles').map(path => getAsset(state, path)); diff --git a/src/components/ControlPanel/ControlPane.js b/src/components/ControlPanel/ControlPane.js index cbb56190..ee00069a 100644 --- a/src/components/ControlPanel/ControlPane.js +++ b/src/components/ControlPanel/ControlPane.js @@ -37,9 +37,11 @@ export default class ControlPane extends Component { ({ error: false }); @@ -57,16 +58,27 @@ class ControlHOC extends Component { (value.hasOwnProperty('length') && value.length === 0) || (value.constructor === Object && Object.keys(value).length === 0) )) { - return { error: true }; + const error = { + type: ValidationErrorTypes.PRESENCE, + message: `${ field.get('label', field.get('name')) } is required.`, + }; + + return { error }; } - return { error: false }; + return { error: false }; } validatePattern(field, value) { const pattern = field.get('pattern', false); if (pattern && !RegExp(pattern.first()).test(value)) { - return { error: `${ field.get('label', field.get('name')) } didn't match the pattern: ${ pattern.last() }` }; + const error = { + type: ValidationErrorTypes.PATTERN, + message: `${ field.get('label', field.get('name')) } didn't match the pattern: ${ pattern.last() }`, + }; + + return { error }; } + return { error: false }; } @@ -80,11 +92,22 @@ class ControlHOC extends Component { } else if (response instanceof Promise) { response.then( () => { this.validate({ error: false }); }, - (error) => { - this.validate({ error: `${ field.get('label', field.get('name')) } - ${ error }.` }); + (err) => { + const error = { + type: ValidationErrorTypes.CUSTOM, + message: `${ field.get('label', field.get('name')) } - ${ err }.`, + }; + + this.validate({ error }); } ); - return { error: `${ field.get('label', field.get('name')) } is processing.` }; + + const error = { + type: ValidationErrorTypes.CUSTOM, + message: `${ field.get('label', field.get('name')) } is processing.`, + }; + + return { error }; } return { error: false }; }; diff --git a/src/constants/validationErrorTypes.js b/src/constants/validationErrorTypes.js new file mode 100644 index 00000000..2305209c --- /dev/null +++ b/src/constants/validationErrorTypes.js @@ -0,0 +1,5 @@ +export default { + PRESENCE: 'PRESENCE', + PATTERN: 'PATTERN', + CUSTOM: 'CUSTOM', +};