@elementor/editor-styles-repository 4.3.0-beta1 → 4.3.0-beta3
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 +29 -1
- package/dist/index.mjs +30 -2
- package/package.json +8 -8
- package/src/__tests__/document-elements-styles-provider.test.ts +103 -1
- package/src/hooks/__tests__/use-user-styles-capability.test.ts +68 -0
- package/src/hooks/use-user-styles-capability.ts +13 -1
- package/src/providers/document-elements-styles-provider.ts +23 -1
package/dist/index.js
CHANGED
|
@@ -88,9 +88,19 @@ var DEFAULT_CAPABILITIES = {
|
|
|
88
88
|
update: true,
|
|
89
89
|
updateProps: true
|
|
90
90
|
};
|
|
91
|
+
var DENIED_CAPABILITIES = {
|
|
92
|
+
create: false,
|
|
93
|
+
delete: false,
|
|
94
|
+
update: false,
|
|
95
|
+
updateProps: false
|
|
96
|
+
};
|
|
91
97
|
var useUserStylesCapability = () => {
|
|
92
98
|
const { capabilities } = (0, import_editor_current_user.useCurrentUserCapabilities)();
|
|
99
|
+
const hasContentOnlyAccess = (0, import_editor_current_user.useHasContentOnlyAccess)();
|
|
93
100
|
const userCan = (providerKey) => {
|
|
101
|
+
if (hasContentOnlyAccess) {
|
|
102
|
+
return DENIED_CAPABILITIES;
|
|
103
|
+
}
|
|
94
104
|
const provider = stylesRepository.getProviderByKey(providerKey);
|
|
95
105
|
if (!provider?.capabilities) {
|
|
96
106
|
return DEFAULT_CAPABILITIES;
|
|
@@ -197,7 +207,25 @@ var documentElementsStylesProvider = createStylesProvider({
|
|
|
197
207
|
},
|
|
198
208
|
priority: 50,
|
|
199
209
|
isPregeneratedLink: ({ id }) => PREGENERATED_LINK_PATTERN.test(id),
|
|
200
|
-
subscribe: (cb) =>
|
|
210
|
+
subscribe: (cb) => {
|
|
211
|
+
let scheduledFrame = null;
|
|
212
|
+
const unsubscribe = (0, import_editor_v1_adapters.__privateListenTo)(import_editor_elements.styleRerenderEvents, () => {
|
|
213
|
+
if (scheduledFrame !== null) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
scheduledFrame = requestAnimationFrame(() => {
|
|
217
|
+
scheduledFrame = null;
|
|
218
|
+
cb();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
return () => {
|
|
222
|
+
if (scheduledFrame !== null) {
|
|
223
|
+
cancelAnimationFrame(scheduledFrame);
|
|
224
|
+
scheduledFrame = null;
|
|
225
|
+
}
|
|
226
|
+
unsubscribe();
|
|
227
|
+
};
|
|
228
|
+
},
|
|
201
229
|
actions: {
|
|
202
230
|
all: (meta = {}) => {
|
|
203
231
|
let elements = (0, import_editor_elements.getElements)();
|
package/dist/index.mjs
CHANGED
|
@@ -45,16 +45,26 @@ function useProviders() {
|
|
|
45
45
|
import { useMemo } from "react";
|
|
46
46
|
|
|
47
47
|
// src/hooks/use-user-styles-capability.ts
|
|
48
|
-
import { useCurrentUserCapabilities } from "@elementor/editor-current-user";
|
|
48
|
+
import { useCurrentUserCapabilities, useHasContentOnlyAccess } from "@elementor/editor-current-user";
|
|
49
49
|
var DEFAULT_CAPABILITIES = {
|
|
50
50
|
create: true,
|
|
51
51
|
delete: true,
|
|
52
52
|
update: true,
|
|
53
53
|
updateProps: true
|
|
54
54
|
};
|
|
55
|
+
var DENIED_CAPABILITIES = {
|
|
56
|
+
create: false,
|
|
57
|
+
delete: false,
|
|
58
|
+
update: false,
|
|
59
|
+
updateProps: false
|
|
60
|
+
};
|
|
55
61
|
var useUserStylesCapability = () => {
|
|
56
62
|
const { capabilities } = useCurrentUserCapabilities();
|
|
63
|
+
const hasContentOnlyAccess = useHasContentOnlyAccess();
|
|
57
64
|
const userCan = (providerKey) => {
|
|
65
|
+
if (hasContentOnlyAccess) {
|
|
66
|
+
return DENIED_CAPABILITIES;
|
|
67
|
+
}
|
|
58
68
|
const provider = stylesRepository.getProviderByKey(providerKey);
|
|
59
69
|
if (!provider?.capabilities) {
|
|
60
70
|
return DEFAULT_CAPABILITIES;
|
|
@@ -167,7 +177,25 @@ var documentElementsStylesProvider = createStylesProvider({
|
|
|
167
177
|
},
|
|
168
178
|
priority: 50,
|
|
169
179
|
isPregeneratedLink: ({ id }) => PREGENERATED_LINK_PATTERN.test(id),
|
|
170
|
-
subscribe: (cb) =>
|
|
180
|
+
subscribe: (cb) => {
|
|
181
|
+
let scheduledFrame = null;
|
|
182
|
+
const unsubscribe = listenTo(styleRerenderEvents, () => {
|
|
183
|
+
if (scheduledFrame !== null) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
scheduledFrame = requestAnimationFrame(() => {
|
|
187
|
+
scheduledFrame = null;
|
|
188
|
+
cb();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
return () => {
|
|
192
|
+
if (scheduledFrame !== null) {
|
|
193
|
+
cancelAnimationFrame(scheduledFrame);
|
|
194
|
+
scheduledFrame = null;
|
|
195
|
+
}
|
|
196
|
+
unsubscribe();
|
|
197
|
+
};
|
|
198
|
+
},
|
|
171
199
|
actions: {
|
|
172
200
|
all: (meta = {}) => {
|
|
173
201
|
let elements = getElements();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-styles-repository",
|
|
3
3
|
"description": "Elementor Editor Styles Repository",
|
|
4
|
-
"version": "4.3.0-
|
|
4
|
+
"version": "4.3.0-beta3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@elementor/editor-current-user": "4.3.0-
|
|
37
|
-
"@elementor/editor-elements": "4.3.0-
|
|
38
|
-
"@elementor/editor-props": "4.3.0-
|
|
39
|
-
"@elementor/editor-styles": "4.3.0-
|
|
40
|
-
"@elementor/editor-v1-adapters": "4.3.0-
|
|
41
|
-
"@elementor/schema": "4.3.0-
|
|
42
|
-
"@elementor/utils": "4.3.0-
|
|
36
|
+
"@elementor/editor-current-user": "4.3.0-beta3",
|
|
37
|
+
"@elementor/editor-elements": "4.3.0-beta3",
|
|
38
|
+
"@elementor/editor-props": "4.3.0-beta3",
|
|
39
|
+
"@elementor/editor-styles": "4.3.0-beta3",
|
|
40
|
+
"@elementor/editor-v1-adapters": "4.3.0-beta3",
|
|
41
|
+
"@elementor/schema": "4.3.0-beta3",
|
|
42
|
+
"@elementor/utils": "4.3.0-beta3",
|
|
43
43
|
"@wordpress/i18n": "^5.13.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { createMockElement, createMockStyleDefinition } from 'test-utils';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ELEMENT_STYLE_CHANGE_EVENT,
|
|
4
|
+
getCurrentDocumentId,
|
|
5
|
+
getElements,
|
|
6
|
+
getElementStyles,
|
|
7
|
+
updateElementStyle,
|
|
8
|
+
} from '@elementor/editor-elements';
|
|
3
9
|
import type { StyleDefinition } from '@elementor/editor-styles';
|
|
10
|
+
import { __privateListenTo as listenTo } from '@elementor/editor-v1-adapters';
|
|
4
11
|
|
|
5
12
|
import { InvalidElementsStyleProviderMetaError } from '../errors';
|
|
6
13
|
import { documentElementsStylesProvider } from '../providers/document-elements-styles-provider';
|
|
7
14
|
|
|
8
15
|
jest.mock( '@elementor/editor-elements' );
|
|
9
16
|
|
|
17
|
+
jest.mock( '@elementor/editor-v1-adapters', () => ( {
|
|
18
|
+
...jest.requireActual( '@elementor/editor-v1-adapters' ),
|
|
19
|
+
__privateListenTo: jest.fn(),
|
|
20
|
+
} ) );
|
|
21
|
+
|
|
10
22
|
describe( 'documentElementsStylesProvider', () => {
|
|
11
23
|
beforeEach( () => {
|
|
12
24
|
jest.mocked( getElements ).mockReturnValue( [
|
|
@@ -155,4 +167,94 @@ describe( 'documentElementsStylesProvider', () => {
|
|
|
155
167
|
)
|
|
156
168
|
).toThrow( new InvalidElementsStyleProviderMetaError() );
|
|
157
169
|
} );
|
|
170
|
+
|
|
171
|
+
describe( 'subscribe', () => {
|
|
172
|
+
const setupListener = () => {
|
|
173
|
+
const unsubscribeFromEvents = jest.fn();
|
|
174
|
+
let emit = () => {};
|
|
175
|
+
|
|
176
|
+
jest.mocked( listenTo ).mockImplementation( ( _events, callback ) => {
|
|
177
|
+
emit = () =>
|
|
178
|
+
callback( {
|
|
179
|
+
type: 'window-event',
|
|
180
|
+
event: ELEMENT_STYLE_CHANGE_EVENT,
|
|
181
|
+
originalEvent: new CustomEvent( ELEMENT_STYLE_CHANGE_EVENT ),
|
|
182
|
+
} );
|
|
183
|
+
|
|
184
|
+
return unsubscribeFromEvents;
|
|
185
|
+
} );
|
|
186
|
+
|
|
187
|
+
const frames: FrameRequestCallback[] = [];
|
|
188
|
+
|
|
189
|
+
jest.spyOn( window, 'requestAnimationFrame' ).mockImplementation( ( frame: FrameRequestCallback ) => {
|
|
190
|
+
frames.push( frame );
|
|
191
|
+
|
|
192
|
+
return frames.length;
|
|
193
|
+
} );
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
emitStyleRerenderEvent: () => emit(),
|
|
197
|
+
runFrame: ( index: number ) => frames[ index ]( 0 ),
|
|
198
|
+
frames: () => frames,
|
|
199
|
+
unsubscribeFromEvents,
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
it( 'should notify subscribers once per frame regardless of the number of events', () => {
|
|
204
|
+
// Arrange.
|
|
205
|
+
const listener = setupListener();
|
|
206
|
+
const subscriber = jest.fn();
|
|
207
|
+
|
|
208
|
+
documentElementsStylesProvider.subscribe( subscriber );
|
|
209
|
+
|
|
210
|
+
// Act.
|
|
211
|
+
listener.emitStyleRerenderEvent();
|
|
212
|
+
listener.emitStyleRerenderEvent();
|
|
213
|
+
listener.emitStyleRerenderEvent();
|
|
214
|
+
|
|
215
|
+
// Assert.
|
|
216
|
+
expect( listener.frames() ).toHaveLength( 1 );
|
|
217
|
+
expect( subscriber ).not.toHaveBeenCalled();
|
|
218
|
+
|
|
219
|
+
// Act.
|
|
220
|
+
listener.runFrame( 0 );
|
|
221
|
+
|
|
222
|
+
// Assert.
|
|
223
|
+
expect( subscriber ).toHaveBeenCalledTimes( 1 );
|
|
224
|
+
} );
|
|
225
|
+
|
|
226
|
+
it( 'should schedule a new frame after the pending one ran', () => {
|
|
227
|
+
// Arrange.
|
|
228
|
+
const listener = setupListener();
|
|
229
|
+
const subscriber = jest.fn();
|
|
230
|
+
|
|
231
|
+
documentElementsStylesProvider.subscribe( subscriber );
|
|
232
|
+
|
|
233
|
+
// Act.
|
|
234
|
+
listener.emitStyleRerenderEvent();
|
|
235
|
+
listener.runFrame( 0 );
|
|
236
|
+
listener.emitStyleRerenderEvent();
|
|
237
|
+
listener.runFrame( 1 );
|
|
238
|
+
|
|
239
|
+
// Assert.
|
|
240
|
+
expect( subscriber ).toHaveBeenCalledTimes( 2 );
|
|
241
|
+
} );
|
|
242
|
+
|
|
243
|
+
it( 'should cancel a pending frame when unsubscribing', () => {
|
|
244
|
+
// Arrange.
|
|
245
|
+
const listener = setupListener();
|
|
246
|
+
const subscriber = jest.fn();
|
|
247
|
+
const cancelAnimationFrameSpy = jest.spyOn( window, 'cancelAnimationFrame' ).mockImplementation();
|
|
248
|
+
|
|
249
|
+
const unsubscribe = documentElementsStylesProvider.subscribe( subscriber );
|
|
250
|
+
|
|
251
|
+
// Act.
|
|
252
|
+
listener.emitStyleRerenderEvent();
|
|
253
|
+
unsubscribe();
|
|
254
|
+
|
|
255
|
+
// Assert.
|
|
256
|
+
expect( cancelAnimationFrameSpy ).toHaveBeenCalledTimes( 1 );
|
|
257
|
+
expect( listener.unsubscribeFromEvents ).toHaveBeenCalledTimes( 1 );
|
|
258
|
+
} );
|
|
259
|
+
} );
|
|
158
260
|
} );
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useCurrentUserCapabilities, useHasContentOnlyAccess } from '@elementor/editor-current-user';
|
|
2
|
+
import { renderHook } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { documentElementsStylesProvider } from '../../providers/document-elements-styles-provider';
|
|
5
|
+
import { stylesRepository } from '../../styles-repository';
|
|
6
|
+
import { type StylesProvider } from '../../types';
|
|
7
|
+
import { useUserStylesCapability } from '../use-user-styles-capability';
|
|
8
|
+
|
|
9
|
+
jest.mock( '@elementor/editor-current-user' );
|
|
10
|
+
jest.mock( '../../styles-repository' );
|
|
11
|
+
|
|
12
|
+
const UPDATE_CAPABILITY = 'update_style';
|
|
13
|
+
const PROVIDER_WITH_CAPABILITIES_KEY = 'provider-with-capabilities';
|
|
14
|
+
|
|
15
|
+
const providerWithCapabilities: Pick< StylesProvider, 'getKey' | 'capabilities' > = {
|
|
16
|
+
getKey: () => PROVIDER_WITH_CAPABILITIES_KEY,
|
|
17
|
+
capabilities: {
|
|
18
|
+
create: UPDATE_CAPABILITY,
|
|
19
|
+
delete: UPDATE_CAPABILITY,
|
|
20
|
+
update: UPDATE_CAPABILITY,
|
|
21
|
+
updateProps: UPDATE_CAPABILITY,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const deniedCapabilities = {
|
|
26
|
+
create: false,
|
|
27
|
+
delete: false,
|
|
28
|
+
update: false,
|
|
29
|
+
updateProps: false,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
describe( 'useUserStylesCapability', () => {
|
|
33
|
+
beforeEach( () => {
|
|
34
|
+
jest.mocked( useHasContentOnlyAccess ).mockReturnValue( false );
|
|
35
|
+
|
|
36
|
+
jest.mocked( useCurrentUserCapabilities ).mockReturnValue( {
|
|
37
|
+
capabilities: [ UPDATE_CAPABILITY ],
|
|
38
|
+
canUser: ( capability ) => capability === UPDATE_CAPABILITY,
|
|
39
|
+
isAdmin: false,
|
|
40
|
+
} );
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'should deny all capabilities for content-only users on a provider without capabilities', () => {
|
|
44
|
+
// Arrange
|
|
45
|
+
jest.mocked( useHasContentOnlyAccess ).mockReturnValue( true );
|
|
46
|
+
|
|
47
|
+
jest.mocked( stylesRepository.getProviderByKey ).mockReturnValue( documentElementsStylesProvider );
|
|
48
|
+
|
|
49
|
+
// Act
|
|
50
|
+
const { result } = renderHook( () => useUserStylesCapability() );
|
|
51
|
+
|
|
52
|
+
// Assert
|
|
53
|
+
expect( result.current.userCan( 'document-elements-42' ) ).toEqual( deniedCapabilities );
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'should deny all capabilities for content-only users on a provider with capabilities', () => {
|
|
57
|
+
// Arrange
|
|
58
|
+
jest.mocked( useHasContentOnlyAccess ).mockReturnValue( true );
|
|
59
|
+
|
|
60
|
+
jest.mocked( stylesRepository.getProviderByKey ).mockReturnValue( providerWithCapabilities as StylesProvider );
|
|
61
|
+
|
|
62
|
+
// Act
|
|
63
|
+
const { result } = renderHook( () => useUserStylesCapability() );
|
|
64
|
+
|
|
65
|
+
// Assert
|
|
66
|
+
expect( result.current.userCan( PROVIDER_WITH_CAPABILITIES_KEY ) ).toEqual( deniedCapabilities );
|
|
67
|
+
} );
|
|
68
|
+
} );
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
|
|
1
|
+
import { useCurrentUserCapabilities, useHasContentOnlyAccess } from '@elementor/editor-current-user';
|
|
2
2
|
|
|
3
3
|
import { stylesRepository } from '../styles-repository';
|
|
4
4
|
import { type UserCapabilities } from '../types';
|
|
@@ -14,10 +14,22 @@ const DEFAULT_CAPABILITIES: UserCan = {
|
|
|
14
14
|
updateProps: true,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const DENIED_CAPABILITIES: UserCan = {
|
|
18
|
+
create: false,
|
|
19
|
+
delete: false,
|
|
20
|
+
update: false,
|
|
21
|
+
updateProps: false,
|
|
22
|
+
};
|
|
23
|
+
|
|
17
24
|
export const useUserStylesCapability = () => {
|
|
18
25
|
const { capabilities } = useCurrentUserCapabilities();
|
|
26
|
+
const hasContentOnlyAccess = useHasContentOnlyAccess();
|
|
19
27
|
|
|
20
28
|
const userCan = ( providerKey: string ): UserCan => {
|
|
29
|
+
if ( hasContentOnlyAccess ) {
|
|
30
|
+
return DENIED_CAPABILITIES;
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
const provider = stylesRepository.getProviderByKey( providerKey );
|
|
22
34
|
|
|
23
35
|
if ( ! provider?.capabilities ) {
|
|
@@ -30,7 +30,29 @@ export const documentElementsStylesProvider = createStylesProvider( {
|
|
|
30
30
|
},
|
|
31
31
|
priority: 50,
|
|
32
32
|
isPregeneratedLink: ( { id } ) => PREGENERATED_LINK_PATTERN.test( id ),
|
|
33
|
-
subscribe: ( cb ) =>
|
|
33
|
+
subscribe: ( cb ) => {
|
|
34
|
+
let scheduledFrame: number | null = null;
|
|
35
|
+
|
|
36
|
+
const unsubscribe = listenTo( styleRerenderEvents, () => {
|
|
37
|
+
if ( scheduledFrame !== null ) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
scheduledFrame = requestAnimationFrame( () => {
|
|
42
|
+
scheduledFrame = null;
|
|
43
|
+
cb();
|
|
44
|
+
} );
|
|
45
|
+
} );
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
if ( scheduledFrame !== null ) {
|
|
49
|
+
cancelAnimationFrame( scheduledFrame );
|
|
50
|
+
scheduledFrame = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
unsubscribe();
|
|
54
|
+
};
|
|
55
|
+
},
|
|
34
56
|
actions: {
|
|
35
57
|
all: ( meta = {} ) => {
|
|
36
58
|
let elements = getElements();
|