payload 0.15.1 → 0.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -0
- package/CHANGELOG.md +35 -0
- package/dist/admin/components/elements/WhereBuilder/field-types.d.ts +2 -2
- package/dist/admin/components/elements/WhereBuilder/field-types.js +11 -7
- package/dist/admin/components/elements/elements.spec.d.ts +1 -0
- package/dist/admin/components/elements/elements.spec.js +12 -0
- package/dist/admin/components/forms/DraggableSection/index.js +2 -3
- package/dist/admin/components/forms/Form/buildStateFromSchema.d.ts +10 -1
- package/dist/admin/components/forms/Form/buildStateFromSchema.js +14 -6
- package/dist/admin/components/forms/Form/getDataByPath.js +6 -11
- package/dist/admin/components/forms/Form/getSiblingData.js +12 -14
- package/dist/admin/components/forms/Form/index.js +23 -6
- package/dist/admin/components/forms/Form/reduceFieldsToValues.d.ts +1 -1
- package/dist/admin/components/forms/Form/reduceFieldsToValues.js +1 -2
- package/dist/admin/components/forms/Form/types.d.ts +3 -2
- package/dist/admin/components/forms/RenderFields/index.d.ts +1 -2
- package/dist/admin/components/forms/RenderFields/index.js +6 -16
- package/dist/admin/components/forms/RenderFields/types.d.ts +0 -5
- package/dist/admin/components/forms/field-types/Array/Array.js +18 -9
- package/dist/admin/components/forms/field-types/Blocks/Blocks.js +21 -15
- package/dist/admin/components/forms/field-types/Checkbox/index.js +3 -4
- package/dist/admin/components/forms/field-types/Code/Code.js +4 -5
- package/dist/admin/components/forms/field-types/DateTime/index.js +2 -3
- package/dist/admin/components/forms/field-types/Email/index.js +2 -3
- package/dist/admin/components/forms/field-types/Number/index.js +3 -4
- package/dist/admin/components/forms/field-types/Password/index.js +2 -2
- package/dist/admin/components/forms/field-types/Point/index.js +2 -3
- package/dist/admin/components/forms/field-types/RadioGroup/index.js +3 -4
- package/dist/admin/components/forms/field-types/Relationship/index.js +49 -17
- package/dist/admin/components/forms/field-types/RichText/RichText.js +3 -4
- package/dist/admin/components/forms/field-types/RichText/elements/upload/Element/EditModal/index.js +4 -2
- package/dist/admin/components/forms/field-types/Row/index.js +7 -2
- package/dist/admin/components/forms/field-types/Select/index.js +4 -5
- package/dist/admin/components/forms/field-types/Text/index.js +6 -3
- package/dist/admin/components/forms/field-types/Textarea/index.js +4 -5
- package/dist/admin/components/forms/field-types/Upload/Add/index.js +1 -1
- package/dist/admin/components/forms/field-types/Upload/Input.d.ts +2 -1
- package/dist/admin/components/forms/field-types/Upload/Input.js +3 -1
- package/dist/admin/components/forms/field-types/Upload/SelectExisting/index.js +27 -4
- package/dist/admin/components/forms/field-types/Upload/SelectExisting/types.d.ts +3 -0
- package/dist/admin/components/forms/field-types/Upload/index.js +4 -5
- package/dist/admin/components/forms/field-types/getFilterOptionsQuery.d.ts +5 -0
- package/dist/admin/components/forms/field-types/getFilterOptionsQuery.js +11 -0
- package/dist/admin/components/forms/useField/index.js +39 -8
- package/dist/admin/components/utilities/OperationProvider/index.d.ts +4 -0
- package/dist/admin/components/utilities/OperationProvider/index.js +3 -0
- package/dist/admin/components/views/Account/Default.js +41 -39
- package/dist/admin/components/views/Account/index.js +4 -2
- package/dist/admin/components/views/CreateFirstUser/index.js +1 -1
- package/dist/admin/components/views/Global/Default.js +45 -43
- package/dist/admin/components/views/Global/index.js +5 -5
- package/dist/admin/components/views/collections/Edit/Auth/APIKey.js +1 -1
- package/dist/admin/components/views/collections/Edit/Default.js +55 -53
- package/dist/admin/components/views/collections/Edit/index.js +5 -5
- package/dist/auth/auth.spec.d.ts +1 -0
- package/dist/auth/auth.spec.js +198 -0
- package/dist/collections/graphql/resolvers/resolvers.spec.d.ts +1 -0
- package/dist/collections/graphql/resolvers/resolvers.spec.js +360 -0
- package/dist/collections/operations/local/local.spec.d.ts +1 -0
- package/dist/collections/operations/local/local.spec.js +175 -0
- package/dist/collections/tests/collections.spec.d.ts +1 -0
- package/dist/collections/tests/collections.spec.js +725 -0
- package/dist/collections/tests/hooks.spec.d.ts +1 -0
- package/dist/collections/tests/hooks.spec.js +152 -0
- package/dist/collections/tests/pointField.spec.d.ts +1 -0
- package/dist/collections/tests/pointField.spec.js +219 -0
- package/dist/collections/tests/relationships.spec.d.ts +1 -0
- package/dist/collections/tests/relationships.spec.js +194 -0
- package/dist/collections/tests/uploads.spec.d.ts +1 -0
- package/dist/collections/tests/uploads.spec.js +328 -0
- package/dist/config/load.d.ts +3 -1
- package/dist/config/load.js +5 -3
- package/dist/config/types.d.ts +3 -0
- package/dist/config/types.js +1 -1
- package/dist/config/validate.d.ts +2 -1
- package/dist/config/validate.js +2 -4
- package/dist/email/build.d.ts +2 -1
- package/dist/email/build.js +7 -9
- package/dist/errors/errors.spec.d.ts +1 -0
- package/dist/errors/errors.spec.js +29 -0
- package/dist/express/middleware/errorHandler.spec.d.ts +1 -0
- package/dist/express/middleware/errorHandler.spec.js +91 -0
- package/dist/fields/config/sanitize.js +2 -2
- package/dist/fields/config/schema.js +3 -1
- package/dist/fields/config/types.d.ts +25 -1
- package/dist/fields/config/types.js +1 -1
- package/dist/fields/performFieldOperations.js +3 -3
- package/dist/fields/traverseFields.js +15 -5
- package/dist/fields/validationPromise.d.ts +10 -3
- package/dist/fields/validationPromise.js +16 -5
- package/dist/fields/validations.d.ts +33 -35
- package/dist/fields/validations.js +130 -76
- package/dist/fields/validations.spec.js +115 -43
- package/dist/globals/requestHandlers/globals.spec.d.ts +4 -0
- package/dist/globals/requestHandlers/globals.spec.js +161 -0
- package/dist/graphql/schema/buildMutationInputType.js +2 -2
- package/dist/graphql/schema/fieldToSchemaMap.js +7 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/mongoose/buildQuery.js +2 -2
- package/dist/mongoose/connect.d.ts +2 -1
- package/dist/mongoose/connect.js +2 -4
- package/dist/mongoose/sanitizeFormattedValue.js +12 -3
- package/dist/preferences/graphql/resolvers.spec.d.ts +1 -0
- package/dist/preferences/graphql/resolvers.spec.js +115 -0
- package/dist/utilities/logger.d.ts +1 -1
- package/dist/utilities/logger.js +10 -6
- package/dist/versions/drafts/appendVersionToQueryKey.js +8 -5
- package/dist/webpack/getBaseConfig.js +2 -1
- package/docs/fields/overview.mdx +39 -3
- package/docs/fields/relationship.mdx +45 -0
- package/docs/fields/upload.mdx +1 -0
- package/docs/getting-started/installation.mdx +14 -10
- package/docs/queries/overview.mdx +2 -1
- package/package.json +1 -1
package/.eslintrc.js
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
## [0.15.4](https://github.com/payloadcms/payload/compare/v0.15.3...v0.15.4) (2022-04-05)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* [#495](https://github.com/payloadcms/payload/issues/495), avoids appending version to id queries ([ab432a4](https://github.com/payloadcms/payload/commit/ab432a43dc568da0b7e65e275aed335d729600fa))
|
|
7
|
+
* default point validation allows not required and some edge cases ([29405bb](https://github.com/payloadcms/payload/commit/29405bbc0e3a5c3c1f3dadb2386a68e1fe159c42))
|
|
8
|
+
* maintains field validation internally for speed ([b99f6b1](https://github.com/payloadcms/payload/commit/b99f6b16af1573185bb8de07c6e54ac02caf6de8))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* allows like to search by many words, adds contain to match exact strings ([ec91757](https://github.com/payloadcms/payload/commit/ec91757257ed062c7743fca3d07d1b6af21cacb4))
|
|
14
|
+
* extended validation function arguments ([#494](https://github.com/payloadcms/payload/issues/494)) ([1b4b570](https://github.com/payloadcms/payload/commit/1b4b5707bfa731bedc5d9ca49ac9f425932b999c)), closes [#495](https://github.com/payloadcms/payload/issues/495)
|
|
15
|
+
* filter relationship options in admin ui using filterOptions ([485991b](https://github.com/payloadcms/payload/commit/485991bd48c3512acca8dd94b3ab6c160bf1f153))
|
|
16
|
+
* **logging:** allow pino logger options to be passed into init ([6620a4f](https://github.com/payloadcms/payload/commit/6620a4f682f0a3169218dd83e1de315f95726287))
|
|
17
|
+
* support className config for row, block and array fields ([#504](https://github.com/payloadcms/payload/issues/504)) ([0461c21](https://github.com/payloadcms/payload/commit/0461c2109bea76742f94ae6f830c655ec67d1428))
|
|
18
|
+
* upload field implements filterOptions ([1482fde](https://github.com/payloadcms/payload/commit/1482fded9a2e700416e7d0b0c753a05f56f4aee1))
|
|
19
|
+
* WIP extended validation function arguments ([647cac3](https://github.com/payloadcms/payload/commit/647cac3612c02dc164254e050ef589a88077b55f))
|
|
20
|
+
* working PoC for reusing relationship filters in validate ([df934df](https://github.com/payloadcms/payload/commit/df934dfeff8b12857d133b2da9f441bc0285dee4))
|
|
21
|
+
|
|
22
|
+
## [0.15.3](https://github.com/payloadcms/payload/compare/v0.15.2...v0.15.3) (2022-04-04)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* [#499](https://github.com/payloadcms/payload/issues/499), graphql row / ui field bug ([f4a2dff](https://github.com/payloadcms/payload/commit/f4a2dff892e6e8a6aa201c2a66b4db4fa2cd98b8))
|
|
28
|
+
|
|
29
|
+
## [0.15.2](https://github.com/payloadcms/payload/compare/v0.15.1...v0.15.2) (2022-04-04)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Bug Fixes
|
|
33
|
+
|
|
34
|
+
* [#495](https://github.com/payloadcms/payload/issues/495), avoids appending version to id queries ([a703e05](https://github.com/payloadcms/payload/commit/a703e0582df3f4706ee051cf1752c79ff4b551ef))
|
|
35
|
+
|
|
1
36
|
## [0.15.1](https://github.com/payloadcms/payload/compare/v0.15.0...v0.15.1) (2022-03-28)
|
|
2
37
|
|
|
3
38
|
|
|
@@ -20,14 +20,14 @@ declare const fieldTypeConditions: {
|
|
|
20
20
|
value: string;
|
|
21
21
|
}[];
|
|
22
22
|
};
|
|
23
|
-
|
|
23
|
+
code: {
|
|
24
24
|
component: string;
|
|
25
25
|
operators: {
|
|
26
26
|
label: string;
|
|
27
27
|
value: string;
|
|
28
28
|
}[];
|
|
29
29
|
};
|
|
30
|
-
|
|
30
|
+
richText: {
|
|
31
31
|
component: string;
|
|
32
32
|
operators: {
|
|
33
33
|
label: string;
|
|
@@ -57,26 +57,30 @@ const like = {
|
|
|
57
57
|
label: 'is like',
|
|
58
58
|
value: 'like',
|
|
59
59
|
};
|
|
60
|
+
const contains = {
|
|
61
|
+
label: 'contains',
|
|
62
|
+
value: 'contains',
|
|
63
|
+
};
|
|
60
64
|
const fieldTypeConditions = {
|
|
61
65
|
text: {
|
|
62
66
|
component: 'Text',
|
|
63
|
-
operators: [...base, like],
|
|
67
|
+
operators: [...base, like, contains],
|
|
64
68
|
},
|
|
65
69
|
email: {
|
|
66
70
|
component: 'Text',
|
|
67
|
-
operators: [...base,
|
|
71
|
+
operators: [...base, contains],
|
|
68
72
|
},
|
|
69
73
|
textarea: {
|
|
70
74
|
component: 'Text',
|
|
71
|
-
operators: [...base, like],
|
|
75
|
+
operators: [...base, like, contains],
|
|
72
76
|
},
|
|
73
|
-
|
|
77
|
+
code: {
|
|
74
78
|
component: 'Text',
|
|
75
|
-
operators: [...base, like],
|
|
79
|
+
operators: [...base, like, contains],
|
|
76
80
|
},
|
|
77
|
-
|
|
81
|
+
richText: {
|
|
78
82
|
component: 'Text',
|
|
79
|
-
operators: [...base, like],
|
|
83
|
+
operators: [...base, like, contains],
|
|
80
84
|
},
|
|
81
85
|
number: {
|
|
82
86
|
component: 'Number',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import Separator from './Paginator/Separator';
|
|
4
|
+
describe('Elements', () => {
|
|
5
|
+
describe('Paginator', () => {
|
|
6
|
+
it('separator - renders dash', () => {
|
|
7
|
+
const { getByText } = render(React.createElement(Separator, null));
|
|
8
|
+
const linkElement = getByText(/—/i); // —
|
|
9
|
+
expect(linkElement).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -7,7 +7,7 @@ import PositionPanel from './PositionPanel';
|
|
|
7
7
|
import Button from '../../elements/Button';
|
|
8
8
|
import { NegativeFieldGutterProvider } from '../FieldTypeGutter/context';
|
|
9
9
|
import FieldTypeGutter from '../FieldTypeGutter';
|
|
10
|
-
import RenderFields
|
|
10
|
+
import RenderFields from '../RenderFields';
|
|
11
11
|
import HiddenInput from '../field-types/HiddenInput';
|
|
12
12
|
import './index.scss';
|
|
13
13
|
import { fieldAffectsData } from '../../../../fields/config/types';
|
|
@@ -15,7 +15,6 @@ const baseClass = 'draggable-section';
|
|
|
15
15
|
const DraggableSection = (props) => {
|
|
16
16
|
const { moveRow, addRow, removeRow, rowIndex, rowCount, parentPath, fieldSchema, label, blockType, fieldTypes, id, setRowCollapse, isCollapsed, permissions, readOnly, hasMaxRows, } = props;
|
|
17
17
|
const [isHovered, setIsHovered] = useState(false);
|
|
18
|
-
const { operation } = useRenderedFields();
|
|
19
18
|
const classes = [
|
|
20
19
|
baseClass,
|
|
21
20
|
isCollapsed ? 'is-collapsed' : 'is-open',
|
|
@@ -32,7 +31,7 @@ const DraggableSection = (props) => {
|
|
|
32
31
|
React.createElement(Button, { icon: "chevron", onClick: () => setRowCollapse(id, !isCollapsed), buttonStyle: "icon-label", className: `toggle-collapse toggle-collapse--is-${isCollapsed ? 'collapsed' : 'open'}`, round: true }))),
|
|
33
32
|
React.createElement(AnimateHeight, { height: isCollapsed ? 0 : 'auto', duration: 200 },
|
|
34
33
|
React.createElement(NegativeFieldGutterProvider, { allow: false },
|
|
35
|
-
React.createElement(RenderFields, {
|
|
34
|
+
React.createElement(RenderFields, { readOnly: readOnly, fieldTypes: fieldTypes, key: rowIndex, permissions: permissions === null || permissions === void 0 ? void 0 : permissions.fields, fieldSchema: fieldSchema.map((field) => ({
|
|
36
35
|
...field,
|
|
37
36
|
path: `${parentPath}.${rowIndex}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
|
|
38
37
|
})) })))),
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import { User } from '../../../../auth';
|
|
1
2
|
import { Field as FieldSchema } from '../../../../fields/config/types';
|
|
2
3
|
import { Fields, Data } from './types';
|
|
3
|
-
declare
|
|
4
|
+
declare type Args = {
|
|
5
|
+
fieldSchema: FieldSchema[];
|
|
6
|
+
data?: Data;
|
|
7
|
+
siblingData?: Data;
|
|
8
|
+
user?: User;
|
|
9
|
+
id?: string | number;
|
|
10
|
+
operation?: 'create' | 'update';
|
|
11
|
+
};
|
|
12
|
+
declare const buildStateFromSchema: (args: Args) => Promise<Fields>;
|
|
4
13
|
export default buildStateFromSchema;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ObjectID from 'bson-objectid';
|
|
2
|
-
import { fieldAffectsData, fieldIsPresentationalOnly } from '../../../../fields/config/types';
|
|
3
|
-
const buildValidationPromise = async (fieldState,
|
|
2
|
+
import { fieldAffectsData, fieldIsPresentationalOnly, } from '../../../../fields/config/types';
|
|
3
|
+
const buildValidationPromise = async (fieldState, options) => {
|
|
4
4
|
const validatedFieldState = fieldState;
|
|
5
5
|
let validationResult = true;
|
|
6
|
-
if (typeof
|
|
7
|
-
validationResult = await
|
|
6
|
+
if (typeof fieldState.validate === 'function') {
|
|
7
|
+
validationResult = await fieldState.validate(fieldState.value, options);
|
|
8
8
|
}
|
|
9
9
|
if (typeof validationResult === 'string') {
|
|
10
10
|
validatedFieldState.errorMessage = validationResult;
|
|
@@ -14,7 +14,8 @@ const buildValidationPromise = async (fieldState, field) => {
|
|
|
14
14
|
validatedFieldState.valid = true;
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
-
const buildStateFromSchema = async (
|
|
17
|
+
const buildStateFromSchema = async (args) => {
|
|
18
|
+
const { fieldSchema, data: fullData = {}, user, id, operation, } = args;
|
|
18
19
|
if (fieldSchema) {
|
|
19
20
|
const validationPromises = [];
|
|
20
21
|
const structureFieldState = (field, passesCondition, data = {}) => {
|
|
@@ -28,7 +29,14 @@ const buildStateFromSchema = async (fieldSchema, fullData = {}) => {
|
|
|
28
29
|
condition: (_a = field.admin) === null || _a === void 0 ? void 0 : _a.condition,
|
|
29
30
|
passesCondition,
|
|
30
31
|
};
|
|
31
|
-
validationPromises.push(buildValidationPromise(fieldState,
|
|
32
|
+
validationPromises.push(buildValidationPromise(fieldState, {
|
|
33
|
+
...field,
|
|
34
|
+
fullData,
|
|
35
|
+
user,
|
|
36
|
+
siblingData: data,
|
|
37
|
+
id,
|
|
38
|
+
operation,
|
|
39
|
+
}));
|
|
32
40
|
return fieldState;
|
|
33
41
|
};
|
|
34
42
|
const iterateFields = (fields, data, parentPassesCondition, path = '') => fields.reduce((state, field) => {
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { unflatten } from 'flatley';
|
|
2
|
-
import reduceFieldsToValues from './reduceFieldsToValues';
|
|
3
2
|
const getDataByPath = (fields, path) => {
|
|
4
3
|
const pathPrefixToRemove = path.substring(0, path.lastIndexOf('.') + 1);
|
|
5
4
|
const name = path.split('.').pop();
|
|
6
|
-
const data =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
[key.replace(pathPrefixToRemove, '')]: fields[key],
|
|
11
|
-
};
|
|
5
|
+
const data = {};
|
|
6
|
+
Object.keys(fields).forEach((key) => {
|
|
7
|
+
if (!fields[key].disableFormData && (key.indexOf(`${path}.`) === 0 || key === path)) {
|
|
8
|
+
data[key.replace(pathPrefixToRemove, '')] = fields[key].value;
|
|
12
9
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const values = reduceFieldsToValues(data, true);
|
|
16
|
-
const unflattenedData = unflatten(values);
|
|
10
|
+
});
|
|
11
|
+
const unflattenedData = unflatten(data);
|
|
17
12
|
return unflattenedData === null || unflattenedData === void 0 ? void 0 : unflattenedData[name];
|
|
18
13
|
};
|
|
19
14
|
export default getDataByPath;
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
import { unflatten } from 'flatley';
|
|
1
2
|
import reduceFieldsToValues from './reduceFieldsToValues';
|
|
2
3
|
const getSiblingData = (fields, path) => {
|
|
3
|
-
|
|
4
|
+
if (path.indexOf('.') === -1) {
|
|
5
|
+
return reduceFieldsToValues(fields, true);
|
|
6
|
+
}
|
|
7
|
+
const siblingFields = {};
|
|
4
8
|
// If this field is nested
|
|
5
9
|
// We can provide a list of sibling fields
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
return siblings;
|
|
16
|
-
}, {});
|
|
17
|
-
}
|
|
18
|
-
return reduceFieldsToValues(siblingFields, true);
|
|
10
|
+
const parentFieldPath = path.substring(0, path.lastIndexOf('.') + 1);
|
|
11
|
+
Object.keys(fields).forEach((fieldKey) => {
|
|
12
|
+
if (!fields[fieldKey].disableFormData && fieldKey.indexOf(parentFieldPath) === 0) {
|
|
13
|
+
siblingFields[fieldKey.replace(parentFieldPath, '')] = fields[fieldKey].value;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return unflatten(siblingFields, { safe: true });
|
|
19
17
|
};
|
|
20
18
|
export default getSiblingData;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
|
2
2
|
import React, { useReducer, useEffect, useRef, useState, useCallback, } from 'react';
|
|
3
|
+
import isDeepEqual from 'deep-equal';
|
|
3
4
|
import { serialize } from 'object-to-formdata';
|
|
4
5
|
import { useHistory } from 'react-router-dom';
|
|
5
6
|
import { toast } from 'react-toastify';
|
|
6
7
|
import { useAuth } from '@payloadcms/config-provider';
|
|
7
8
|
import { useLocale } from '../../utilities/Locale';
|
|
9
|
+
import { useDocumentInfo } from '../../utilities/DocumentInfo';
|
|
8
10
|
import { requests } from '../../../api';
|
|
9
11
|
import useThrottledEffect from '../../../hooks/useThrottledEffect';
|
|
10
12
|
import fieldReducer from './fieldReducer';
|
|
@@ -17,6 +19,7 @@ import buildInitialState from './buildInitialState';
|
|
|
17
19
|
import errorMessages from './errorMessages';
|
|
18
20
|
import { SubmittedContext, ProcessingContext, ModifiedContext, FormContext, FormWatchContext } from './context';
|
|
19
21
|
import buildStateFromSchema from './buildStateFromSchema';
|
|
22
|
+
import { useOperation } from '../../utilities/OperationProvider';
|
|
20
23
|
const baseClass = 'form';
|
|
21
24
|
const Form = (props) => {
|
|
22
25
|
const { disabled, onSubmit, method, action, handleResponse, onSuccess, children, className, redirect, disableSuccessStatus, initialState, // fully formed initial field state
|
|
@@ -24,7 +27,9 @@ const Form = (props) => {
|
|
|
24
27
|
waitForAutocomplete, log, } = props;
|
|
25
28
|
const history = useHistory();
|
|
26
29
|
const locale = useLocale();
|
|
27
|
-
const { refreshCookie } = useAuth();
|
|
30
|
+
const { refreshCookie, user } = useAuth();
|
|
31
|
+
const { id } = useDocumentInfo();
|
|
32
|
+
const operation = useOperation();
|
|
28
33
|
const [modified, setModified] = useState(false);
|
|
29
34
|
const [processing, setProcessing] = useState(false);
|
|
30
35
|
const [submitted, setSubmitted] = useState(false);
|
|
@@ -43,6 +48,7 @@ const Form = (props) => {
|
|
|
43
48
|
const validateForm = useCallback(async () => {
|
|
44
49
|
const validatedFieldState = {};
|
|
45
50
|
let isValid = true;
|
|
51
|
+
const data = contextRef.current.getData();
|
|
46
52
|
const validationPromises = Object.entries(contextRef.current.fields).map(async ([path, field]) => {
|
|
47
53
|
const validatedField = {
|
|
48
54
|
...field,
|
|
@@ -51,7 +57,13 @@ const Form = (props) => {
|
|
|
51
57
|
if (field.passesCondition !== false) {
|
|
52
58
|
let validationResult = true;
|
|
53
59
|
if (typeof field.validate === 'function') {
|
|
54
|
-
validationResult = await field.validate(field.value
|
|
60
|
+
validationResult = await field.validate(field.value, {
|
|
61
|
+
data,
|
|
62
|
+
siblingData: contextRef.current.getSiblingData(path),
|
|
63
|
+
user,
|
|
64
|
+
id,
|
|
65
|
+
operation,
|
|
66
|
+
});
|
|
55
67
|
}
|
|
56
68
|
if (typeof validationResult === 'string') {
|
|
57
69
|
validatedField.errorMessage = validationResult;
|
|
@@ -62,9 +74,11 @@ const Form = (props) => {
|
|
|
62
74
|
validatedFieldState[path] = validatedField;
|
|
63
75
|
});
|
|
64
76
|
await Promise.all(validationPromises);
|
|
65
|
-
|
|
77
|
+
if (!isDeepEqual(contextRef.current.fields, validatedFieldState)) {
|
|
78
|
+
dispatchFields({ type: 'REPLACE_STATE', state: validatedFieldState });
|
|
79
|
+
}
|
|
66
80
|
return isValid;
|
|
67
|
-
}, [contextRef]);
|
|
81
|
+
}, [contextRef, id, user, operation]);
|
|
68
82
|
const submit = useCallback(async (options = {}, e) => {
|
|
69
83
|
const { overrides = {}, action: actionToUse = action, method: methodToUse = method, skipValidation, } = options;
|
|
70
84
|
if (disabled) {
|
|
@@ -233,10 +247,10 @@ const Form = (props) => {
|
|
|
233
247
|
return formData;
|
|
234
248
|
}, [contextRef]);
|
|
235
249
|
const reset = useCallback(async (fieldSchema, data) => {
|
|
236
|
-
const state = await buildStateFromSchema(fieldSchema, data);
|
|
250
|
+
const state = await buildStateFromSchema({ fieldSchema, data, user, id, operation });
|
|
237
251
|
contextRef.current = { ...initContextState };
|
|
238
252
|
dispatchFields({ type: 'REPLACE_STATE', state });
|
|
239
|
-
}, []);
|
|
253
|
+
}, [id, user, operation]);
|
|
240
254
|
contextRef.current.dispatchFields = dispatchFields;
|
|
241
255
|
contextRef.current.submit = submit;
|
|
242
256
|
contextRef.current.getFields = getFields;
|
|
@@ -270,6 +284,9 @@ const Form = (props) => {
|
|
|
270
284
|
useThrottledEffect(() => {
|
|
271
285
|
refreshCookie();
|
|
272
286
|
}, 15000, [fields]);
|
|
287
|
+
useThrottledEffect(() => {
|
|
288
|
+
validateForm();
|
|
289
|
+
}, 1000, [validateForm, fields]);
|
|
273
290
|
useEffect(() => {
|
|
274
291
|
contextRef.current = { ...contextRef.current }; // triggers rerender of all components that subscribe to form
|
|
275
292
|
setModified(false);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { Field as FieldConfig, Condition } from '../../../../fields/config/types';
|
|
2
|
+
import { Field as FieldConfig, Condition, Validate } from '../../../../fields/config/types';
|
|
3
3
|
export declare type Field = {
|
|
4
4
|
value: unknown;
|
|
5
5
|
initialValue: unknown;
|
|
6
6
|
errorMessage?: string;
|
|
7
7
|
valid: boolean;
|
|
8
|
-
validate?:
|
|
8
|
+
validate?: Validate;
|
|
9
9
|
disableFormData?: boolean;
|
|
10
10
|
ignoreWhileFlattening?: boolean;
|
|
11
11
|
condition?: Condition;
|
|
@@ -34,6 +34,7 @@ export declare type Props = {
|
|
|
34
34
|
initialData?: Data;
|
|
35
35
|
waitForAutocomplete?: boolean;
|
|
36
36
|
log?: boolean;
|
|
37
|
+
validationOperation?: 'create' | 'update';
|
|
37
38
|
};
|
|
38
39
|
export declare type SubmitOptions = {
|
|
39
40
|
action?: string;
|
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
|
3
3
|
import useIntersect from '../../../hooks/useIntersect';
|
|
4
4
|
import { fieldAffectsData, fieldIsPresentationalOnly } from '../../../../fields/config/types';
|
|
5
|
+
import { useOperation } from '../../utilities/OperationProvider';
|
|
5
6
|
const baseClass = 'render-fields';
|
|
6
7
|
const intersectionObserverOptions = {
|
|
7
8
|
rootMargin: '1000px',
|
|
8
9
|
};
|
|
9
|
-
const RenderedFieldContext = createContext({});
|
|
10
|
-
export const useRenderedFields = () => useContext(RenderedFieldContext);
|
|
11
10
|
const RenderFields = (props) => {
|
|
12
11
|
var _a;
|
|
13
|
-
const { fieldSchema, fieldTypes, filter, permissions, readOnly: readOnlyOverride,
|
|
12
|
+
const { fieldSchema, fieldTypes, filter, permissions, readOnly: readOnlyOverride, className, } = props;
|
|
14
13
|
const [hasRendered, setHasRendered] = useState(false);
|
|
15
14
|
const [intersectionRef, entry] = useIntersect(intersectionObserverOptions);
|
|
15
|
+
const operation = useOperation();
|
|
16
16
|
const isIntersecting = Boolean(entry === null || entry === void 0 ? void 0 : entry.isIntersecting);
|
|
17
17
|
const isAboveViewport = ((_a = entry === null || entry === void 0 ? void 0 : entry.boundingClientRect) === null || _a === void 0 ? void 0 : _a.top) < 0;
|
|
18
18
|
const shouldRender = isIntersecting || isAboveViewport;
|
|
19
|
-
const { operation: operationFromContext } = useRenderedFields();
|
|
20
|
-
const operation = operationFromProps || operationFromContext;
|
|
21
|
-
const [contextValue, setContextValue] = useState({
|
|
22
|
-
operation,
|
|
23
|
-
});
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
setContextValue({
|
|
26
|
-
operation,
|
|
27
|
-
});
|
|
28
|
-
}, [operation]);
|
|
29
19
|
useEffect(() => {
|
|
30
20
|
if (shouldRender && !hasRendered) {
|
|
31
21
|
setHasRendered(true);
|
|
@@ -36,7 +26,7 @@ const RenderFields = (props) => {
|
|
|
36
26
|
className,
|
|
37
27
|
].filter(Boolean).join(' ');
|
|
38
28
|
if (fieldSchema) {
|
|
39
|
-
return (React.createElement("div", { ref: intersectionRef, className: classes }, hasRendered && (
|
|
29
|
+
return (React.createElement("div", { ref: intersectionRef, className: classes }, hasRendered && (fieldSchema.map((field, i) => {
|
|
40
30
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
41
31
|
const fieldIsPresentational = fieldIsPresentationalOnly(field);
|
|
42
32
|
let FieldComponent = fieldTypes[field.type];
|
|
@@ -80,7 +70,7 @@ const RenderFields = (props) => {
|
|
|
80
70
|
return null;
|
|
81
71
|
}
|
|
82
72
|
return null;
|
|
83
|
-
}))))
|
|
73
|
+
}))));
|
|
84
74
|
}
|
|
85
75
|
return null;
|
|
86
76
|
};
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { FieldPermissions } from '../../../../auth/types';
|
|
2
2
|
import { FieldWithPath, Field } from '../../../../fields/config/types';
|
|
3
3
|
import { FieldTypes } from '../field-types';
|
|
4
|
-
export declare type Operation = 'create' | 'update';
|
|
5
|
-
export declare type Context = {
|
|
6
|
-
operation: Operation;
|
|
7
|
-
};
|
|
8
4
|
export declare type Props = {
|
|
9
5
|
className?: string;
|
|
10
|
-
operation?: Operation;
|
|
11
6
|
readOnly?: boolean;
|
|
12
7
|
permissions?: {
|
|
13
8
|
[field: string]: FieldPermissions;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useEffect, useReducer,
|
|
1
|
+
import React, { useCallback, useEffect, useReducer, useState } from 'react';
|
|
2
2
|
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
|
3
|
+
import { useAuth } from '@payloadcms/config-provider';
|
|
3
4
|
import withCondition from '../../withCondition';
|
|
4
5
|
import Button from '../../../elements/Button';
|
|
5
6
|
import DraggableSection from '../../DraggableSection';
|
|
@@ -11,11 +12,13 @@ import Error from '../../Error';
|
|
|
11
12
|
import { array } from '../../../../../fields/validations';
|
|
12
13
|
import Banner from '../../../elements/Banner';
|
|
13
14
|
import FieldDescription from '../../FieldDescription';
|
|
15
|
+
import { useDocumentInfo } from '../../../utilities/DocumentInfo';
|
|
16
|
+
import { useOperation } from '../../../utilities/OperationProvider';
|
|
14
17
|
import './index.scss';
|
|
15
18
|
const baseClass = 'field-type array';
|
|
16
19
|
const ArrayFieldType = (props) => {
|
|
17
20
|
var _a, _b;
|
|
18
|
-
const { name, path: pathFromProps, fields, fieldTypes, validate = array, required, maxRows, minRows, permissions, admin: { readOnly, description, condition, }, } = props;
|
|
21
|
+
const { name, path: pathFromProps, fields, fieldTypes, validate = array, required, maxRows, minRows, permissions, admin: { readOnly, description, condition, className, }, } = props;
|
|
19
22
|
// Handle labeling for Arrays, Global Arrays, and Blocks
|
|
20
23
|
const getLabels = (p) => {
|
|
21
24
|
if (p === null || p === void 0 ? void 0 : p.labels)
|
|
@@ -29,12 +32,14 @@ const ArrayFieldType = (props) => {
|
|
|
29
32
|
const label = (_a = props === null || props === void 0 ? void 0 : props.label) !== null && _a !== void 0 ? _a : (_b = props === null || props === void 0 ? void 0 : props.labels) === null || _b === void 0 ? void 0 : _b.singular;
|
|
30
33
|
const [rows, dispatchRows] = useReducer(reducer, []);
|
|
31
34
|
const formContext = useForm();
|
|
35
|
+
const { user } = useAuth();
|
|
36
|
+
const { id } = useDocumentInfo();
|
|
37
|
+
const operation = useOperation();
|
|
32
38
|
const { dispatchFields } = formContext;
|
|
33
39
|
const path = pathFromProps || name;
|
|
34
|
-
const memoizedValidate = useCallback((value) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}, [validate, maxRows, minRows, required]);
|
|
40
|
+
const memoizedValidate = useCallback((value, options) => {
|
|
41
|
+
return validate(value, { ...options, minRows, maxRows, required });
|
|
42
|
+
}, [maxRows, minRows, required, validate]);
|
|
38
43
|
const [disableFormData, setDisableFormData] = useState(false);
|
|
39
44
|
const { showError, errorMessage, value, setValue, } = useField({
|
|
40
45
|
path,
|
|
@@ -44,11 +49,11 @@ const ArrayFieldType = (props) => {
|
|
|
44
49
|
condition,
|
|
45
50
|
});
|
|
46
51
|
const addRow = useCallback(async (rowIndex) => {
|
|
47
|
-
const subFieldState = await buildStateFromSchema(fields);
|
|
52
|
+
const subFieldState = await buildStateFromSchema({ fieldSchema: fields, operation, id, user });
|
|
48
53
|
dispatchFields({ type: 'ADD_ROW', rowIndex, subFieldState, path });
|
|
49
54
|
dispatchRows({ type: 'ADD', rowIndex });
|
|
50
55
|
setValue(value + 1);
|
|
51
|
-
}, [dispatchRows, dispatchFields, fields, path, setValue, value]);
|
|
56
|
+
}, [dispatchRows, dispatchFields, fields, path, setValue, value, operation, id, user]);
|
|
52
57
|
const removeRow = useCallback((rowIndex) => {
|
|
53
58
|
dispatchRows({ type: 'REMOVE', rowIndex });
|
|
54
59
|
dispatchFields({ type: 'REMOVE_ROW', rowIndex, path });
|
|
@@ -78,8 +83,12 @@ const ArrayFieldType = (props) => {
|
|
|
78
83
|
}
|
|
79
84
|
}, [rows, setValue]);
|
|
80
85
|
const hasMaxRows = maxRows && rows.length >= maxRows;
|
|
86
|
+
const classes = [
|
|
87
|
+
baseClass,
|
|
88
|
+
className,
|
|
89
|
+
].filter(Boolean).join(' ');
|
|
81
90
|
return (React.createElement(DragDropContext, { onDragEnd: onDragEnd },
|
|
82
|
-
React.createElement("div", { className:
|
|
91
|
+
React.createElement("div", { className: classes },
|
|
83
92
|
React.createElement("div", { className: `${baseClass}__error-wrap` },
|
|
84
93
|
React.createElement(Error, { showError: showError, message: errorMessage })),
|
|
85
94
|
React.createElement("header", { className: `${baseClass}__header` },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useEffect, useReducer,
|
|
1
|
+
import React, { useCallback, useEffect, useReducer, useState } from 'react';
|
|
2
2
|
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
|
3
|
+
import { useAuth } from '@payloadcms/config-provider';
|
|
3
4
|
import { usePreferences } from '../../../utilities/Preferences';
|
|
4
5
|
import withCondition from '../../withCondition';
|
|
5
6
|
import Button from '../../../elements/Button';
|
|
@@ -15,6 +16,7 @@ import BlockSelector from './BlockSelector';
|
|
|
15
16
|
import { blocks as blocksValidator } from '../../../../../fields/validations';
|
|
16
17
|
import Banner from '../../../elements/Banner';
|
|
17
18
|
import FieldDescription from '../../FieldDescription';
|
|
19
|
+
import { useOperation } from '../../../utilities/OperationProvider';
|
|
18
20
|
import './index.scss';
|
|
19
21
|
const baseClass = 'field-type blocks';
|
|
20
22
|
const labelDefaults = {
|
|
@@ -22,19 +24,19 @@ const labelDefaults = {
|
|
|
22
24
|
plural: 'Blocks',
|
|
23
25
|
};
|
|
24
26
|
const Blocks = (props) => {
|
|
25
|
-
const { label, name, path: pathFromProps, blocks, labels = labelDefaults, fieldTypes, maxRows, minRows, required, validate = blocksValidator, permissions, admin: { readOnly, description, condition, }, } = props;
|
|
27
|
+
const { label, name, path: pathFromProps, blocks, labels = labelDefaults, fieldTypes, maxRows, minRows, required, validate = blocksValidator, permissions, admin: { readOnly, description, condition, className, }, } = props;
|
|
26
28
|
const path = pathFromProps || name;
|
|
27
29
|
const { preferencesKey } = useDocumentInfo();
|
|
28
30
|
const { getPreference, setPreference } = usePreferences();
|
|
29
31
|
const [rows, dispatchRows] = useReducer(reducer, []);
|
|
30
32
|
const formContext = useForm();
|
|
33
|
+
const { user } = useAuth();
|
|
34
|
+
const { id } = useDocumentInfo();
|
|
35
|
+
const operation = useOperation();
|
|
31
36
|
const { dispatchFields } = formContext;
|
|
32
|
-
const memoizedValidate = useCallback((value) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
return validationResult;
|
|
37
|
-
}, [validate, maxRows, minRows, labels, blocks, required]);
|
|
37
|
+
const memoizedValidate = useCallback((value, options) => {
|
|
38
|
+
return validate(value, { ...options, minRows, maxRows, required });
|
|
39
|
+
}, [maxRows, minRows, required, validate]);
|
|
38
40
|
const [disableFormData, setDisableFormData] = useState(false);
|
|
39
41
|
const { showError, errorMessage, value, setValue, } = useField({
|
|
40
42
|
path,
|
|
@@ -45,11 +47,11 @@ const Blocks = (props) => {
|
|
|
45
47
|
});
|
|
46
48
|
const addRow = useCallback(async (rowIndex, blockType) => {
|
|
47
49
|
const block = blocks.find((potentialBlock) => potentialBlock.slug === blockType);
|
|
48
|
-
const subFieldState = await buildStateFromSchema(block.fields);
|
|
50
|
+
const subFieldState = await buildStateFromSchema({ fieldSchema: block.fields, operation, id, user });
|
|
49
51
|
dispatchFields({ type: 'ADD_ROW', rowIndex, subFieldState, path, blockType });
|
|
50
52
|
dispatchRows({ type: 'ADD', rowIndex, blockType });
|
|
51
53
|
setValue(value + 1);
|
|
52
|
-
}, [path, setValue, value, blocks, dispatchFields]);
|
|
54
|
+
}, [path, setValue, value, blocks, dispatchFields, operation, id, user]);
|
|
53
55
|
const removeRow = useCallback((rowIndex) => {
|
|
54
56
|
dispatchRows({ type: 'REMOVE', rowIndex });
|
|
55
57
|
dispatchFields({ type: 'REMOVE_ROW', rowIndex, path });
|
|
@@ -59,19 +61,19 @@ const Blocks = (props) => {
|
|
|
59
61
|
dispatchRows({ type: 'MOVE', moveFromIndex, moveToIndex });
|
|
60
62
|
dispatchFields({ type: 'MOVE_ROW', moveFromIndex, moveToIndex, path });
|
|
61
63
|
}, [dispatchRows, dispatchFields, path]);
|
|
62
|
-
const setCollapse = useCallback(async (
|
|
64
|
+
const setCollapse = useCallback(async (rowID, collapsed) => {
|
|
63
65
|
var _a, _b, _c;
|
|
64
|
-
dispatchRows({ type: 'SET_COLLAPSE',
|
|
66
|
+
dispatchRows({ type: 'SET_COLLAPSE', rowID, collapsed });
|
|
65
67
|
if (preferencesKey) {
|
|
66
68
|
const preferences = await getPreference(preferencesKey);
|
|
67
69
|
const preferencesToSet = preferences || { fields: {} };
|
|
68
70
|
let newCollapsedState = ((_b = (_a = preferencesToSet === null || preferencesToSet === void 0 ? void 0 : preferencesToSet.fields) === null || _a === void 0 ? void 0 : _a[path]) === null || _b === void 0 ? void 0 : _b.collapsed.filter((filterID) => (rows.find((row) => row.id === filterID))))
|
|
69
71
|
|| [];
|
|
70
72
|
if (!collapsed) {
|
|
71
|
-
newCollapsedState = newCollapsedState.filter((existingID) => existingID !==
|
|
73
|
+
newCollapsedState = newCollapsedState.filter((existingID) => existingID !== rowID);
|
|
72
74
|
}
|
|
73
75
|
else {
|
|
74
|
-
newCollapsedState.push(
|
|
76
|
+
newCollapsedState.push(rowID);
|
|
75
77
|
}
|
|
76
78
|
setPreference(preferencesKey, {
|
|
77
79
|
...preferencesToSet,
|
|
@@ -118,8 +120,12 @@ const Blocks = (props) => {
|
|
|
118
120
|
}
|
|
119
121
|
}, [rows, setValue]);
|
|
120
122
|
const hasMaxRows = maxRows && rows.length >= maxRows;
|
|
123
|
+
const classes = [
|
|
124
|
+
baseClass,
|
|
125
|
+
className,
|
|
126
|
+
].filter(Boolean).join(' ');
|
|
121
127
|
return (React.createElement(DragDropContext, { onDragEnd: onDragEnd },
|
|
122
|
-
React.createElement("div", { className:
|
|
128
|
+
React.createElement("div", { className: classes },
|
|
123
129
|
React.createElement("div", { className: `${baseClass}__error-wrap` },
|
|
124
130
|
React.createElement(Error, { showError: showError, message: errorMessage })),
|
|
125
131
|
React.createElement("header", { className: `${baseClass}__header` },
|