Merge branch 'master' of github.com:keen-studio/payload
This commit is contained in:
@@ -3,13 +3,35 @@
|
||||
$cal-icon-width: 18px;
|
||||
|
||||
.date-time-picker {
|
||||
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box,
|
||||
.react-datepicker__time-container {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
&--hide-dates {
|
||||
.react-datepicker {
|
||||
&__month-container {
|
||||
width: 100%;
|
||||
|
||||
&__month-container,
|
||||
&__navigation--previous,
|
||||
&__navigation--next {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&-popper,
|
||||
&__time-container,
|
||||
&__time-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__time-container {
|
||||
.react-datepicker__time {
|
||||
.react-datepicker__time-box {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +91,7 @@ $cal-icon-width: 18px;
|
||||
&--time {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid $color-light-gray;
|
||||
font-weight: bold;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +133,15 @@ $cal-icon-width: 18px;
|
||||
|
||||
&__current-month {
|
||||
padding: 10px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__month-container {
|
||||
border-right: 1px solid $color-light-gray;
|
||||
}
|
||||
|
||||
&__time-container {
|
||||
border-left: 1px solid $color-light-gray;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
&__day-names {
|
||||
@@ -131,38 +158,54 @@ $cal-icon-width: 18px;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
font-weight: 600;
|
||||
|
||||
&:focus {
|
||||
background-color: $color-dark-gray;
|
||||
background-color: $color-light-gray;
|
||||
}
|
||||
}
|
||||
|
||||
&--keyboard-selected {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
|
||||
&:focus {
|
||||
background-color: $color-light-gray;
|
||||
box-shadow: inset 0px 0px 0px 1px $color-dark-gray, 0px 0px 0px 1px $color-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
&--today {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&__day,
|
||||
&__day-name {
|
||||
width: base(1.5);
|
||||
margin: base(.15);
|
||||
line-height: base(1.25);
|
||||
}
|
||||
}
|
||||
|
||||
.react-datepicker-popper {
|
||||
z-index: 10;
|
||||
border: 1px solid $color-light-gray;
|
||||
}
|
||||
|
||||
.react-datepicker__day--keyboard-selected,
|
||||
.react-datepicker__month-text--keyboard-selected,
|
||||
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected {
|
||||
background: $color-dark-gray;
|
||||
box-shadow: inset 0px 0px 0px 1px $color-dark-gray, 0px 0px 0px 1px $color-dark-gray;
|
||||
background-color: $color-light-gray;
|
||||
color: $color-dark-gray;
|
||||
font-weight: normal;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected,
|
||||
.react-datepicker__day--selected, .react-datepicker__day--in-selecting-range, .react-datepicker__day--in-range, .react-datepicker__month-text--selected, .react-datepicker__month-text--in-selecting-range, .react-datepicker__month-text--in-range {
|
||||
background: $color-dark-gray;
|
||||
box-shadow: inset 0px 0px 0px 1px $color-dark-gray, 0px 0px 0px 1px $color-dark-gray;
|
||||
background-color: $color-light-gray;
|
||||
color: $color-dark-gray;
|
||||
border-radius: 0;
|
||||
}
|
||||
@@ -171,17 +214,14 @@ $cal-icon-width: 18px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box,
|
||||
.react-datepicker__time-container {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button) {
|
||||
right: 130px;
|
||||
}
|
||||
|
||||
.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item {
|
||||
line-height: 20px;
|
||||
font-size: base(.5);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
|
||||
@@ -257,7 +257,8 @@ const sanitizeCollection = (collections, collection) => {
|
||||
// Sanitize fields
|
||||
// /////////////////////////////////
|
||||
|
||||
sanitized.fields = sanitizeFields(sanitized.fields);
|
||||
const validRelationships = collections.map((c) => c.slug);
|
||||
sanitized.fields = sanitizeFields(sanitized.fields, validRelationships);
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
9
src/errors/InvalidFieldRelationship.js
Normal file
9
src/errors/InvalidFieldRelationship.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const APIError = require('./APIError');
|
||||
|
||||
class InvalidFieldRelationship extends APIError {
|
||||
constructor(field, relationship) {
|
||||
super(`Field ${field.label} has invalid relationship '${relationship}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InvalidFieldRelationship;
|
||||
@@ -2,28 +2,32 @@ const APIError = require('./APIError');
|
||||
const AuthenticationError = require('./AuthenticationError');
|
||||
const DuplicateCollection = require('./DuplicateCollection');
|
||||
const DuplicateGlobal = require('./DuplicateGlobal');
|
||||
const ErrorDeletingFile = require('./ErrorDeletingFile');
|
||||
const errorHandler = require('../express/middleware/errorHandler');
|
||||
const Forbidden = require('./Forbidden');
|
||||
const InvalidFieldRelationship = require('./InvalidFieldRelationship');
|
||||
const MissingCollectionLabel = require('./MissingCollectionLabel');
|
||||
const MissingFieldInputOptions = require('./MissingFieldInputOptions');
|
||||
const MissingFieldType = require('./MissingFieldType');
|
||||
const MissingFile = require('./MissingFile');
|
||||
const MissingGlobalLabel = require('./MissingGlobalLabel');
|
||||
const NotFound = require('./NotFound');
|
||||
const Forbidden = require('./Forbidden');
|
||||
const ValidationError = require('./ValidationError');
|
||||
const errorHandler = require('../express/middleware/errorHandler');
|
||||
const MissingFile = require('./MissingFile');
|
||||
const MissingFieldInputOptions = require('./MissingFieldInputOptions');
|
||||
const ErrorDeletingFile = require('./ErrorDeletingFile');
|
||||
|
||||
module.exports = {
|
||||
errorHandler,
|
||||
ErrorDeletingFile,
|
||||
APIError,
|
||||
AuthenticationError,
|
||||
DuplicateCollection,
|
||||
DuplicateGlobal,
|
||||
ErrorDeletingFile,
|
||||
errorHandler,
|
||||
Forbidden,
|
||||
InvalidFieldRelationship,
|
||||
MissingCollectionLabel,
|
||||
MissingFieldInputOptions,
|
||||
MissingFieldType,
|
||||
MissingFile,
|
||||
MissingGlobalLabel,
|
||||
NotFound,
|
||||
Forbidden,
|
||||
ValidationError,
|
||||
MissingFile,
|
||||
MissingFieldInputOptions,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { MissingFieldType } = require('../errors');
|
||||
const { MissingFieldType, InvalidFieldRelationship } = require('../errors');
|
||||
const validations = require('./validations');
|
||||
|
||||
const sanitizeFields = (fields) => {
|
||||
const sanitizeFields = (fields, validRelationships) => {
|
||||
if (!fields) return [];
|
||||
|
||||
return fields.map((unsanitizedField) => {
|
||||
@@ -9,6 +9,15 @@ const sanitizeFields = (fields) => {
|
||||
|
||||
if (!field.type) throw new MissingFieldType(field);
|
||||
|
||||
if (field.type === 'relationship') {
|
||||
const relationships = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
|
||||
relationships.forEach((relationship) => {
|
||||
if (!validRelationships.includes(relationship)) {
|
||||
throw new InvalidFieldRelationship(field, relationship);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof field.validate === 'undefined') {
|
||||
const defaultValidate = validations[field.type];
|
||||
const noValidate = () => true;
|
||||
@@ -24,7 +33,7 @@ const sanitizeFields = (fields) => {
|
||||
if (field.blocks) {
|
||||
field.blocks = field.blocks.map((block) => {
|
||||
const unsanitizedBlock = { ...block };
|
||||
unsanitizedBlock.fields = sanitizeFields(block.fields);
|
||||
unsanitizedBlock.fields = sanitizeFields(block.fields, validRelationships);
|
||||
return unsanitizedBlock;
|
||||
});
|
||||
}
|
||||
|
||||
111
src/fields/sanitize.spec.js
Normal file
111
src/fields/sanitize.spec.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/* eslint-disable jest/require-to-throw-message */
|
||||
const sanitizeFields = require('./sanitize');
|
||||
const { MissingFieldType, InvalidFieldRelationship } = require('../errors');
|
||||
|
||||
describe('sanitizeFields', () => {
|
||||
it('should throw on missing type field', () => {
|
||||
const fields = [{
|
||||
label: 'some-collection',
|
||||
name: 'Some Collection',
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, []);
|
||||
}).toThrow(MissingFieldType);
|
||||
});
|
||||
|
||||
describe('relationships', () => {
|
||||
it('should not throw on valid relationship', () => {
|
||||
const validRelationships = ['some-collection'];
|
||||
const fields = [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: 'some-collection',
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw on valid relationship - multiple', () => {
|
||||
const validRelationships = ['some-collection', 'another-collection'];
|
||||
const fields = [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: ['some-collection', 'another-collection'],
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw on valid relationship inside blocks', () => {
|
||||
const validRelationships = ['some-collection'];
|
||||
const fields = [{
|
||||
name: 'layout',
|
||||
label: 'Layout Blocks',
|
||||
singularLabel: 'Block',
|
||||
type: 'blocks',
|
||||
blocks: [{
|
||||
fields: [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: 'some-collection',
|
||||
}],
|
||||
}],
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw on invalid relationship', () => {
|
||||
const validRelationships = ['some-collection'];
|
||||
const fields = [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: 'not-valid',
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).toThrow(InvalidFieldRelationship);
|
||||
});
|
||||
|
||||
it('should throw on invalid relationship - multiple', () => {
|
||||
const validRelationships = ['some-collection', 'another-collection'];
|
||||
const fields = [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: ['some-collection', 'not-valid'],
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).toThrow(InvalidFieldRelationship);
|
||||
});
|
||||
|
||||
it('should throw on invalid relationship inside blocks', () => {
|
||||
const validRelationships = ['some-collection'];
|
||||
const fields = [{
|
||||
name: 'layout',
|
||||
label: 'Layout Blocks',
|
||||
singularLabel: 'Block',
|
||||
type: 'blocks',
|
||||
blocks: [{
|
||||
fields: [{
|
||||
type: 'relationship',
|
||||
label: 'my-relationship',
|
||||
name: 'My Relationship',
|
||||
relationTo: 'not-valid',
|
||||
}],
|
||||
}],
|
||||
}];
|
||||
expect(() => {
|
||||
sanitizeFields(fields, validRelationships);
|
||||
}).toThrow(InvalidFieldRelationship);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -115,7 +115,7 @@ const optionsToValidatorMap = {
|
||||
return true;
|
||||
},
|
||||
date: (value, options = {}) => {
|
||||
if (value && value instanceof Date) {
|
||||
if (value && !isNaN(Date.parse(value.toString()))) { /* eslint-disable-line */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { MissingGlobalLabel } = require('../errors');
|
||||
const sanitizeFields = require('../fields/sanitize');
|
||||
|
||||
const sanitizeGlobals = (globals) => {
|
||||
const sanitizeGlobals = (collections, globals) => {
|
||||
// /////////////////////////////////
|
||||
// Ensure globals are valid
|
||||
// /////////////////////////////////
|
||||
@@ -33,7 +33,8 @@ const sanitizeGlobals = (globals) => {
|
||||
// Sanitize fields
|
||||
// /////////////////////////////////
|
||||
|
||||
sanitizedGlobal.fields = sanitizeFields(global.fields);
|
||||
const validRelationships = collections.map((c) => c.slug);
|
||||
sanitizedGlobal.fields = sanitizeFields(global.fields, validRelationships);
|
||||
|
||||
return sanitizedGlobal;
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ const formatBaseSchema = (field) => {
|
||||
unique: field.unique || false,
|
||||
required: (field.required && !field.localized && !condition && !createAccess) || false,
|
||||
default: field.defaultValue || undefined,
|
||||
index: field.index || field.unique || false,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const connectMongoose = async (url) => {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
useCreateIndex: true,
|
||||
autoIndex: false,
|
||||
});
|
||||
logger.info(successfulConnectionMessage);
|
||||
} catch (err) {
|
||||
|
||||
@@ -10,7 +10,7 @@ const sanitizeConfig = (config) => {
|
||||
sanitizedConfig.collections = sanitizedConfig.collections.map((collection) => sanitizeCollection(sanitizedConfig.collections, collection));
|
||||
|
||||
if (sanitizedConfig.globals) {
|
||||
sanitizedConfig.globals = sanitizeGlobals(sanitizedConfig.globals);
|
||||
sanitizedConfig.globals = sanitizeGlobals(sanitizedConfig.collections, sanitizedConfig.globals);
|
||||
} else {
|
||||
sanitizedConfig.globals = [];
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ module.exports = (config) => {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
plugins: [
|
||||
[removeObjectProperties, { values: ['graphQL', 'hooks', 'webpack', 'access', 'hooks'] }],
|
||||
[removeObjectProperties, { values: ['graphQL', 'hooks', 'webpack', 'access'] }],
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user