Skip to content

Commit

Permalink
feat(app-headless-cms): create bind component on field config change (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 authored Apr 22, 2024
1 parent 42fcc1a commit cc06068
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 17 deletions.
2 changes: 1 addition & 1 deletion packages/api-headless-cms-aco/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"references": [
{ "path": "../api/tsconfig.build.json" },
{ "path": "../api-aco/tsconfig.build.json" },
{ "path": "../api-headless-cms/tsconfig.build.json" },
{ "path": "../error/tsconfig.build.json" },
{ "path": "../handler/tsconfig.build.json" },
{ "path": "../api-admin-users/tsconfig.build.json" },
{ "path": "../api-headless-cms/tsconfig.build.json" },
{ "path": "../api-i18n/tsconfig.build.json" },
{ "path": "../api-security/tsconfig.build.json" },
{ "path": "../api-tenancy/tsconfig.build.json" },
Expand Down
6 changes: 3 additions & 3 deletions packages/api-headless-cms-aco/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"references": [
{ "path": "../api" },
{ "path": "../api-aco" },
{ "path": "../api-headless-cms" },
{ "path": "../error" },
{ "path": "../handler" },
{ "path": "../api-admin-users" },
{ "path": "../api-headless-cms" },
{ "path": "../api-i18n" },
{ "path": "../api-security" },
{ "path": "../api-tenancy" },
Expand All @@ -28,14 +28,14 @@
"@webiny/api": ["../api/src"],
"@webiny/api-aco/*": ["../api-aco/src/*"],
"@webiny/api-aco": ["../api-aco/src"],
"@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"],
"@webiny/api-headless-cms": ["../api-headless-cms/src"],
"@webiny/error/*": ["../error/src/*"],
"@webiny/error": ["../error/src"],
"@webiny/handler/*": ["../handler/src/*"],
"@webiny/handler": ["../handler/src"],
"@webiny/api-admin-users/*": ["../api-admin-users/src/*"],
"@webiny/api-admin-users": ["../api-admin-users/src"],
"@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"],
"@webiny/api-headless-cms": ["../api-headless-cms/src"],
"@webiny/api-i18n/*": ["../api-i18n/src/*"],
"@webiny/api-i18n": ["../api-i18n/src"],
"@webiny/api-security/*": ["../api-security/src/*"],
Expand Down
1 change: 1 addition & 0 deletions packages/app-headless-cms-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@webiny/app-security": "0.0.0",
"@webiny/form": "0.0.0",
"@webiny/plugins": "0.0.0",
"@webiny/validation": "0.0.0",
"graphql": "^15.7.2",
"graphql-tag": "^2.12.6",
"prop-types": "^15.7.2",
Expand Down
3 changes: 2 additions & 1 deletion packages/app-headless-cms-common/src/types/model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Validator } from "@webiny/validation/types";
import { CmsModelFieldValidator } from "~/types/validation";
import {
CmsDynamicZoneTemplate,
Expand Down Expand Up @@ -31,7 +32,7 @@ export type CmsModelField<T = unknown> = T & {
label: string;
helpText?: string;
placeholderText?: string;
validation?: CmsModelFieldValidator[];
validation?: (CmsModelFieldValidator | Validator)[];
listValidation?: CmsModelFieldValidator[];
multipleValues?: boolean;
predefinedValues?: CmsEditorFieldPredefinedValues;
Expand Down
3 changes: 2 additions & 1 deletion packages/app-headless-cms-common/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"references": [
{ "path": "../app-security/tsconfig.build.json" },
{ "path": "../form/tsconfig.build.json" },
{ "path": "../plugins/tsconfig.build.json" }
{ "path": "../plugins/tsconfig.build.json" },
{ "path": "../validation/tsconfig.build.json" }
],
"compilerOptions": {
"rootDir": "./src",
Expand Down
11 changes: 9 additions & 2 deletions packages/app-headless-cms-common/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "__tests__"],
"references": [{ "path": "../app-security" }, { "path": "../form" }, { "path": "../plugins" }],
"references": [
{ "path": "../app-security" },
{ "path": "../form" },
{ "path": "../plugins" },
{ "path": "../validation" }
],
"compilerOptions": {
"rootDirs": ["./src", "./__tests__"],
"outDir": "./dist",
Expand All @@ -14,7 +19,9 @@
"@webiny/form/*": ["../form/src/*"],
"@webiny/form": ["../form/src"],
"@webiny/plugins/*": ["../plugins/src/*"],
"@webiny/plugins": ["../plugins/src"]
"@webiny/plugins": ["../plugins/src"],
"@webiny/validation/*": ["../validation/src/*"],
"@webiny/validation": ["../validation/src"]
},
"baseUrl": "."
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,37 @@ interface UseBindParams {
children?: any;
}

const createFieldCacheKey = (field: CmsModelField) => {
return [
field.id,
field.fieldId,
JSON.stringify(field.validation),
JSON.stringify(field.listValidation)
].join(";");
};

export interface GetBindCallable {
(index?: number): BindComponent;
}

export function useBind({ Bind, field }: UseBindProps) {
const memoizedBindComponents = useRef<Record<string, BindComponent>>({});
const cacheKey = createFieldCacheKey(field);

return useCallback(
(index = -1) => {
const { parentName } = Bind;

// If there's a parent name assigned to the given Bind component, we need to include it in the new field "name".
// This allows us to have nested fields (like "object" field with nested properties)
const name = [parentName, field.fieldId, index >= 0 ? index : undefined]
.filter(v => v !== undefined)
.join(".");

if (memoizedBindComponents.current[name]) {
return memoizedBindComponents.current[name];
const componentId = `${name};${cacheKey}`;

if (memoizedBindComponents.current[componentId]) {
return memoizedBindComponents.current[componentId];
}

const validators = createValidators(field, field.validation || []);
Expand All @@ -40,7 +53,7 @@ export function useBind({ Bind, field }: UseBindProps) {
const isMultipleValues = index === -1 && field.multipleValues;
const inputValidators = isMultipleValues ? listValidators : validators;

memoizedBindComponents.current[name] = function UseBind(params: UseBindParams) {
memoizedBindComponents.current[componentId] = function UseBind(params: UseBindParams) {
const { name: childName, validators: childValidators, children } = params;

return (
Expand Down Expand Up @@ -120,11 +133,11 @@ export function useBind({ Bind, field }: UseBindProps) {
} as BindComponent;

// We need to keep track of current field name, to support nested fields.
memoizedBindComponents.current[name].parentName = name;
memoizedBindComponents.current[name].displayName = `Bind<${name}>`;
memoizedBindComponents.current[componentId].parentName = name;
memoizedBindComponents.current[componentId].displayName = `Bind<${name}>`;

return memoizedBindComponents.current[name];
return memoizedBindComponents.current[componentId];
},
[field.fieldId]
[field.fieldId, cacheKey]
);
}
6 changes: 5 additions & 1 deletion packages/app-headless-cms/src/utils/createValidators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import camelCase from "lodash/camelCase";

export const createValidators = (
field: CmsModelField,
validation: CmsModelFieldValidator[]
validation: (CmsModelFieldValidator | Validator)[]
): Validator[] => {
const validatorPlugins = plugins.byType<CmsModelFieldValidatorPlugin>(
"cms-model-field-validator"
);

return validation.reduce<Validator[]>((collection, item) => {
if (typeof item === "function") {
return [...collection, item];
}

const validatorPlugin = validatorPlugins.find(
plugin => plugin.validator.name === item.name
);
Expand Down
1 change: 0 additions & 1 deletion packages/app-headless-cms/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"references": [
{ "path": "../app" },
{ "path": "../app-aco" },
{ "path": "../app-bin" },
{ "path": "../app-admin" },
{ "path": "../app-graphql-playground" },
{ "path": "../app-headless-cms-common" },
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14976,6 +14976,7 @@ __metadata:
"@webiny/form": 0.0.0
"@webiny/plugins": 0.0.0
"@webiny/project-utils": 0.0.0
"@webiny/validation": 0.0.0
babel-plugin-module-resolver: ^5.0.0
graphql: ^15.7.2
graphql-tag: ^2.12.6
Expand Down

0 comments on commit cc06068

Please sign in to comment.