adds number, tests with buildQuery

This commit is contained in:
James
2020-02-08 11:45:44 -05:00
parent 551780287a
commit e7d7c0334b
5 changed files with 133 additions and 179 deletions

View File

@@ -31,6 +31,12 @@ module.exports = {
required: true,
localized: true,
},
{
name: 'priority',
label: 'Priority',
type: 'number',
required: true,
},
],
timestamps: true,
};

View File

@@ -0,0 +1,98 @@
import React from 'react';
import PropTypes from 'prop-types';
import useFieldType from '../../useFieldType';
import Label from '../../Label';
import Error from '../../Error';
import './index.scss';
const defaultError = 'Please fill in the field';
const defaultValidate = value => value.length > 0;
const Number = (props) => {
const {
name,
required,
defaultValue,
validate,
style,
width,
errorMessage,
label,
placeholder,
} = props;
const {
value,
showError,
onFieldChange,
formProcessing,
} = useFieldType({
name,
required,
defaultValue,
validate,
});
const classes = [
'field-type',
'text',
showError && 'error',
].filter(Boolean).join(' ');
const fieldWidth = width ? `${width}%` : undefined;
return (
<div
className={classes}
style={{
...style,
width: fieldWidth,
}}
>
<Error
showError={showError}
message={errorMessage}
/>
<Label
htmlFor={name}
label={label}
required={required}
/>
<input
value={value || ''}
onChange={onFieldChange}
disabled={formProcessing ? 'disabled' : undefined}
placeholder={placeholder}
type="number"
id={name}
name={name}
/>
</div>
);
};
Number.defaultProps = {
label: null,
required: false,
defaultValue: null,
placeholder: undefined,
validate: defaultValidate,
errorMessage: defaultError,
width: 100,
style: {},
};
Number.propTypes = {
name: PropTypes.string.isRequired,
required: PropTypes.bool,
placeholder: PropTypes.string,
defaultValue: PropTypes.string,
validate: PropTypes.func,
errorMessage: PropTypes.string,
width: PropTypes.number,
style: PropTypes.shape({}),
label: PropTypes.string,
};
export default Number;

View File

@@ -0,0 +1,17 @@
@import '~payload/client/scss/styles';
@import '../shared';
.field-type.number {
margin-bottom: base(.5);
position: relative;
input {
@include formInput;
}
&.error {
input {
background-color: lighten($error, 20%);
}
}
}

View File

@@ -8,6 +8,7 @@ import password from './Password';
import repeater from './Repeater';
import textarea from './Textarea';
import select from './Select';
import number from './Number';
export default {
email,
@@ -16,6 +17,7 @@ export default {
text,
relationship,
// upload,
number,
password,
repeater,
textarea,

View File

@@ -30,59 +30,28 @@ class ParamParser {
}
async parse() {
// Construct searchParams
// let keys = Object.keys(this.rawParams);
Object.keys(this.rawParams).forEach(async (key) => {
const separatedParams = this.rawParams[key].match(/{\w+}(.[^{}]*)/g);
if (separatedParams === null) {
await this.parseParam(key, this.rawParams[key], this.model, this.locale);
await this.parseParam(key, this.rawParams[key]);
} else {
for (let i = 0; i < separatedParams.length; ++i) {
await this.parseParam(key, separatedParams[i], this.model, this.locale);
}
await separatedParams.forEach(separatedParam => this.parseParam(key, separatedParam));
}
});
return this.query;
}
async parseParam(key, val, model, locale) {
const lcKey = key;
let operator = val.match(/\{(.*)\}/);
val = val.replace(/\{(.*)\}/, '');
if (operator) operator = operator[1];
if (val === '') {
return {};
} if (lcKey === 'sort_by' || lcKey === 'order_by') {
const parts = val.split(',');
this.query.sort = {};
this.query.sort[parts[0]] = parts[1] === 'asc' || parts.length <= 1 ? 1 : parts[1];
} else if (lcKey === 'include') {
if (val.match(',')) {
const orArray = [];
val.split(',').map(id => orArray.push({ _id: id }));
this.addSearchParam('$or', orArray);
} else {
this.query.searchParams['_id'] = val;
}
} else if (lcKey === 'exclude') {
if (val.match(',')) {
const andArray = [];
val.split(',').map(id => andArray.push({ _id: { $ne: id } }));
this.addSearchParam('$and', andArray);
} else {
this.query.searchParams['_id'] = { $ne: val };
}
} else if (lcKey === 'locale') {
// Do nothing
} else {
await this.parseSchemaForKey(model.schema, '', lcKey, val, operator, locale);
}
async parseParam(key, val) {
await this.parseSchemaForKey(this.model.schema, key, val);
return Promise.resolve(true);
}
async parseSchemaForKey(schema, key, val) {
const schemaObject = schema.obj[key];
const localizedKey = `${key}${(schemaObject && schemaObject.localized) ? `.${this.locale}` : ''}`;
this.addSearchParam(localizedKey, val);
}
addSearchParam(key, value) {
if (typeof this.query.searchParams[key] !== 'undefined') {
Object.keys(value).forEach((i) => {
@@ -96,144 +65,6 @@ class ParamParser {
this.query.searchParams[key] = value;
}
}
async parseSchemaForKey(schema, keyPrefix, lcKey, val, operator, locale) {
let paramType;
let key = keyPrefix + lcKey;
const split = lcKey.split('.');
if (split.length > 1 && schema.paths[lcKey] === undefined) {
// Parse SubSchema
if (schema.paths[split[0]].constructor.name === 'DocumentArray'
|| schema.paths[split[0]].constructor.name === 'Mixed') {
await this.parseSchemaForKey(schema.paths[split[0]].schema, `${split[0]}.`, split[1], val, operator);
} else if (schema.paths[split[0]].constructor.name === 'SchemaType') {
// This wasn't handled in the original package but seems to work
paramType = schema.paths[split[0]].schema.paths.name.instance;
} else if (schema.paths[split[0]].constructor.name === 'SchemaArray') {
paramType = 'Array';
}
} else if (schema.obj[lcKey] && typeof schema === 'object') {
if (schema.obj[lcKey].localized) {
key = `${key}.${locale}`;
}
paramType = schema.obj[lcKey].name || schema.obj[lcKey].type.name;
} else if (typeof schema === 'undefined') {
paramType = 'String';
} else if (typeof schema.paths[lcKey] === 'undefined') {
// nada, not found
} else if (schema.paths[lcKey].constructor.name === 'SchemaBoolean') {
paramType = 'Boolean';
} else if (schema.paths[lcKey].constructor.name === 'SchemaString') {
paramType = 'String';
} else if (schema.paths[lcKey].constructor.name === 'SchemaNumber') {
paramType = 'Number';
} else if (schema.paths[lcKey].constructor.name === 'ObjectId') {
paramType = 'ObjectId';
} else if (schema.paths[lcKey].constructor.name === 'SchemaArray') {
paramType = 'Array';
}
if (paramType === 'Boolean') {
const convertToBoolean = (str) => {
return str.toLowerCase() === 'true'
|| str.toLowerCase() === 't'
|| str.toLowerCase() === 'yes'
|| str.toLowerCase() === 'y'
|| str === '1';
};
this.addSearchParam(key, convertToBoolean(val));
} else if (paramType === 'Number') {
if (val.match(/([0-9]+,?)/) && val.match(',')) {
if (operator === 'all') {
this.addSearchParam(key, { $all: val.split(',') });
} else if (operator === 'nin') {
this.addSearchParam(key, { $nin: val.split(',') });
} else if (operator === 'mod') {
this.addSearchParam(key, { $mod: [val.split(',')[0], val.split(',')[1]] });
} else {
this.addSearchParam(key, { $in: val.split(',') });
}
} else if (val.match(/([0-9]+)/)) {
if (operator === 'gt'
|| operator === 'gte'
|| operator === 'lt'
|| operator === 'lte'
|| operator === 'ne') {
const newParam = {};
newParam[`$${operator}`] = val;
this.addSearchParam(key, newParam);
} else {
this.addSearchParam(key, parseInt(val));
}
}
} else if (paramType === 'String') {
if (val.match(',')) {
const options = val.split(',').map(str => new RegExp(str, 'i'));
if (operator === 'all') {
this.addSearchParam(key, { $all: options });
} else if (operator === 'nin') {
this.addSearchParam(key, { $nin: options });
} else {
this.addSearchParam(key, { $in: options });
}
} else if (val.match(/([0-9]+)/)) {
if (operator === 'gt'
|| operator === 'gte'
|| operator === 'lt'
|| operator === 'lte') {
const newParam = {};
newParam[`$${operator}`] = val;
this.addSearchParam(key, newParam);
} else {
this.addSearchParam(key, val);
}
} else if (operator === 'ne' || operator === 'not') {
const neregex = new RegExp(val, 'i');
this.addSearchParam(key, { $not: neregex });
} else if (operator === 'like') {
this.addSearchParam(key, { $regex: val, $options: '-i' });
} else {
this.addSearchParam(key, val);
}
} else if (paramType === 'ObjectId') {
this.addSearchParam(key, val);
} else if (paramType === 'Array') {
const recurseSchema = async (count, tempSchema) => {
const path = tempSchema.paths[split[count]];
const ref = path && path.options && path.options.type && path.options.type[0].ref;
let refKey = split[count + 1];
if (ref && refKey) {
const refModel = mongoose.model(ref);
if (count < split.length - 1) {
count++;
await recurseSchema(count, refModel.schema);
}
if (refKey && refModel.schema.obj[refKey].intl) {
refKey = `${refKey}.${locale}`;
}
// TODO: Need to recursively traverse paths longer than two segments.
// Example: this code handles "categories.title" but will not handle "categories.pages.title".
// TODO: Support operators as above
const refQuery = await refModel.find({ [refKey]: val });
if (refQuery) {
this.query.searchParams[split[count]] = { $in: refQuery.map(doc => doc.id) };
}
} else if (!ref) {
this.addSearchParam(key, val);
}
};
await recurseSchema(0, schema);
}
}
}
module.exports = buildQueryPlugin;