@teleporthq/teleport-component-generator-react 0.44.1-alpha.0 → 0.44.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.
@@ -26,7 +26,7 @@ describe('React Component Generator', () => {
26
26
  expect(result.files).toBeDefined()
27
27
  expect(Array.isArray(result.files)).toBeTruthy()
28
28
  expect(result.files.length).toBeTruthy()
29
- expect(jsFile.content).toContain('import React')
29
+ expect(jsFile?.content).toContain('import React')
30
30
  expect(result.dependencies).toBeDefined()
31
31
  })
32
32
  })
@@ -42,7 +42,7 @@ describe('React Component Generator', () => {
42
42
 
43
43
  expect(jsFile).toBeDefined()
44
44
  expect(result.files.length).toBe(1)
45
- expect(jsFile.content).toContain('import React')
45
+ expect(jsFile?.content).toContain('import React')
46
46
  expect(result.dependencies).toBeDefined()
47
47
  })
48
48
  })
@@ -58,7 +58,7 @@ describe('React Component Generator', () => {
58
58
 
59
59
  expect(jsFile).toBeDefined()
60
60
  expect(result.files.length).toBe(1)
61
- expect(jsFile.content).toContain('import React')
61
+ expect(jsFile?.content).toContain('import React')
62
62
  expect(result.dependencies).toBeDefined()
63
63
  })
64
64
  })
@@ -30,7 +30,7 @@ describe('Generates media, pseudo and normal styles', () => {
30
30
  }
31
31
  const uidl = component(
32
32
  'MyComponent',
33
- elementNode('container', null, [staticNode('Hello !!')], null, style, null, referencedStyles)
33
+ elementNode('container', {}, [staticNode('Hello !!')], null, style, null, referencedStyles)
34
34
  )
35
35
 
36
36
  it('Generates styles using CSS Modules', async () => {
@@ -40,12 +40,12 @@ describe('Generates media, pseudo and normal styles', () => {
40
40
  const jsFile = findFileByType(files, FileType.JS)
41
41
 
42
42
  expect(cssFile).toBeDefined()
43
- expect(cssFile.content).toContain('@media(max-width: 991px)')
44
- expect(cssFile.content).toContain('width: 100px')
43
+ expect(cssFile?.content).toContain('@media(max-width: 991px)')
44
+ expect(cssFile?.content).toContain('width: 100px')
45
45
 
46
46
  expect(jsFile).toBeDefined()
47
- expect(jsFile.content).toContain(`className={styles['container']}`)
48
- expect(jsFile.content).toContain(`import styles from './my-component.module.css`)
47
+ expect(jsFile?.content).toContain(`className={styles['my-component-container']}`)
48
+ expect(jsFile?.content).toContain(`import styles from './my-component.module.css`)
49
49
  })
50
50
 
51
51
  it('Generates styles using CSS', async () => {
@@ -57,12 +57,12 @@ describe('Generates media, pseudo and normal styles', () => {
57
57
  const jsFile = findFileByType(files, FileType.JS)
58
58
 
59
59
  expect(cssFile).toBeDefined()
60
- expect(cssFile.content).toContain('@media(max-width: 991px)')
61
- expect(cssFile.content).toContain('width: 100px')
60
+ expect(cssFile?.content).toContain('@media(max-width: 991px)')
61
+ expect(cssFile?.content).toContain('width: 100px')
62
62
 
63
63
  expect(jsFile).toBeDefined()
64
- expect(jsFile.content).toContain(`className="my-component-container"`)
65
- expect(jsFile.content).toContain(`import './my-component.css`)
64
+ expect(jsFile?.content).toContain(`className="my-component-container"`)
65
+ expect(jsFile?.content).toContain(`import './my-component.css`)
66
66
  })
67
67
 
68
68
  it('Generates styles using Styled-Components', async () => {
@@ -76,10 +76,10 @@ describe('Generates media, pseudo and normal styles', () => {
76
76
 
77
77
  expect(cssFile).not.toBeDefined()
78
78
  expect(jsFile).toBeDefined()
79
- expect(jsFile.content).toContain(`<Container>Hello !!</Container>`)
80
- expect(jsFile.content).toContain(`const Container = styled('div')`)
81
- expect(jsFile.content).toContain(`width: '100px'`)
82
- expect(jsFile.content).toContain(`display: 'none'`)
79
+ expect(jsFile?.content).toContain(`<MyComponentContainer>Hello !!</MyComponentContainer>`)
80
+ expect(jsFile?.content).toContain(`const MyComponentContainer = styled('div')`)
81
+ expect(jsFile?.content).toContain(`width: '100px'`)
82
+ expect(jsFile?.content).toContain(`display: 'none'`)
83
83
  })
84
84
 
85
85
  it('Generates styles using ReactJSS', async () => {
@@ -93,11 +93,11 @@ describe('Generates media, pseudo and normal styles', () => {
93
93
  expect(cssFile).not.toBeDefined()
94
94
  expect(jsFile).toBeDefined()
95
95
 
96
- expect(jsFile.content).toContain(`const classes = useStyles()`)
97
- expect(jsFile.content).toContain(`className={classes['container']}`)
98
- expect(jsFile.content).toContain(`container: {`)
99
- expect(jsFile.content).toContain(`width: '100px'`)
100
- expect(jsFile.content).toContain(`display: 'none'`)
96
+ expect(jsFile?.content).toContain(`const classes = useStyles()`)
97
+ expect(jsFile?.content).toContain(`className={classes['myComponentContainer']}`)
98
+ expect(jsFile?.content).toContain(`myComponentContainer: {`)
99
+ expect(jsFile?.content).toContain(`width: '100px'`)
100
+ expect(jsFile?.content).toContain(`display: 'none'`)
101
101
  })
102
102
 
103
103
  it('Generates styles using StyledJSX', async () => {
@@ -110,10 +110,10 @@ describe('Generates media, pseudo and normal styles', () => {
110
110
 
111
111
  expect(cssFile).not.toBeDefined()
112
112
  expect(jsFile).toBeDefined()
113
- expect(jsFile.content).toContain(`className`)
114
- expect(jsFile.content).toContain(`container {`)
115
- expect(jsFile.content).toContain(`width: 100px`)
116
- expect(jsFile.content).toContain(`display: none`)
113
+ expect(jsFile?.content).toContain(`className`)
114
+ expect(jsFile?.content).toContain(`container {`)
115
+ expect(jsFile?.content).toContain(`width: 100px`)
116
+ expect(jsFile?.content).toContain(`display: none`)
117
117
  })
118
118
  })
119
119
 
@@ -143,12 +143,12 @@ describe('Add referenced styles even when direct styles are not present on node'
143
143
  const jsFile = findFileByType(files, FileType.JS)
144
144
 
145
145
  expect(cssFile).toBeDefined()
146
- expect(cssFile.content).toContain('@media(max-width: 991px)')
147
- expect(cssFile.content).not.toContain('width: 100px')
146
+ expect(cssFile?.content).toContain('@media(max-width: 991px)')
147
+ expect(cssFile?.content).not.toContain('width: 100px')
148
148
 
149
149
  expect(jsFile).toBeDefined()
150
- expect(jsFile.content).toContain(`className={styles['container']}`)
151
- expect(jsFile.content).toContain(`import styles from './my-component.module.css`)
150
+ expect(jsFile?.content).toContain(`className={styles['my-component-container']}`)
151
+ expect(jsFile?.content).toContain(`import styles from './my-component.module.css`)
152
152
  })
153
153
 
154
154
  it('Generates styles using CSS', async () => {
@@ -160,12 +160,12 @@ describe('Add referenced styles even when direct styles are not present on node'
160
160
  const jsFile = findFileByType(files, FileType.JS)
161
161
 
162
162
  expect(cssFile).toBeDefined()
163
- expect(cssFile.content).toContain('@media(max-width: 991px)')
164
- expect(cssFile.content).not.toContain('width: 100px')
163
+ expect(cssFile?.content).toContain('@media(max-width: 991px)')
164
+ expect(cssFile?.content).not.toContain('width: 100px')
165
165
 
166
166
  expect(jsFile).toBeDefined()
167
- expect(jsFile.content).toContain(`className="my-component-container"`)
168
- expect(jsFile.content).toContain(`import './my-component.css`)
167
+ expect(jsFile?.content).toContain(`className="my-component-container"`)
168
+ expect(jsFile?.content).toContain(`import './my-component.css`)
169
169
  })
170
170
 
171
171
  it('Generates styles using Styled-Components', async () => {
@@ -178,10 +178,10 @@ describe('Add referenced styles even when direct styles are not present on node'
178
178
 
179
179
  expect(cssFile).not.toBeDefined()
180
180
  expect(jsFile).toBeDefined()
181
- expect(jsFile.content).toContain(`<Container>Hello !!</Container>`)
182
- expect(jsFile.content).toContain(`const Container = styled('div')`)
183
- expect(jsFile.content).not.toContain(`width: '100px'`)
184
- expect(jsFile.content).toContain(`display: 'none'`)
181
+ expect(jsFile?.content).toContain(`<MyComponentContainer>Hello !!</MyComponentContainer>`)
182
+ expect(jsFile?.content).toContain(`const MyComponentContainer = styled('div')`)
183
+ expect(jsFile?.content).not.toContain(`width: '100px'`)
184
+ expect(jsFile?.content).toContain(`display: 'none'`)
185
185
  })
186
186
 
187
187
  it('Generates styles using ReactJSS', async () => {
@@ -195,10 +195,10 @@ describe('Add referenced styles even when direct styles are not present on node'
195
195
 
196
196
  expect(cssFile).not.toBeDefined()
197
197
  expect(jsFile).toBeDefined()
198
- expect(jsFile.content).toContain(`className={classes['container']}`)
199
- expect(jsFile.content).toContain(`container: {`)
200
- expect(jsFile.content).not.toContain(`width: '100px'`)
201
- expect(jsFile.content).toContain(`display: 'none'`)
198
+ expect(jsFile?.content).toContain(`className={classes['myComponentContainer']}`)
199
+ expect(jsFile?.content).toContain(`myComponentContainer: {`)
200
+ expect(jsFile?.content).not.toContain(`width: '100px'`)
201
+ expect(jsFile?.content).toContain(`display: 'none'`)
202
202
  })
203
203
 
204
204
  it('Generates styles using StyledJSX', async () => {
@@ -211,14 +211,18 @@ describe('Add referenced styles even when direct styles are not present on node'
211
211
 
212
212
  expect(cssFile).not.toBeDefined()
213
213
  expect(jsFile).toBeDefined()
214
- expect(jsFile.content).toContain(`className`)
215
- expect(jsFile.content).toContain(`container {`)
216
- expect(jsFile.content).not.toContain(`width: '100px'`)
217
- expect(jsFile.content).toContain(`display: none`)
214
+ expect(jsFile?.content).toContain(`className`)
215
+ expect(jsFile?.content).toContain(`container {`)
216
+ expect(jsFile?.content).not.toContain(`width: '100px'`)
217
+ expect(jsFile?.content).toContain(`display: none`)
218
218
  })
219
219
  })
220
220
 
221
- describe('Throws Error when a node is using project-styles but not present in UIDL', () => {
221
+ describe('Silently drops project-styles whose referenceId is missing from the UIDL', () => {
222
+ // Tolerant fallback for agent-generated UIDLs: when a node references a
223
+ // project style that wasn't defined in `projectStyleSet.styleSetDefinitions`,
224
+ // the plugin pipeline drops the reference and emits the component without
225
+ // that style rather than aborting the whole generation.
222
226
  const styles: UIDLReferencedStyles = {
223
227
  '123456789': {
224
228
  type: 'style-map',
@@ -235,14 +239,20 @@ describe('Throws Error when a node is using project-styles but not present in UI
235
239
 
236
240
  it('CSS Modules', async () => {
237
241
  const generator = createReactComponentGenerator()
238
- await expect(generator.generateComponent(uidl)).rejects.toThrow(Error)
242
+ const { files } = await generator.generateComponent(uidl)
243
+ const jsFile = findFileByType(files, FileType.JS)
244
+ expect(jsFile).toBeDefined()
245
+ expect(jsFile?.content).not.toContain('987654321')
239
246
  })
240
247
 
241
248
  it('CSS', async () => {
242
249
  const generator = createReactComponentGenerator({
243
250
  variation: ReactStyleVariation.CSS,
244
251
  })
245
- await expect(generator.generateComponent(uidl)).rejects.toThrow(Error)
252
+ const { files } = await generator.generateComponent(uidl)
253
+ const jsFile = findFileByType(files, FileType.JS)
254
+ expect(jsFile).toBeDefined()
255
+ expect(jsFile?.content).not.toContain('987654321')
246
256
  })
247
257
 
248
258
  it('Styled Components', async () => {
@@ -310,9 +320,9 @@ describe('Referes from project style and adds it to the node, without any styles
310
320
 
311
321
  const { files } = await generator.generateComponent(uidl, cssOptions)
312
322
  const jsFile = findFileByType(files, FileType.JS)
313
- expect(jsFile.content).toContain(`className={projectStyles['primary-button']}`)
314
- expect(jsFile.content).toContain(`import projectStyles from '../style.module.css'`)
315
- expect(jsFile.content).not.toContain(`import styles from './my-component.module.css'`)
323
+ expect(jsFile?.content).toContain(`className={projectStyles['primary-button']}`)
324
+ expect(jsFile?.content).toContain(`import projectStyles from '../style.module.css'`)
325
+ expect(jsFile?.content).not.toContain(`import styles from './my-component.module.css'`)
316
326
  })
317
327
 
318
328
  it('CSS', async () => {
@@ -329,9 +339,9 @@ describe('Referes from project style and adds it to the node, without any styles
329
339
  const { files } = await generator.generateComponent(uidl, cssOptions)
330
340
  const jsFile = findFileByType(files, FileType.JS)
331
341
 
332
- expect(jsFile.content).toContain('className="primaryButton"')
333
- expect(jsFile.content).not.toContain(`import '../style.css'`)
334
- expect(jsFile.content).not.toContain(`import './my-component.css'`)
342
+ expect(jsFile?.content).toContain('className="primaryButton"')
343
+ expect(jsFile?.content).not.toContain(`import '../style.css'`)
344
+ expect(jsFile?.content).not.toContain(`import './my-component.css'`)
335
345
  })
336
346
 
337
347
  it('StyledComponents', async () => {
@@ -342,9 +352,11 @@ describe('Referes from project style and adds it to the node, without any styles
342
352
  const { files } = await generator.generateComponent(uidl, options)
343
353
  const jsFile = findFileByType(files, FileType.JS)
344
354
 
345
- expect(jsFile.content).toContain(`<Container projVariant="primaryButton">Hello</Container>`)
346
- expect(jsFile.content).toContain(`import { projectStyleVariants } from '../style'`)
347
- expect(jsFile.content).toContain(`const Container = styled('div')(projectStyleVariants)`)
355
+ expect(jsFile?.content).toContain(`<MyComponentContainer projVariant=\"primaryButton\">`)
356
+ expect(jsFile?.content).toContain(`import { projectStyleVariants } from '../style'`)
357
+ expect(jsFile?.content).toContain(
358
+ `const MyComponentContainer = styled('div')(projectStyleVariants)`
359
+ )
348
360
  })
349
361
 
350
362
  it('Styled JSX', async () => {
@@ -355,7 +367,7 @@ describe('Referes from project style and adds it to the node, without any styles
355
367
  const { files } = await generator.generateComponent(uidl, options)
356
368
  const jsFile = findFileByType(files, FileType.JS)
357
369
  // Styled JSX is used only with NextJS, for NextJS we don't need to import anything
358
- expect(jsFile.content).toContain('<div className="primaryButton">')
370
+ expect(jsFile?.content).toContain('<div className="primaryButton">')
359
371
  })
360
372
 
361
373
  it('React JSS', async () => {
@@ -366,8 +378,8 @@ describe('Referes from project style and adds it to the node, without any styles
366
378
  const { files } = await generator.generateComponent(uidl, options)
367
379
  const jsFile = findFileByType(files, FileType.JS)
368
380
 
369
- expect(jsFile.content).toContain(`div className={projectStyles['primaryButton']}>`)
370
- expect(jsFile.content).toContain(`const projectStyles = useProjectStyles()`)
371
- expect(jsFile.content).toContain(`import { useProjectStyles } from '../style'`)
381
+ expect(jsFile?.content).toContain(`div className={projectStyles['primaryButton']}>`)
382
+ expect(jsFile?.content).toContain(`const projectStyles = useProjectStyles()`)
383
+ expect(jsFile?.content).toContain(`import { useProjectStyles } from '../style'`)
372
384
  })
373
385
  })
@@ -0,0 +1,105 @@
1
+ /**
2
+ * A UIDL state name is DATA — it mirrors a database column, and a WoW character
3
+ * sheet has a column called `class`. The generator turned that straight into a
4
+ * JS binding:
5
+ *
6
+ * const [class, setClass] = useState("")
7
+ *
8
+ * which is a SyntaxError, so the prettier post-processor threw and `packProject`
9
+ * aborted — every page of the project failed to generate, not just this one.
10
+ *
11
+ * These tests exercise the reserved name in every position a state can appear
12
+ * (declaration, text binding, attribute binding, conditional, event setter).
13
+ * `createReactComponentGenerator()` runs the prettier post-processor, so a
14
+ * generation that RESOLVES is itself proof that the emitted module parses —
15
+ * a missed emission site would reject here.
16
+ */
17
+
18
+ import { createReactComponentGenerator } from '../../src'
19
+ import { GeneratedFile } from '@teleporthq/teleport-types'
20
+ import {
21
+ component,
22
+ definition,
23
+ staticNode,
24
+ dynamicNode,
25
+ elementNode,
26
+ conditionalNode,
27
+ } from '@teleporthq/teleport-uidl-builders'
28
+
29
+ const generator = createReactComponentGenerator()
30
+
31
+ const JS_FILE = 'js'
32
+ const findFileByType = (files: GeneratedFile[], type: string = JS_FILE) =>
33
+ files.find((file) => file.fileType === type)
34
+
35
+ const reservedStateUidl = component(
36
+ 'Character Sheet',
37
+ elementNode('container', {}, [
38
+ // Text binding.
39
+ elementNode('text', {}, [dynamicNode('state', 'class')]),
40
+ // Attribute binding.
41
+ elementNode('input', { value: dynamicNode('state', 'class') }, []),
42
+ // Conditional.
43
+ conditionalNode(
44
+ dynamicNode('state', 'class'),
45
+ elementNode('text', {}, [staticNode('Class chosen')]),
46
+ true
47
+ ),
48
+ // Event setter.
49
+ elementNode('button', {}, [staticNode('Reset')], null, null, {
50
+ click: [{ type: 'stateChange', modifies: 'class', newState: '' }],
51
+ }),
52
+ // A neighbouring ordinary state must be untouched by the fix.
53
+ elementNode('text', {}, [dynamicNode('state', 'realm')]),
54
+ ]),
55
+ {},
56
+ {
57
+ class: definition('string', ''),
58
+ realm: definition('string', ''),
59
+ }
60
+ )
61
+
62
+ describe('Component with a RESERVED-WORD state name', () => {
63
+ it('generates a module that parses (prettier would reject `const [class, …]`)', async () => {
64
+ const result = await generator.generateComponent(reservedStateUidl)
65
+ expect(findFileByType(result.files, JS_FILE)).toBeDefined()
66
+ })
67
+
68
+ it('declares the state under a bindable identifier', async () => {
69
+ const { content } = findFileByType(
70
+ (await generator.generateComponent(reservedStateUidl)).files,
71
+ JS_FILE
72
+ )
73
+ expect(content).toContain('const [class_, setClass] = useState')
74
+ expect(content).not.toContain('const [class,')
75
+ })
76
+
77
+ it('reads the state through the SAME identifier it declared', async () => {
78
+ const { content } = findFileByType(
79
+ (await generator.generateComponent(reservedStateUidl)).files,
80
+ JS_FILE
81
+ )
82
+ // Text + attribute + conditional all bind the sanitised name.
83
+ // (`value` is emitted as React's `defaultValue` by the attribute mapper.)
84
+ expect(content).toContain('{class_}')
85
+ expect(content).toContain('defaultValue={class_}')
86
+ expect(content).toContain('{class_ && ')
87
+ })
88
+
89
+ it('calls the setter for the state-change event', async () => {
90
+ const { content } = findFileByType(
91
+ (await generator.generateComponent(reservedStateUidl)).files,
92
+ JS_FILE
93
+ )
94
+ expect(content).toContain('setClass(')
95
+ })
96
+
97
+ it('leaves an ordinary neighbouring state byte-identical', async () => {
98
+ const { content } = findFileByType(
99
+ (await generator.generateComponent(reservedStateUidl)).files,
100
+ JS_FILE
101
+ )
102
+ expect(content).toContain('const [realm, setRealm] = useState')
103
+ expect(content).toContain('{realm}')
104
+ })
105
+ })
@@ -49,8 +49,8 @@ describe('React Styles in Component', () => {
49
49
  const jsFile = findFileByType(result.files, FileType.JS)
50
50
 
51
51
  expect(jsFile).toBeDefined()
52
- expect(jsFile.content).toContain('display: active')
53
- expect(jsFile.content).toContain('height: props.config.height')
52
+ expect(jsFile?.content).toContain('display: active')
53
+ expect(jsFile?.content).toContain('height: props.config.height')
54
54
  })
55
55
 
56
56
  it('CSSModules should refer state in styles when state is mapped', async () => {
@@ -63,9 +63,9 @@ describe('React Styles in Component', () => {
63
63
 
64
64
  expect(jsFile).toBeDefined()
65
65
  expect(cssFile).toBeDefined()
66
- expect(jsFile.content).toContain('display: active')
67
- expect(jsFile.content).toContain('height: props.config.height')
68
- expect(cssFile.content).toContain('align-self: center;')
66
+ expect(jsFile?.content).toContain('display: active')
67
+ expect(jsFile?.content).toContain('height: props.config.height')
68
+ expect(cssFile?.content).toContain('align-self: center;')
69
69
  })
70
70
 
71
71
  it('Basic CSS should refer state in styles when state is mapped', async () => {
@@ -78,9 +78,9 @@ describe('React Styles in Component', () => {
78
78
 
79
79
  expect(jsFile).toBeDefined()
80
80
  expect(cssFile).toBeDefined()
81
- expect(jsFile.content).toContain('display: active')
82
- expect(jsFile.content).toContain('height: props.config.height')
83
- expect(cssFile.content).toContain('align-self: center;')
81
+ expect(jsFile?.content).toContain('display: active')
82
+ expect(jsFile?.content).toContain('height: props.config.height')
83
+ expect(cssFile?.content).toContain('align-self: center;')
84
84
  })
85
85
 
86
86
  it('JSS should through error when state is refered', async () => {
@@ -106,8 +106,8 @@ describe('React Styles in Component', () => {
106
106
  const jsFile = findFileByType(result.files, FileType.JS)
107
107
 
108
108
  expect(jsFile).toBeDefined()
109
- expect(jsFile.content).toContain('props.direction')
110
- expect(jsFile.content).toContain(`alignSelf: 'center'`)
109
+ expect(jsFile?.content).toContain('props.direction')
110
+ expect(jsFile?.content).toContain(`alignSelf: 'center'`)
111
111
  })
112
112
 
113
113
  it('should support object props in styledjsx', async () => {
@@ -118,21 +118,19 @@ describe('React Styles in Component', () => {
118
118
  const jsFile = findFileByType(result.files, FileType.JS)
119
119
 
120
120
  expect(jsFile).toBeDefined()
121
- expect(jsFile.content).toContain(`align-self: center`)
121
+ expect(jsFile?.content).toContain(`align-self: center`)
122
122
  })
123
123
 
124
- it('should throw error when a state is being refered in generated StyledJSX ', async () => {
124
+ it('should resolve a state reference in StyledJSX to an inline style', async () => {
125
125
  const styledJSXGenerator = createReactComponentGenerator({
126
126
  variation: ReactStyleVariation.StyledJSX,
127
127
  })
128
- try {
129
- await styledJSXGenerator.generateComponent(ComponentWithInvalidStateStyles)
130
- expect(true).toBe(false)
131
- } catch (e) {
132
- expect(e.message).toContain(
133
- 'Error running transformDynamicStyles in reactStyledJSXChunkPlugin'
134
- )
135
- }
128
+ const result = await styledJSXGenerator.generateComponent(ComponentWithInvalidStateStyles)
129
+ const jsFile = findFileByType(result.files, FileType.JS)
130
+
131
+ expect(jsFile).toBeDefined()
132
+ expect(jsFile?.content).toContain('backgroundColor: active')
133
+ expect(jsFile?.content).toContain(`height: \${props.config.height}`)
136
134
  })
137
135
 
138
136
  it('should explicitly send prop if style is using one prop variable', async () => {
@@ -145,8 +143,8 @@ describe('React Styles in Component', () => {
145
143
  const jsFile = findFileByType(result.files, FileType.JS)
146
144
 
147
145
  expect(jsFile).toBeDefined()
148
- expect(jsFile.content).toContain('<Container {...props}')
149
- expect(jsFile.content).toContain('height: props.config.height')
146
+ expect(jsFile?.content).toContain('<ComponentWithSinglePropContainer')
147
+ expect(jsFile?.content).toContain('height: props.config.height')
150
148
  })
151
149
 
152
150
  it('should inject props only once for styled components', async () => {
@@ -156,21 +154,21 @@ describe('React Styles in Component', () => {
156
154
  const result = await styledJSXGenerator.generateComponent(ComponentWithValidStyle)
157
155
 
158
156
  const jsFile = findFileByType(result.files, FileType.JS)
159
- expect(jsFile.content).toContain('<Container {...props}')
157
+ expect(jsFile?.content).toContain('<ComponentWithAttrPropContainer {...props}')
160
158
  })
161
159
 
162
- it('should throw error when a state is being refered in generated StyledComponents ', async () => {
160
+ it('should resolve a state reference in StyledComponents to an inline style', async () => {
163
161
  const styledComponentsGenerator = createReactComponentGenerator({
164
162
  variation: ReactStyleVariation.StyledComponents,
165
163
  })
166
- try {
167
- await styledComponentsGenerator.generateComponent(ComponentWithInvalidStateStyles)
168
- expect(true).toBe(false)
169
- } catch (e) {
170
- expect(e.message).toContain(
171
- 'Error running transformDynamicStyles in reactStyledComponentsPlugin'
172
- )
173
- }
164
+ const result = await styledComponentsGenerator.generateComponent(
165
+ ComponentWithInvalidStateStyles
166
+ )
167
+ const jsFile = findFileByType(result.files, FileType.JS)
168
+
169
+ expect(jsFile).toBeDefined()
170
+ expect(jsFile?.content).toContain('backgroundColor: active')
171
+ expect(jsFile?.content).toContain('height: props.config.height')
174
172
  })
175
173
  })
176
174
 
@@ -184,9 +182,9 @@ describe('React Styles in Component', () => {
184
182
 
185
183
  expect(jsFile).toBeDefined()
186
184
  expect(cssFile).toBeDefined()
187
- expect(jsFile.content).toContain('import React')
188
- expect(jsFile.content).toContain('flexDirection: props.direction')
189
- expect(cssFile.content).toContain(`align-self: center`)
185
+ expect(jsFile?.content).toContain('import React')
186
+ expect(jsFile?.content).toContain('flexDirection: props.direction')
187
+ expect(cssFile?.content).toContain(`align-self: center`)
190
188
  })
191
189
  })
192
190
  })
@@ -0,0 +1,19 @@
1
+ import { createReactComponentGenerator } from '../../src'
2
+ import componentUIDL from '../../../../examples/uidl-samples/component.json'
3
+ import { FileType } from '@teleporthq/teleport-types'
4
+
5
+ describe('declares a propDefinitions with type object and use it', () => {
6
+ test('use the value from the object in prop', async () => {
7
+ const generator = createReactComponentGenerator()
8
+ const result = await generator.generateComponent(componentUIDL)
9
+ const jsFile = result.files.find((file) => file.fileType === FileType.JS)
10
+
11
+ expect(jsFile).toBeDefined()
12
+ expect(jsFile?.content).toContain('config: PropTypes.object')
13
+ expect(jsFile?.content).toContain("height: props.config?.['height']")
14
+ expect(jsFile?.content).toContain(`config: {
15
+ height: 30,
16
+ width: 30,
17
+ }`)
18
+ })
19
+ })
@@ -0,0 +1,72 @@
1
+ import { createReactComponentGenerator } from '../../src'
2
+ import {
3
+ ComponentUIDL,
4
+ GeneratedFile,
5
+ FileType,
6
+ ReactStyleVariation,
7
+ } from '@teleporthq/teleport-types'
8
+ import { dynamicNode, staticNode, component, elementNode } from '@teleporthq/teleport-uidl-builders'
9
+
10
+ const findFileByType = (files: GeneratedFile[], type: string = FileType.JS) =>
11
+ files.find((file) => file.fileType === type)
12
+
13
+ /**
14
+ * A dynamic STYLE value bound to an object PROP field (a prop reference with a
15
+ * refPath) must be emitted as an INLINE `style={{ ... }}` expression that keeps
16
+ * the full member access — NOT left to the styled-jsx `${props.x}` interpolation,
17
+ * which only uses `content.id` and would drop the refPath (producing
18
+ * `props.product` instead of `props.product.color`).
19
+ */
20
+ describe('Dynamic style values → inline style', () => {
21
+ const ComponentWithObjectPropStyle: ComponentUIDL = component(
22
+ 'ComponentWithObjectPropStyle',
23
+ elementNode('container', {}, [], undefined, {
24
+ backgroundColor: dynamicNode('prop', 'product', ['color']),
25
+ alignSelf: staticNode('center'),
26
+ }),
27
+ {
28
+ product: {
29
+ type: 'object',
30
+ defaultValue: {},
31
+ },
32
+ }
33
+ )
34
+
35
+ it('inlines an object-prop-field style (prop + refPath) with the full path', async () => {
36
+ const generator = createReactComponentGenerator({ variation: ReactStyleVariation.StyledJSX })
37
+ const result = await generator.generateComponent(ComponentWithObjectPropStyle)
38
+ const jsFile = findFileByType(result.files, FileType.JS)
39
+
40
+ expect(jsFile).toBeDefined()
41
+ // The full object-field access is inlined (optional-chained), keeping refPath.
42
+ expect(jsFile?.content).toContain('style={{')
43
+ expect(jsFile?.content).toContain("props.product?.['color']")
44
+ // …and the styled-jsx CSS block does NOT carry a broken `props.product`
45
+ // background-color declaration (which would drop the `.color` refPath).
46
+ expect(jsFile?.content).not.toContain(`background-color: \${props.product}`)
47
+ // The static sibling still goes through the shared CSS class.
48
+ expect(jsFile?.content).toContain('align-self: center')
49
+ })
50
+
51
+ it('keeps a SIMPLE prop style (no refPath) on the styled-jsx interpolation path', async () => {
52
+ const ComponentWithSimplePropStyle: ComponentUIDL = component(
53
+ 'ComponentWithSimplePropStyle',
54
+ elementNode('container', {}, [], undefined, {
55
+ color: dynamicNode('prop', 'textColor'),
56
+ }),
57
+ {
58
+ textColor: {
59
+ type: 'string',
60
+ defaultValue: 'black',
61
+ },
62
+ }
63
+ )
64
+ const generator = createReactComponentGenerator({ variation: ReactStyleVariation.StyledJSX })
65
+ const result = await generator.generateComponent(ComponentWithSimplePropStyle)
66
+ const jsFile = findFileByType(result.files, FileType.JS)
67
+
68
+ expect(jsFile).toBeDefined()
69
+ // Simple prop stays a styled-jsx interpolation inside the <style jsx> block.
70
+ expect(jsFile?.content).toContain(`\${props.textColor}`)
71
+ })
72
+ })
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,mBAAmB,EACnB,0BAA0B,EAE3B,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,QAAA,MAAM,6BAA6B,EAAE,0BAiDpC,CAAA;AAED,OAAO,EAAE,6BAA6B,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,mBAAmB,EACnB,0BAA0B,EAE3B,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAoB9C,QAAA,MAAM,6BAA6B,EAAE,0BA6BpC,CAAA;AAED,OAAO,EAAE,6BAA6B,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAA"}