@elementor/editor-canvas 4.2.0-861 → 4.2.0-863

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.
package/dist/index.js CHANGED
@@ -4083,6 +4083,7 @@ var validateInput = {
4083
4083
  };
4084
4084
 
4085
4085
  // src/composition-builder/composition-builder.ts
4086
+ var CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = "createElement did not return an element container with a model.";
4086
4087
  var CompositionBuilder = class _CompositionBuilder {
4087
4088
  elementConfig = {};
4088
4089
  elementStylesConfig = {};
@@ -4301,6 +4302,9 @@ ${childTypeErrors.join("\n")}`);
4301
4302
  model: modelTree,
4302
4303
  options: { useHistory: false }
4303
4304
  });
4305
+ if (!newElement?.model) {
4306
+ throw new Error(CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE);
4307
+ }
4304
4308
  this.rootContainers.push(newElement);
4305
4309
  await this.awaitViewRender(newElement);
4306
4310
  } catch (e) {
@@ -4766,6 +4770,13 @@ Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property fr
4766
4770
  configureElementToolPrompt.instruction(
4767
4771
  "The $$type property is MANDATORY for every value; it is required to parse the value and apply application-level effects."
4768
4772
  );
4773
+ configureElementToolPrompt.instruction(`
4774
+ V4 only: If MCP fails, give manual steps using V4 UI.
4775
+
4776
+ V4 Editor structure:
4777
+ Panel tabs: General (\u2192 Settings section: ID, Tag, Link), Style, Interactions.
4778
+ NO Advanced tab. Never mention Advanced tab.
4779
+ `);
4769
4780
  return configureElementToolPrompt.prompt();
4770
4781
  };
4771
4782
  var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt2();
package/dist/index.mjs CHANGED
@@ -4078,6 +4078,7 @@ var validateInput = {
4078
4078
  };
4079
4079
 
4080
4080
  // src/composition-builder/composition-builder.ts
4081
+ var CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = "createElement did not return an element container with a model.";
4081
4082
  var CompositionBuilder = class _CompositionBuilder {
4082
4083
  elementConfig = {};
4083
4084
  elementStylesConfig = {};
@@ -4296,6 +4297,9 @@ ${childTypeErrors.join("\n")}`);
4296
4297
  model: modelTree,
4297
4298
  options: { useHistory: false }
4298
4299
  });
4300
+ if (!newElement?.model) {
4301
+ throw new Error(CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE);
4302
+ }
4299
4303
  this.rootContainers.push(newElement);
4300
4304
  await this.awaitViewRender(newElement);
4301
4305
  } catch (e) {
@@ -4761,6 +4765,13 @@ Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property fr
4761
4765
  configureElementToolPrompt.instruction(
4762
4766
  "The $$type property is MANDATORY for every value; it is required to parse the value and apply application-level effects."
4763
4767
  );
4768
+ configureElementToolPrompt.instruction(`
4769
+ V4 only: If MCP fails, give manual steps using V4 UI.
4770
+
4771
+ V4 Editor structure:
4772
+ Panel tabs: General (\u2192 Settings section: ID, Tag, Link), Style, Interactions.
4773
+ NO Advanced tab. Never mention Advanced tab.
4774
+ `);
4764
4775
  return configureElementToolPrompt.prompt();
4765
4776
  };
4766
4777
  var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt2();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-canvas",
3
3
  "description": "Elementor Editor Canvas",
4
- "version": "4.2.0-861",
4
+ "version": "4.2.0-863",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,25 +37,25 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "4.2.0-861",
40
+ "@elementor/editor": "4.2.0-863",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.2.0-861",
43
- "@elementor/editor-documents": "4.2.0-861",
44
- "@elementor/editor-elements": "4.2.0-861",
45
- "@elementor/editor-interactions": "4.2.0-861",
46
- "@elementor/editor-mcp": "4.2.0-861",
47
- "@elementor/editor-notifications": "4.2.0-861",
48
- "@elementor/editor-props": "4.2.0-861",
49
- "@elementor/editor-responsive": "4.2.0-861",
50
- "@elementor/editor-styles": "4.2.0-861",
51
- "@elementor/editor-styles-repository": "4.2.0-861",
52
- "@elementor/editor-ui": "4.2.0-861",
53
- "@elementor/editor-v1-adapters": "4.2.0-861",
54
- "@elementor/schema": "4.2.0-861",
55
- "@elementor/twing": "4.2.0-861",
42
+ "@elementor/editor-controls": "4.2.0-863",
43
+ "@elementor/editor-documents": "4.2.0-863",
44
+ "@elementor/editor-elements": "4.2.0-863",
45
+ "@elementor/editor-interactions": "4.2.0-863",
46
+ "@elementor/editor-mcp": "4.2.0-863",
47
+ "@elementor/editor-notifications": "4.2.0-863",
48
+ "@elementor/editor-props": "4.2.0-863",
49
+ "@elementor/editor-responsive": "4.2.0-863",
50
+ "@elementor/editor-styles": "4.2.0-863",
51
+ "@elementor/editor-styles-repository": "4.2.0-863",
52
+ "@elementor/editor-ui": "4.2.0-863",
53
+ "@elementor/editor-v1-adapters": "4.2.0-863",
54
+ "@elementor/schema": "4.2.0-863",
55
+ "@elementor/twing": "4.2.0-863",
56
56
  "@elementor/ui": "1.37.5",
57
- "@elementor/utils": "4.2.0-861",
58
- "@elementor/wp-media": "4.2.0-861",
57
+ "@elementor/utils": "4.2.0-863",
58
+ "@elementor/wp-media": "4.2.0-863",
59
59
  "@floating-ui/react": "^0.27.5",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -0,0 +1,190 @@
1
+ import { type V1Element } from '@elementor/editor-elements';
2
+
3
+ import { CompositionBuilder } from '../composition-builder';
4
+
5
+ const ROOT_CHILD_TAG = 'column';
6
+ const GENERATED_ELEMENT_ID = 'generated-element-id';
7
+ const CONFIG_ID = 'cfg-a';
8
+ const ELEMENT_CONFIG_PROPERTY = 'title';
9
+ const ELEMENT_CONFIG_VALUE = 'configured-value';
10
+
11
+ const xmlStringWithConfiguration = `<${ ROOT_CHILD_TAG } configuration-id="${ CONFIG_ID }" />`;
12
+
13
+ const createElementConfigPayload = () => ( {
14
+ [ CONFIG_ID ]: {
15
+ [ ELEMENT_CONFIG_PROPERTY ]: ELEMENT_CONFIG_VALUE,
16
+ },
17
+ } );
18
+
19
+ const createMinimalWidgetsCache = () =>
20
+ ( {
21
+ [ ROOT_CHILD_TAG ]: {
22
+ elType: 'column',
23
+ },
24
+ } ) as Record< string, { elType: string } >;
25
+
26
+ const createMockRootContainer = (): V1Element =>
27
+ ( {
28
+ id: 'root',
29
+ model: { get: jest.fn(), set: jest.fn(), toJSON: jest.fn() },
30
+ settings: { get: jest.fn(), set: jest.fn(), toJSON: jest.fn() },
31
+ children: [],
32
+ } ) as unknown as V1Element;
33
+
34
+ const createMockPartialContainer = ( id: string ): V1Element =>
35
+ ( {
36
+ id,
37
+ model: { get: jest.fn(), set: jest.fn(), toJSON: jest.fn() },
38
+ settings: { get: jest.fn(), set: jest.fn(), toJSON: jest.fn() },
39
+ children: [],
40
+ } ) as unknown as V1Element;
41
+
42
+ describe( 'CompositionBuilder.build createElement failure cleanup', () => {
43
+ it( 'calls deleteElement when createElement fails and getContainer returns a container', async () => {
44
+ // Arrange
45
+ const partialContainer = createMockPartialContainer( GENERATED_ELEMENT_ID );
46
+ const deleteElement = jest.fn();
47
+ const doUpdateElementProperty = jest.fn();
48
+ const createElement = jest.fn().mockImplementation( () => {
49
+ throw new Error( 'create failed' );
50
+ } );
51
+ const getContainer = jest
52
+ .fn()
53
+ .mockImplementation( ( id: string ) => ( id === GENERATED_ELEMENT_ID ? partialContainer : undefined ) );
54
+ const builder = CompositionBuilder.fromXMLString( xmlStringWithConfiguration, {
55
+ createElement,
56
+ deleteElement,
57
+ getContainer,
58
+ generateElementId: jest.fn().mockReturnValue( GENERATED_ELEMENT_ID ),
59
+ getWidgetsCache: jest.fn().mockReturnValue( createMinimalWidgetsCache() ),
60
+ doUpdateElementProperty,
61
+ } );
62
+ builder.setElementConfig( createElementConfigPayload() );
63
+
64
+ // Act
65
+ await expect( builder.build( createMockRootContainer() ) ).rejects.toThrow( 'create failed' );
66
+
67
+ // Assert
68
+ expect( getContainer ).toHaveBeenCalledWith( GENERATED_ELEMENT_ID );
69
+ expect( deleteElement ).toHaveBeenCalledTimes( 1 );
70
+ expect( deleteElement ).toHaveBeenCalledWith( { container: partialContainer } );
71
+ expect( doUpdateElementProperty ).not.toHaveBeenCalled();
72
+ } );
73
+
74
+ it( 'does not call deleteElement when createElement fails and getContainer returns undefined', async () => {
75
+ // Arrange
76
+ const deleteElement = jest.fn();
77
+ const doUpdateElementProperty = jest.fn();
78
+ const createElement = jest.fn().mockImplementation( () => {
79
+ throw new Error( 'create failed' );
80
+ } );
81
+ const getContainer = jest.fn().mockReturnValue( undefined );
82
+ const builder = CompositionBuilder.fromXMLString( xmlStringWithConfiguration, {
83
+ createElement,
84
+ deleteElement,
85
+ getContainer,
86
+ generateElementId: jest.fn().mockReturnValue( GENERATED_ELEMENT_ID ),
87
+ getWidgetsCache: jest.fn().mockReturnValue( createMinimalWidgetsCache() ),
88
+ doUpdateElementProperty,
89
+ } );
90
+ builder.setElementConfig( createElementConfigPayload() );
91
+
92
+ // Act
93
+ await expect( builder.build( createMockRootContainer() ) ).rejects.toThrow( 'create failed' );
94
+
95
+ // Assert
96
+ expect( getContainer ).toHaveBeenCalledWith( GENERATED_ELEMENT_ID );
97
+ expect( deleteElement ).not.toHaveBeenCalled();
98
+ expect( doUpdateElementProperty ).not.toHaveBeenCalled();
99
+ } );
100
+
101
+ it( 'calls deleteElement when createElement returns without a model', async () => {
102
+ // Arrange
103
+ const partialContainer = createMockPartialContainer( GENERATED_ELEMENT_ID );
104
+ const deleteElement = jest.fn();
105
+ const doUpdateElementProperty = jest.fn();
106
+ const createElement = jest.fn().mockReturnValue( {} as V1Element );
107
+ const getContainer = jest
108
+ .fn()
109
+ .mockImplementation( ( id: string ) => ( id === GENERATED_ELEMENT_ID ? partialContainer : undefined ) );
110
+ const builder = CompositionBuilder.fromXMLString( xmlStringWithConfiguration, {
111
+ createElement,
112
+ deleteElement,
113
+ getContainer,
114
+ generateElementId: jest.fn().mockReturnValue( GENERATED_ELEMENT_ID ),
115
+ getWidgetsCache: jest.fn().mockReturnValue( createMinimalWidgetsCache() ),
116
+ doUpdateElementProperty,
117
+ } );
118
+ builder.setElementConfig( createElementConfigPayload() );
119
+
120
+ // Act
121
+ await expect( builder.build( createMockRootContainer() ) ).rejects.toThrow(
122
+ 'createElement did not return an element container with a model.'
123
+ );
124
+
125
+ // Assert
126
+ expect( getContainer ).toHaveBeenCalledWith( GENERATED_ELEMENT_ID );
127
+ expect( deleteElement ).toHaveBeenCalledTimes( 1 );
128
+ expect( deleteElement ).toHaveBeenCalledWith( { container: partialContainer } );
129
+ expect( doUpdateElementProperty ).not.toHaveBeenCalled();
130
+ } );
131
+ } );
132
+
133
+ describe( 'CompositionBuilder.build applyProperties after create', () => {
134
+ it( 'calls doUpdateElementProperty when create succeeds with element config', async () => {
135
+ // Arrange
136
+ const deleteElement = jest.fn();
137
+ const doUpdateElementProperty = jest.fn();
138
+ const createdElement = createMockPartialContainer( GENERATED_ELEMENT_ID );
139
+ const createElement = jest.fn().mockReturnValue( createdElement );
140
+ const getContainer = jest
141
+ .fn()
142
+ .mockImplementation( ( id: string ) => ( id === GENERATED_ELEMENT_ID ? createdElement : undefined ) );
143
+ const builder = CompositionBuilder.fromXMLString( xmlStringWithConfiguration, {
144
+ createElement,
145
+ deleteElement,
146
+ getContainer,
147
+ generateElementId: jest.fn().mockReturnValue( GENERATED_ELEMENT_ID ),
148
+ getWidgetsCache: jest.fn().mockReturnValue( createMinimalWidgetsCache() ),
149
+ doUpdateElementProperty,
150
+ } );
151
+ builder.setElementConfig( createElementConfigPayload() );
152
+
153
+ // Act
154
+ await builder.build( createMockRootContainer() );
155
+
156
+ // Assert
157
+ expect( deleteElement ).not.toHaveBeenCalled();
158
+ expect( createElement ).toHaveBeenCalledTimes( 1 );
159
+ expect( doUpdateElementProperty ).toHaveBeenCalledTimes( 1 );
160
+ expect( doUpdateElementProperty ).toHaveBeenCalledWith( {
161
+ elementId: GENERATED_ELEMENT_ID,
162
+ propertyName: ELEMENT_CONFIG_PROPERTY,
163
+ propertyValue: ELEMENT_CONFIG_VALUE,
164
+ elementType: ROOT_CHILD_TAG,
165
+ } );
166
+ } );
167
+
168
+ it( 'does not call doUpdateElementProperty when create succeeds without element config', async () => {
169
+ // Arrange
170
+ const deleteElement = jest.fn();
171
+ const doUpdateElementProperty = jest.fn();
172
+ const createdElement = createMockPartialContainer( GENERATED_ELEMENT_ID );
173
+ const createElement = jest.fn().mockReturnValue( createdElement );
174
+ const builder = CompositionBuilder.fromXMLString( `<${ ROOT_CHILD_TAG } />`, {
175
+ createElement,
176
+ deleteElement,
177
+ getContainer: jest.fn(),
178
+ generateElementId: jest.fn().mockReturnValue( GENERATED_ELEMENT_ID ),
179
+ getWidgetsCache: jest.fn().mockReturnValue( createMinimalWidgetsCache() ),
180
+ doUpdateElementProperty,
181
+ } );
182
+
183
+ // Act
184
+ await builder.build( createMockRootContainer() );
185
+
186
+ // Assert
187
+ expect( deleteElement ).not.toHaveBeenCalled();
188
+ expect( doUpdateElementProperty ).not.toHaveBeenCalled();
189
+ } );
190
+ } );
@@ -17,6 +17,8 @@ import { validateInput } from '../mcp/utils/validate-input';
17
17
  type AnyValue = z.infer< z.ZodTypeAny >;
18
18
  type AnyConfig = Record< string, Record< string, AnyValue > >;
19
19
 
20
+ const CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = 'createElement did not return an element container with a model.';
21
+
20
22
  type API = {
21
23
  createElement: typeof createElement;
22
24
  deleteElement: typeof deleteElement;
@@ -296,6 +298,9 @@ export class CompositionBuilder {
296
298
  model: modelTree as CreateElementParams[ 'model' ],
297
299
  options: { useHistory: false },
298
300
  } );
301
+ if ( ! newElement?.model ) {
302
+ throw new Error( CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE );
303
+ }
299
304
  this.rootContainers.push( newElement );
300
305
  await this.awaitViewRender( newElement );
301
306
  } catch ( e: unknown ) {
@@ -117,6 +117,14 @@ Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property fr
117
117
  'The $$type property is MANDATORY for every value; it is required to parse the value and apply application-level effects.'
118
118
  );
119
119
 
120
+ configureElementToolPrompt.instruction( `
121
+ V4 only: If MCP fails, give manual steps using V4 UI.
122
+
123
+ V4 Editor structure:
124
+ Panel tabs: General (→ Settings section: ID, Tag, Link), Style, Interactions.
125
+ NO Advanced tab. Never mention Advanced tab.
126
+ ` );
127
+
120
128
  return configureElementToolPrompt.prompt();
121
129
  };
122
130