@elementor/editor-canvas 0.28.0 → 3.32.0-20

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 (31) hide show
  1. package/CHANGELOG.md +0 -20
  2. package/dist/index.js +167 -36
  3. package/dist/index.mjs +163 -32
  4. package/package.json +15 -15
  5. package/src/__tests__/flex-transformer.test.ts +272 -0
  6. package/src/__tests__/styles-prop-resolver.test.ts +78 -63
  7. package/src/components/__tests__/style-renderer.test.tsx +2 -2
  8. package/src/components/style-renderer.tsx +1 -1
  9. package/src/hooks/__tests__/use-style-items.test.ts +106 -7
  10. package/src/hooks/use-style-items.ts +47 -5
  11. package/src/init-settings-transformers.ts +2 -0
  12. package/src/init-style-transformers.ts +10 -0
  13. package/src/renderers/__tests__/__snapshots__/create-styles-renderer.test.ts.snap +2 -0
  14. package/src/renderers/__tests__/create-styles-renderer.test.ts +98 -0
  15. package/src/renderers/create-styles-renderer.ts +26 -2
  16. package/src/style-commands/__tests__/paste-style.test.ts +30 -0
  17. package/src/style-commands/__tests__/reset-style.test.ts +4 -0
  18. package/src/style-commands/undoable-actions/paste-element-style.ts +2 -1
  19. package/src/transformers/settings/attributes-transformer.ts +25 -0
  20. package/src/transformers/styles/background-overlay-transformer.ts +1 -10
  21. package/src/transformers/styles/create-combine-array-transformer.ts +3 -1
  22. package/src/transformers/styles/filter-transformer.ts +10 -18
  23. package/src/transformers/styles/flex-transformer.ts +55 -0
  24. package/src/transformers/styles/transform-move-transformer.ts +3 -1
  25. package/src/transformers/styles/transform-rotate-transformer.ts +19 -0
  26. package/src/transformers/styles/transform-scale-transformer.ts +11 -0
  27. package/src/transformers/styles/transform-skew-transformer.ts +15 -0
  28. package/src/transformers/styles/transition-transformer.ts +25 -0
  29. package/.turbo/turbo-build.log +0 -22
  30. package/dist/index.js.map +0 -1
  31. package/dist/index.mjs.map +0 -1
@@ -9,34 +9,26 @@ import {
9
9
  backgroundImageSizeScalePropTypeUtil,
10
10
  backgroundOverlayPropTypeUtil,
11
11
  backgroundPropTypeUtil,
12
- blurFilterPropTypeUtil,
13
12
  borderRadiusPropTypeUtil,
14
13
  borderWidthPropTypeUtil,
15
14
  boxShadowPropTypeUtil,
16
- brightnessFilterPropTypeUtil,
17
15
  colorPropTypeUtil,
18
16
  colorStopPropTypeUtil,
19
- contrastFilterPropTypeUtil,
17
+ cssFilterFunctionPropUtil,
20
18
  dimensionsPropTypeUtil,
21
19
  filterPropTypeUtil,
22
20
  gradientColorStopPropTypeUtil,
23
- grayscaleFilterPropTypeUtil,
24
- hueRotateFilterPropTypeUtil,
25
21
  imageAttachmentIdPropType,
26
22
  imagePropTypeUtil,
27
23
  imageSrcPropTypeUtil,
28
- invertFilterPropTypeUtil,
29
24
  layoutDirectionPropTypeUtil,
30
25
  numberPropTypeUtil,
31
26
  type Props,
32
- saturateFilterPropTypeUtil,
33
- sepiaFilterPropTypeUtil,
34
27
  shadowPropTypeUtil,
35
28
  sizePropTypeUtil,
36
29
  stringPropTypeUtil,
37
30
  strokePropTypeUtil,
38
31
  } from '@elementor/editor-props';
39
- import { isExperimentActive } from '@elementor/editor-v1-adapters';
40
32
  import { getMediaAttachment } from '@elementor/wp-media';
41
33
 
42
34
  import { initStyleTransformers } from '../init-style-transformers';
@@ -55,6 +47,76 @@ type Payload = {
55
47
  expected: Record< string, unknown >;
56
48
  };
57
49
 
50
+ const filters = filterPropTypeUtil.create( [
51
+ cssFilterFunctionPropUtil.create( {
52
+ func: stringPropTypeUtil.create( 'blur' ),
53
+ args: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
54
+ } ),
55
+ cssFilterFunctionPropUtil.create( {
56
+ func: stringPropTypeUtil.create( 'brightness' ),
57
+ args: sizePropTypeUtil.create( { size: 90, unit: '%' } ),
58
+ } ),
59
+ cssFilterFunctionPropUtil.create( {
60
+ func: stringPropTypeUtil.create( 'contrast' ),
61
+ args: sizePropTypeUtil.create( { size: 50, unit: '%' } ),
62
+ } ),
63
+ cssFilterFunctionPropUtil.create( {
64
+ func: stringPropTypeUtil.create( 'grayscale' ),
65
+ args: sizePropTypeUtil.create( { size: 70, unit: '%' } ),
66
+ } ),
67
+ cssFilterFunctionPropUtil.create( {
68
+ func: stringPropTypeUtil.create( 'invert' ),
69
+ args: sizePropTypeUtil.create( { size: 60, unit: '%' } ),
70
+ } ),
71
+ cssFilterFunctionPropUtil.create( {
72
+ func: stringPropTypeUtil.create( 'sepia' ),
73
+ args: sizePropTypeUtil.create( { size: 30, unit: '%' } ),
74
+ } ),
75
+ cssFilterFunctionPropUtil.create( {
76
+ func: stringPropTypeUtil.create( 'saturate' ),
77
+ args: sizePropTypeUtil.create( { size: 25, unit: '%' } ),
78
+ } ),
79
+ cssFilterFunctionPropUtil.create( {
80
+ func: stringPropTypeUtil.create( 'hue-rotate' ),
81
+ args: sizePropTypeUtil.create( { size: 10, unit: 'deg' } ),
82
+ } ),
83
+ ] );
84
+
85
+ const backDropFilters = backdropFilterPropTypeUtil.create( [
86
+ cssFilterFunctionPropUtil.create( {
87
+ func: stringPropTypeUtil.create( 'blur' ),
88
+ args: sizePropTypeUtil.create( { size: 2, unit: 'rem' } ),
89
+ } ),
90
+ cssFilterFunctionPropUtil.create( {
91
+ func: stringPropTypeUtil.create( 'brightness' ),
92
+ args: sizePropTypeUtil.create( { size: 80, unit: '%' } ),
93
+ } ),
94
+ cssFilterFunctionPropUtil.create( {
95
+ func: stringPropTypeUtil.create( 'contrast' ),
96
+ args: sizePropTypeUtil.create( { size: 50, unit: '%' } ),
97
+ } ),
98
+ cssFilterFunctionPropUtil.create( {
99
+ func: stringPropTypeUtil.create( 'grayscale' ),
100
+ args: sizePropTypeUtil.create( { size: 70, unit: '%' } ),
101
+ } ),
102
+ cssFilterFunctionPropUtil.create( {
103
+ func: stringPropTypeUtil.create( 'invert' ),
104
+ args: sizePropTypeUtil.create( { size: 60, unit: '%' } ),
105
+ } ),
106
+ cssFilterFunctionPropUtil.create( {
107
+ func: stringPropTypeUtil.create( 'sepia' ),
108
+ args: sizePropTypeUtil.create( { size: 30, unit: '%' } ),
109
+ } ),
110
+ cssFilterFunctionPropUtil.create( {
111
+ func: stringPropTypeUtil.create( 'saturate' ),
112
+ args: sizePropTypeUtil.create( { size: 25, unit: '%' } ),
113
+ } ),
114
+ cssFilterFunctionPropUtil.create( {
115
+ func: stringPropTypeUtil.create( 'hue-rotate' ),
116
+ args: sizePropTypeUtil.create( { size: 10, unit: 'deg' } ),
117
+ } ),
118
+ ] );
119
+
58
120
  describe( 'styles prop resolver', () => {
59
121
  it.each< Payload >( [
60
122
  {
@@ -184,7 +246,7 @@ describe( 'styles prop resolver', () => {
184
246
  },
185
247
  },
186
248
  {
187
- name: 'background (only image url)',
249
+ name: 'background (image url and default values)',
188
250
  props: {
189
251
  background: backgroundPropTypeUtil.create( {
190
252
  'background-overlay': backgroundOverlayPropTypeUtil.create( [
@@ -202,12 +264,15 @@ describe( 'styles prop resolver', () => {
202
264
  },
203
265
  expected: {
204
266
  'background-image': 'url(https://localhost.test/test-image.png)',
267
+ 'background-repeat': 'repeat',
268
+ 'background-attachment': 'scroll',
269
+ 'background-size': 'auto auto',
270
+ 'background-position': '0% 0%',
205
271
  },
206
272
  },
207
273
  {
208
274
  name: 'background (full)',
209
275
  prepare: () => {
210
- jest.mocked( isExperimentActive ).mockReturnValue( true );
211
276
  jest.mocked( getMediaAttachment ).mockImplementation(
212
277
  ( args ) => Promise.resolve( mockAttachmentData( args.id ) ) as never
213
278
  );
@@ -280,32 +345,7 @@ describe( 'styles prop resolver', () => {
280
345
  {
281
346
  name: 'filter',
282
347
  props: {
283
- filter: filterPropTypeUtil.create( [
284
- blurFilterPropTypeUtil.create( {
285
- radius: sizePropTypeUtil.create( { size: 1, unit: 'px' } ),
286
- } ),
287
- brightnessFilterPropTypeUtil.create( {
288
- amount: sizePropTypeUtil.create( { size: 90, unit: '%' } ),
289
- } ),
290
- contrastFilterPropTypeUtil.create( {
291
- contrast: sizePropTypeUtil.create( { size: 50, unit: '%' } ),
292
- } ),
293
- grayscaleFilterPropTypeUtil.create( {
294
- grayscale: sizePropTypeUtil.create( { size: 70, unit: '%' } ),
295
- } ),
296
- invertFilterPropTypeUtil.create( {
297
- invert: sizePropTypeUtil.create( { size: 60, unit: '%' } ),
298
- } ),
299
- sepiaFilterPropTypeUtil.create( {
300
- sepia: sizePropTypeUtil.create( { size: 30, unit: '%' } ),
301
- } ),
302
- saturateFilterPropTypeUtil.create( {
303
- saturate: sizePropTypeUtil.create( { size: 25, unit: '%' } ),
304
- } ),
305
- hueRotateFilterPropTypeUtil.create( {
306
- 'hue-rotate': sizePropTypeUtil.create( { size: 10, unit: 'deg' } ),
307
- } ),
308
- ] ),
348
+ filter: filters,
309
349
  },
310
350
  expected: {
311
351
  filter: 'blur(1px) brightness(90%) contrast(50%) grayscale(70%) invert(60%) sepia(30%) saturate(25%) hue-rotate(10deg)',
@@ -314,32 +354,7 @@ describe( 'styles prop resolver', () => {
314
354
  {
315
355
  name: 'backdrop-filter',
316
356
  props: {
317
- 'backdrop-filter': backdropFilterPropTypeUtil.create( [
318
- blurFilterPropTypeUtil.create( {
319
- radius: sizePropTypeUtil.create( { size: 2, unit: 'rem' } ),
320
- } ),
321
- brightnessFilterPropTypeUtil.create( {
322
- amount: sizePropTypeUtil.create( { size: 80, unit: '%' } ),
323
- } ),
324
- contrastFilterPropTypeUtil.create( {
325
- contrast: sizePropTypeUtil.create( { size: 50, unit: '%' } ),
326
- } ),
327
- grayscaleFilterPropTypeUtil.create( {
328
- grayscale: sizePropTypeUtil.create( { size: 70, unit: '%' } ),
329
- } ),
330
- invertFilterPropTypeUtil.create( {
331
- invert: sizePropTypeUtil.create( { size: 60, unit: '%' } ),
332
- } ),
333
- sepiaFilterPropTypeUtil.create( {
334
- sepia: sizePropTypeUtil.create( { size: 30, unit: '%' } ),
335
- } ),
336
- saturateFilterPropTypeUtil.create( {
337
- saturate: sizePropTypeUtil.create( { size: 25, unit: '%' } ),
338
- } ),
339
- hueRotateFilterPropTypeUtil.create( {
340
- 'hue-rotate': sizePropTypeUtil.create( { size: 10, unit: 'deg' } ),
341
- } ),
342
- ] ),
357
+ 'backdrop-filter': backDropFilters,
343
358
  },
344
359
  expected: {
345
360
  'backdrop-filter':
@@ -46,8 +46,8 @@ describe( '<StyleRenderer />', () => {
46
46
  const mockContainer = document.createElement( 'div' );
47
47
 
48
48
  const mockCssItems = [
49
- { id: 'style1', value: '.test { color: red; }' },
50
- { id: 'style2', value: '.test2 { color: blue; }' },
49
+ { id: 'style1', value: '.test { color: red; }', breakpoint: 'desktop' },
50
+ { id: 'style2', value: '.test2 { color: blue; }', breakpoint: 'desktop' },
51
51
  ];
52
52
 
53
53
  const mockLinkAttrs = [
@@ -19,7 +19,7 @@ export function StyleRenderer() {
19
19
  return (
20
20
  <Portal container={ container }>
21
21
  { styleItems.map( ( item ) => (
22
- <style data-e-style-id={ item.id } key={ item.id }>
22
+ <style data-e-style-id={ item.id } key={ `${ item.id }-${ item.breakpoint }` }>
23
23
  { item.value }
24
24
  </style>
25
25
  ) ) }
@@ -1,4 +1,5 @@
1
- import { createMockStyleDefinition, createMockStylesProvider } from 'test-utils';
1
+ import { createMockStyleDefinition, createMockStyleDefinitionWithVariants, createMockStylesProvider } from 'test-utils';
2
+ import { type StyleDefinition } from '@elementor/editor-styles';
2
3
  import { stylesRepository } from '@elementor/editor-styles-repository';
3
4
  import { registerDataHook } from '@elementor/editor-v1-adapters';
4
5
  import { act, renderHook } from '@testing-library/react';
@@ -26,12 +27,23 @@ jest.mock( '../use-style-renderer', () => ( {
26
27
  useStyleRenderer: jest.fn(),
27
28
  } ) );
28
29
 
30
+ jest.mock( '@elementor/editor-responsive', () => ( {
31
+ getBreakpoints: jest.fn().mockReturnValue( [
32
+ { id: 'desktop', label: 'Desktop' },
33
+ { id: 'tablet', label: 'Tablet' },
34
+ { id: 'mobile', label: 'Mobile' },
35
+ ] ),
36
+ } ) );
37
+
29
38
  describe( 'useStyleItems', () => {
30
39
  beforeEach( () => {
31
40
  jest.mocked( useStyleRenderer ).mockReturnValue(
32
- jest
33
- .fn()
34
- .mockImplementation( ( { styles } ) => styles.map( ( style: { id: string } ) => ( { id: style.id } ) ) )
41
+ jest.fn().mockImplementation( ( { styles } ) =>
42
+ styles.map( ( style: StyleDefinition ) => ( {
43
+ id: style.id,
44
+ breakpoint: style?.variants[ 0 ]?.meta.breakpoint || 'desktop',
45
+ } ) )
46
+ )
35
47
  );
36
48
  } );
37
49
 
@@ -71,7 +83,10 @@ describe( 'useStyleItems', () => {
71
83
  } );
72
84
 
73
85
  // Assert.
74
- expect( result.current ).toEqual( [ { id: 'style2' }, { id: 'style1' } ] );
86
+ expect( result.current ).toEqual( [
87
+ { id: 'style2', breakpoint: 'desktop' },
88
+ { id: 'style1', breakpoint: 'desktop' },
89
+ ] );
75
90
 
76
91
  // Act.
77
92
  await act( async () => {
@@ -83,7 +98,12 @@ describe( 'useStyleItems', () => {
83
98
  } );
84
99
 
85
100
  // Assert.
86
- expect( result.current ).toEqual( [ { id: 'style4' }, { id: 'style3' }, { id: 'style2' }, { id: 'style1' } ] );
101
+ expect( result.current ).toEqual( [
102
+ { id: 'style4', breakpoint: 'desktop' },
103
+ { id: 'style3', breakpoint: 'desktop' },
104
+ { id: 'style2', breakpoint: 'desktop' },
105
+ { id: 'style1', breakpoint: 'desktop' },
106
+ ] );
87
107
  } );
88
108
 
89
109
  it( 'should return style items when attach-preview command is triggered', async () => {
@@ -128,6 +148,85 @@ describe( 'useStyleItems', () => {
128
148
  } );
129
149
 
130
150
  // Assert.
131
- expect( result.current ).toEqual( [ { id: 'style4' }, { id: 'style3' }, { id: 'style2' }, { id: 'style1' } ] );
151
+ expect( result.current ).toEqual( [
152
+ { id: 'style4', breakpoint: 'desktop' },
153
+ { id: 'style3', breakpoint: 'desktop' },
154
+ { id: 'style2', breakpoint: 'desktop' },
155
+ { id: 'style1', breakpoint: 'desktop' },
156
+ ] );
157
+ } );
158
+
159
+ it( 'should return style items ordered by provider priority and breakpoint', async () => {
160
+ // Arrange.
161
+ const mockProvider1 = createMockStylesProvider(
162
+ {
163
+ key: 'provider1',
164
+ priority: 2,
165
+ },
166
+ [
167
+ createMockStyleDefinitionWithVariants( {
168
+ id: 'style1',
169
+ variants: [
170
+ {
171
+ meta: { breakpoint: 'mobile', state: null },
172
+ props: {
173
+ padding: '10px',
174
+ },
175
+ custom_css: null,
176
+ },
177
+ {
178
+ meta: { breakpoint: 'mobile', state: 'hover' },
179
+ props: {
180
+ padding: '20px',
181
+ },
182
+ custom_css: null,
183
+ },
184
+ ],
185
+ } ),
186
+ createMockStyleDefinition( { id: 'style2' } ),
187
+ ]
188
+ );
189
+
190
+ const mockProvider2 = createMockStylesProvider(
191
+ {
192
+ key: 'provider2',
193
+ priority: 1,
194
+ },
195
+ [
196
+ createMockStyleDefinition( { id: 'style3', meta: { breakpoint: 'tablet', state: null } } ),
197
+ createMockStyleDefinition( { id: 'style4' } ),
198
+ ]
199
+ );
200
+
201
+ jest.mocked( stylesRepository ).getProviders.mockReturnValue( [ mockProvider1, mockProvider2 ] );
202
+
203
+ let attachPreviewCallback: () => Promise< void >;
204
+
205
+ jest.mocked( registerDataHook ).mockImplementation( ( position, command, callback ) => {
206
+ if ( command === 'editor/documents/attach-preview' && position === 'after' ) {
207
+ attachPreviewCallback = callback as never;
208
+ }
209
+
210
+ return null as never;
211
+ } );
212
+
213
+ // Act.
214
+ const { result } = renderHook( () => useStyleItems() );
215
+
216
+ // Assert.
217
+ expect( result.current ).toEqual( [] );
218
+
219
+ // Act.
220
+ await act( async () => {
221
+ await attachPreviewCallback?.();
222
+ } );
223
+
224
+ // Assert.
225
+ expect( result.current ).toEqual( [
226
+ { id: 'style4', breakpoint: 'desktop' },
227
+ { id: 'style2', breakpoint: 'desktop' },
228
+ { id: 'style3', breakpoint: 'tablet' },
229
+ { id: 'style1', breakpoint: 'mobile' },
230
+ ] );
132
231
  } );
133
232
  } );
@@ -1,8 +1,9 @@
1
1
  import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react';
2
+ import { type BreakpointId, getBreakpoints } from '@elementor/editor-responsive';
2
3
  import { type StylesProvider, stylesRepository } from '@elementor/editor-styles-repository';
3
4
  import { registerDataHook } from '@elementor/editor-v1-adapters';
4
5
 
5
- import { type StyleItem, type StyleRenderer } from '../renderers/create-styles-renderer';
6
+ import { type RendererStyleDefinition, type StyleItem, type StyleRenderer } from '../renderers/create-styles-renderer';
6
7
  import { abortPreviousRuns } from '../utils/abort-previous-runs';
7
8
  import { signalizedProcess } from '../utils/signalized-process';
8
9
  import { useOnMount } from './use-on-mount';
@@ -52,9 +53,23 @@ export function useStyleItems() {
52
53
  } );
53
54
  } );
54
55
 
55
- return Object.values( styleItems )
56
- .sort( ( { provider: providerA }, { provider: providerB } ) => providerA.priority - providerB.priority )
57
- .flatMap( ( { items } ) => items );
56
+ const breakpointsOrder = getBreakpoints().map( ( breakpoint ) => breakpoint.id );
57
+
58
+ return useMemo(
59
+ () =>
60
+ Object.values( styleItems )
61
+ .sort( ( { provider: providerA }, { provider: providerB } ) => providerA.priority - providerB.priority )
62
+ .flatMap( ( { items } ) => items )
63
+ .sort( ( { breakpoint: breakpointA }, { breakpoint: breakpointB } ) => {
64
+ return (
65
+ breakpointsOrder.indexOf( breakpointA as BreakpointId ) -
66
+ breakpointsOrder.indexOf( breakpointB as BreakpointId )
67
+ );
68
+ } ),
69
+ // eslint-disable-next-line
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ [ styleItems, breakpointsOrder.join( '-' ) ]
72
+ );
58
73
  }
59
74
 
60
75
  type CreateProviderSubscriberArgs = {
@@ -78,7 +93,7 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems }: Cr
78
93
  };
79
94
  } );
80
95
 
81
- return renderStyles( { styles, signal } );
96
+ return renderStyles( { styles: breakToBreakpoints( styles ), signal } );
82
97
  } )
83
98
  .then( ( items ) => {
84
99
  setStyleItems( ( prev ) => ( {
@@ -88,4 +103,31 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems }: Cr
88
103
  } )
89
104
  .execute()
90
105
  );
106
+
107
+ function breakToBreakpoints( styles: RendererStyleDefinition[] ) {
108
+ return Object.values(
109
+ styles.reduce(
110
+ ( acc, style ) => {
111
+ style.variants.forEach( ( variant ) => {
112
+ const breakpoint = variant.meta.breakpoint || 'desktop';
113
+
114
+ if ( ! acc[ style.id ] ) {
115
+ acc[ style.id ] = {};
116
+ }
117
+
118
+ if ( ! acc[ style.id ][ breakpoint ] ) {
119
+ acc[ style.id ][ breakpoint ] = {
120
+ ...style,
121
+ variants: [],
122
+ };
123
+ }
124
+
125
+ acc[ style.id ][ breakpoint ].variants.push( variant );
126
+ } );
127
+ return acc;
128
+ },
129
+ {} as Record< string, Record< string, RendererStyleDefinition > >
130
+ )
131
+ ).flatMap( ( breakpointMap ) => Object.values( breakpointMap ) );
132
+ }
91
133
  }
@@ -1,4 +1,5 @@
1
1
  import { settingsTransformersRegistry } from './settings-transformers-registry';
2
+ import { attributesTransformer } from './transformers/settings/attributes-transformer';
2
3
  import { createClassesTransformer } from './transformers/settings/classes-transformer';
3
4
  import { linkTransformer } from './transformers/settings/link-transformer';
4
5
  import { imageSrcTransformer } from './transformers/shared/image-src-transformer';
@@ -11,5 +12,6 @@ export function initSettingsTransformers() {
11
12
  .register( 'link', linkTransformer )
12
13
  .register( 'image', imageTransformer )
13
14
  .register( 'image-src', imageSrcTransformer )
15
+ .register( 'key-value-array', attributesTransformer )
14
16
  .registerFallback( plainTransformer );
15
17
  }
@@ -12,12 +12,17 @@ import { colorStopTransformer } from './transformers/styles/color-stop-transform
12
12
  import { createCombineArrayTransformer } from './transformers/styles/create-combine-array-transformer';
13
13
  import { createMultiPropsTransformer } from './transformers/styles/create-multi-props-transformer';
14
14
  import { filterTransformer } from './transformers/styles/filter-transformer';
15
+ import { flexTransformer } from './transformers/styles/flex-transformer';
15
16
  import { positionTransformer } from './transformers/styles/position-transformer';
16
17
  import { shadowTransformer } from './transformers/styles/shadow-transformer';
17
18
  import { sizeTransformer } from './transformers/styles/size-transformer';
18
19
  import { strokeTransformer } from './transformers/styles/stroke-transformer';
19
20
  import { transformMoveTransformer } from './transformers/styles/transform-move-transformer';
21
+ import { transformRotateTransformer } from './transformers/styles/transform-rotate-transformer';
22
+ import { transformScaleTransformer } from './transformers/styles/transform-scale-transformer';
23
+ import { transformSkewTransformer } from './transformers/styles/transform-skew-transformer';
20
24
  import { transformTransformer } from './transformers/styles/transform-transformer';
25
+ import { transitionTransformer } from './transformers/styles/transition-transformer';
21
26
 
22
27
  export function initStyleTransformers() {
23
28
  styleTransformersRegistry
@@ -47,11 +52,16 @@ export function initStyleTransformers() {
47
52
  .register( 'image', imageTransformer )
48
53
  .register( 'object-position', positionTransformer )
49
54
  .register( 'transform-move', transformMoveTransformer )
55
+ .register( 'transform-scale', transformScaleTransformer )
56
+ .register( 'transform-rotate', transformRotateTransformer )
57
+ .register( 'transform-skew', transformSkewTransformer )
50
58
  .register( 'transform', transformTransformer )
59
+ .register( 'transition', transitionTransformer )
51
60
  .register(
52
61
  'layout-direction',
53
62
  createMultiPropsTransformer( [ 'row', 'column' ], ( { propKey, key } ) => `${ key }-${ propKey }` )
54
63
  )
64
+ .register( 'flex', flexTransformer )
55
65
  .register(
56
66
  'border-width',
57
67
  createMultiPropsTransformer(
@@ -3,10 +3,12 @@
3
3
  exports[`renderStyles should render styles 1`] = `
4
4
  [
5
5
  {
6
+ "breakpoint": "desktop",
6
7
  "id": "test",
7
8
  "value": ".test{font-size:10px;}.test:hover{font-size:20px;}@media(max-width:992px){.test{font-size:30px;}}@media(max-width:768px){.test:focus{font-size:40px;}}",
8
9
  },
9
10
  {
11
+ "breakpoint": "desktop",
10
12
  "id": "test-2",
11
13
  "value": ".custom-name{font-size:50px;}",
12
14
  },
@@ -1,8 +1,14 @@
1
1
  /* eslint-disable testing-library/render-result-naming-convention */
2
2
  import type { BreakpointsMap } from '@elementor/editor-responsive';
3
+ import { encodeString } from '@elementor/utils';
3
4
 
4
5
  import { createStylesRenderer, type RendererStyleDefinition } from '../create-styles-renderer';
5
6
 
7
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
8
+ ...jest.requireActual( '@elementor/editor-v1-adapters' ),
9
+ isExperimentActive: jest.fn().mockReturnValue( true ),
10
+ } ) );
11
+
6
12
  describe( 'renderStyles', () => {
7
13
  it( 'should render styles', async () => {
8
14
  // Arrange.
@@ -15,18 +21,22 @@ describe( 'renderStyles', () => {
15
21
  {
16
22
  meta: { breakpoint: null, state: null },
17
23
  props: { 'font-size': '10px' },
24
+ custom_css: null,
18
25
  },
19
26
  {
20
27
  meta: { breakpoint: null, state: 'hover' },
21
28
  props: { 'font-size': '20px' },
29
+ custom_css: null,
22
30
  },
23
31
  {
24
32
  meta: { breakpoint: 'tablet', state: null },
25
33
  props: { 'font-size': '30px' },
34
+ custom_css: null,
26
35
  },
27
36
  {
28
37
  meta: { breakpoint: 'mobile', state: 'focus' },
29
38
  props: { 'font-size': '40px' },
39
+ custom_css: null,
30
40
  },
31
41
  ],
32
42
  };
@@ -40,6 +50,7 @@ describe( 'renderStyles', () => {
40
50
  {
41
51
  meta: { breakpoint: null, state: null },
42
52
  props: { 'font-size': '50px' },
53
+ custom_css: null,
43
54
  },
44
55
  ],
45
56
  };
@@ -79,6 +90,7 @@ describe( 'renderStyles', () => {
79
90
  {
80
91
  meta: { breakpoint: null, state: null },
81
92
  props: { 'font-size': '24px' },
93
+ custom_css: null,
82
94
  },
83
95
  ],
84
96
  };
@@ -97,9 +109,95 @@ describe( 'renderStyles', () => {
97
109
  // Assert.
98
110
  expect( result ).toEqual( [
99
111
  {
112
+ breakpoint: 'desktop',
100
113
  id: 'test',
101
114
  value: '.elementor-prefix .test{font-size:24px;}',
102
115
  },
103
116
  ] );
104
117
  } );
105
118
  } );
119
+
120
+ describe( 'custom_css rendering', () => {
121
+ it( 'should not render custom_css if raw is empty', async () => {
122
+ // Arrange.
123
+ const styleDef: RendererStyleDefinition = {
124
+ id: 'test',
125
+ type: 'class',
126
+ cssName: 'test',
127
+ label: 'Test',
128
+ variants: [ { meta: { breakpoint: null, state: null }, props: {}, custom_css: { raw: '' } } ],
129
+ };
130
+
131
+ // Act.
132
+ const renderStyles = createStylesRenderer( { breakpoints: {} as BreakpointsMap, resolve: async () => ( {} ) } );
133
+ const result = await renderStyles( { styles: [ styleDef ] } );
134
+
135
+ // Assert.
136
+ expect( result[ 0 ].value ).not.toContain( '{;}' );
137
+ } );
138
+
139
+ it( 'should not render custom_css if raw is whitespace', async () => {
140
+ // Arrange.
141
+ const styleDef: RendererStyleDefinition = {
142
+ id: 'test',
143
+ type: 'class',
144
+ cssName: 'test',
145
+ label: 'Test',
146
+ variants: [
147
+ { meta: { breakpoint: null, state: null }, props: {}, custom_css: { raw: encodeString( ' \n\t' ) } },
148
+ ],
149
+ };
150
+
151
+ // Act.
152
+ const renderStyles = createStylesRenderer( { breakpoints: {} as BreakpointsMap, resolve: async () => ( {} ) } );
153
+ const result = await renderStyles( { styles: [ styleDef ] } );
154
+
155
+ // Assert.
156
+ expect( result[ 0 ].value ).not.toContain( '{;}' );
157
+ } );
158
+
159
+ it( 'should not render custom_css if raw is invalid encoded string', async () => {
160
+ // Arrange.
161
+ const styleDef: RendererStyleDefinition = {
162
+ id: 'test',
163
+ type: 'class',
164
+ cssName: 'test',
165
+ label: 'Test',
166
+ variants: [
167
+ { meta: { breakpoint: null, state: null }, props: {}, custom_css: { raw: 'I cannot be decoded' } },
168
+ ],
169
+ };
170
+
171
+ // Act.
172
+ const renderStyles = createStylesRenderer( { breakpoints: {} as BreakpointsMap, resolve: async () => ( {} ) } );
173
+ const result = await renderStyles( { styles: [ styleDef ] } );
174
+
175
+ // Assert.
176
+ expect( result[ 0 ].value ).not.toContain( '{;}' );
177
+ } );
178
+
179
+ it( 'should render custom_css if raw is valid base64 encoded string', async () => {
180
+ // Arrange.
181
+ const css = 'transition: 100s; \n .inner-selector { color: red; }';
182
+ const styleDef: RendererStyleDefinition = {
183
+ id: 'test',
184
+ type: 'class',
185
+ cssName: 'test',
186
+ label: 'Test',
187
+ variants: [
188
+ {
189
+ meta: { breakpoint: null, state: null },
190
+ props: {},
191
+ custom_css: { raw: encodeString( css ) },
192
+ },
193
+ ],
194
+ };
195
+
196
+ // Act.
197
+ const renderStyles = createStylesRenderer( { breakpoints: {} as BreakpointsMap, resolve: async () => ( {} ) } );
198
+ const result = await renderStyles( { styles: [ styleDef ] } );
199
+
200
+ // Assert.
201
+ expect( result[ 0 ].value ).toContain( css );
202
+ } );
203
+ } );