@elementor/editor-styles-repository 0.8.1 → 0.8.4

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.
@@ -10,8 +10,8 @@ describe( 'createStylesRepository', () => {
10
10
  // Arrange
11
11
  const repo = createStylesRepository();
12
12
 
13
- repo.register( createMockStylesProvider( { key: 'mock1', priority: 10, styleDefinitions: [ styleDef1 ] } ) );
14
- repo.register( createMockStylesProvider( { key: 'mock2', priority: 20, styleDefinitions: [ styleDef2 ] } ) );
13
+ repo.register( createMockStylesProvider( { key: 'mock1', priority: 10 }, [ styleDef1 ] ) );
14
+ repo.register( createMockStylesProvider( { key: 'mock2', priority: 20 }, [ styleDef2 ] ) );
15
15
 
16
16
  // Assert
17
17
  expect( repo.all() ).toEqual( [ styleDef2, styleDef1 ] );
@@ -21,17 +21,21 @@ describe( 'createStylesRepository', () => {
21
21
  // Arrange
22
22
  const repo = createStylesRepository();
23
23
 
24
- const mockStylesProvider1 = createMockStylesProvider( {
25
- key: 'mock1',
26
- priority: 10,
27
- styleDefinitions: [ styleDef1 ],
28
- } );
29
-
30
- const mockStylesProvider2 = createMockStylesProvider( {
31
- key: 'mock2',
32
- priority: 20,
33
- styleDefinitions: [ styleDef2 ],
34
- } );
24
+ const mockStylesProvider1 = createMockStylesProvider(
25
+ {
26
+ key: 'mock1',
27
+ priority: 10,
28
+ },
29
+ [ styleDef1 ]
30
+ );
31
+
32
+ const mockStylesProvider2 = createMockStylesProvider(
33
+ {
34
+ key: 'mock2',
35
+ priority: 20,
36
+ },
37
+ [ styleDef2 ]
38
+ );
35
39
 
36
40
  repo.register( mockStylesProvider1 );
37
41
  repo.register( mockStylesProvider2 );
@@ -64,84 +68,4 @@ describe( 'createStylesRepository', () => {
64
68
  // Assert
65
69
  expect( cb ).toHaveBeenCalledTimes( 2 );
66
70
  } );
67
-
68
- it( 'should return true if label is valid', () => {
69
- // Arrange
70
- const repo = createStylesRepository();
71
-
72
- // Act
73
- const result = repo.isLabelValid( 'valid-label' );
74
-
75
- // Assert
76
- expect( result ).toBe( true );
77
- } );
78
-
79
- it( 'should return false if label is not valid', () => {
80
- // Arrange
81
- const repo = createStylesRepository();
82
-
83
- // Act
84
- const result = repo.isLabelValid( '.invalid-label' );
85
-
86
- // Assert
87
- expect( result ).toBe( false );
88
- } );
89
-
90
- it( 'should return true if label is reserved', () => {
91
- // Arrange
92
- const repo = createStylesRepository();
93
-
94
- const mockStylesProvider1 = createMockStylesProvider( {
95
- key: 'mock1',
96
- priority: 10,
97
- styleDefinitions: [],
98
- reservedLabel: 'reserved-label',
99
- } );
100
-
101
- repo.register( mockStylesProvider1 );
102
-
103
- // Act
104
- const result = repo.isLabelExist( 'reserved-label' );
105
-
106
- // Assert
107
- expect( result ).toBe( true );
108
- } );
109
-
110
- it( 'should return true if label exist', () => {
111
- // Arrange
112
- const repo = createStylesRepository();
113
-
114
- const mockStylesProvider1 = createMockStylesProvider( {
115
- key: 'mock1',
116
- priority: 10,
117
- styleDefinitions: [ styleDef1 ],
118
- } );
119
-
120
- repo.register( mockStylesProvider1 );
121
-
122
- // Act
123
- const result = repo.isLabelExist( styleDef1.label );
124
-
125
- // Assert
126
- expect( result ).toBe( true );
127
- } );
128
-
129
- it( 'should return false if label does not exist', () => {
130
- // Arrange
131
- const repo = createStylesRepository();
132
-
133
- const mockStylesProvider1 = createMockStylesProvider( {
134
- key: 'mock1',
135
- priority: 10,
136
- styleDefinitions: [],
137
- } );
138
-
139
- repo.register( mockStylesProvider1 );
140
-
141
- // Act
142
- const result = repo.isLabelExist( 'new-label' );
143
-
144
- // Assert
145
- expect( result ).toBe( false );
146
- } );
147
71
  } );
@@ -0,0 +1,37 @@
1
+ import { createMockStyleDefinition } from 'test-utils';
2
+ import { stylesRepository, validateStyleLabel } from '@elementor/editor-styles-repository';
3
+
4
+ jest.mock( '../../styles-repository' );
5
+
6
+ describe( 'validateStyleLabel', () => {
7
+ beforeEach( () => {
8
+ jest.mocked( stylesRepository.all ).mockReturnValue( [
9
+ createMockStyleDefinition( { id: '1', label: 'class-1' } ),
10
+ createMockStyleDefinition( { id: '2', label: 'class-2' } ),
11
+ ] );
12
+ } );
13
+
14
+ it( 'should pass validation if label is valid', () => {
15
+ // Act.
16
+ const result = validateStyleLabel( 'valid-label' );
17
+
18
+ // Assert.
19
+ expect( result.isValid ).toBe( true );
20
+ expect( result.error ).toBeNull();
21
+ } );
22
+
23
+ it.each( [
24
+ { reason: 'label is empty', label: '', message: 'Cannot be empty' },
25
+ { reason: 'label is too long', label: 'a'.repeat( 51 ), message: 'Cannot be longer than 50 characters' },
26
+ { reason: 'label contains invalid characters', label: 'invalid label!', message: 'Invalid format' },
27
+ { reason: 'label already exists', label: 'class-1', message: 'Name exists' },
28
+ { reason: 'label is reserved', label: 'local', message: 'Name exists' },
29
+ ] )( 'should fail validation if $reason', ( { label, message } ) => {
30
+ // Act.
31
+ const result = validateStyleLabel( label );
32
+
33
+ // Assert.
34
+ expect( result.isValid ).toBe( false );
35
+ expect( result.error ).toBe( message );
36
+ } );
37
+ } );
@@ -0,0 +1,51 @@
1
+ import { type StylesProvider } from '../types';
2
+
3
+ export type CreateStylesProviderOptions = {
4
+ key: string | ( () => string );
5
+ priority?: number;
6
+ limit?: number;
7
+ subscribe?: ( callback: () => void ) => () => void;
8
+ labels?: {
9
+ singular: string;
10
+ plural: string;
11
+ };
12
+ actions: {
13
+ all: StylesProvider[ 'actions' ][ 'all' ];
14
+ get: StylesProvider[ 'actions' ][ 'get' ];
15
+ create?: StylesProvider[ 'actions' ][ 'create' ];
16
+ delete?: StylesProvider[ 'actions' ][ 'delete' ];
17
+ update?: StylesProvider[ 'actions' ][ 'update' ];
18
+ updateProps?: StylesProvider[ 'actions' ][ 'updateProps' ];
19
+ };
20
+ };
21
+
22
+ const DEFAULT_LIMIT = 10000;
23
+ const DEFAULT_PRIORITY = 10;
24
+
25
+ export function createStylesProvider( {
26
+ key,
27
+ priority = DEFAULT_PRIORITY,
28
+ limit = DEFAULT_LIMIT,
29
+ subscribe = () => () => {},
30
+ labels,
31
+ actions,
32
+ }: CreateStylesProviderOptions ): StylesProvider {
33
+ return {
34
+ getKey: typeof key === 'string' ? () => key : key,
35
+ priority,
36
+ limit,
37
+ subscribe,
38
+ labels: {
39
+ singular: labels?.singular ?? null,
40
+ plural: labels?.plural ?? null,
41
+ },
42
+ actions: {
43
+ all: actions.all,
44
+ get: actions.get,
45
+ create: actions.create,
46
+ delete: actions.delete,
47
+ update: actions.update,
48
+ updateProps: actions.updateProps,
49
+ },
50
+ };
51
+ }
@@ -1,40 +1,4 @@
1
- import { type Props } from '@elementor/editor-props';
2
- import { type StyleDefinition, type StyleDefinitionID, type StyleDefinitionVariant } from '@elementor/editor-styles';
3
-
4
- type MakeOptional< T, K extends keyof T > = Omit< T, K > & Partial< T >;
5
-
6
- export type UpdateActionPayload = MakeOptional< StyleDefinition, 'label' | 'variants' | 'type' >;
7
-
8
- export type UpdatePropsActionPayload = {
9
- id: StyleDefinitionID;
10
- meta: StyleDefinitionVariant[ 'meta' ];
11
- props: Props;
12
- };
13
-
14
- export type Meta = Record< string, unknown >;
15
-
16
- export type StylesProvider = {
17
- key: string;
18
- priority: number;
19
- actions: {
20
- get: ( meta?: Meta ) => StyleDefinition[];
21
- getById: ( id: StyleDefinitionID, meta?: Meta ) => StyleDefinition | null;
22
- create?: ( label: StyleDefinition[ 'label' ] ) => StyleDefinitionID;
23
- delete?: ( id: StyleDefinitionID ) => void;
24
- setOrder?: ( order: StyleDefinitionID[] ) => void;
25
- update?: ( data: UpdateActionPayload ) => void;
26
- updateProps?: ( args: UpdatePropsActionPayload, meta?: Meta ) => void;
27
- };
28
- subscribe: ( callback: () => void ) => () => void;
29
- labels?: {
30
- singular: string;
31
- plural: string;
32
- };
33
- reservedLabel?: string;
34
- limit?: number;
35
- };
36
-
37
- const VALID_SELECTOR_REGEX = /^[a-zA-Z0-9_-]+$/;
1
+ import { type Meta, type StylesProvider } from '../types';
38
2
 
39
3
  export const createStylesRepository = () => {
40
4
  const providers: StylesProvider[] = [];
@@ -48,7 +12,7 @@ export const createStylesRepository = () => {
48
12
  };
49
13
 
50
14
  const all = ( meta: Meta = {} ) => {
51
- return getProviders().flatMap( ( provider ) => provider.actions.get( meta ) );
15
+ return getProviders().flatMap( ( provider ) => provider.actions.all( meta ) );
52
16
  };
53
17
 
54
18
  const subscribe = ( cb: () => void ) => {
@@ -62,33 +26,14 @@ export const createStylesRepository = () => {
62
26
  };
63
27
 
64
28
  const getProviderByKey = ( key: string ) => {
65
- return providers.find( ( provider ) => provider.key === key );
66
- };
67
-
68
- const isLabelExist = ( newLabel: string ) => {
69
- const classes = all();
70
- const reservedLabels = providers.map( ( { reservedLabel } ) => reservedLabel ).filter( Boolean );
71
-
72
- if ( reservedLabels.includes( newLabel ) ) {
73
- return true;
74
- }
75
-
76
- if ( ! classes?.length ) {
77
- return false;
78
- }
79
-
80
- return classes.some( ( { label } ) => label.toLowerCase() === newLabel.toLowerCase() );
29
+ return providers.find( ( provider ) => provider.getKey() === key );
81
30
  };
82
31
 
83
- const isLabelValid = ( newLabel: string ) => VALID_SELECTOR_REGEX.test( newLabel );
84
-
85
32
  return {
86
33
  all,
87
34
  register,
88
35
  subscribe,
89
36
  getProviders,
90
37
  getProviderByKey,
91
- isLabelExist,
92
- isLabelValid,
93
38
  };
94
39
  };
@@ -0,0 +1,5 @@
1
+ import { ELEMENTS_STYLES_PROVIDER_KEY_PREFIX } from '../providers/document-elements-styles-provider';
2
+
3
+ export function isElementsStylesProvider( key: string ) {
4
+ return new RegExp( `^${ ELEMENTS_STYLES_PROVIDER_KEY_PREFIX }\\d+$` ).test( key );
5
+ }
@@ -0,0 +1,38 @@
1
+ import { z } from '@elementor/schema';
2
+ import { __ } from '@wordpress/i18n';
3
+
4
+ import { ELEMENTS_STYLES_RESERVED_LABEL } from '../providers/document-elements-styles-provider';
5
+ import { stylesRepository } from '../styles-repository';
6
+
7
+ const VALID_LABEL_REGEX = /^[a-zA-Z0-9_-]+$/;
8
+
9
+ const schema = z
10
+ .string()
11
+ .min( 1, __( 'Cannot be empty', 'elementor' ) )
12
+ .max( 50, __( 'Cannot be longer than 50 characters', 'elementor' ) )
13
+ .regex( VALID_LABEL_REGEX, __( 'Invalid format', 'elementor' ) );
14
+
15
+ export function validateStyleLabel( label: string ) {
16
+ const existingLabels = new Set( [
17
+ ELEMENTS_STYLES_RESERVED_LABEL,
18
+ ...stylesRepository.all().map( ( styleDef ) => styleDef.label.toLowerCase() ),
19
+ ] );
20
+
21
+ const result = schema
22
+ .refine( ( value ) => ! existingLabels.has( value ), {
23
+ message: __( 'Name exists', 'elementor' ),
24
+ } )
25
+ .safeParse( label.toLowerCase() );
26
+
27
+ if ( result.success ) {
28
+ return {
29
+ isValid: true,
30
+ error: null,
31
+ };
32
+ }
33
+
34
+ return {
35
+ isValid: false,
36
+ error: result.error.format()._errors[ 0 ],
37
+ };
38
+ }
@@ -1,26 +0,0 @@
1
- import { getWidgetsCache } from '@elementor/editor-elements';
2
-
3
- import { type StylesProvider } from './utils/create-styles-repository';
4
-
5
- const ELEMENTS_BASE_STYLES_PROVIDER_KEY = 'element-base-styles';
6
-
7
- export const elementBaseStylesProvider: StylesProvider = {
8
- key: ELEMENTS_BASE_STYLES_PROVIDER_KEY,
9
- priority: 10,
10
- actions: {
11
- get() {
12
- const widgetsCache = getWidgetsCache();
13
-
14
- return Object.values( widgetsCache ?? {} ).flatMap( ( widget ) =>
15
- Object.values( widget.base_styles ?? {} )
16
- );
17
- },
18
-
19
- getById( id ) {
20
- return this.get().find( ( style ) => style.id === id ) ?? null;
21
- },
22
- },
23
- subscribe: () => {
24
- return () => {};
25
- },
26
- };