diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap index 77af8228b..08e5e1c74 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap @@ -64,6 +64,7 @@ export default function CustomDataForm(props) { { diff --git a/packages/codegen-ui/lib/__tests__/__utils__/basic-form-definition.ts b/packages/codegen-ui/lib/__tests__/__utils__/basic-form-definition.ts index 8fac88c0c..a816e1fed 100644 --- a/packages/codegen-ui/lib/__tests__/__utils__/basic-form-definition.ts +++ b/packages/codegen-ui/lib/__tests__/__utils__/basic-form-definition.ts @@ -17,10 +17,14 @@ import { FormDefinition } from '../../types'; export const getBasicFormDefinition = (): FormDefinition => ({ form: { - layoutStyle: {}, + layoutStyle: { + horizontalGap: { value: '15px' }, + verticalGap: { value: '15px' }, + outerPadding: { value: '20px' }, + }, }, elements: {}, - elementMatrix: [[]], + elementMatrix: [], buttons: { buttonConfigs: {}, position: 'bottom', diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts index 140924f48..993bca503 100644 --- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts +++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts @@ -25,6 +25,7 @@ import { StudioGenericFieldConfig, ValidationTypes, } from '../../../types'; +import { getBasicFormDefinition } from '../../__utils__/basic-form-definition'; describe('mapFormFieldConfig', () => { it('should map fields', () => { @@ -44,13 +45,7 @@ describe('mapFormFieldConfig', () => { }; const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: [['price']], }; @@ -80,13 +75,7 @@ describe('mapFormFieldConfig', () => { }; const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: [['price']], }; @@ -485,7 +474,7 @@ describe('getFormDefinitionInputElement', () => { expect(getFormDefinitionInputElement(config)).toStrictEqual({ componentType: 'CheckboxField', - props: { label: 'Label', name: 'fieldName', value: 'true', defaultChecked: true }, + props: { label: 'Label', name: 'fieldName', value: 'fieldName', defaultChecked: true }, }); }); diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/map-elements.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/map-elements.test.ts index f4dc1d727..ec110144d 100644 --- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/map-elements.test.ts +++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/map-elements.test.ts @@ -15,6 +15,7 @@ */ import { mapElements } from '../../../generate-form-definition/helpers'; import { FormDefinition, SectionalElement, ModelFieldsConfigs, StudioForm } from '../../../types'; +import { getBasicFormDefinition } from '../../__utils__/basic-form-definition'; describe('mapElements', () => { it('should map sectional elements & input elements with and without overrides', () => { @@ -25,13 +26,7 @@ describe('mapElements', () => { }; const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: [['myText', 'name'], ['price']], }; @@ -77,13 +72,7 @@ describe('mapElements', () => { it('should throw if config for element not found', () => { const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: [['myText']], }; diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts index 0b12f9115..f2bca94c2 100644 --- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts +++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts @@ -16,19 +16,11 @@ import { mapModelFieldsConfigs, getFieldTypeMapKey } from '../../../generate-form-definition/helpers'; import { FormDefinition, GenericDataSchema } from '../../../types'; +import { getBasicFormDefinition } from '../../__utils__/basic-form-definition'; describe('mapModelFieldsConfigs', () => { it('should map to elementMatrix and add to modelFieldsConfigs', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', @@ -49,21 +41,12 @@ describe('mapModelFieldsConfigs', () => { expect(modelFieldsConfigs.name).toStrictEqual({ label: 'Name', dataType: 'String', - inputType: { type: 'TextField', required: false, readOnly: false, name: 'name', value: 'true' }, + inputType: { type: 'TextField', required: false, readOnly: false, name: 'name', value: 'name' }, }); }); it('should properly map different field names casings to sentence case', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', @@ -92,16 +75,7 @@ describe('mapModelFieldsConfigs', () => { }); it('should throw if specified model is not found', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', @@ -120,16 +94,7 @@ describe('mapModelFieldsConfigs', () => { }); it('should generate config from id field but not add it to matrix', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', @@ -155,7 +120,7 @@ describe('mapModelFieldsConfigs', () => { readOnly: false, required: true, type: 'TextField', - value: 'true', + value: 'id', }, label: 'Id', }, @@ -163,16 +128,7 @@ describe('mapModelFieldsConfigs', () => { }); it('should add read-only fields to configs but not to matrix', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', @@ -198,25 +154,56 @@ describe('mapModelFieldsConfigs', () => { readOnly: true, required: false, type: 'TextField', - value: 'true', + value: 'name', }, label: 'Name', }, }); }); - it('should add value mappings from enums', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], + it('should add relationship fields to configs but not to matrix', () => { + const formDefinition: FormDefinition = getBasicFormDefinition(); + + const dataSchema: GenericDataSchema = { + dataSourceType: 'DataStore', + enums: {}, + nonModels: {}, + models: { + Dog: { + fields: { + ownerId: { + dataType: 'ID', + readOnly: true, + required: false, + isArray: false, + relationship: { type: 'HAS_ONE', relatedModelName: 'Owner' }, + }, + }, + }, }, - elementMatrix: [], }; + const modelFieldsConfigs = mapModelFieldsConfigs({ dataTypeName: 'Dog', formDefinition, dataSchema }); + + expect(formDefinition.elementMatrix).toStrictEqual([]); + expect(modelFieldsConfigs).toStrictEqual({ + ownerId: { + dataType: 'ID', + inputType: { + name: 'ownerId', + readOnly: true, + required: false, + type: 'SelectField', + value: 'ownerId', + }, + label: 'Owner id', + }, + }); + }); + + it('should add value mappings from enums', () => { + const formDefinition: FormDefinition = getBasicFormDefinition(); + const nonEnglishAlphabetTest = 'ㅎ🌱يَّة'; const dataSchema: GenericDataSchema = { @@ -244,7 +231,7 @@ describe('mapModelFieldsConfigs', () => { readOnly: false, required: false, type: 'SelectField', - value: 'true', + value: 'city', valueMappings: { values: [ { value: { value: 'NEW_YORK' }, displayValue: { value: 'New york' } }, @@ -260,16 +247,7 @@ describe('mapModelFieldsConfigs', () => { }); it('should throw if type is enum but no matching enum provided', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const dataSchema: GenericDataSchema = { dataSourceType: 'DataStore', diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/position.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/position.test.ts index 6829ba063..eef73fde0 100644 --- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/position.test.ts +++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/position.test.ts @@ -15,6 +15,7 @@ */ import { findIndices, removeAndReturnItemOnward, removeFromMatrix } from '../../../generate-form-definition/helpers'; +import { getBasicFormDefinition } from '../../__utils__/basic-form-definition'; describe('findIndices', () => { it('should find the indices of a string in a two-dimensional array', () => { @@ -34,13 +35,7 @@ describe('removeFromMatrix', () => { const matrix = [['one', 'two', 'three', 'four'], ['five'], ['six', 'seven', 'eight']]; const formDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: matrix, }; @@ -55,13 +50,7 @@ describe('removeAndReturnItemOnward', () => { const matrix = [['one', 'two', 'three', 'four'], ['five'], ['six', 'seven', 'eight']]; const formDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, + ...getBasicFormDefinition(), elementMatrix: matrix, }; diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/sectional-element.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/sectional-element.test.ts index d7c90b9ec..bf4b773f1 100644 --- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/sectional-element.test.ts +++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/sectional-element.test.ts @@ -16,18 +16,13 @@ import { mapSectionalElement, getFormDefinitionSectionalElement } from '../../../generate-form-definition/helpers'; import { FormDefinition, SectionalElement } from '../../../types'; +import { getBasicFormDefinition } from '../../__utils__/basic-form-definition'; describe('mapSectionalElement', () => { it('should throw if there is already an element with the same name', () => { const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, + ...getBasicFormDefinition(), elements: { Heading123: { componentType: 'Heading', props: {} } }, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], }; const element: { name: string; config: SectionalElement } = { @@ -39,16 +34,7 @@ describe('mapSectionalElement', () => { }); it('should map configurations', () => { - const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, - elements: {}, - buttons: { - buttonConfigs: {}, - position: '', - buttonMatrix: [[]], - }, - elementMatrix: [], - }; + const formDefinition: FormDefinition = getBasicFormDefinition(); const element: { name: string; config: SectionalElement } = { name: 'Heading123', diff --git a/packages/codegen-ui/lib/__tests__/utils/form-component-metadata.test.ts b/packages/codegen-ui/lib/__tests__/utils/form-component-metadata.test.ts index f2e54fa5c..1d636bfe8 100644 --- a/packages/codegen-ui/lib/__tests__/utils/form-component-metadata.test.ts +++ b/packages/codegen-ui/lib/__tests__/utils/form-component-metadata.test.ts @@ -68,6 +68,7 @@ describe('mapFormMetaData', () => { }, }, style: {}, + cta: {}, }; const { fieldConfigs } = mapFormMetadata(form, formDefinition); diff --git a/packages/codegen-ui/lib/generate-form-definition/generate-form-definition.ts b/packages/codegen-ui/lib/generate-form-definition/generate-form-definition.ts index de45620cb..1ea15244e 100644 --- a/packages/codegen-ui/lib/generate-form-definition/generate-form-definition.ts +++ b/packages/codegen-ui/lib/generate-form-definition/generate-form-definition.ts @@ -33,7 +33,7 @@ export function generateFormDefinition({ dataSchema?: GenericDataSchema; }): FormDefinition { const formDefinition: FormDefinition = { - form: { layoutStyle: {} }, + form: { layoutStyle: mapStyles(form.style) }, elements: {}, buttons: { buttonConfigs: {}, @@ -78,8 +78,6 @@ export function generateFormDefinition({ mapElements({ form, formDefinition, modelFieldsConfigs }); - formDefinition.form.layoutStyle = mapStyles(form.style); - formDefinition.buttons = mapButtons(form.cta); return formDefinition; diff --git a/packages/codegen-ui/lib/generate-form-definition/helpers/defaults.ts b/packages/codegen-ui/lib/generate-form-definition/helpers/defaults.ts index 3f035db9d..ba1b4f312 100644 --- a/packages/codegen-ui/lib/generate-form-definition/helpers/defaults.ts +++ b/packages/codegen-ui/lib/generate-form-definition/helpers/defaults.ts @@ -31,7 +31,7 @@ export const FORM_DEFINITION_DEFAULTS = { inputType: { label: 'Label', defaultCountryCode: '+1', - value: 'true', + value: 'fieldName', name: 'fieldName', valueMappings: { values: [{ value: { value: 'Option' } }] }, }, diff --git a/packages/codegen-ui/lib/generate-form-definition/helpers/map-styles.ts b/packages/codegen-ui/lib/generate-form-definition/helpers/map-styles.ts index 83183a2a4..cf0b39af3 100644 --- a/packages/codegen-ui/lib/generate-form-definition/helpers/map-styles.ts +++ b/packages/codegen-ui/lib/generate-form-definition/helpers/map-styles.ts @@ -13,14 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { StudioFormStyle } from '../../types'; +import { StudioFormStyle, FormDefinition } from '../../types'; import { FORM_DEFINITION_DEFAULTS } from './defaults'; -function hasValue(config: { tokenReference?: string; value?: string } | undefined): boolean { +function hasValue( + config: { tokenReference?: string; value?: string } | undefined, +): config is { tokenReference: string } | { value: string } { return !!(config && (config.tokenReference || config.value)); } -export function mapStyles(styles: StudioFormStyle): StudioFormStyle { +export function mapStyles(styles: StudioFormStyle): FormDefinition['form']['layoutStyle'] { const defaults = FORM_DEFINITION_DEFAULTS.styles; return { horizontalGap: hasValue(styles.horizontalGap) ? styles.horizontalGap : defaults.horizontalGap, diff --git a/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts b/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts index 44338e845..1daa46d7a 100644 --- a/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts +++ b/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts @@ -68,7 +68,7 @@ export function getFieldConfigFromModelField({ required: field.required, readOnly: field.readOnly, name: fieldName, - value: 'true', + value: fieldName, }, }; @@ -110,7 +110,8 @@ export function mapModelFieldsConfigs({ } Object.entries(model.fields).forEach(([fieldName, field]) => { - const isAutoExcludedField = field.readOnly || (fieldName === 'id' && field.dataType === 'ID' && field.required); + const isAutoExcludedField = + field.readOnly || (fieldName === 'id' && field.dataType === 'ID' && field.required) || field.relationship; if (!isAutoExcludedField) { formDefinition.elementMatrix.push([fieldName]); diff --git a/packages/codegen-ui/lib/types/form/form-definition.ts b/packages/codegen-ui/lib/types/form/form-definition.ts index c7bd5b2ae..4113b0f28 100644 --- a/packages/codegen-ui/lib/types/form/form-definition.ts +++ b/packages/codegen-ui/lib/types/form/form-definition.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { StudioFormStyle } from './style'; +import { FormStyleConfig } from './style'; import { FormDefinitionElement, FormDefinitionButtonElement } from './form-definition-element'; import { StudioGenericFieldConfig } from './fields'; @@ -31,7 +31,7 @@ export type ButtonConfig = { export type FormDefinition = { form: { - layoutStyle: StudioFormStyle; + layoutStyle: { horizontalGap: FormStyleConfig; verticalGap: FormStyleConfig; outerPadding: FormStyleConfig }; }; elements: { [element: string]: FormDefinitionElement }; buttons: ButtonConfig; diff --git a/packages/codegen-ui/lib/types/form/style.ts b/packages/codegen-ui/lib/types/form/style.ts index 7b272fd38..97a17166a 100644 --- a/packages/codegen-ui/lib/types/form/style.ts +++ b/packages/codegen-ui/lib/types/form/style.ts @@ -21,13 +21,8 @@ export type FormStyleConfig = { value?: string; } & FormStyleConfigCommon; -export type FormAlignmentConfig = { - value?: 'left' | 'center' | 'right'; -} & FormStyleConfigCommon; - export type StudioFormStyle = { horizontalGap?: FormStyleConfig; verticalGap?: FormStyleConfig; outerPadding?: FormStyleConfig; - alignment?: FormAlignmentConfig; }; diff --git a/packages/codegen-ui/lib/utils/form-to-component/map-form-definition-to-component.ts b/packages/codegen-ui/lib/utils/form-to-component/map-form-definition-to-component.ts index 60d902bba..e735fd37e 100644 --- a/packages/codegen-ui/lib/utils/form-to-component/map-form-definition-to-component.ts +++ b/packages/codegen-ui/lib/utils/form-to-component/map-form-definition-to-component.ts @@ -24,14 +24,17 @@ import { } from '../../types'; import { mapElementChildren } from './helpers/map-element-children'; import { ctaButtonMapper, addCTAPosition } from './helpers/map-cta-buttons'; +import { InternalError } from '../../errors'; -const getStyleResolvedValue = (config?: FormStyleConfig): string | undefined => { - return config?.value ?? config?.tokenReference; +const getStyleResolvedValue = (config: FormStyleConfig): string => { + const value = config.value ?? config.tokenReference; + if (!value) { + throw new InternalError('Form layout style not found'); + } + return value; }; -const resolveStyles = ( - style: StudioFormStyle, -): Record, string | undefined> => { +const resolveStyles = (style: FormDefinition['form']['layoutStyle']): Record => { return { verticalGap: getStyleResolvedValue(style.verticalGap), horizontalGap: getStyleResolvedValue(style.horizontalGap), @@ -39,14 +42,19 @@ const resolveStyles = ( }; }; -const parentGrid = (name: string, style: StudioFormStyle, children: StudioComponentChild[]): StudioComponentChild => { - const { verticalGap, horizontalGap } = resolveStyles(style); +const parentGrid = ( + name: string, + style: FormDefinition['form']['layoutStyle'], + children: StudioComponentChild[], +): StudioComponentChild => { + const { verticalGap, horizontalGap, outerPadding } = resolveStyles(style); return { name, componentType: 'Grid', properties: { - ...(horizontalGap && { columnGap: { value: horizontalGap } }), - ...(verticalGap && { rowGap: { value: verticalGap } }), + columnGap: { value: horizontalGap }, + rowGap: { value: verticalGap }, + padding: { value: outerPadding }, }, children, };