@elementor/editor-canvas 4.1.0-751 → 4.1.0-753

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
@@ -968,22 +968,30 @@ function useStyleItems() {
968
968
  const [styleItems, setStyleItems] = (0, import_react10.useState)({});
969
969
  const styleItemsCacheRef = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
970
970
  const providerAndSubscribers = (0, import_react10.useMemo)(() => {
971
- return import_editor_styles_repository2.stylesRepository.getProviders().map((provider) => {
972
- const providerKey = provider.getKey();
971
+ const createEmptyCache = () => {
972
+ return { orderedIds: [], itemsById: /* @__PURE__ */ new Map() };
973
+ };
974
+ const getCache = (provider) => {
975
+ const providerKey = safeGetKey(provider);
976
+ if (!providerKey) {
977
+ return createEmptyCache();
978
+ }
973
979
  if (!styleItemsCacheRef.current.has(providerKey)) {
974
- styleItemsCacheRef.current.set(providerKey, { orderedIds: [], itemsById: /* @__PURE__ */ new Map() });
980
+ styleItemsCacheRef.current.set(providerKey, createEmptyCache());
975
981
  }
976
- const cache = styleItemsCacheRef.current.get(providerKey);
977
- return {
982
+ return styleItemsCacheRef.current.get(providerKey);
983
+ };
984
+ return import_editor_styles_repository2.stylesRepository.getProviders().map(
985
+ (provider) => ({
978
986
  provider,
979
987
  subscriber: createProviderSubscriber2({
980
988
  provider,
981
989
  renderStyles,
982
990
  setStyleItems,
983
- cache
991
+ getCache: () => getCache(provider)
984
992
  })
985
- };
986
- });
993
+ })
994
+ );
987
995
  }, [renderStyles]);
988
996
  (0, import_react10.useEffect)(() => {
989
997
  const unsubscribes = providerAndSubscribers.map(
@@ -1024,18 +1032,26 @@ function stateSorter({ state: stateA }, { state: stateB }) {
1024
1032
  function createBreakpointSorter(breakpointsOrder) {
1025
1033
  return ({ breakpoint: breakpointA }, { breakpoint: breakpointB }) => breakpointsOrder.indexOf(breakpointA) - breakpointsOrder.indexOf(breakpointB);
1026
1034
  }
1027
- function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cache }) {
1035
+ function safeGetKey(provider) {
1036
+ try {
1037
+ return provider.getKey();
1038
+ } catch {
1039
+ return null;
1040
+ }
1041
+ }
1042
+ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, getCache }) {
1028
1043
  return abortPreviousRuns(
1029
1044
  (abortController, previous, current) => signalizedProcess(abortController.signal).then((_, signal) => {
1045
+ const cache = getCache();
1030
1046
  const hasDiffInfo = current !== void 0 && previous !== void 0;
1031
1047
  const hasCache = cache.orderedIds.length > 0;
1032
1048
  if (hasCache && provider.isPregeneratedLink) {
1033
1049
  removeProviderPregeneratedLinks(provider.getKey(), provider.isPregeneratedLink);
1034
1050
  }
1035
1051
  if (hasDiffInfo && hasCache) {
1036
- return updateItems(previous, current, signal);
1052
+ return updateItems(cache, previous, current, signal);
1037
1053
  }
1038
- return createItems(signal);
1054
+ return createItems(cache, signal);
1039
1055
  }).then((items) => {
1040
1056
  setStyleItems((prev) => ({
1041
1057
  ...prev,
@@ -1043,7 +1059,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
1043
1059
  }));
1044
1060
  }).execute()
1045
1061
  );
1046
- async function updateItems(previous, current, signal) {
1062
+ async function updateItems(cache, previous, current, signal) {
1047
1063
  const changedIds = getChangedStyleIds(previous, current);
1048
1064
  cache.orderedIds = provider.actions.all().map((style) => style.id).reverse();
1049
1065
  if (changedIds.length > 0) {
@@ -1058,7 +1074,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
1058
1074
  }
1059
1075
  return getOrderedItems(cache);
1060
1076
  }
1061
- async function createItems(signal) {
1077
+ async function createItems(cache, signal) {
1062
1078
  const allStyles = provider.actions.all();
1063
1079
  const styles = [...allStyles].reverse().map((style) => {
1064
1080
  return {
package/dist/index.mjs CHANGED
@@ -934,22 +934,30 @@ function useStyleItems() {
934
934
  const [styleItems, setStyleItems] = useState3({});
935
935
  const styleItemsCacheRef = useRef2(/* @__PURE__ */ new Map());
936
936
  const providerAndSubscribers = useMemo4(() => {
937
- return stylesRepository2.getProviders().map((provider) => {
938
- const providerKey = provider.getKey();
937
+ const createEmptyCache = () => {
938
+ return { orderedIds: [], itemsById: /* @__PURE__ */ new Map() };
939
+ };
940
+ const getCache = (provider) => {
941
+ const providerKey = safeGetKey(provider);
942
+ if (!providerKey) {
943
+ return createEmptyCache();
944
+ }
939
945
  if (!styleItemsCacheRef.current.has(providerKey)) {
940
- styleItemsCacheRef.current.set(providerKey, { orderedIds: [], itemsById: /* @__PURE__ */ new Map() });
946
+ styleItemsCacheRef.current.set(providerKey, createEmptyCache());
941
947
  }
942
- const cache = styleItemsCacheRef.current.get(providerKey);
943
- return {
948
+ return styleItemsCacheRef.current.get(providerKey);
949
+ };
950
+ return stylesRepository2.getProviders().map(
951
+ (provider) => ({
944
952
  provider,
945
953
  subscriber: createProviderSubscriber2({
946
954
  provider,
947
955
  renderStyles,
948
956
  setStyleItems,
949
- cache
957
+ getCache: () => getCache(provider)
950
958
  })
951
- };
952
- });
959
+ })
960
+ );
953
961
  }, [renderStyles]);
954
962
  useEffect6(() => {
955
963
  const unsubscribes = providerAndSubscribers.map(
@@ -990,18 +998,26 @@ function stateSorter({ state: stateA }, { state: stateB }) {
990
998
  function createBreakpointSorter(breakpointsOrder) {
991
999
  return ({ breakpoint: breakpointA }, { breakpoint: breakpointB }) => breakpointsOrder.indexOf(breakpointA) - breakpointsOrder.indexOf(breakpointB);
992
1000
  }
993
- function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cache }) {
1001
+ function safeGetKey(provider) {
1002
+ try {
1003
+ return provider.getKey();
1004
+ } catch {
1005
+ return null;
1006
+ }
1007
+ }
1008
+ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, getCache }) {
994
1009
  return abortPreviousRuns(
995
1010
  (abortController, previous, current) => signalizedProcess(abortController.signal).then((_, signal) => {
1011
+ const cache = getCache();
996
1012
  const hasDiffInfo = current !== void 0 && previous !== void 0;
997
1013
  const hasCache = cache.orderedIds.length > 0;
998
1014
  if (hasCache && provider.isPregeneratedLink) {
999
1015
  removeProviderPregeneratedLinks(provider.getKey(), provider.isPregeneratedLink);
1000
1016
  }
1001
1017
  if (hasDiffInfo && hasCache) {
1002
- return updateItems(previous, current, signal);
1018
+ return updateItems(cache, previous, current, signal);
1003
1019
  }
1004
- return createItems(signal);
1020
+ return createItems(cache, signal);
1005
1021
  }).then((items) => {
1006
1022
  setStyleItems((prev) => ({
1007
1023
  ...prev,
@@ -1009,7 +1025,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
1009
1025
  }));
1010
1026
  }).execute()
1011
1027
  );
1012
- async function updateItems(previous, current, signal) {
1028
+ async function updateItems(cache, previous, current, signal) {
1013
1029
  const changedIds = getChangedStyleIds(previous, current);
1014
1030
  cache.orderedIds = provider.actions.all().map((style) => style.id).reverse();
1015
1031
  if (changedIds.length > 0) {
@@ -1024,7 +1040,7 @@ function createProviderSubscriber2({ provider, renderStyles, setStyleItems, cach
1024
1040
  }
1025
1041
  return getOrderedItems(cache);
1026
1042
  }
1027
- async function createItems(signal) {
1043
+ async function createItems(cache, signal) {
1028
1044
  const allStyles = provider.actions.all();
1029
1045
  const styles = [...allStyles].reverse().map((style) => {
1030
1046
  return {
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.1.0-751",
4
+ "version": "4.1.0-753",
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.1.0-751",
40
+ "@elementor/editor": "4.1.0-753",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.1.0-751",
43
- "@elementor/editor-documents": "4.1.0-751",
44
- "@elementor/editor-elements": "4.1.0-751",
45
- "@elementor/editor-interactions": "4.1.0-751",
46
- "@elementor/editor-mcp": "4.1.0-751",
47
- "@elementor/editor-notifications": "4.1.0-751",
48
- "@elementor/editor-props": "4.1.0-751",
49
- "@elementor/editor-responsive": "4.1.0-751",
50
- "@elementor/editor-styles": "4.1.0-751",
51
- "@elementor/editor-styles-repository": "4.1.0-751",
52
- "@elementor/editor-ui": "4.1.0-751",
53
- "@elementor/editor-v1-adapters": "4.1.0-751",
54
- "@elementor/schema": "4.1.0-751",
55
- "@elementor/twing": "4.1.0-751",
42
+ "@elementor/editor-controls": "4.1.0-753",
43
+ "@elementor/editor-documents": "4.1.0-753",
44
+ "@elementor/editor-elements": "4.1.0-753",
45
+ "@elementor/editor-interactions": "4.1.0-753",
46
+ "@elementor/editor-mcp": "4.1.0-753",
47
+ "@elementor/editor-notifications": "4.1.0-753",
48
+ "@elementor/editor-props": "4.1.0-753",
49
+ "@elementor/editor-responsive": "4.1.0-753",
50
+ "@elementor/editor-styles": "4.1.0-753",
51
+ "@elementor/editor-styles-repository": "4.1.0-753",
52
+ "@elementor/editor-ui": "4.1.0-753",
53
+ "@elementor/editor-v1-adapters": "4.1.0-753",
54
+ "@elementor/schema": "4.1.0-753",
55
+ "@elementor/twing": "4.1.0-753",
56
56
  "@elementor/ui": "1.36.17",
57
- "@elementor/utils": "4.1.0-751",
58
- "@elementor/wp-media": "4.1.0-751",
57
+ "@elementor/utils": "4.1.0-753",
58
+ "@elementor/wp-media": "4.1.0-753",
59
59
  "@floating-ui/react": "^0.27.5",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -13,6 +13,10 @@ jest.mock( '@elementor/editor-v1-adapters', () => ( {
13
13
  commandEndEvent: jest.fn(),
14
14
  } ) );
15
15
 
16
+ jest.mock( '@elementor/editor-documents', () => ( {
17
+ getCurrentDocument: jest.fn().mockReturnValue( { id: 1 } ),
18
+ } ) );
19
+
16
20
  jest.mock( '@elementor/ui', () => ( {
17
21
  Portal: jest.fn( ( { children } ) => <div data-testid="portal">{ children }</div> ),
18
22
  } ) );
@@ -340,6 +340,74 @@ describe( 'useStyleItems', () => {
340
340
  expect( breakpointOrderAfterUpdate ).toEqual( [ 'desktop', 'tablet', 'mobile' ] );
341
341
  } );
342
342
 
343
+ it( 'should recover and render styles when a provider key becomes available after initial failure', async () => {
344
+ // Arrange.
345
+ let shouldThrow = true;
346
+
347
+ const dynamicKey: () => string = () => {
348
+ if ( shouldThrow ) {
349
+ throw new Error( 'Document not ready' );
350
+ }
351
+
352
+ return 'late-provider';
353
+ };
354
+
355
+ const failingThenSucceedingProvider = createMockStylesProvider(
356
+ {
357
+ key: dynamicKey,
358
+ priority: 2,
359
+ },
360
+ [ createMockStyleDefinition( { id: 'late-style1' } ), createMockStyleDefinition( { id: 'late-style2' } ) ]
361
+ );
362
+
363
+ const stableProvider = createMockStylesProvider(
364
+ {
365
+ key: 'stable-provider',
366
+ priority: 1,
367
+ },
368
+ [
369
+ createMockStyleDefinition( { id: 'stable-style1' } ),
370
+ createMockStyleDefinition( { id: 'stable-style2' } ),
371
+ ]
372
+ );
373
+
374
+ jest.mocked( stylesRepository ).getProviders.mockReturnValue( [
375
+ failingThenSucceedingProvider,
376
+ stableProvider,
377
+ ] );
378
+
379
+ let attachPreviewCallback: () => Promise< void >;
380
+
381
+ jest.mocked( registerDataHook ).mockImplementation( ( position, command, callback ) => {
382
+ if ( command === 'editor/documents/attach-preview' && position === 'after' ) {
383
+ attachPreviewCallback = callback as never;
384
+ }
385
+
386
+ return null as never;
387
+ } );
388
+
389
+ // Act.
390
+ const { result } = renderHook( () => useStyleItems() );
391
+
392
+ // Assert - hook should not crash, should return empty initially.
393
+ expect( result.current ).toEqual( [] );
394
+
395
+ // Act - simulate document becoming ready, then trigger attach-preview.
396
+ shouldThrow = false;
397
+
398
+ await act( async () => {
399
+ await attachPreviewCallback?.();
400
+ } );
401
+
402
+ // Assert - both providers' styles should render in correct priority order.
403
+ expect( result.current ).toEqual( [
404
+ { id: 'stable-style2', breakpoint: 'desktop' },
405
+ { id: 'stable-style1', breakpoint: 'desktop' },
406
+ { id: 'late-style2', breakpoint: 'desktop' },
407
+ { id: 'late-style1', breakpoint: 'desktop' },
408
+ ] );
409
+ } );
410
+
343
411
  it( 'should only re-render changed styles on differential update', async () => {
344
412
  // Arrange.
345
413
  const renderStylesMock = jest.fn().mockImplementation( ( { styles } ) =>
@@ -37,25 +37,35 @@ export function useStyleItems() {
37
37
  const styleItemsCacheRef = useRef< Map< string, StyleItemsCache > >( new Map() );
38
38
 
39
39
  const providerAndSubscribers = useMemo( () => {
40
- return stylesRepository.getProviders().map( ( provider ): ProviderAndSubscriber => {
41
- const providerKey = provider.getKey();
40
+ const createEmptyCache = () => {
41
+ return { orderedIds: [], itemsById: new Map() };
42
+ };
43
+
44
+ const getCache = ( provider: StylesProvider ): StyleItemsCache => {
45
+ const providerKey = safeGetKey( provider );
46
+
47
+ if ( ! providerKey ) {
48
+ return createEmptyCache();
49
+ }
42
50
 
43
51
  if ( ! styleItemsCacheRef.current.has( providerKey ) ) {
44
- styleItemsCacheRef.current.set( providerKey, { orderedIds: [], itemsById: new Map() } );
52
+ styleItemsCacheRef.current.set( providerKey, createEmptyCache() );
45
53
  }
46
54
 
47
- const cache = styleItemsCacheRef.current.get( providerKey ) as StyleItemsCache;
55
+ return styleItemsCacheRef.current.get( providerKey ) as StyleItemsCache;
56
+ };
48
57
 
49
- return {
58
+ return stylesRepository.getProviders().map(
59
+ ( provider ): ProviderAndSubscriber => ( {
50
60
  provider,
51
61
  subscriber: createProviderSubscriber( {
52
62
  provider,
53
63
  renderStyles,
54
64
  setStyleItems,
55
- cache,
65
+ getCache: () => getCache( provider ),
56
66
  } ),
57
- };
58
- } );
67
+ } )
68
+ );
59
69
  }, [ renderStyles ] );
60
70
 
61
71
  useEffect( () => {
@@ -125,17 +135,26 @@ function createBreakpointSorter( breakpointsOrder: BreakpointId[] ) {
125
135
  breakpointsOrder.indexOf( breakpointB as BreakpointId );
126
136
  }
127
137
 
138
+ function safeGetKey( provider: StylesProvider ): string | null {
139
+ try {
140
+ return provider.getKey();
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+
128
146
  type CreateProviderSubscriberArgs = {
129
147
  provider: StylesProvider;
130
148
  renderStyles: StyleRenderer;
131
149
  setStyleItems: Dispatch< SetStateAction< ProviderAndStyleItemsMap > >;
132
- cache: StyleItemsCache;
150
+ getCache: () => StyleItemsCache;
133
151
  };
134
152
 
135
- function createProviderSubscriber( { provider, renderStyles, setStyleItems, cache }: CreateProviderSubscriberArgs ) {
153
+ function createProviderSubscriber( { provider, renderStyles, setStyleItems, getCache }: CreateProviderSubscriberArgs ) {
136
154
  return abortPreviousRuns( ( abortController, previous?: StylesCollection, current?: StylesCollection ) =>
137
155
  signalizedProcess( abortController.signal )
138
156
  .then( ( _, signal ) => {
157
+ const cache = getCache();
139
158
  const hasDiffInfo = current !== undefined && previous !== undefined;
140
159
  const hasCache = cache.orderedIds.length > 0;
141
160
 
@@ -145,10 +164,10 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems, cach
145
164
  }
146
165
 
147
166
  if ( hasDiffInfo && hasCache ) {
148
- return updateItems( previous, current, signal );
167
+ return updateItems( cache, previous, current, signal );
149
168
  }
150
169
 
151
- return createItems( signal );
170
+ return createItems( cache, signal );
152
171
  } )
153
172
  .then( ( items ) => {
154
173
  setStyleItems( ( prev ) => ( {
@@ -159,7 +178,12 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems, cach
159
178
  .execute()
160
179
  );
161
180
 
162
- async function updateItems( previous: StylesCollection, current: StylesCollection, signal: AbortSignal ) {
181
+ async function updateItems(
182
+ cache: StyleItemsCache,
183
+ previous: StylesCollection,
184
+ current: StylesCollection,
185
+ signal: AbortSignal
186
+ ) {
163
187
  const changedIds = getChangedStyleIds( previous, current );
164
188
 
165
189
  cache.orderedIds = provider.actions
@@ -186,7 +210,7 @@ function createProviderSubscriber( { provider, renderStyles, setStyleItems, cach
186
210
  return getOrderedItems( cache );
187
211
  }
188
212
 
189
- async function createItems( signal: AbortSignal ) {
213
+ async function createItems( cache: StyleItemsCache, signal: AbortSignal ) {
190
214
  const allStyles = provider.actions.all();
191
215
 
192
216
  const styles = [ ...allStyles ].reverse().map( ( style ) => {