ckeditor5-blazor 1.10.3 → 1.11.0

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.
Files changed (33) hide show
  1. package/dist/elements/context/context.d.ts.map +1 -1
  2. package/dist/elements/editor/editor.d.ts.map +1 -1
  3. package/dist/elements/editor/types/editor-relaxed-constructor.type.d.ts +6 -0
  4. package/dist/elements/editor/types/editor-relaxed-constructor.type.d.ts.map +1 -0
  5. package/dist/elements/editor/types/index.d.ts +2 -0
  6. package/dist/elements/editor/types/index.d.ts.map +1 -0
  7. package/dist/elements/editor/utils/assign-initial-data-to-editor-config.d.ts +10 -0
  8. package/dist/elements/editor/utils/assign-initial-data-to-editor-config.d.ts.map +1 -0
  9. package/dist/elements/editor/utils/assign-source-elements-to-editor-config.d.ts +12 -0
  10. package/dist/elements/editor/utils/assign-source-elements-to-editor-config.d.ts.map +1 -0
  11. package/dist/elements/editor/utils/create-editor-in-context.d.ts +1 -3
  12. package/dist/elements/editor/utils/create-editor-in-context.d.ts.map +1 -1
  13. package/dist/elements/editor/utils/index.d.ts +3 -0
  14. package/dist/elements/editor/utils/index.d.ts.map +1 -1
  15. package/dist/elements/editor/utils/normalize-editor-language.d.ts +9 -0
  16. package/dist/elements/editor/utils/normalize-editor-language.d.ts.map +1 -0
  17. package/dist/index.cjs +2 -2
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.mjs +410 -353
  20. package/dist/index.mjs.map +1 -1
  21. package/package.json +3 -3
  22. package/src/elements/context/context.test.ts +107 -66
  23. package/src/elements/context/context.ts +9 -3
  24. package/src/elements/editable.ts +2 -2
  25. package/src/elements/editor/editor.ts +33 -29
  26. package/src/elements/editor/types/editor-relaxed-constructor.type.ts +6 -0
  27. package/src/elements/editor/types/index.ts +1 -0
  28. package/src/elements/editor/utils/assign-initial-data-to-editor-config.ts +47 -0
  29. package/src/elements/editor/utils/assign-source-elements-to-editor-config.ts +59 -0
  30. package/src/elements/editor/utils/create-editor-in-context.ts +2 -5
  31. package/src/elements/editor/utils/index.ts +3 -0
  32. package/src/elements/editor/utils/normalize-editor-language.test.ts +50 -0
  33. package/src/elements/editor/utils/normalize-editor-language.ts +25 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ckeditor5-blazor",
3
- "version": "1.10.3",
3
+ "version": "1.11.0",
4
4
  "description": "CKEditor 5 integration for Blazor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,8 +28,8 @@
28
28
  ],
29
29
  "devDependencies": {
30
30
  "@vitest/coverage-v8": "^4.0.18",
31
- "ckeditor5": "^47.6.0",
32
- "ckeditor5-premium-features": "^47.6.0",
31
+ "ckeditor5": "^48.1.0",
32
+ "ckeditor5-premium-features": "^48.1.0",
33
33
  "happy-dom": "^20.8.9",
34
34
  "typescript": "^5.9.3",
35
35
  "vite": "^7.3.2",
@@ -84,102 +84,143 @@ describe('context component', () => {
84
84
  expect(context?.plugins.get('CustomPlugin')).toBeInstanceOf(CustomPlugin);
85
85
  });
86
86
 
87
- it('registered plugins should support custom translations', async () => {
88
- class CustomPlugin extends ContextPlugin {
89
- static get pluginName() {
90
- return 'CustomPlugin';
87
+ describe('language param', () => {
88
+ it('should support passing null language for context translations', async () => {
89
+ class CustomPlugin extends ContextPlugin {
90
+ static get pluginName() {
91
+ return 'CustomPlugin';
92
+ }
93
+
94
+ getHelloTitle() {
95
+ return this.context.t('HELLO');
96
+ }
91
97
  }
92
98
 
93
- getHelloTitle() {
94
- return this.context.t('HELLO');
95
- }
96
- }
99
+ CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
100
+
101
+ renderTestContext(createContextSnapshot(
102
+ DEFAULT_TEST_CONTEXT_ID,
103
+ {
104
+ customTranslations: {
105
+ en: {
106
+ HELLO: 'Hello from CustomPlugin',
107
+ },
108
+ pl: {
109
+ HELLO: 'Witaj z CustomPlugin',
110
+ },
111
+ },
112
+ config: {
113
+ plugins: ['CustomPlugin'],
114
+ },
115
+ },
116
+ null,
117
+ ));
97
118
 
98
- CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
119
+ const { context } = await waitForTestContext();
120
+ const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
99
121
 
100
- renderTestContext(createContextSnapshot(DEFAULT_TEST_CONTEXT_ID, {
101
- customTranslations: {
102
- en: {
103
- HELLO: 'Hello from CustomPlugin',
122
+ expect(plugin.getHelloTitle()).toBe('Hello from CustomPlugin');
123
+ });
124
+
125
+ it('should support custom language for context translations', async () => {
126
+ class CustomPlugin extends ContextPlugin {
127
+ static get pluginName() {
128
+ return 'CustomPlugin';
129
+ }
130
+
131
+ getHelloTitle() {
132
+ return this.context.t('HELLO');
133
+ }
134
+ }
135
+
136
+ CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
137
+
138
+ renderTestContext(createContextSnapshot(
139
+ DEFAULT_TEST_CONTEXT_ID,
140
+ {
141
+ customTranslations: {
142
+ en: {
143
+ HELLO: 'Hello from CustomPlugin',
144
+ },
145
+ pl: {
146
+ HELLO: 'Witaj z CustomPlugin',
147
+ },
148
+ },
149
+ config: {
150
+ plugins: ['CustomPlugin'],
151
+ },
104
152
  },
105
- },
106
- config: {
107
- plugins: ['CustomPlugin'],
108
- },
109
- }));
153
+ {
154
+ ui: 'pl',
155
+ content: 'pl',
156
+ },
157
+ ));
110
158
 
111
- const { context } = await waitForTestContext();
112
- const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
159
+ const { context } = await waitForTestContext();
160
+ const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
113
161
 
114
- expect(plugin.getHelloTitle()).toBe('Hello from CustomPlugin');
162
+ expect(plugin.getHelloTitle()).toBe('Witaj z CustomPlugin');
163
+ });
115
164
  });
116
165
 
117
- it('should support custom language for context translations', async () => {
118
- class CustomPlugin extends ContextPlugin {
119
- static get pluginName() {
120
- return 'CustomPlugin';
121
- }
166
+ describe('translations', () => {
167
+ it('registered plugins should support custom translations', async () => {
168
+ class CustomPlugin extends ContextPlugin {
169
+ static get pluginName() {
170
+ return 'CustomPlugin';
171
+ }
122
172
 
123
- getHelloTitle() {
124
- return this.context.t('HELLO');
173
+ getHelloTitle() {
174
+ return this.context.t('HELLO');
175
+ }
125
176
  }
126
- }
127
177
 
128
- CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
178
+ CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
129
179
 
130
- renderTestContext(createContextSnapshot(
131
- DEFAULT_TEST_CONTEXT_ID,
132
- {
180
+ renderTestContext(createContextSnapshot(DEFAULT_TEST_CONTEXT_ID, {
133
181
  customTranslations: {
134
182
  en: {
135
183
  HELLO: 'Hello from CustomPlugin',
136
184
  },
137
- pl: {
138
- HELLO: 'Witaj z CustomPlugin',
139
- },
140
185
  },
141
186
  config: {
142
187
  plugins: ['CustomPlugin'],
143
188
  },
144
- },
145
- {
146
- ui: 'pl',
147
- content: 'pl',
148
- },
149
- ));
189
+ }));
150
190
 
151
- const { context } = await waitForTestContext();
152
- const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
191
+ const { context } = await waitForTestContext();
192
+ const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
153
193
 
154
- expect(plugin.getHelloTitle()).toBe('Witaj z CustomPlugin');
155
- });
194
+ expect(plugin.getHelloTitle()).toBe('Hello from CustomPlugin');
195
+ });
156
196
 
157
- it('should support translations references in config', async () => {
158
- renderTestContext(createContextSnapshot(
159
- DEFAULT_TEST_CONTEXT_ID,
160
- {
161
- customTranslations: {
162
- en: {
163
- HELLO: 'Hello from CustomPlugin',
197
+ it('should support translations references in config', async () => {
198
+ renderTestContext(createContextSnapshot(
199
+ DEFAULT_TEST_CONTEXT_ID,
200
+ {
201
+ customTranslations: {
202
+ en: {
203
+ HELLO: 'Hello from CustomPlugin',
204
+ },
205
+ pl: {
206
+ HELLO: 'Witaj z CustomPlugin',
207
+ },
164
208
  },
165
- pl: {
166
- HELLO: 'Witaj z CustomPlugin',
209
+ config: {
210
+ customConfig: { $translation: 'HELLO' },
167
211
  },
168
212
  },
169
- config: {
170
- customConfig: { $translation: 'HELLO' },
213
+ {
214
+ ui: 'pl',
215
+ content: 'pl',
171
216
  },
172
- },
173
- {
174
- ui: 'pl',
175
- content: 'pl',
176
- },
177
- ));
217
+ ));
178
218
 
179
- const { context } = await waitForTestContext();
180
- const translation = context?.config.get('customConfig') as string;
219
+ const { context } = await waitForTestContext();
220
+ const translation = context?.config.get('customConfig') as string;
181
221
 
182
- expect(translation).toBe('Witaj z CustomPlugin');
222
+ expect(translation).toBe('Witaj z CustomPlugin');
223
+ });
183
224
  });
184
225
  });
185
226
 
@@ -8,6 +8,7 @@ import {
8
8
  loadAllEditorTranslations,
9
9
  loadEditorPlugins,
10
10
  normalizeCustomTranslations,
11
+ normalizeEditorLanguage,
11
12
  resolveEditorConfigElementReferences,
12
13
  resolveEditorConfigTranslations,
13
14
  } from '../editor/utils';
@@ -46,11 +47,16 @@ export class ContextComponentElement extends HTMLElement {
46
47
  * Initializes the context component.
47
48
  */
48
49
  private async initializeContext(): Promise<void> {
49
- const contextId = this.getAttribute('data-cke-context-id')!;
50
- const language = JSON.parse(this.getAttribute('data-cke-language')!) as EditorLanguage;
51
50
  const contextConfig = JSON.parse(this.getAttribute('data-cke-context')!) as ContextConfig;
52
-
53
51
  const { customTranslations, watchdogConfig, config: { plugins, ...config } } = contextConfig;
52
+
53
+ const contextId = this.getAttribute('data-cke-context-id')!;
54
+ const language = (
55
+ this.getAttribute('data-cke-language')
56
+ ? JSON.parse(this.getAttribute('data-cke-language')!)
57
+ : normalizeEditorLanguage(config['language'])
58
+ ) as EditorLanguage;
59
+
54
60
  const { loadedPlugins, hasPremium } = await loadEditorPlugins(plugins ?? []);
55
61
 
56
62
  // Mix custom translations with loaded translations.
@@ -88,9 +88,9 @@ export class EditableComponentElement extends HTMLElement {
88
88
 
89
89
  editor.addRoot(rootName, {
90
90
  isUndoable: false,
91
- attributes: { ...rootAttributes },
91
+ modelAttributes: { ...rootAttributes },
92
92
  ...content !== null && {
93
- data: content,
93
+ initialData: content,
94
94
  },
95
95
  });
96
96
 
@@ -14,6 +14,8 @@ import {
14
14
  createSyncEditorWithInputPlugin,
15
15
  } from './plugins';
16
16
  import {
17
+ assignInitialDataToEditorConfig,
18
+ assignSourceElementsToEditorConfig,
17
19
  cleanupOrphanEditorElements,
18
20
  createEditorInContext,
19
21
  isSingleRootEditor,
@@ -21,6 +23,7 @@ import {
21
23
  loadEditorConstructor,
22
24
  loadEditorPlugins,
23
25
  normalizeCustomTranslations,
26
+ normalizeEditorLanguage,
24
27
  queryEditablesElements,
25
28
  queryEditablesSnapshotContent,
26
29
  resolveEditorConfigElementReferences,
@@ -140,16 +143,7 @@ export class EditorComponentElement extends HTMLElement {
140
143
  * Creates the CKEditor instance.
141
144
  */
142
145
  private async createEditor() {
143
- const editorId = this.getAttribute('data-cke-editor-id')!;
144
146
  const preset = JSON.parse(this.getAttribute('data-cke-preset')!) as EditorPreset;
145
- const contextId = this.getAttribute('data-cke-context-id');
146
- const rootAttributes = JSON.parse(this.getAttribute('data-cke-root-attributes') || '{}');
147
- const editableHeight = this.getAttribute('data-cke-editable-height') ? Number.parseInt(this.getAttribute('data-cke-editable-height')!, 10) : null;
148
- const saveDebounceMs = Number.parseInt(this.getAttribute('data-cke-save-debounce-ms')!, 10);
149
- const language = JSON.parse(this.getAttribute('data-cke-language')!) as EditorLanguage;
150
- const useWatchdog = this.hasAttribute('data-cke-watchdog');
151
- const content = JSON.parse(this.getAttribute('data-cke-content')!) as Record<string, string>;
152
-
153
147
  const {
154
148
  customTranslations,
155
149
  editorType,
@@ -158,6 +152,19 @@ export class EditorComponentElement extends HTMLElement {
158
152
  config: { plugins, ...config },
159
153
  } = preset;
160
154
 
155
+ const editorId = this.getAttribute('data-cke-editor-id')!;
156
+ const contextId = this.getAttribute('data-cke-context-id');
157
+ const rootAttributes = JSON.parse(this.getAttribute('data-cke-root-attributes') || '{}');
158
+ const editableHeight = this.getAttribute('data-cke-editable-height') ? Number.parseInt(this.getAttribute('data-cke-editable-height')!, 10) : null;
159
+ const saveDebounceMs = Number.parseInt(this.getAttribute('data-cke-save-debounce-ms')!, 10);
160
+ const useWatchdog = this.hasAttribute('data-cke-watchdog');
161
+ const content = JSON.parse(this.getAttribute('data-cke-content')!) as Record<string, string>;
162
+ const language = (
163
+ this.getAttribute('data-cke-language')
164
+ ? JSON.parse(this.getAttribute('data-cke-language')!)
165
+ : normalizeEditorLanguage(preset.config['language'])
166
+ ) as EditorLanguage;
167
+
161
168
  const Constructor = await loadEditorConstructor(editorType);
162
169
  const context = await (
163
170
  contextId
@@ -205,19 +212,19 @@ export class EditorComponentElement extends HTMLElement {
205
212
 
206
213
  // Depending of the editor type, and parent lookup for nearest context or initialize it without it.
207
214
  const editor = await (async () => {
208
- let sourceElementOrData: HTMLElement | Record<string, HTMLElement> = queryEditablesElements(editorId);
215
+ let sourceElements: HTMLElement | Record<string, HTMLElement> = queryEditablesElements(editorId);
209
216
 
210
217
  // Handle special case when user specified `initialData` of several root elements, but editable components
211
218
  // are not yet present in the DOM. In other words - editor is initialized before attaching root elements.
212
- if (!sourceElementOrData['main']) {
219
+ if (!sourceElements['main']) {
213
220
  const requiredRoots = (
214
221
  isSingleRootEditor(editorType)
215
222
  ? ['main']
216
223
  : Object.keys(initialData as Record<string, string>)
217
224
  );
218
225
 
219
- if (!checkIfAllRootsArePresent(sourceElementOrData, requiredRoots)) {
220
- sourceElementOrData = await waitForAllRootsToBePresent(editorId, requiredRoots);
226
+ if (!checkIfAllRootsArePresent(sourceElements, requiredRoots)) {
227
+ sourceElements = await waitForAllRootsToBePresent(editorId, requiredRoots);
221
228
  initialData = {
222
229
  ...content,
223
230
  ...queryEditablesSnapshotContent(editorId),
@@ -226,20 +233,13 @@ export class EditorComponentElement extends HTMLElement {
226
233
  }
227
234
 
228
235
  // If single root editor, unwrap the element from the object.
229
- if (isSingleRootEditor(editorType) && 'main' in sourceElementOrData) {
230
- sourceElementOrData = sourceElementOrData['main'];
236
+ if (isSingleRootEditor(editorType) && 'main' in sourceElements) {
237
+ sourceElements = sourceElements['main'];
231
238
  }
232
239
 
233
- // Construct parsed config. First resolve DOM element references in the provided configuration.
234
- let resolvedConfig = resolveEditorConfigElementReferences(config);
235
-
236
- // Then resolve translation references in the provided configuration, using the mixed translations.
237
- resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
238
-
239
- // Construct parsed config.
240
- const parsedConfig = {
241
- ...resolvedConfig,
242
- initialData,
240
+ // Do some postprocessing on received configuration.
241
+ let resolvedConfig = {
242
+ ...config,
243
243
  licenseKey,
244
244
  plugins: loadedPlugins,
245
245
  language,
@@ -248,15 +248,19 @@ export class EditorComponentElement extends HTMLElement {
248
248
  },
249
249
  };
250
250
 
251
- if (!context || !(sourceElementOrData instanceof HTMLElement)) {
252
- return Constructor.create(sourceElementOrData as any, parsedConfig);
251
+ resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
252
+ resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
253
+ resolvedConfig = assignSourceElementsToEditorConfig(Constructor, sourceElements, resolvedConfig);
254
+ resolvedConfig = assignInitialDataToEditorConfig(initialData, resolvedConfig);
255
+
256
+ if (!context) {
257
+ return Constructor.create(resolvedConfig);
253
258
  }
254
259
 
255
260
  const result = await createEditorInContext({
256
261
  context,
257
- element: sourceElementOrData,
258
262
  creator: Constructor,
259
- config: parsedConfig,
263
+ config: resolvedConfig,
260
264
  });
261
265
 
262
266
  return result.editor;
@@ -0,0 +1,6 @@
1
+ import type { Editor } from 'ckeditor5';
2
+
3
+ export type EditorRelaxedConstructor<TEditor extends Editor = Editor> = {
4
+ create: (...args: any) => Promise<TEditor>;
5
+ editorName?: string;
6
+ };
@@ -0,0 +1 @@
1
+ export * from './editor-relaxed-constructor.type';
@@ -0,0 +1,47 @@
1
+ import type { EditorConfig } from 'ckeditor5';
2
+
3
+ /**
4
+ * Assigns initial data to specified editor config.
5
+ *
6
+ * @param dataOrMap Initial data to be assigned to config.
7
+ * @param config Config of the editor.
8
+ * @returns The updated configuration object.
9
+ */
10
+ export function assignInitialDataToEditorConfig<C extends EditorConfig>(
11
+ dataOrMap: string | Record<string, string>,
12
+ config: C,
13
+ ): C {
14
+ const dataMap = toDataMap(dataOrMap);
15
+ const allRootsKeys = new Set([
16
+ ...Object.keys(dataMap),
17
+ ...Object.keys(config.roots ?? {}),
18
+ ]);
19
+
20
+ const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
21
+ ...acc,
22
+ [rootKey]: {
23
+ ...config.roots?.[rootKey],
24
+ ...rootKey === 'main' ? config.root : {},
25
+
26
+ /* v8 ignore next 5 */
27
+ ...rootKey in dataMap
28
+ ? {
29
+ initialData: dataMap[rootKey],
30
+ }
31
+ : {},
32
+ },
33
+ }), Object.create(config.roots || {}));
34
+
35
+ const mappedConfig: C = {
36
+ ...config,
37
+ roots: rootsConfig,
38
+ };
39
+
40
+ delete mappedConfig.root;
41
+
42
+ return mappedConfig;
43
+ }
44
+
45
+ function toDataMap(element: string | Record<string, string>): Record<string, string> {
46
+ return typeof element === 'string' ? { main: element } : { ...element };
47
+ }
@@ -0,0 +1,59 @@
1
+ import type { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
2
+ import type { EditorConfig } from 'ckeditor5';
3
+
4
+ /**
5
+ * Assigns a DOM element to the editor configuration in a way that is compatible with the specific editor type.
6
+ *
7
+ * @param Editor Constructor of the editor used to determine the location of element config entry.
8
+ * @param elementOrMap Element to be assigned to config.
9
+ * @param config Config of the editor.
10
+ * @returns The updated configuration object.
11
+ */
12
+ export function assignSourceElementsToEditorConfig<C extends EditorConfig>(
13
+ Editor: EditorRelaxedConstructor,
14
+ elementOrMap: HTMLElement | Record<string, HTMLElement>,
15
+ config: C,
16
+ ): C {
17
+ const elementsMap = toElementsMap(elementOrMap);
18
+
19
+ if (!Editor.editorName || Editor.editorName === 'ClassicEditor') {
20
+ return {
21
+ ...config,
22
+ attachTo: elementsMap['main'],
23
+ };
24
+ }
25
+
26
+ const allRootsKeys = new Set([
27
+ ...Object.keys(elementsMap),
28
+ ...Object.keys(config.roots ?? {}),
29
+ ]);
30
+
31
+ const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
32
+ ...acc,
33
+ [rootKey]: {
34
+ /* v8 ignore next */
35
+ ...config.roots?.[rootKey],
36
+ ...rootKey === 'main' ? config.root : {},
37
+
38
+ /* v8 ignore next 5 */
39
+ ...rootKey in elementsMap
40
+ ? {
41
+ element: elementsMap[rootKey],
42
+ }
43
+ : {},
44
+ },
45
+ }), Object.create(config.roots || {}));
46
+
47
+ const mappedConfig: C = {
48
+ ...config,
49
+ roots: rootsConfig,
50
+ };
51
+
52
+ delete mappedConfig.root;
53
+
54
+ return mappedConfig;
55
+ }
56
+
57
+ function toElementsMap(element: HTMLElement | Record<string, HTMLElement>): Record<string, HTMLElement> {
58
+ return element instanceof HTMLElement ? { main: element } : { ...element };
59
+ }
@@ -12,19 +12,17 @@ const CONTEXT_EDITOR_WATCHDOG_SYMBOL = Symbol.for('context-editor-watchdog');
12
12
  * Creates a CKEditor 5 editor instance within a given context watchdog.
13
13
  *
14
14
  * @param params Parameters for editor creation.
15
- * @param params.element The DOM element or data for the editor.
16
15
  * @param params.context The context watchdog instance.
17
16
  * @param params.creator The editor creator utility.
18
17
  * @param params.config The editor configuration object.
19
18
  * @returns The created editor instance.
20
19
  */
21
- export async function createEditorInContext({ element, context, creator, config }: Attrs) {
20
+ export async function createEditorInContext({ context, creator, config }: Attrs) {
22
21
  const editorContextId = uid();
23
22
 
24
23
  await context.add({
25
- creator: (_element, _config) => creator.create(_element, _config),
24
+ creator: creator.create.bind(creator),
26
25
  id: editorContextId,
27
- sourceElementOrData: element,
28
26
  type: 'editor',
29
27
  config,
30
28
  });
@@ -74,7 +72,6 @@ export function unwrapEditorContext(editor: Editor): EditorContextDescriptor | n
74
72
  type Attrs = {
75
73
  context: ContextWatchdog<Context>;
76
74
  creator: EditorCreator;
77
- element: HTMLElement;
78
75
  config: EditorConfig;
79
76
  };
80
77
 
@@ -1,3 +1,5 @@
1
+ export * from './assign-initial-data-to-editor-config';
2
+ export * from './assign-source-elements-to-editor-config';
1
3
  export * from './cleanup-orphan-editor-elements';
2
4
  export * from './create-editor-in-context';
3
5
  export * from './get-editor-roots-values';
@@ -6,6 +8,7 @@ export * from './load-editor-constructor';
6
8
  export * from './load-editor-plugins';
7
9
  export * from './load-editor-translations';
8
10
  export * from './normalize-custom-translations';
11
+ export * from './normalize-editor-language';
9
12
  export * from './query-all-editor-ids';
10
13
  export * from './query-editor-editables';
11
14
  export * from './resolve-editor-config-elements-references';
@@ -0,0 +1,50 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { normalizeEditorLanguage } from './normalize-editor-language';
4
+
5
+ describe('normalizeEditorLanguage', () => {
6
+ describe('when lang is falsy', () => {
7
+ it('returns default object with "en" for empty string', () => {
8
+ expect(normalizeEditorLanguage('')).toEqual({ ui: 'en', content: 'en' });
9
+ });
10
+
11
+ it('returns default object with "en" for null', () => {
12
+ expect(normalizeEditorLanguage(null as any)).toEqual({ ui: 'en', content: 'en' });
13
+ });
14
+
15
+ it('returns default object with "en" for undefined', () => {
16
+ expect(normalizeEditorLanguage(undefined as any)).toEqual({ ui: 'en', content: 'en' });
17
+ });
18
+ });
19
+
20
+ describe('when lang is a string', () => {
21
+ it('returns object with ui and content set to the given language', () => {
22
+ expect(normalizeEditorLanguage('pl')).toEqual({ ui: 'pl', content: 'pl' });
23
+ });
24
+
25
+ it('handles "en" language code correctly', () => {
26
+ expect(normalizeEditorLanguage('en')).toEqual({ ui: 'en', content: 'en' });
27
+ });
28
+
29
+ it('handles "de" language code correctly', () => {
30
+ expect(normalizeEditorLanguage('de')).toEqual({ ui: 'de', content: 'de' });
31
+ });
32
+ });
33
+
34
+ describe('when lang is an object', () => {
35
+ it('returns the object unchanged when ui and content are the same', () => {
36
+ const lang = { ui: 'pl', content: 'pl' };
37
+ expect(normalizeEditorLanguage(lang)).toEqual(lang);
38
+ });
39
+
40
+ it('returns the object unchanged when ui and content differ', () => {
41
+ const lang = { ui: 'pl', content: 'en' };
42
+ expect(normalizeEditorLanguage(lang)).toEqual(lang);
43
+ });
44
+
45
+ it('returns the exact same object reference', () => {
46
+ const lang = { ui: 'fr', content: 'en' };
47
+ expect(normalizeEditorLanguage(lang)).toBe(lang);
48
+ });
49
+ });
50
+ });
@@ -0,0 +1,25 @@
1
+ import type { EditorLanguage } from '../typings';
2
+
3
+ /**
4
+ * Normalize editor language into object.
5
+ *
6
+ * @param lang Editor language.
7
+ * @returns Language object.
8
+ */
9
+ export function normalizeEditorLanguage(lang: EditorLanguage | string): EditorLanguage {
10
+ if (!lang) {
11
+ return {
12
+ ui: 'en',
13
+ content: 'en',
14
+ };
15
+ }
16
+
17
+ if (typeof lang === 'string') {
18
+ return {
19
+ ui: lang,
20
+ content: lang,
21
+ };
22
+ }
23
+
24
+ return lang;
25
+ }