adds number, tests with buildQuery
This commit is contained in:
@@ -31,6 +31,12 @@ module.exports = {
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
type: 'number',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
98
src/client/components/forms/field-types/Number/index.js
Normal file
98
src/client/components/forms/field-types/Number/index.js
Normal 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;
|
||||
17
src/client/components/forms/field-types/Number/index.scss
Normal file
17
src/client/components/forms/field-types/Number/index.scss
Normal 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%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user