mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 01:51:40 -05:00
Enable required locale validation for individual locales; add support for intl: true
on schema fields
This commit is contained in:
parent
a01ca2ab6e
commit
2d2d1033b5
6 changed files with 86 additions and 31 deletions
|
@ -196,10 +196,12 @@ class FormComponent extends Component {
|
|||
|
||||
Get errors from Form state through context
|
||||
|
||||
Note: we use `includes` to get all errors from nested components, which have longer paths
|
||||
|
||||
*/
|
||||
getErrors = errors => {
|
||||
errors = errors || this.props.errors;
|
||||
const fieldErrors = errors.filter(error => error.path === this.props.path);
|
||||
const fieldErrors = errors.filter(error => error.path.includes(this.props.path));
|
||||
return fieldErrors;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Components, registerComponent, Locales } from 'meteor/vulcan:core';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
class FormIntl extends PureComponent {
|
||||
|
||||
|
@ -11,20 +12,20 @@ class FormIntl extends PureComponent {
|
|||
so we just use the order of the Locales array.
|
||||
|
||||
*/
|
||||
getLocalePath = (locale, defaultIndex) => {
|
||||
getLocalePath = (defaultIndex) => {
|
||||
return `${this.props.path}_intl.${defaultIndex}`;
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
// do not pass FormIntl's own value, inputProperties, and intlInput props down
|
||||
const properties = _.omit(this.props, 'value', 'inputProperties', 'intlInput');
|
||||
const properties = omit(this.props, 'value', 'inputProperties', 'intlInput');
|
||||
|
||||
return (
|
||||
<div className="form-intl">
|
||||
{Locales.map((locale, i) => (
|
||||
<div className={`form-intl-${locale.id}`} key={locale.id}>
|
||||
<Components.FormComponent {...properties} label={`${this.props.label} (${locale.id})`} path={this.getLocalePath(locale.id, i)} locale={locale.id} />
|
||||
<Components.FormComponent {...properties} label={`${this.props.label} (${locale.id})`} path={this.getLocalePath(i)} locale={locale.id} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { addGraphQLCollection, addGraphQLQuery, addGraphQLMutation, addGraphQLResolvers, addToGraphQLContext } from './graphql.js';
|
||||
import { addGraphQLCollection, addToGraphQLContext } from './graphql.js';
|
||||
import { Utils } from './utils.js';
|
||||
import { runCallbacks } from './callbacks.js';
|
||||
import { getSetting, registerSetting } from './settings.js';
|
||||
import { registerFragment, getDefaultFragmentText } from './fragments.js';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import { multiQueryTemplate, singleQueryTemplate, createMutationTemplate, updateMutationTemplate, upsertMutationTemplate, deleteMutationTemplate } from './graphql_templates';
|
||||
import { Locales, getIntlString } from './intl';
|
||||
|
||||
const wrapAsync = (Meteor.wrapAsync)? Meteor.wrapAsync : Meteor._wrapAsync;
|
||||
// import { debug } from './debug.js';
|
||||
|
@ -119,6 +119,33 @@ Mongo.Collection.prototype.helpers = function(helpers) {
|
|||
});
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Custom validation function to check for required locales
|
||||
|
||||
See https://github.com/aldeed/simple-schema-js#custom-field-validation
|
||||
|
||||
*/
|
||||
const validateIntlField = function () {
|
||||
let errors = [];
|
||||
|
||||
// if field is required, go through locales to check which one are required
|
||||
if (!this.definition.optional) {
|
||||
const requiredLocales = Locales.filter(locale => locale.required);
|
||||
|
||||
requiredLocales.forEach((locale, index) => {
|
||||
const strings = this.value;
|
||||
const hasString = strings.some(s => s.locale === locale.id && s.value);
|
||||
if (!hasString) {
|
||||
errors.push({ path: this.key, locale: locale.id, index, name: `${this.key.replace('_intl', '')} (${locale.id})` });
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
// hack to work around the fact that custom validation function can only return a single string
|
||||
return `intlError|${JSON.stringify(errors)}`;
|
||||
}
|
||||
|
||||
export const createCollection = options => {
|
||||
|
||||
const { typeName, collectionName = getCollectionName(typeName), schema, generateGraphQLSchema = true, dbCollectionName } = options;
|
||||
|
@ -138,23 +165,23 @@ export const createCollection = options => {
|
|||
// generate foo_intl fields
|
||||
Object.keys(schema).forEach(fieldName => {
|
||||
const fieldSchema = schema[fieldName];
|
||||
if (fieldSchema.type && fieldSchema.type.name === 'IntlString') {
|
||||
if (fieldSchema.intl || (fieldSchema.type && fieldSchema.type.name === 'IntlString')) {
|
||||
|
||||
// we have at least one intl field
|
||||
hasIntlFields = true;
|
||||
|
||||
// make non-intl field optional
|
||||
schema[fieldName].optional = true;
|
||||
|
||||
schema[`${fieldName}_intl`] = {
|
||||
...schema[fieldName], // copy properties from regular field
|
||||
hidden: true,
|
||||
type: Array,
|
||||
custom: validateIntlField,
|
||||
}
|
||||
schema[`${fieldName}_intl.$`] = {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
type: getIntlString(),
|
||||
}
|
||||
|
||||
// make non-intl field optional
|
||||
schema[fieldName].optional = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ SimpleSchema.extendOptions([
|
|||
'query', // field-specific data loading query
|
||||
'selectable', // field can be used as part of a selector when querying for data
|
||||
'orderable', // field can be used to order results when querying for data
|
||||
|
||||
'intl', // set to `true` to make a field international
|
||||
]);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import SimpleSchema from 'simpl-schema';
|
||||
import { Strings } from './strings';
|
||||
|
||||
export const Locales = [];
|
||||
|
||||
|
@ -31,14 +30,16 @@ Generate custom IntlString SimpleSchema type
|
|||
*/
|
||||
export const getIntlString = () => {
|
||||
|
||||
const schema = {};
|
||||
|
||||
Object.keys(Strings).forEach(locale => {
|
||||
schema[locale] = {
|
||||
const schema = {
|
||||
locale: {
|
||||
type: String,
|
||||
optional: true,
|
||||
};
|
||||
});
|
||||
optional: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
optional: false,
|
||||
}
|
||||
};
|
||||
|
||||
const IntlString = new SimpleSchema(schema);
|
||||
IntlString.name = 'IntlString';
|
||||
|
|
|
@ -47,11 +47,22 @@ export const validateDocument = (document, collection, context) => {
|
|||
errors.forEach(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log(error);
|
||||
validationErrors.push({
|
||||
id: `errors.${error.type}`,
|
||||
path: error.name,
|
||||
properties: error,
|
||||
});
|
||||
if (error.type.includes('intlError')) {
|
||||
const intlErrors = JSON.parse(error.type.replace('intlError|', ''));
|
||||
intlErrors.forEach(intlError => {
|
||||
validationErrors.push({
|
||||
id: `errors.required`,
|
||||
path: `${intlError.path}.${intlError.index}`,
|
||||
properties: intlError,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
validationErrors.push({
|
||||
id: `errors.${error.type}`,
|
||||
path: error.name,
|
||||
properties: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -96,11 +107,22 @@ export const validateModifier = (modifier, document, collection, context) => {
|
|||
errors.forEach(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log(error);
|
||||
validationErrors.push({
|
||||
id: `errors.${error.type}`,
|
||||
path: error.name,
|
||||
properties: error,
|
||||
});
|
||||
if (error.type.includes('intlError')) {
|
||||
const intlErrors = JSON.parse(error.type.replace('intlError|', ''));
|
||||
intlErrors.forEach(intlError => {
|
||||
validationErrors.push({
|
||||
id: `errors.required`,
|
||||
path: `${intlError.path}.${intlError.index}`,
|
||||
properties: intlError,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
validationErrors.push({
|
||||
id: `errors.${error.type}`,
|
||||
path: error.name,
|
||||
properties: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue