@elementor/editor-canvas 0.7.1 → 0.9.0

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.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/CHANGELOG.md +57 -0
  3. package/dist/index.js +241 -215
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +243 -217
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +15 -12
  8. package/src/__tests__/init-styles-renderer.test.ts +19 -9
  9. package/src/init-styles-renderer.ts +20 -7
  10. package/src/{styles-renderer → renderers}/__tests__/__mocks__/styles-schema.ts +214 -16
  11. package/src/renderers/__tests__/create-props-resolver.test.ts +175 -0
  12. package/src/renderers/__tests__/create-props-resolver.transformers.test.ts +325 -0
  13. package/src/renderers/__tests__/render-styles.test.ts +126 -0
  14. package/src/renderers/create-props-resolver.ts +123 -0
  15. package/src/{styles-renderer → renderers}/multi-props.ts +1 -1
  16. package/src/{styles-renderer/render.ts → renderers/render-styles.ts} +18 -17
  17. package/src/renderers/style-transformers/background-image-overlay-transformer.ts +24 -0
  18. package/src/renderers/style-transformers/background-image-position-offset-transformer.ts +10 -0
  19. package/src/renderers/style-transformers/background-image-size-scale-transformer.ts +10 -0
  20. package/src/{styles-renderer/transformers → renderers/style-transformers}/create-corner-sizes-transformer.ts +7 -9
  21. package/src/{styles-renderer/transformers → renderers/style-transformers}/create-edge-sizes-transformer.ts +2 -2
  22. package/src/{styles-renderer/transformers → renderers/style-transformers}/image-attachment.ts +1 -1
  23. package/src/{styles-renderer/transformers → renderers/style-transformers}/image-src.ts +4 -7
  24. package/src/renderers/style-transformers/image.ts +25 -0
  25. package/src/{styles-renderer/transformers → renderers/style-transformers}/index.ts +9 -2
  26. package/src/renderers/style-transformers/stroke-transformer.ts +16 -0
  27. package/src/renderers/types.ts +12 -0
  28. package/src/styles-renderer/__tests__/enqueue-used-fonts.test.ts +0 -60
  29. package/src/styles-renderer/__tests__/index.test.ts +0 -777
  30. package/src/styles-renderer/enqueue-used-fonts.ts +0 -22
  31. package/src/styles-renderer/index.ts +0 -2
  32. package/src/styles-renderer/resolve.ts +0 -103
  33. package/src/styles-renderer/transformers/background-image-overlay-transformer.ts +0 -31
  34. package/src/styles-renderer/transformers/stroke-transformer.ts +0 -9
  35. package/src/styles-renderer/types.ts +0 -16
  36. /package/src/{styles-renderer → renderers}/errors.ts +0 -0
  37. /package/src/{styles-renderer/transformers → renderers/style-transformers}/background-color-overlay-transformer.ts +0 -0
  38. /package/src/{styles-renderer/transformers → renderers/style-transformers}/background-transformer.ts +0 -0
  39. /package/src/{styles-renderer/transformers → renderers/style-transformers}/create-combine-array-transformer.ts +0 -0
  40. /package/src/{styles-renderer/transformers → renderers/style-transformers}/dimensions.ts +0 -0
  41. /package/src/{styles-renderer/transformers → renderers/style-transformers}/layout-direction-transformer.ts +0 -0
  42. /package/src/{styles-renderer/transformers → renderers/style-transformers}/primitive-transformer.ts +0 -0
  43. /package/src/{styles-renderer/transformers → renderers/style-transformers}/shadow-transformer.ts +0 -0
  44. /package/src/{styles-renderer/transformers → renderers/style-transformers}/size-transformer.ts +0 -0
@@ -0,0 +1,175 @@
1
+ import { createMockPropType } from 'test-utils';
2
+
3
+ import { createPropsResolver } from '../create-props-resolver';
4
+
5
+ describe( 'createPropsResolver', () => {
6
+ it( 'should resolve simple props', async () => {
7
+ // Arrange.
8
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 } );
9
+
10
+ // Act.
11
+ const result = await resolve( {
12
+ props: {
13
+ int: { $$type: 'int', value: 0 },
14
+ },
15
+ schema: {
16
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
17
+ },
18
+ } );
19
+
20
+ // Assert.
21
+ expect( result ).toEqual( { int: 1 } );
22
+ } );
23
+
24
+ it( 'should skip disabled props', async () => {
25
+ // Arrange.
26
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 } );
27
+
28
+ // Act.
29
+ const result = await resolve( {
30
+ props: {
31
+ int: {
32
+ $$type: 'int',
33
+ value: 1,
34
+ disabled: true,
35
+ },
36
+ },
37
+ schema: {
38
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
39
+ },
40
+ } );
41
+
42
+ // Assert.
43
+ expect( result ).toEqual( {} );
44
+ } );
45
+
46
+ it( 'should fallback to default value when there is no value', async () => {
47
+ // Arrange.
48
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 } );
49
+
50
+ // Act.
51
+ const result = await resolve( {
52
+ props: {},
53
+ schema: {
54
+ int: createMockPropType( {
55
+ kind: 'plain',
56
+ key: 'int',
57
+ default: { $$type: 'int', value: 3 },
58
+ } ),
59
+ },
60
+ } );
61
+
62
+ // Assert.
63
+ expect( result ).toEqual( { int: 4 } );
64
+ } );
65
+
66
+ it( 'should skip props that are not in the schema', async () => {
67
+ // Arrange.
68
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 } );
69
+
70
+ // Act.
71
+ const result = await resolve( {
72
+ props: {
73
+ int: {
74
+ $$type: 'int',
75
+ value: 1,
76
+ },
77
+ 'not-in-schema': {
78
+ $$type: 'int',
79
+ value: 1,
80
+ },
81
+ },
82
+ schema: {
83
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
84
+ },
85
+ } );
86
+
87
+ // Assert.
88
+ expect( result ).toEqual( { int: 2 } );
89
+ } );
90
+
91
+ it( "should skip props that don't have a transformer", async () => {
92
+ // Arrange.
93
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 } );
94
+
95
+ // Act.
96
+ const result = await resolve( {
97
+ props: {
98
+ int: {
99
+ $$type: 'int',
100
+ value: 1,
101
+ },
102
+ invalid: {
103
+ $$type: 'invalid',
104
+ value: 1,
105
+ },
106
+ },
107
+ schema: {
108
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
109
+ invalid: createMockPropType( { kind: 'plain', key: 'invalid' } ),
110
+ },
111
+ } );
112
+
113
+ // Assert.
114
+ expect( result ).toEqual( { int: 2 } );
115
+ } );
116
+
117
+ it( 'should skip props when their transformer throws an error', async () => {
118
+ const resolve = createPropsResolver( {
119
+ int: ( value ) => value + 1,
120
+ throws: () => {
121
+ throw new Error( 'Not Working!' );
122
+ },
123
+ } );
124
+
125
+ // Act.
126
+ const result = await resolve( {
127
+ props: {
128
+ int: {
129
+ $$type: 'int',
130
+ value: 1,
131
+ },
132
+ throws: {
133
+ $$type: 'throws',
134
+ value: 1,
135
+ },
136
+ },
137
+ schema: {
138
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
139
+ invalid: createMockPropType( { kind: 'plain', key: 'throws' } ),
140
+ },
141
+ } );
142
+
143
+ // Assert.
144
+ expect( result ).toEqual( { int: 2 } );
145
+ } );
146
+
147
+ it( 'should trigger onResolve when resolving a prop', async () => {
148
+ const onResolve = jest.fn();
149
+
150
+ const resolve = createPropsResolver( { int: ( value ) => value + 1 }, { onPropResolve: onResolve } );
151
+
152
+ // Act.
153
+ await resolve( {
154
+ props: {
155
+ int: {
156
+ $$type: 'int',
157
+ value: 1,
158
+ },
159
+ int2: {
160
+ $$type: 'int',
161
+ value: 3,
162
+ },
163
+ },
164
+ schema: {
165
+ int: createMockPropType( { kind: 'plain', key: 'int' } ),
166
+ int2: createMockPropType( { kind: 'plain', key: 'int' } ),
167
+ },
168
+ } );
169
+
170
+ // Assert.
171
+ expect( onResolve ).toHaveBeenCalledTimes( 2 );
172
+ expect( onResolve ).toHaveBeenNthCalledWith( 1, { key: 'int', value: 2 } );
173
+ expect( onResolve ).toHaveBeenNthCalledWith( 2, { key: 'int2', value: 4 } );
174
+ } );
175
+ } );
@@ -0,0 +1,325 @@
1
+ import {
2
+ backgroundColorOverlayPropTypeUtil,
3
+ backgroundImageOverlayPropTypeUtil,
4
+ backgroundImagePositionOffsetPropTypeUtil,
5
+ backgroundImageSizeScalePropTypeUtil,
6
+ backgroundOverlayPropTypeUtil,
7
+ backgroundPropTypeUtil,
8
+ borderRadiusPropTypeUtil,
9
+ borderWidthPropTypeUtil,
10
+ boxShadowPropTypeUtil,
11
+ colorPropTypeUtil,
12
+ dimensionsPropTypeUtil,
13
+ imageAttachmentIdPropType,
14
+ imagePropTypeUtil,
15
+ imageSrcPropTypeUtil,
16
+ layoutDirectionPropTypeUtil,
17
+ type Props,
18
+ type PropsSchema,
19
+ shadowPropTypeUtil,
20
+ sizePropTypeUtil,
21
+ strokePropTypeUtil,
22
+ } from '@elementor/editor-props';
23
+ import { getMediaAttachment } from '@elementor/wp-media';
24
+
25
+ import { createPropsResolver } from '../create-props-resolver';
26
+ import { styleTransformers } from '../style-transformers';
27
+ import { stylesSchemaMock } from './__mocks__/styles-schema';
28
+
29
+ jest.mock( '@elementor/wp-media' );
30
+
31
+ type Payload = {
32
+ name: string;
33
+ prepare?: () => void;
34
+ props: Props;
35
+ schema: PropsSchema;
36
+ expected: Record< string, unknown >;
37
+ };
38
+
39
+ describe( 'createPropsResolver - transformers', () => {
40
+ it.each< Payload >( [
41
+ {
42
+ name: 'text-stroke',
43
+ props: {
44
+ 'text-stroke': strokePropTypeUtil.create( {
45
+ width: sizePropTypeUtil.create( {
46
+ size: 1,
47
+ unit: 'px',
48
+ } ),
49
+ color: colorPropTypeUtil.create( '#000000' ),
50
+ } ),
51
+ },
52
+ schema: stylesSchemaMock,
53
+ expected: {
54
+ '-webkit-text-stroke': '1px #000000',
55
+ stroke: '#000000',
56
+ 'stroke-width': '1px',
57
+ },
58
+ },
59
+ {
60
+ name: 'box-shadow',
61
+ props: {
62
+ 'box-shadow': boxShadowPropTypeUtil.create( [
63
+ shadowPropTypeUtil.create( {
64
+ hOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
65
+ vOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
66
+ blur: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
67
+ spread: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
68
+ color: colorPropTypeUtil.create( '#000000' ),
69
+ } ),
70
+ shadowPropTypeUtil.create( {
71
+ hOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
72
+ vOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
73
+ blur: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
74
+ spread: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
75
+ color: colorPropTypeUtil.create( '#000000' ),
76
+ position: 'inset',
77
+ } ),
78
+ ] ),
79
+ },
80
+ schema: stylesSchemaMock,
81
+ expected: {
82
+ 'box-shadow': '1px 1px 1px 1px #000000,1px 1px 1px 1px #000000 inset',
83
+ },
84
+ },
85
+ {
86
+ name: 'linked-dimensional',
87
+ props: {
88
+ padding: dimensionsPropTypeUtil.create( {
89
+ top: sizePropTypeUtil.create( {
90
+ size: 10,
91
+ unit: 'px',
92
+ } ),
93
+ right: sizePropTypeUtil.create( {
94
+ size: 20,
95
+ unit: 'px',
96
+ } ),
97
+ bottom: sizePropTypeUtil.create( {
98
+ size: 30,
99
+ unit: 'px',
100
+ } ),
101
+ left: sizePropTypeUtil.create( {
102
+ size: 40,
103
+ unit: 'px',
104
+ } ),
105
+ } ),
106
+ },
107
+ schema: stylesSchemaMock,
108
+ expected: {
109
+ 'padding-top': '10px',
110
+ 'padding-right': '20px',
111
+ 'padding-bottom': '30px',
112
+ 'padding-left': '40px',
113
+ },
114
+ },
115
+ {
116
+ name: 'border-width',
117
+ props: {
118
+ 'border-width': borderWidthPropTypeUtil.create( {
119
+ top: sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
120
+ right: sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
121
+ bottom: sizePropTypeUtil.create( { size: 30, unit: 'px' } ),
122
+ left: sizePropTypeUtil.create( { size: 40, unit: 'px' } ),
123
+ } ),
124
+ },
125
+ schema: stylesSchemaMock,
126
+ expected: {
127
+ 'border-top-width': '10px',
128
+ 'border-right-width': '20px',
129
+ 'border-bottom-width': '30px',
130
+ 'border-left-width': '40px',
131
+ },
132
+ },
133
+ {
134
+ name: 'border-radius',
135
+ props: {
136
+ 'border-radius': borderRadiusPropTypeUtil.create( {
137
+ 'start-start': sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
138
+ 'start-end': sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
139
+ 'end-start': sizePropTypeUtil.create( { size: 30, unit: 'px' } ),
140
+ 'end-end': sizePropTypeUtil.create( { size: 40, unit: 'px' } ),
141
+ } ),
142
+ },
143
+ schema: stylesSchemaMock,
144
+ expected: {
145
+ 'border-start-start-radius': '10px',
146
+ 'border-start-end-radius': '20px',
147
+ 'border-end-start-radius': '30px',
148
+ 'border-end-end-radius': '40px',
149
+ },
150
+ },
151
+ {
152
+ name: 'gap',
153
+ props: {
154
+ gap: layoutDirectionPropTypeUtil.create( {
155
+ row: sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
156
+ column: sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
157
+ } ),
158
+ },
159
+ schema: stylesSchemaMock,
160
+ expected: {
161
+ 'column-gap': '20px',
162
+ 'row-gap': '10px',
163
+ },
164
+ },
165
+ {
166
+ name: 'background (only color)',
167
+ props: {
168
+ background: backgroundPropTypeUtil.create( {
169
+ color: colorPropTypeUtil.create( '#ee00ff' ),
170
+ } ),
171
+ },
172
+ schema: stylesSchemaMock,
173
+ expected: {
174
+ background: '#ee00ff',
175
+ },
176
+ },
177
+ {
178
+ name: 'background (only image url)',
179
+ props: {
180
+ background: backgroundPropTypeUtil.create( {
181
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
182
+ backgroundImageOverlayPropTypeUtil.create( {
183
+ image: imagePropTypeUtil.create( {
184
+ src: imageSrcPropTypeUtil.create( {
185
+ id: null,
186
+ url: 'https://localhost.test/test-image.png',
187
+ } ),
188
+ size: null,
189
+ } ),
190
+ } ),
191
+ ] ),
192
+ } ),
193
+ },
194
+ schema: stylesSchemaMock,
195
+ expected: {
196
+ background: 'url(https://localhost.test/test-image.png)',
197
+ },
198
+ },
199
+ {
200
+ name: 'background (full)',
201
+ prepare: () => {
202
+ jest.mocked( getMediaAttachment ).mockImplementation(
203
+ ( args ) => Promise.resolve( mockAttachmentData( args.id ) ) as never
204
+ );
205
+ },
206
+ props: {
207
+ background: backgroundPropTypeUtil.create( {
208
+ color: colorPropTypeUtil.create( '#000' ),
209
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
210
+ backgroundColorOverlayPropTypeUtil.create( 'blue' ),
211
+ backgroundColorOverlayPropTypeUtil.create( 'yellow' ),
212
+ backgroundImageOverlayPropTypeUtil.create( {
213
+ image: imagePropTypeUtil.create( {
214
+ src: imageSrcPropTypeUtil.create( {
215
+ id: imageAttachmentIdPropType.create( 123 ),
216
+ url: null,
217
+ } ),
218
+ size: 'thumbnail',
219
+ } ),
220
+ size: backgroundImageSizeScalePropTypeUtil.create( {
221
+ width: sizePropTypeUtil.create( {
222
+ size: 1400,
223
+ unit: 'px',
224
+ } ),
225
+ } ),
226
+ } ),
227
+ backgroundImageOverlayPropTypeUtil.create( {
228
+ image: imagePropTypeUtil.create( {
229
+ src: imageSrcPropTypeUtil.create( {
230
+ id: imageAttachmentIdPropType.create( 123 ),
231
+ url: null,
232
+ } ),
233
+ size: 'medium_large',
234
+ } ),
235
+ size: 'auto',
236
+ position: backgroundImagePositionOffsetPropTypeUtil.create( {
237
+ x: sizePropTypeUtil.create( {
238
+ size: 200,
239
+ unit: 'px',
240
+ } ),
241
+ y: sizePropTypeUtil.create( {
242
+ size: 30,
243
+ unit: 'px',
244
+ } ),
245
+ } ),
246
+ repeat: 'repeat-x',
247
+ attachment: 'fixed',
248
+ } ),
249
+ ] ),
250
+ } ),
251
+ },
252
+ schema: stylesSchemaMock,
253
+ expected: {
254
+ background:
255
+ 'linear-gradient(blue, blue),linear-gradient(yellow, yellow),' +
256
+ 'url(thumbnail-image-url-123) 0% 0% / 1400px auto,' +
257
+ 'url(medium_large-image-url-123) repeat-x fixed 200px 30px / auto' +
258
+ ' #000',
259
+ },
260
+ },
261
+ {
262
+ name: 'background (url only repeat and attachment)',
263
+ prepare: () => {
264
+ jest.mocked( getMediaAttachment ).mockImplementation(
265
+ ( args ) => Promise.resolve( mockAttachmentData( args.id ) ) as never
266
+ );
267
+ },
268
+ props: {
269
+ background: backgroundPropTypeUtil.create( {
270
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
271
+ backgroundImageOverlayPropTypeUtil.create( {
272
+ image: imagePropTypeUtil.create( {
273
+ src: imageSrcPropTypeUtil.create( {
274
+ id: imageAttachmentIdPropType.create( 123 ),
275
+ url: null,
276
+ } ),
277
+ size: 'medium_large',
278
+ } ),
279
+ repeat: 'repeat-x',
280
+ } ),
281
+ backgroundImageOverlayPropTypeUtil.create( {
282
+ image: imagePropTypeUtil.create( {
283
+ src: imageSrcPropTypeUtil.create( {
284
+ id: imageAttachmentIdPropType.create( 123 ),
285
+ url: null,
286
+ } ),
287
+ size: 'thumbnail',
288
+ } ),
289
+ attachment: 'fixed',
290
+ } ),
291
+ ] ),
292
+ } ),
293
+ },
294
+ schema: stylesSchemaMock,
295
+ expected: {
296
+ background: 'url(medium_large-image-url-123) repeat-x,url(thumbnail-image-url-123) fixed',
297
+ },
298
+ },
299
+ ] )( 'it should resolve props for `$name`', async ( { prepare, props, schema, expected } ) => {
300
+ // Arrange.
301
+ prepare?.();
302
+
303
+ const resolve = createPropsResolver( styleTransformers );
304
+
305
+ // Act.
306
+ const result = await resolve( { props, schema } );
307
+
308
+ // Assert.
309
+ expect( result ).toStrictEqual( expected );
310
+ } );
311
+ } );
312
+
313
+ const mockAttachmentData = ( id: number | null ) => {
314
+ const originalUrl = `original-image-url-${ id }`;
315
+
316
+ return {
317
+ url: originalUrl,
318
+ sizes: {
319
+ thumbnail: { url: `thumbnail-image-url-${ id }` },
320
+ medium_large: { url: `medium_large-image-url-${ id }` },
321
+ large: { url: `large-image-url-${ id }` },
322
+ full: { url: originalUrl },
323
+ },
324
+ };
325
+ };
@@ -0,0 +1,126 @@
1
+ /* eslint-disable testing-library/render-result-naming-convention */
2
+ import { createMockPropType } from 'test-utils';
3
+ import type { BreakpointsMap } from '@elementor/editor-responsive';
4
+ import { getStylesSchema, type StyleDefinition } from '@elementor/editor-styles';
5
+
6
+ import renderStyles from '../render-styles';
7
+ import { stylesSchemaMock } from './__mocks__/styles-schema';
8
+
9
+ jest.mock( '@elementor/editor-styles' );
10
+
11
+ describe( 'renderStyles', () => {
12
+ beforeEach( () => {
13
+ jest.mocked( getStylesSchema ).mockReturnValue( stylesSchemaMock );
14
+ } );
15
+
16
+ it( 'should render media queries', async () => {
17
+ // Arrange.
18
+ const styleDef: StyleDefinition = {
19
+ id: 'test',
20
+ type: 'class',
21
+ label: 'Test',
22
+ variants: [
23
+ {
24
+ meta: { breakpoint: null, state: null },
25
+ props: { 'font-size': '24px' },
26
+ },
27
+ {
28
+ meta: { breakpoint: 'tablet', state: null },
29
+ props: { 'font-size': '18px' },
30
+ },
31
+ ],
32
+ };
33
+
34
+ const resolve = jest.fn( ( { props } ) => props );
35
+ const schema = { 'font-size': createMockPropType( { kind: 'plain', key: 'string' } ) };
36
+
37
+ // Act.
38
+ const cssString = await renderStyles( {
39
+ styles: [ styleDef ],
40
+ resolve,
41
+ breakpoints: { tablet: { width: 992, type: 'max-width' } } as BreakpointsMap,
42
+ schema,
43
+ } );
44
+
45
+ // Assert.
46
+ const defaultStyle = '.test{font-size:24px;}';
47
+ const tabletStyle = '@media(max-width:992px){.test{font-size:18px;}}';
48
+
49
+ expect( cssString ).toBe( `<style data-style-id="test">${ defaultStyle }${ tabletStyle }</style>` );
50
+
51
+ expect( resolve ).toHaveBeenCalledTimes( 2 );
52
+ expect( resolve ).toHaveBeenNthCalledWith( 1, { props: { 'font-size': '24px' }, schema } );
53
+ expect( resolve ).toHaveBeenNthCalledWith( 2, { props: { 'font-size': '18px' }, schema } );
54
+ } );
55
+
56
+ it( 'should render pseudo classes', async () => {
57
+ // Arrange.
58
+ const styleDef: StyleDefinition = {
59
+ id: 'test',
60
+ type: 'class',
61
+ label: 'Test',
62
+ variants: [
63
+ {
64
+ meta: { breakpoint: null, state: null },
65
+ props: { 'font-size': '24px' },
66
+ },
67
+ {
68
+ meta: { breakpoint: null, state: 'hover' },
69
+ props: { 'font-size': '18px' },
70
+ },
71
+ {
72
+ meta: { breakpoint: 'mobile', state: 'focus' },
73
+ props: { 'font-size': '12px' },
74
+ },
75
+ ],
76
+ };
77
+
78
+ const resolve = jest.fn( ( { props } ) => props );
79
+
80
+ // Act.
81
+ const cssString = await renderStyles( {
82
+ styles: [ styleDef ],
83
+ resolve,
84
+ breakpoints: { mobile: { width: 768, type: 'max-width' } } as BreakpointsMap,
85
+ schema: { 'font-size': createMockPropType( { kind: 'plain', key: 'string' } ) },
86
+ } );
87
+
88
+ // Assert.
89
+ const defaultStyle = '.test{font-size:24px;}';
90
+ const hoverStyle = '.test:hover{font-size:18px;}';
91
+ const focusStyle = '@media(max-width:768px){.test:focus{font-size:12px;}}';
92
+
93
+ expect( cssString ).toBe(
94
+ `<style data-style-id="test">${ defaultStyle }${ hoverStyle }${ focusStyle }</style>`
95
+ );
96
+ } );
97
+
98
+ it( 'should add selector prefix to the output', async () => {
99
+ // Arrange.
100
+ const styleDef: StyleDefinition = {
101
+ id: 'test',
102
+ type: 'class',
103
+ label: 'Test',
104
+ variants: [
105
+ {
106
+ meta: { breakpoint: null, state: null },
107
+ props: { 'font-size': '24px' },
108
+ },
109
+ ],
110
+ };
111
+
112
+ const resolve = jest.fn( ( { props } ) => props );
113
+
114
+ // Act.
115
+ const result = await renderStyles( {
116
+ styles: [ styleDef ],
117
+ resolve,
118
+ schema: getStylesSchema(),
119
+ selectorPrefix: '.elementor-prefix',
120
+ breakpoints: {} as BreakpointsMap,
121
+ } );
122
+
123
+ // Assert.
124
+ expect( result ).toBe( `<style data-style-id="test">.elementor-prefix .test{font-size:24px;}</style>` );
125
+ } );
126
+ } );