@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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +30 -0
- package/dist/index.d.mts +47 -17
- package/dist/index.d.ts +47 -17
- package/dist/index.js +111 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -5
- package/src/__tests__/{elements-styles-provider.test.ts → document-elements-styles-provider.test.ts} +33 -21
- package/src/__tests__/element-base-styles-provider.test.ts +3 -3
- package/src/errors.ts +5 -0
- package/src/hooks/use-create-actions-by-provider.ts +1 -1
- package/src/index.ts +10 -10
- package/src/init.ts +3 -3
- package/src/{elements-styles-provider.ts → providers/document-elements-styles-provider.ts} +29 -16
- package/src/providers/element-base-styles-provider.ts +22 -0
- package/src/types.ts +33 -0
- package/src/utils/__tests__/create-styles-repository.test.ts +17 -93
- package/src/utils/__tests__/validate-style-label.test.ts +37 -0
- package/src/utils/create-styles-provider.ts +51 -0
- package/src/utils/create-styles-repository.ts +3 -58
- package/src/utils/is-elements-styles-provider.ts +5 -0
- package/src/utils/validate-style-label.ts +38 -0
- package/src/element-base-styles-provider.ts +0 -26
|
@@ -10,8 +10,8 @@ describe( 'createStylesRepository', () => {
|
|
|
10
10
|
// Arrange
|
|
11
11
|
const repo = createStylesRepository();
|
|
12
12
|
|
|
13
|
-
repo.register( createMockStylesProvider( { key: 'mock1', priority: 10,
|
|
14
|
-
repo.register( createMockStylesProvider( { key: 'mock2', priority: 20,
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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,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
|
-
};
|