Merge branch 'master' of github.com:keen-studio/payload

This commit is contained in:
James
2020-09-11 14:11:51 -04:00
13 changed files with 214 additions and 31 deletions

View File

@@ -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 {

View File

@@ -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;
};

View 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;

View File

@@ -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,
};

View File

@@ -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
View 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);
});
});
});

View File

@@ -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;
}

View File

@@ -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;
});

View File

@@ -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,
};
};

View File

@@ -19,6 +19,7 @@ const connectMongoose = async (url) => {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
autoIndex: false,
});
logger.info(successfulConnectionMessage);
} catch (err) {

View File

@@ -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 = [];
}

View File

@@ -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'] }],
],
},
},