@elementor/editor-canvas 0.7.1 → 0.8.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 (43) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/CHANGELOG.md +42 -0
  3. package/dist/index.js +234 -213
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +236 -215
  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 +210 -12
  11. package/src/renderers/__tests__/create-props-resolver.test.ts +175 -0
  12. package/src/renderers/__tests__/create-props-resolver.transformers.test.ts +323 -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 +2 -4
  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/types.ts +12 -0
  27. package/src/styles-renderer/__tests__/enqueue-used-fonts.test.ts +0 -60
  28. package/src/styles-renderer/__tests__/index.test.ts +0 -777
  29. package/src/styles-renderer/enqueue-used-fonts.ts +0 -22
  30. package/src/styles-renderer/index.ts +0 -2
  31. package/src/styles-renderer/resolve.ts +0 -103
  32. package/src/styles-renderer/transformers/background-image-overlay-transformer.ts +0 -31
  33. package/src/styles-renderer/types.ts +0 -16
  34. /package/src/{styles-renderer → renderers}/errors.ts +0 -0
  35. /package/src/{styles-renderer/transformers → renderers/style-transformers}/background-color-overlay-transformer.ts +0 -0
  36. /package/src/{styles-renderer/transformers → renderers/style-transformers}/background-transformer.ts +0 -0
  37. /package/src/{styles-renderer/transformers → renderers/style-transformers}/create-combine-array-transformer.ts +0 -0
  38. /package/src/{styles-renderer/transformers → renderers/style-transformers}/dimensions.ts +0 -0
  39. /package/src/{styles-renderer/transformers → renderers/style-transformers}/layout-direction-transformer.ts +0 -0
  40. /package/src/{styles-renderer/transformers → renderers/style-transformers}/primitive-transformer.ts +0 -0
  41. /package/src/{styles-renderer/transformers → renderers/style-transformers}/shadow-transformer.ts +0 -0
  42. /package/src/{styles-renderer/transformers → renderers/style-transformers}/size-transformer.ts +0 -0
  43. /package/src/{styles-renderer/transformers → renderers/style-transformers}/stroke-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,323 @@
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
+ 'text-stroke': '1px #000000',
55
+ },
56
+ },
57
+ {
58
+ name: 'box-shadow',
59
+ props: {
60
+ 'box-shadow': boxShadowPropTypeUtil.create( [
61
+ shadowPropTypeUtil.create( {
62
+ hOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
63
+ vOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
64
+ blur: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
65
+ spread: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
66
+ color: colorPropTypeUtil.create( '#000000' ),
67
+ } ),
68
+ shadowPropTypeUtil.create( {
69
+ hOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
70
+ vOffset: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
71
+ blur: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
72
+ spread: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
73
+ color: colorPropTypeUtil.create( '#000000' ),
74
+ position: 'inset',
75
+ } ),
76
+ ] ),
77
+ },
78
+ schema: stylesSchemaMock,
79
+ expected: {
80
+ 'box-shadow': '1px 1px 1px 1px #000000,1px 1px 1px 1px #000000 inset',
81
+ },
82
+ },
83
+ {
84
+ name: 'linked-dimensional',
85
+ props: {
86
+ padding: dimensionsPropTypeUtil.create( {
87
+ top: sizePropTypeUtil.create( {
88
+ size: 10,
89
+ unit: 'px',
90
+ } ),
91
+ right: sizePropTypeUtil.create( {
92
+ size: 20,
93
+ unit: 'px',
94
+ } ),
95
+ bottom: sizePropTypeUtil.create( {
96
+ size: 30,
97
+ unit: 'px',
98
+ } ),
99
+ left: sizePropTypeUtil.create( {
100
+ size: 40,
101
+ unit: 'px',
102
+ } ),
103
+ } ),
104
+ },
105
+ schema: stylesSchemaMock,
106
+ expected: {
107
+ 'padding-top': '10px',
108
+ 'padding-right': '20px',
109
+ 'padding-bottom': '30px',
110
+ 'padding-left': '40px',
111
+ },
112
+ },
113
+ {
114
+ name: 'border-width',
115
+ props: {
116
+ 'border-width': borderWidthPropTypeUtil.create( {
117
+ top: sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
118
+ right: sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
119
+ bottom: sizePropTypeUtil.create( { size: 30, unit: 'px' } ),
120
+ left: sizePropTypeUtil.create( { size: 40, unit: 'px' } ),
121
+ } ),
122
+ },
123
+ schema: stylesSchemaMock,
124
+ expected: {
125
+ 'border-top-width': '10px',
126
+ 'border-right-width': '20px',
127
+ 'border-bottom-width': '30px',
128
+ 'border-left-width': '40px',
129
+ },
130
+ },
131
+ {
132
+ name: 'border-radius',
133
+ props: {
134
+ 'border-radius': borderRadiusPropTypeUtil.create( {
135
+ 'top-left': sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
136
+ 'top-right': sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
137
+ 'bottom-right': sizePropTypeUtil.create( { size: 30, unit: 'px' } ),
138
+ 'bottom-left': sizePropTypeUtil.create( { size: 40, unit: 'px' } ),
139
+ } ),
140
+ },
141
+ schema: stylesSchemaMock,
142
+ expected: {
143
+ 'border-top-left-radius': '10px',
144
+ 'border-top-right-radius': '20px',
145
+ 'border-bottom-right-radius': '30px',
146
+ 'border-bottom-left-radius': '40px',
147
+ },
148
+ },
149
+ {
150
+ name: 'gap',
151
+ props: {
152
+ gap: layoutDirectionPropTypeUtil.create( {
153
+ row: sizePropTypeUtil.create( { size: 10, unit: 'px' } ),
154
+ column: sizePropTypeUtil.create( { size: 20, unit: 'px' } ),
155
+ } ),
156
+ },
157
+ schema: stylesSchemaMock,
158
+ expected: {
159
+ 'column-gap': '20px',
160
+ 'row-gap': '10px',
161
+ },
162
+ },
163
+ {
164
+ name: 'background (only color)',
165
+ props: {
166
+ background: backgroundPropTypeUtil.create( {
167
+ color: colorPropTypeUtil.create( '#ee00ff' ),
168
+ } ),
169
+ },
170
+ schema: stylesSchemaMock,
171
+ expected: {
172
+ background: '#ee00ff',
173
+ },
174
+ },
175
+ {
176
+ name: 'background (only image url)',
177
+ props: {
178
+ background: backgroundPropTypeUtil.create( {
179
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
180
+ backgroundImageOverlayPropTypeUtil.create( {
181
+ image: imagePropTypeUtil.create( {
182
+ src: imageSrcPropTypeUtil.create( {
183
+ id: null,
184
+ url: 'https://localhost.test/test-image.png',
185
+ } ),
186
+ size: null,
187
+ } ),
188
+ } ),
189
+ ] ),
190
+ } ),
191
+ },
192
+ schema: stylesSchemaMock,
193
+ expected: {
194
+ background: 'url(https://localhost.test/test-image.png)',
195
+ },
196
+ },
197
+ {
198
+ name: 'background (full)',
199
+ prepare: () => {
200
+ jest.mocked( getMediaAttachment ).mockImplementation(
201
+ ( args ) => Promise.resolve( mockAttachmentData( args.id ) ) as never
202
+ );
203
+ },
204
+ props: {
205
+ background: backgroundPropTypeUtil.create( {
206
+ color: colorPropTypeUtil.create( '#000' ),
207
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
208
+ backgroundColorOverlayPropTypeUtil.create( 'blue' ),
209
+ backgroundColorOverlayPropTypeUtil.create( 'yellow' ),
210
+ backgroundImageOverlayPropTypeUtil.create( {
211
+ image: imagePropTypeUtil.create( {
212
+ src: imageSrcPropTypeUtil.create( {
213
+ id: imageAttachmentIdPropType.create( 123 ),
214
+ url: null,
215
+ } ),
216
+ size: 'thumbnail',
217
+ } ),
218
+ size: backgroundImageSizeScalePropTypeUtil.create( {
219
+ width: sizePropTypeUtil.create( {
220
+ size: 1400,
221
+ unit: 'px',
222
+ } ),
223
+ } ),
224
+ } ),
225
+ backgroundImageOverlayPropTypeUtil.create( {
226
+ image: imagePropTypeUtil.create( {
227
+ src: imageSrcPropTypeUtil.create( {
228
+ id: imageAttachmentIdPropType.create( 123 ),
229
+ url: null,
230
+ } ),
231
+ size: 'medium_large',
232
+ } ),
233
+ size: 'auto',
234
+ position: backgroundImagePositionOffsetPropTypeUtil.create( {
235
+ x: sizePropTypeUtil.create( {
236
+ size: 200,
237
+ unit: 'px',
238
+ } ),
239
+ y: sizePropTypeUtil.create( {
240
+ size: 30,
241
+ unit: 'px',
242
+ } ),
243
+ } ),
244
+ repeat: 'repeat-x',
245
+ attachment: 'fixed',
246
+ } ),
247
+ ] ),
248
+ } ),
249
+ },
250
+ schema: stylesSchemaMock,
251
+ expected: {
252
+ background:
253
+ 'linear-gradient(blue, blue),linear-gradient(yellow, yellow),' +
254
+ 'url(thumbnail-image-url-123) 0% 0% / 1400px auto,' +
255
+ 'url(medium_large-image-url-123) repeat-x fixed 200px 30px / auto' +
256
+ ' #000',
257
+ },
258
+ },
259
+ {
260
+ name: 'background (url only repeat and attachment)',
261
+ prepare: () => {
262
+ jest.mocked( getMediaAttachment ).mockImplementation(
263
+ ( args ) => Promise.resolve( mockAttachmentData( args.id ) ) as never
264
+ );
265
+ },
266
+ props: {
267
+ background: backgroundPropTypeUtil.create( {
268
+ 'background-overlay': backgroundOverlayPropTypeUtil.create( [
269
+ backgroundImageOverlayPropTypeUtil.create( {
270
+ image: imagePropTypeUtil.create( {
271
+ src: imageSrcPropTypeUtil.create( {
272
+ id: imageAttachmentIdPropType.create( 123 ),
273
+ url: null,
274
+ } ),
275
+ size: 'medium_large',
276
+ } ),
277
+ repeat: 'repeat-x',
278
+ } ),
279
+ backgroundImageOverlayPropTypeUtil.create( {
280
+ image: imagePropTypeUtil.create( {
281
+ src: imageSrcPropTypeUtil.create( {
282
+ id: imageAttachmentIdPropType.create( 123 ),
283
+ url: null,
284
+ } ),
285
+ size: 'thumbnail',
286
+ } ),
287
+ attachment: 'fixed',
288
+ } ),
289
+ ] ),
290
+ } ),
291
+ },
292
+ schema: stylesSchemaMock,
293
+ expected: {
294
+ background: 'url(medium_large-image-url-123) repeat-x,url(thumbnail-image-url-123) fixed',
295
+ },
296
+ },
297
+ ] )( 'it should resolve props for `$name`', async ( { prepare, props, schema, expected } ) => {
298
+ // Arrange.
299
+ prepare?.();
300
+
301
+ const resolve = createPropsResolver( styleTransformers );
302
+
303
+ // Act.
304
+ const result = await resolve( { props, schema } );
305
+
306
+ // Assert.
307
+ expect( result ).toStrictEqual( expected );
308
+ } );
309
+ } );
310
+
311
+ const mockAttachmentData = ( id: number | null ) => {
312
+ const originalUrl = `original-image-url-${ id }`;
313
+
314
+ return {
315
+ url: originalUrl,
316
+ sizes: {
317
+ thumbnail: { url: `thumbnail-image-url-${ id }` },
318
+ medium_large: { url: `medium_large-image-url-${ id }` },
319
+ large: { url: `large-image-url-${ id }` },
320
+ full: { url: originalUrl },
321
+ },
322
+ };
323
+ };
@@ -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
+ } );