diff --git a/packages/teleport-plugin-common/src/node-handlers/node-to-jsx/index.ts b/packages/teleport-plugin-common/src/node-handlers/node-to-jsx/index.ts index a1185bb6f..827a7adc0 100644 --- a/packages/teleport-plugin-common/src/node-handlers/node-to-jsx/index.ts +++ b/packages/teleport-plugin-common/src/node-handlers/node-to-jsx/index.ts @@ -33,6 +33,7 @@ import { generateDynamicWindowImport, addDynamicExpressionAttributeToJSXTag, resolveObjectValue, + objectToObjectExpression, } from '../../utils/ast-utils' import { createJSXTag, createSelfClosingJSXTag } from '../../builders/ast-builders' import { DEFAULT_JSX_OPTIONS } from './constants' @@ -167,6 +168,19 @@ const addAttributesToJSXTag = ( ) break + + case 'object': { + const content = attributeValue.content + if (typeof content !== 'object') { + return + } + const expression = objectToObjectExpression(content as Record) + elementTag.openingElement.attributes.push( + types.jsxAttribute(types.jsxIdentifier(attrKey), types.jsxExpressionContainer(expression)) + ) + break + } + default: throw new Error( `generateElementNode could not generate code for attribute of type ${JSON.stringify( diff --git a/packages/teleport-plugin-html-base-component/src/node-handlers.ts b/packages/teleport-plugin-html-base-component/src/node-handlers.ts index dd7fc0e72..d291659e8 100644 --- a/packages/teleport-plugin-html-base-component/src/node-handlers.ts +++ b/packages/teleport-plugin-html-base-component/src/node-handlers.ts @@ -339,10 +339,15 @@ const generateComponentContent = async ( const combinedStates = { ...stateDefinitions, ...(componentClone?.stateDefinitions || {}) } const statesForInstance = Object.keys(combinedStates).reduce( (acc: Record, propKey) => { - if (attrs[propKey]) { + const attr = attrs[propKey] + if (attr.type === 'object') { + throw new Error(`Object attributes are not supported in html exports`) + } + + if (attr) { acc[propKey] = { ...combinedStates[propKey], - defaultValue: attrs[propKey]?.content || combinedStates[propKey]?.defaultValue, + defaultValue: attr?.content || combinedStates[propKey]?.defaultValue, } } else { acc[propKey] = combinedStates[propKey] @@ -359,6 +364,10 @@ const generateComponentContent = async ( // the component instance that we are using here. for (const propKey of Object.keys(combinedProps)) { const prop = attrs[propKey] + if (prop?.type === 'object') { + throw new Error(`Object attributes are not supported in html exports`) + } + if (prop?.type === 'element') { propsForInstance[propKey] = { ...combinedProps[propKey], @@ -394,7 +403,7 @@ const generateComponentContent = async ( } else if (prop) { propsForInstance[propKey] = { ...combinedProps[propKey], - defaultValue: attrs[propKey]?.content || combinedProps[propKey]?.defaultValue, + defaultValue: prop?.content || combinedProps[propKey]?.defaultValue, } } else { const propFromCurrentComponent = combinedProps[propKey] diff --git a/packages/teleport-shared/src/utils/uidl-utils.ts b/packages/teleport-shared/src/utils/uidl-utils.ts index abc0fdd36..a17592310 100644 --- a/packages/teleport-shared/src/utils/uidl-utils.ts +++ b/packages/teleport-shared/src/utils/uidl-utils.ts @@ -205,7 +205,11 @@ export const traverseNodes = ( const { attrs, children, style, abilities, referencedStyles } = node.content if (attrs) { Object.keys(attrs).forEach((attrKey) => { - traverseNodes(attrs[attrKey], fn, node) + const attr = attrs[attrKey] + if (attr.type === 'object') { + return + } + traverseNodes(attr, fn, node) }) } @@ -902,6 +906,7 @@ export const transformAttributesAssignmentsToJson = ( return acc } + case 'object': case 'element': acc[key] = attributeContent as UIDLAttributeValue return acc diff --git a/packages/teleport-types/src/uidl.ts b/packages/teleport-types/src/uidl.ts index 95b82a7e8..56ff2e9ff 100755 --- a/packages/teleport-types/src/uidl.ts +++ b/packages/teleport-types/src/uidl.ts @@ -349,6 +349,11 @@ export interface UIDLCMSItemNode { content: UIDLCMSItemNodeContent } +export interface UIDLObjectValue { + type: 'object' + content: unknown +} + export interface UIDLCMSMixedTypeNode { type: 'cms-mixed-type' content: { @@ -547,6 +552,7 @@ export type UIDLAttributeValue = | UIDLComponentStyleReference | UIDLRawValue | UIDLElementNode + | UIDLObjectValue export type UIDLStyleValue = UIDLDynamicReference | UIDLStaticValue diff --git a/packages/teleport-uidl-resolver/src/utils.ts b/packages/teleport-uidl-resolver/src/utils.ts index de2845160..406c047e9 100644 --- a/packages/teleport-uidl-resolver/src/utils.ts +++ b/packages/teleport-uidl-resolver/src/utils.ts @@ -277,6 +277,9 @@ const resolveRepeat = (repeatContent: UIDLRepeatContent, parentNode: UIDLNode) = if (dataSourceValue.type === 'element') { throw new Error(`Dynamic data source for repeat cannot be an element`) } + if (dataSourceValue.type === 'object') { + throw new Error(`Data source for repeat cannot be an object`) + } repeatContent.dataSource = dataSourceValue // remove original attribute so it is not added as a static/dynamic value on the node delete parentElement.attrs[nodeDataSourceAttr] diff --git a/packages/teleport-uidl-validator/src/decoders/utils.ts b/packages/teleport-uidl-validator/src/decoders/utils.ts index 7275dee55..c866726dd 100644 --- a/packages/teleport-uidl-validator/src/decoders/utils.ts +++ b/packages/teleport-uidl-validator/src/decoders/utils.ts @@ -14,6 +14,7 @@ import { intersection, withDefault, anyJson, + unknownJson, } from '@mojotech/json-type-validation' import { UIDLStaticValue, @@ -93,6 +94,7 @@ import { VUIDLCMSMixedTypeNode, UIDLLocalFontAsset, VUIDLPropDefinitions, + UIDLObjectValue, } from '@teleporthq/teleport-types' import { isValidElementName, @@ -507,7 +509,8 @@ export const attributeValueDecoder: Decoder = union( importReferenceDecoder, rawValueDecoder, lazy(() => uidlComponentStyleReference), - lazy(() => elementNodeDecoder) + lazy(() => elementNodeDecoder), + lazy(() => objectValueDecoder) ) export const uidlComponentStyleReference: Decoder = object({ @@ -683,6 +686,11 @@ export const designTokensDecoder: Decoder = dict( union(staticValueDecoder, string(), number()) ) +export const objectValueDecoder: Decoder = object({ + type: constant('object'), + content: unknownJson(), +}) + export const elementDecoder: Decoder = object({ elementType: string(), semanticType: optional(string()),