From 7e1c83d3adf70abda4c01a37e525cc29024ca0cb Mon Sep 17 00:00:00 2001 From: michel Date: Fri, 27 Dec 2024 10:13:52 +0100 Subject: [PATCH 1/4] fix: validate optionActions --- src/validations/prefab/component.ts | 56 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/validations/prefab/component.ts b/src/validations/prefab/component.ts index 981c1ffd..81b34a53 100644 --- a/src/validations/prefab/component.ts +++ b/src/validations/prefab/component.ts @@ -23,6 +23,11 @@ import { linkedPartialSchema } from './linkedPartial'; type StyleValidator = Record; type PrefabTypes = 'partial' | 'page' | undefined; +type TransformationActions = { + onChangeActions: string[]; + onCreateActions: string[]; +}; + const shadows = [ 'none', '0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)', @@ -189,6 +194,36 @@ const validateComponentStyle = return prefabObject; }; +const baseAction = (allowedActions: string[]) => + Joi.object({ + action: Joi.string() + .valid(...allowedActions) + .messages({ + valid: `onChangeAction not of value: ${JSON.stringify(allowedActions)}`, + }), + target: Joi.string(), + }); + +const optionActionsObject = ({ + onChangeActions, + onCreateActions, +}: TransformationActions) => + Joi.object({ + onChange: Joi.array().items(baseAction(onChangeActions)), + onCreate: Joi.array().items(baseAction(onCreateActions)), + }); + +const optionTemplatesSchema = (transformationActions: TransformationActions) => + Joi.object({ + addChild: Joi.object({ + options: Joi.array().items(optionSchema).required(), + optionActions: Joi.object().pattern( + Joi.string(), + optionActionsObject(transformationActions), + ), + }), + }); + const componentSchema = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, @@ -206,11 +241,18 @@ const componentSchema = ( overwrite: overwriteSchema, }); - const optionTemplatesSchema = Joi.object({ - addChild: Joi.object({ - options: Joi.array().items(optionSchema).required(), - }), - }); + // internal actions list. + const definedinternalTransformationActions = { + onChangeActions: [ + 'setVariableOptionWithPropertyLabel', + 'setVariableOptionWithPropertyValue', + 'setVariableOptionWithInputVariableName', + ], + onCreateActions: [ + 'createAndSetActionInputVariableOption', + 'createAndSetPropertyOption', + ], + }; const deprecatedStylesFlag = Object.keys(styles).length === 0; @@ -223,7 +265,9 @@ const componentSchema = ( }), optionCategories: Joi.array().items(optionCategorySchema).min(1), options: Joi.array().items(optionSchema).required(), - optionTemplates: optionTemplatesSchema, + optionTemplates: optionTemplatesSchema( + definedinternalTransformationActions, + ), type: Joi.string().valid('COMPONENT').default('COMPONENT'), descendants: Joi.array() .items( From d5d8209bd07185bfec461e66bb047eaeaeb3da02 Mon Sep 17 00:00:00 2001 From: michel Date: Mon, 30 Dec 2024 14:18:14 +0100 Subject: [PATCH 2/4] fix: enable procoder to conditionally trigger add child based on where its dropped in --- src/bb-components-build.ts | 31 +++++++++++++++++++++----- src/validations/prefab.ts | 17 +++++++++++++-- src/validations/prefab/component.ts | 34 ++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/bb-components-build.ts b/src/bb-components-build.ts index 6e71e8ee..9cbbea0f 100644 --- a/src/bb-components-build.ts +++ b/src/bb-components-build.ts @@ -48,6 +48,7 @@ import { buildReferenceStyle, } from './components-build'; import { buildInteractions } from './components-build/v2/buildInteractions'; +import { reduce } from 'lodash'; const { mkdir, readFile } = promises; @@ -440,20 +441,38 @@ void (async (): Promise => { checkNameReferences(prefabs, finalComponents); - const componentStyleMap: ComponentStyleMap = components.reduce((acc, c) => { - return c.styleType - ? Object.assign(acc, { [c.name]: { styleType: c.styleType } }) - : acc; - }, {}); + const { + availableNames: availableComponentNames, + styleMap: componentStyleMap, + } = components.reduce( + ({ availableNames, styleMap }, c) => { + const newNames = availableNames.includes(c.name) + ? availableNames + : [...availableNames, c.name]; + + const newStyleMap = c.styleType + ? Object.assign(styleMap, { [c.name]: { styleType: c.styleType } }) + : styleMap; + + return { availableNames: newNames, styleMap: newStyleMap }; + }, + { availableNames: [] as string[], styleMap: {} }, + ); await Promise.all([ validateStyles(styles, componentNames), validateComponents(components, validStyleTypes), - validatePrefabs(prefabs, stylesGroupedByTypeAndName, componentStyleMap), + validatePrefabs( + prefabs, + stylesGroupedByTypeAndName, + componentStyleMap, + availableComponentNames, + ), validatePrefabs( allPartialPrefabs, stylesGroupedByTypeAndName, componentStyleMap, + availableComponentNames, 'partial', ), interactions && validateInteractions(interactions), diff --git a/src/validations/prefab.ts b/src/validations/prefab.ts index 2e356dde..03e553c0 100644 --- a/src/validations/prefab.ts +++ b/src/validations/prefab.ts @@ -20,6 +20,7 @@ export type PrefabTypes = 'partial' | 'page' | undefined; const schemaProvider = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], prefabType?: PrefabTypes, ): Joi.ObjectSchema => { return Joi.object({ @@ -43,7 +44,14 @@ const schemaProvider = ( beforeCreate: Joi.any(), structure: Joi.array() .items( - Joi.custom(validateComponent(styles, componentStyleMap, prefabType)), + Joi.custom( + validateComponent( + styles, + componentStyleMap, + availableComponentNames, + prefabType, + ), + ), ) .required(), reconfigure: Joi.any(), @@ -54,6 +62,7 @@ const validate = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], prefabType?: PrefabTypes, ) => (prefab: Prefab): void => { @@ -61,6 +70,7 @@ const validate = const { error } = schemaProvider( styles, componentStyleMap, + availableComponentNames, prefabType, ).validate(prefab); @@ -84,9 +94,12 @@ export default ( prefabs: Prefab[], styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], prefabType?: PrefabTypes, ): void => { - prefabs.forEach(validate(styles, componentStyleMap, prefabType)); + prefabs.forEach( + validate(styles, componentStyleMap, availableComponentNames, prefabType), + ); findDuplicates(prefabs, 'prefab', 'name'); }; diff --git a/src/validations/prefab/component.ts b/src/validations/prefab/component.ts index 81b34a53..bc720c96 100644 --- a/src/validations/prefab/component.ts +++ b/src/validations/prefab/component.ts @@ -111,6 +111,7 @@ const partialSchema = (): Joi.ObjectSchema => { const wrapperSchema = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], prefabType?: PrefabTypes, ): Joi.ObjectSchema => { return Joi.object({ @@ -122,7 +123,14 @@ const wrapperSchema = ( .required(), descendants: Joi.array() .items( - Joi.custom(validateComponent(styles, componentStyleMap, prefabType)), + Joi.custom( + validateComponent( + styles, + componentStyleMap, + availableComponentNames, + prefabType, + ), + ), ) .required(), }); @@ -213,9 +221,17 @@ const optionActionsObject = ({ onCreate: Joi.array().items(baseAction(onCreateActions)), }); -const optionTemplatesSchema = (transformationActions: TransformationActions) => +const optionTemplatesSchema = ( + transformationActions: TransformationActions, + availableComponentNames?: string[], +) => Joi.object({ addChild: Joi.object({ + condition: Joi.object({ + onlyShowWhenDroppedIn: Joi.string().valid( + ...(availableComponentNames || []), + ), + }), options: Joi.array().items(optionSchema).required(), optionActions: Joi.object().pattern( Joi.string(), @@ -227,6 +243,7 @@ const optionTemplatesSchema = (transformationActions: TransformationActions) => const componentSchema = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], styleType?: keyof StyleValidator, prefabType?: PrefabTypes, ): Joi.ObjectSchema => { @@ -267,11 +284,19 @@ const componentSchema = ( options: Joi.array().items(optionSchema).required(), optionTemplates: optionTemplatesSchema( definedinternalTransformationActions, + availableComponentNames, ), type: Joi.string().valid('COMPONENT').default('COMPONENT'), descendants: Joi.array() .items( - Joi.custom(validateComponent(styles, componentStyleMap, prefabType)), + Joi.custom( + validateComponent( + styles, + componentStyleMap, + availableComponentNames, + prefabType, + ), + ), ) .required(), reconfigure: Joi.any(), @@ -350,6 +375,7 @@ export const validateComponent = ( styles: GroupedStyles, componentStyleMap?: ComponentStyleMap, + availableComponentNames?: string[], prefabType?: PrefabTypes, ) => (component: PrefabReference): Prefab | unknown => { @@ -373,6 +399,7 @@ export const validateComponent = const { error } = wrapperSchema( styles, componentStyleMap, + availableComponentNames, prefabType, ).validate(component); const { optionCategories = [], options } = component; @@ -398,6 +425,7 @@ export const validateComponent = const { error } = componentSchema( styles, componentStyleMap, + availableComponentNames, styleType as keyof StyleValidator, prefabType, ).validate(component); From c6e4ae4731ac353343d87b56f8905baffad39b55 Mon Sep 17 00:00:00 2001 From: michel Date: Tue, 31 Dec 2024 10:42:38 +0100 Subject: [PATCH 3/4] fix: change optionActions schema to a more refined setup --- src/bb-components-build.ts | 21 +++++---- src/validations/prefab/component.ts | 70 ++++++++++------------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/bb-components-build.ts b/src/bb-components-build.ts index 9cbbea0f..ee40b94f 100644 --- a/src/bb-components-build.ts +++ b/src/bb-components-build.ts @@ -19,7 +19,6 @@ import { Component, Interaction, Prefab, - ComponentStyleMap, PrefabReference, GroupedStyles, BuildPrefabReference, @@ -48,7 +47,6 @@ import { buildReferenceStyle, } from './components-build'; import { buildInteractions } from './components-build/v2/buildInteractions'; -import { reduce } from 'lodash'; const { mkdir, readFile } = promises; @@ -444,19 +442,24 @@ void (async (): Promise => { const { availableNames: availableComponentNames, styleMap: componentStyleMap, - } = components.reduce( - ({ availableNames, styleMap }, c) => { - const newNames = availableNames.includes(c.name) + } = components.reduce<{ + availableNames: string[]; + styleMap: Record; + }>( + ({ availableNames, styleMap }, component) => { + const newNames = availableNames.includes(component.name) ? availableNames - : [...availableNames, c.name]; + : [...availableNames, component.name]; - const newStyleMap = c.styleType - ? Object.assign(styleMap, { [c.name]: { styleType: c.styleType } }) + const newStyleMap = component.styleType + ? Object.assign(styleMap, { + [component.name]: { styleType: component.styleType }, + }) : styleMap; return { availableNames: newNames, styleMap: newStyleMap }; }, - { availableNames: [] as string[], styleMap: {} }, + { availableNames: [], styleMap: {} }, ); await Promise.all([ diff --git a/src/validations/prefab/component.ts b/src/validations/prefab/component.ts index bc720c96..a7f5b168 100644 --- a/src/validations/prefab/component.ts +++ b/src/validations/prefab/component.ts @@ -23,11 +23,6 @@ import { linkedPartialSchema } from './linkedPartial'; type StyleValidator = Record; type PrefabTypes = 'partial' | 'page' | undefined; -type TransformationActions = { - onChangeActions: string[]; - onCreateActions: string[]; -}; - const shadows = [ 'none', '0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)', @@ -202,29 +197,29 @@ const validateComponentStyle = return prefabObject; }; -const baseAction = (allowedActions: string[]) => - Joi.object({ - action: Joi.string() - .valid(...allowedActions) - .messages({ - valid: `onChangeAction not of value: ${JSON.stringify(allowedActions)}`, - }), - target: Joi.string(), - }); +const allowedChangeActions = ['setVariable', 'setModel']; -const optionActionsObject = ({ - onChangeActions, - onCreateActions, -}: TransformationActions) => - Joi.object({ - onChange: Joi.array().items(baseAction(onChangeActions)), - onCreate: Joi.array().items(baseAction(onCreateActions)), - }); +const onChangeAction = Joi.object({ + action: Joi.string() + .valid(...allowedChangeActions) + .messages({ + valid: `onChangeAction not of value: ${JSON.stringify( + allowedChangeActions, + )}`, + }), + format: Joi.string().when('action', { + is: 'setVariable', + then: Joi.valid('propertyLabel', 'propertyValue', 'static'), + otherwise: Joi.forbidden(), + }), + target: Joi.string(), +}); -const optionTemplatesSchema = ( - transformationActions: TransformationActions, - availableComponentNames?: string[], -) => +const optionActionsObject = Joi.object({ + onChange: Joi.array().items(onChangeAction), +}); + +const optionTemplatesSchema = (availableComponentNames?: string[]) => Joi.object({ addChild: Joi.object({ condition: Joi.object({ @@ -233,10 +228,7 @@ const optionTemplatesSchema = ( ), }), options: Joi.array().items(optionSchema).required(), - optionActions: Joi.object().pattern( - Joi.string(), - optionActionsObject(transformationActions), - ), + optionActions: Joi.object().pattern(Joi.string(), optionActionsObject), }), }); @@ -258,19 +250,6 @@ const componentSchema = ( overwrite: overwriteSchema, }); - // internal actions list. - const definedinternalTransformationActions = { - onChangeActions: [ - 'setVariableOptionWithPropertyLabel', - 'setVariableOptionWithPropertyValue', - 'setVariableOptionWithInputVariableName', - ], - onCreateActions: [ - 'createAndSetActionInputVariableOption', - 'createAndSetPropertyOption', - ], - }; - const deprecatedStylesFlag = Object.keys(styles).length === 0; return Joi.object({ @@ -282,10 +261,7 @@ const componentSchema = ( }), optionCategories: Joi.array().items(optionCategorySchema).min(1), options: Joi.array().items(optionSchema).required(), - optionTemplates: optionTemplatesSchema( - definedinternalTransformationActions, - availableComponentNames, - ), + optionTemplates: optionTemplatesSchema(availableComponentNames), type: Joi.string().valid('COMPONENT').default('COMPONENT'), descendants: Joi.array() .items( From 737eebecd71dacdc2ed5edec205053fa14c52b0b Mon Sep 17 00:00:00 2001 From: michel Date: Tue, 31 Dec 2024 11:48:26 +0100 Subject: [PATCH 4/4] test: make tests pass --- __tests__/validations/component.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/__tests__/validations/component.test.tsx b/__tests__/validations/component.test.tsx index 2f24b9b3..3c99d1dc 100644 --- a/__tests__/validations/component.test.tsx +++ b/__tests__/validations/component.test.tsx @@ -817,7 +817,7 @@ test('Does not throw when valid partial Prefab', (t: Context): void => { }, ]; - validatePrefabs(prefabs, {}, {}, 'partial'); + validatePrefabs(prefabs, {}, {}, [], 'partial'); t.pass(); }); @@ -836,7 +836,7 @@ test('Throw when partialcomponent in partial Prefab', (t: Context): void => { }, ]; - t.throws(() => validatePrefabs(prefabs, {}, {}, 'partial')); + t.throws(() => validatePrefabs(prefabs, {}, {}, [], 'partial')); }); test('Throw when type key in partial Prefab', (t: Context): void => { @@ -862,7 +862,7 @@ test('Throw when type key in partial Prefab', (t: Context): void => { ], }, ]; - t.throws(() => validatePrefabs(prefabs, {}, {}, 'partial')); + t.throws(() => validatePrefabs(prefabs, {}, {}, [], 'partial')); }); test("throws an error when a reserved keyword is used 'PARTIAL'", (t: Context): void => {