From 336f4b81e66206dcd3e067c8f94bf2d0e89533f3 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 5 Oct 2024 11:30:12 +0400 Subject: [PATCH] Feature detect document + fixes on configs (#6189) * Add frame and page getters to Component * Allow to skip dom reset in components * Add detectDocument option to parser config * Add __attrToString * Update all configs in modules to avoid objects by reference * Add tests to detectDocument * Unskip some tests --- .../core/src/asset_manager/config/config.ts | 4 +- packages/core/src/asset_manager/index.ts | 4 +- .../core/src/block_manager/config/config.ts | 4 +- packages/core/src/block_manager/index.ts | 4 +- packages/core/src/canvas/config/config.ts | 4 +- packages/core/src/canvas/index.ts | 4 +- .../core/src/code_manager/config/config.ts | 4 +- packages/core/src/code_manager/index.ts | 4 +- packages/core/src/commands/config/config.ts | 4 +- packages/core/src/commands/index.ts | 4 +- .../core/src/css_composer/config/config.ts | 4 +- packages/core/src/css_composer/index.ts | 4 +- .../core/src/device_manager/config/config.ts | 4 +- packages/core/src/device_manager/index.ts | 4 +- .../core/src/dom_components/config/config.ts | 6 +- packages/core/src/dom_components/index.ts | 11 +-- .../src/dom_components/model/Component.ts | 75 +++++++++++-------- .../dom_components/model/ComponentWrapper.ts | 13 ++-- .../src/dom_components/model/Components.ts | 5 +- .../core/src/dom_components/model/types.ts | 6 ++ .../src/dom_components/view/ComponentsView.ts | 10 ++- packages/core/src/editor/config/config.ts | 4 +- packages/core/src/editor/index.ts | 3 +- packages/core/src/editor/model/Editor.ts | 1 + packages/core/src/i18n/config.ts | 4 +- packages/core/src/i18n/index.ts | 4 +- packages/core/src/keymaps/config.ts | 4 +- packages/core/src/keymaps/index.ts | 4 +- .../core/src/modal_dialog/config/config.ts | 4 +- packages/core/src/modal_dialog/index.ts | 4 +- packages/core/src/navigator/config/config.ts | 4 +- packages/core/src/navigator/index.ts | 4 +- packages/core/src/panels/config/config.ts | 4 +- packages/core/src/panels/index.ts | 9 +-- packages/core/src/parser/config/config.ts | 9 ++- packages/core/src/parser/index.ts | 4 +- packages/core/src/parser/model/ParserHtml.ts | 19 ++++- .../src/rich_text_editor/config/config.ts | 4 +- packages/core/src/rich_text_editor/index.ts | 4 +- .../src/selector_manager/config/config.ts | 4 +- packages/core/src/selector_manager/index.ts | 5 +- .../core/src/storage_manager/config/config.ts | 4 +- packages/core/src/storage_manager/index.ts | 4 +- .../core/src/style_manager/config/config.ts | 6 +- packages/core/src/style_manager/index.ts | 4 +- .../core/src/trait_manager/config/config.ts | 4 +- packages/core/src/trait_manager/index.ts | 4 +- packages/core/src/undo_manager/config.ts | 4 +- packages/core/src/undo_manager/index.ts | 4 +- packages/core/test/common.ts | 11 +++ .../core/test/specs/dom_components/index.ts | 49 +++++++++--- packages/core/test/specs/grapesjs/index.ts | 73 ++++++++++++------ .../view/{ItemView.js => ItemView.ts} | 14 ++-- 53 files changed, 284 insertions(+), 181 deletions(-) rename packages/core/test/specs/navigator/view/{ItemView.js => ItemView.ts} (72%) diff --git a/packages/core/src/asset_manager/config/config.ts b/packages/core/src/asset_manager/config/config.ts index 09c5def67..34ca1c593 100644 --- a/packages/core/src/asset_manager/config/config.ts +++ b/packages/core/src/asset_manager/config/config.ts @@ -141,7 +141,7 @@ export interface AssetManagerConfig { dropzoneContent?: string; } -const config: AssetManagerConfig = { +const config: () => AssetManagerConfig = () => ({ assets: [], noAssets: '', stylePrefix: 'am-', @@ -163,6 +163,6 @@ const config: AssetManagerConfig = { dropzone: false, openAssetsOnDrop: true, dropzoneContent: '', -}; +}); export default config; diff --git a/packages/core/src/asset_manager/index.ts b/packages/core/src/asset_manager/index.ts index b6d547531..cfee7a7b4 100644 --- a/packages/core/src/asset_manager/index.ts +++ b/packages/core/src/asset_manager/index.ts @@ -37,7 +37,7 @@ import { ItemManagerModule } from '../abstract/Module'; import { AddOptions, RemoveOptions } from '../common'; import EditorModel from '../editor/model/Editor'; import { ProjectData } from '../storage_manager'; -import defaults, { AssetManagerConfig } from './config/config'; +import defConfig, { AssetManagerConfig } from './config/config'; import Asset from './model/Asset'; import Assets from './model/Assets'; import AssetsEvents, { AssetOpenOptions } from './types'; @@ -66,7 +66,7 @@ export default class AssetManager extends ItemManagerModule BlockManagerConfig = () => ({ appendTo: '', blocks: [], appendOnClick: false, custom: false, -}; +}); export default config; diff --git a/packages/core/src/block_manager/index.ts b/packages/core/src/block_manager/index.ts index eb3a5ecdb..a134e11fe 100644 --- a/packages/core/src/block_manager/index.ts +++ b/packages/core/src/block_manager/index.ts @@ -31,7 +31,7 @@ import { ItemManagerModule } from '../abstract/Module'; import FrameView from '../canvas/view/FrameView'; import Component from '../dom_components/model/Component'; import EditorModel from '../editor/model/Editor'; -import defaults, { BlockManagerConfig } from './config/config'; +import defConfig, { BlockManagerConfig } from './config/config'; import Block, { BlockProperties } from './model/Block'; import Blocks from './model/Blocks'; import Categories from '../abstract/ModuleCategories'; @@ -61,7 +61,7 @@ export default class BlockManager extends ItemManagerModule CanvasConfig = () => ({ stylePrefix: 'cv-', scripts: [], styles: [], @@ -107,6 +107,6 @@ const config: CanvasConfig = { `, notTextable: ['button', 'a', 'input[type=checkbox]', 'input[type=radio]'], allowExternalDrop: true, -}; +}); export default config; diff --git a/packages/core/src/canvas/index.ts b/packages/core/src/canvas/index.ts index ae2b5fa4e..c3c84fa1f 100644 --- a/packages/core/src/canvas/index.ts +++ b/packages/core/src/canvas/index.ts @@ -35,7 +35,7 @@ import Component from '../dom_components/model/Component'; import ComponentView from '../dom_components/view/ComponentView'; import EditorModel from '../editor/model/Editor'; import { getElement, getViewEl } from '../utils/mixins'; -import defaults, { CanvasConfig } from './config/config'; +import defConfig, { CanvasConfig } from './config/config'; import Canvas from './model/Canvas'; import CanvasSpot, { CanvasSpotBuiltInTypes, CanvasSpotProps } from './model/CanvasSpot'; import CanvasSpots from './model/CanvasSpots'; @@ -76,7 +76,7 @@ export default class CanvasModule extends Module { * @private */ constructor(em: EditorModel) { - super(em, 'Canvas', defaults); + super(em, 'Canvas', defConfig()); this.canvas = new Canvas(this); this.spots = new CanvasSpots(this); diff --git a/packages/core/src/code_manager/config/config.ts b/packages/core/src/code_manager/config/config.ts index 2d0379e78..44c3e2e89 100644 --- a/packages/core/src/code_manager/config/config.ts +++ b/packages/core/src/code_manager/config/config.ts @@ -12,9 +12,9 @@ export interface CodeManagerConfig { optsCodeViewer?: Record; } -const config: CodeManagerConfig = { +const config: () => CodeManagerConfig = () => ({ stylePrefix: 'cm-', optsCodeViewer: {}, -}; +}); export default config; diff --git a/packages/core/src/code_manager/index.ts b/packages/core/src/code_manager/index.ts index e987bfd5f..2e6102f56 100644 --- a/packages/core/src/code_manager/index.ts +++ b/packages/core/src/code_manager/index.ts @@ -18,7 +18,7 @@ * @module CodeManager */ import { isUndefined } from 'underscore'; -import defaults, { CodeManagerConfig } from './config/config'; +import defConfig, { CodeManagerConfig } from './config/config'; import gHtml from './model/HtmlGenerator'; import gCss from './model/CssGenerator'; import gJson from './model/JsonGenerator'; @@ -39,7 +39,7 @@ export default class CodeManagerModule extends Module CommandsConfig = () => ({ stylePrefix: 'com-', defaults: {}, strict: true, -}; +}); export default config; diff --git a/packages/core/src/commands/index.ts b/packages/core/src/commands/index.ts index 3db755134..0cd98354c 100644 --- a/packages/core/src/commands/index.ts +++ b/packages/core/src/commands/index.ts @@ -37,7 +37,7 @@ import { isFunction, includes } from 'underscore'; import CommandAbstract, { Command, CommandOptions, CommandObject, CommandFunction } from './view/CommandAbstract'; -import defaults, { CommandsConfig } from './config/config'; +import defConfig, { CommandsConfig } from './config/config'; import { Module } from '../abstract'; import Component, { eventDrag } from '../dom_components/model/Component'; import Editor from '../editor/model/Editor'; @@ -107,7 +107,7 @@ export default class CommandsModule extends Module; // TODO } -const config: CssComposerConfig = { +const config: () => CssComposerConfig = () => ({ stylePrefix: 'css-', rules: [], -}; +}); export default config; diff --git a/packages/core/src/css_composer/index.ts b/packages/core/src/css_composer/index.ts index 0e226663a..d3783eeb9 100644 --- a/packages/core/src/css_composer/index.ts +++ b/packages/core/src/css_composer/index.ts @@ -31,7 +31,7 @@ import { isArray, isString, isUndefined } from 'underscore'; import { isObject } from '../utils/mixins'; import Selectors from '../selector_manager/model/Selectors'; import Selector from '../selector_manager/model/Selector'; -import defaults, { CssComposerConfig } from './config/config'; +import defConfig, { CssComposerConfig } from './config/config'; import CssRule, { CssRuleJSON, CssRuleProperties } from './model/CssRule'; import CssRules from './model/CssRules'; import CssRulesView from './view/CssRulesView'; @@ -85,7 +85,7 @@ export default class CssComposer extends ItemManagerModule DeviceManagerConfig = () => ({ default: '', devices: [ { @@ -52,6 +52,6 @@ const config: DeviceManagerConfig = { widthMedia: '480px', }, ], -}; +}); export default config; diff --git a/packages/core/src/device_manager/index.ts b/packages/core/src/device_manager/index.ts index bd0c3d1ff..a0d1a3668 100644 --- a/packages/core/src/device_manager/index.ts +++ b/packages/core/src/device_manager/index.ts @@ -35,7 +35,7 @@ import { isString } from 'underscore'; import { ItemManagerModule } from '../abstract/Module'; import EditorModel from '../editor/model/Editor'; -import defaults, { DeviceManagerConfig } from './config/config'; +import defConfig, { DeviceManagerConfig } from './config/config'; import Device, { DeviceProperties } from './model/Device'; import Devices from './model/Devices'; import DevicesView from './view/DevicesView'; @@ -74,7 +74,7 @@ export default class DeviceManager extends ItemManagerModule< storageKey = ''; constructor(em: EditorModel) { - super(em, 'DeviceManager', new Devices(), deviceEvents, defaults); + super(em, 'DeviceManager', new Devices(), deviceEvents, defConfig()); this.devices = this.all; this.config.devices?.forEach((device) => this.add(device, { silent: true })); this.select(this.config.default || this.devices.at(0)); diff --git a/packages/core/src/dom_components/config/config.ts b/packages/core/src/dom_components/config/config.ts index e184c03a2..a7bff2d54 100644 --- a/packages/core/src/dom_components/config/config.ts +++ b/packages/core/src/dom_components/config/config.ts @@ -62,7 +62,7 @@ export interface DomComponentsConfig { useFrameDoc?: boolean; } -export default { +const config: () => DomComponentsConfig = () => ({ stylePrefix: 'comp-', components: [], draggableComponents: true, @@ -87,4 +87,6 @@ export default { 'track', 'wbr', ], -} as DomComponentsConfig; +}); + +export default config; diff --git a/packages/core/src/dom_components/index.ts b/packages/core/src/dom_components/index.ts index 8e987c5a4..008a0380a 100644 --- a/packages/core/src/dom_components/index.ts +++ b/packages/core/src/dom_components/index.ts @@ -55,10 +55,10 @@ */ import { debounce, isArray, isBoolean, isEmpty, isFunction, isString, isSymbol, result } from 'underscore'; import { ItemManagerModule } from '../abstract/Module'; -import { AddOptions, ObjectAny } from '../common'; +import { ObjectAny } from '../common'; import EditorModel from '../editor/model/Editor'; import { isComponent } from '../utils/mixins'; -import defaults, { DomComponentsConfig } from './config/config'; +import defConfig, { DomComponentsConfig } from './config/config'; import Component, { IComponent, keyUpdate, keyUpdateInside } from './model/Component'; import ComponentComment from './model/ComponentComment'; import ComponentFrame from './model/ComponentFrame'; @@ -334,7 +334,7 @@ export default class ComponentManager extends ItemManagerModule { return this.get('locked'); } + get frame() { + return this.opt.frame; + } + + get page() { + return this.frame?.getPage(); + } + + preInit() {} + /** * Hook method, called once the model is created */ @@ -238,7 +248,6 @@ export default class Component extends StyleableModel { views!: ComponentView[]; view?: ComponentView; viewLayer?: ItemView; - frame?: Frame; rule?: CssRule; prevColl?: Components; __hasUm?: boolean; @@ -275,13 +284,13 @@ export default class Component extends StyleableModel { opt.em = em; this.opt = opt; this.em = em!; - this.frame = opt.frame; this.config = opt.config || {}; this.set('attributes', { ...(result(this, 'defaults').attributes || {}), ...(this.get('attributes') || {}), }); this.ccid = Component.createId(this, opt); + this.preInit(); this.initClasses(); this.initComponents(); this.initTraits(); @@ -999,7 +1008,7 @@ export default class Component extends StyleableModel { */ components( components?: T, - opts: AddComponentsOption = {}, + opts: ResetComponentsOptions = {}, ): undefined extends T ? Components : Component[] { const coll = this.get('components')!; @@ -1428,19 +1437,43 @@ export default class Component extends StyleableModel { * // -> */ toHTML(opts: ToHTMLOptions = {}): string { - const model = this; - const attrs = []; const customTag = opts.tag; - const tag = customTag || model.get('tagName'); - const sTag = model.get('void'); + const tag = customTag || this.get('tagName'); + delete opts.tag; + + const attr = this.__attrToString(opts); + const attrString = attr ? ` ${attr}` : ''; + const inner = this.getInnerHTML(opts); + const skipEndTag = !inner && this.get('void'); + let code = `<${tag}${attrString}${skipEndTag ? '/' : ''}>${inner}`; + !skipEndTag && (code += ``); + + return code; + } + + /** + * Get inner HTML of the component + * @param {Object} [opts={}] Same options of `toHTML` + * @returns {String} HTML string + */ + getInnerHTML(opts?: ToHTMLOptions) { + return this.__innerHTML(opts); + } + + __innerHTML(opts: ToHTMLOptions = {}) { + const cmps = this.components(); + return !cmps.length ? this.content : cmps.map((c) => c.toHTML(opts)).join(''); + } + + __attrToString(opts: ToHTMLOptions = {}) { + const attrs = []; const customAttr = opts.attributes; let attributes = this.getAttrToHTML(opts); - delete opts.tag; // Get custom attributes if requested if (customAttr) { if (isFunction(customAttr)) { - attributes = customAttr(model, attributes) || {}; + attributes = customAttr(this, attributes) || {}; } else if (isObject(customAttr)) { attributes = customAttr; } @@ -1448,7 +1481,6 @@ export default class Component extends StyleableModel { if (opts.withProps) { const props = this.toJSON(); - forEach(props, (value, key) => { const skipProps = ['classes', 'attributes', 'components']; if (key[0] !== '_' && skipProps.indexOf(key) < 0) { @@ -1478,26 +1510,7 @@ export default class Component extends StyleableModel { } } - const attrString = attrs.length ? ` ${attrs.join(' ')}` : ''; - const inner = model.getInnerHTML(opts); - let code = `<${tag}${attrString}${sTag ? '/' : ''}>${inner}`; - !sTag && (code += ``); - - return code; - } - - /** - * Get inner HTML of the component - * @param {Object} [opts={}] Same options of `toHTML` - * @returns {String} HTML string - */ - getInnerHTML(opts?: ToHTMLOptions) { - return this.__innerHTML(opts); - } - - __innerHTML(opts: ToHTMLOptions = {}) { - const cmps = this.components(); - return !cmps.length ? this.content : cmps.map((c) => c.toHTML(opts)).join(''); + return attrs.join(' '); } /** diff --git a/packages/core/src/dom_components/model/ComponentWrapper.ts b/packages/core/src/dom_components/model/ComponentWrapper.ts index 615276556..defc6cc8c 100644 --- a/packages/core/src/dom_components/model/ComponentWrapper.ts +++ b/packages/core/src/dom_components/model/ComponentWrapper.ts @@ -30,19 +30,16 @@ export default class ComponentWrapper extends Component { }; } - constructor(...args: ConstructorParameters) { - super(...args); - const props = args[0] || {}; - const opts = args[1]; - - const cmp = opts?.em?.Components; + preInit() { + const { opt, attributes: props } = this; + const cmp = this.em?.Components; const CmpHead = cmp?.getType(typeHead)?.model; const CmpDef = cmp?.getType('default').model; if (CmpHead) { this.set( { - head: new CmpHead({ ...props.head }, opts), - docEl: new CmpDef({ tagName: 'html', ...props.docEl }, opts), + head: new CmpHead({ ...props.head }, opt), + docEl: new CmpDef({ tagName: 'html', ...props.docEl }, opt), }, { silent: true }, ); diff --git a/packages/core/src/dom_components/model/Components.ts b/packages/core/src/dom_components/model/Components.ts index 452fd30ee..a8d225412 100644 --- a/packages/core/src/dom_components/model/Components.ts +++ b/packages/core/src/dom_components/model/Components.ts @@ -254,12 +254,13 @@ Component> { parseString(value: string, opt: ParseStringOptions = {}) { const { em, domc, parent } = this; - const asDocument = opt.asDocument && parent?.is('wrapper'); + const isWrapper = parent?.is('wrapper'); + const asDocument = opt.asDocument && isWrapper; const cssc = em.Css; const parsed = em.Parser.parseHtml(value, { asDocument, ...opt.parserOptions }); let components = parsed.html; - if (asDocument) { + if (isWrapper && parsed.doctype) { const root = parent as ComponentWrapper; const { components: bodyCmps, ...restBody } = (parsed.html as ComponentDefinitionDefined) || {}; const { components: headCmps, ...restHead } = parsed.head || {}; diff --git a/packages/core/src/dom_components/model/types.ts b/packages/core/src/dom_components/model/types.ts index 8364d00d4..10df3a2d1 100644 --- a/packages/core/src/dom_components/model/types.ts +++ b/packages/core/src/dom_components/model/types.ts @@ -19,6 +19,12 @@ export type DraggableDroppableFn = (source: Component, target: Component, index? export interface AddComponentsOption extends AddOptions, OptionAsDocument {} +export interface ResetComponentsOptions extends AddComponentsOption { + previousModels?: Component[]; + keepIds?: string[]; + skipDomReset?: boolean; +} + interface ComponentWithCheck { new (props: any, opt: ComponentOptions): C; isComponent(node: HTMLElement, opts?: ParseNodeOptions): ComponentDefinitionDefined | undefined | boolean; diff --git a/packages/core/src/dom_components/view/ComponentsView.ts b/packages/core/src/dom_components/view/ComponentsView.ts index 15bcb5601..7ae09e0b0 100644 --- a/packages/core/src/dom_components/view/ComponentsView.ts +++ b/packages/core/src/dom_components/view/ComponentsView.ts @@ -7,6 +7,7 @@ import Component from '../model/Component'; import ComponentView from './ComponentView'; import FrameView from '../../canvas/view/FrameView'; import Components from '../model/Components'; +import { ResetComponentsOptions } from '../model/types'; export default class ComponentsView extends View { opts!: any; @@ -124,9 +125,12 @@ export default class ComponentsView extends View { return rendered; } - resetChildren(models: Components, { previousModels = [] } = {}) { - this.parentEl!.innerHTML = ''; - previousModels.forEach((md) => this.removeChildren(md, this.collection)); + resetChildren(models: Components, opts: ResetComponentsOptions = {}) { + const { previousModels } = opts; + if (!opts.skipDomReset) { + this.parentEl!.innerHTML = ''; + } + previousModels?.forEach((md) => this.removeChildren(md, this.collection)); models.each((model) => this.addToCollection(model)); } diff --git a/packages/core/src/editor/config/config.ts b/packages/core/src/editor/config/config.ts index c3fd084d7..3b55d4571 100644 --- a/packages/core/src/editor/config/config.ts +++ b/packages/core/src/editor/config/config.ts @@ -432,7 +432,7 @@ export interface EditorConfig { export type EditorConfigKeys = keyof EditorConfig; -const config: EditorConfig = { +const config: () => EditorConfig = () => ({ stylePrefix: 'gjs-', components: '', style: '', @@ -506,6 +506,6 @@ const config: EditorConfig = { textViewCode: 'Code', keepUnusedStyles: false, customUI: false, -}; +}); export default config; diff --git a/packages/core/src/editor/index.ts b/packages/core/src/editor/index.ts index 9d9bdc8e5..2ea4967b3 100644 --- a/packages/core/src/editor/index.ts +++ b/packages/core/src/editor/index.ts @@ -89,7 +89,7 @@ import TraitManager from '../trait_manager'; import UndoManagerModule from '../undo_manager'; import UtilsModule from '../utils'; import html from '../utils/html'; -import defaults, { EditorConfig, EditorConfigKeys } from './config/config'; +import defConfig, { EditorConfig, EditorConfigKeys } from './config/config'; import EditorModel, { EditorLoadOptions } from './model/Editor'; import EditorView from './view/EditorView'; @@ -132,6 +132,7 @@ export default class Editor implements IBaseModule { config: EditorConfigType; constructor(config: EditorConfig = {}, opts: any = {}) { + const defaults = defConfig(); this.config = { ...defaults, ...config, diff --git a/packages/core/src/editor/model/Editor.ts b/packages/core/src/editor/model/Editor.ts index e3d733117..33390b2ab 100644 --- a/packages/core/src/editor/model/Editor.ts +++ b/packages/core/src/editor/model/Editor.ts @@ -365,6 +365,7 @@ export default class EditorModel extends Model { storageManager: false, undoManager: false, }); + shallow.set({ isShallow: true }); // We only need to load a few modules shallow.Pages.onLoad(); shallow.Canvas.postLoad(); diff --git a/packages/core/src/i18n/config.ts b/packages/core/src/i18n/config.ts index 13e562cec..89ef27b24 100644 --- a/packages/core/src/i18n/config.ts +++ b/packages/core/src/i18n/config.ts @@ -37,13 +37,13 @@ export interface I18nConfig { messagesAdd?: Record; } -const config: I18nConfig = { +const config: () => I18nConfig = () => ({ locale: 'en', localeFallback: 'en', detectLocale: true, debug: false, messages: { en }, messagesAdd: undefined, -}; +}); export default config; diff --git a/packages/core/src/i18n/index.ts b/packages/core/src/i18n/index.ts index 231084c07..b964fb3ac 100644 --- a/packages/core/src/i18n/index.ts +++ b/packages/core/src/i18n/index.ts @@ -27,7 +27,7 @@ import { isUndefined, isString } from 'underscore'; import { Module } from '../abstract'; import EditorModel from '../editor/model/Editor'; import { hasWin, deepMerge } from '../utils/mixins'; -import defaults, { I18nConfig } from './config'; +import defConfig, { I18nConfig } from './config'; import I18nEvents, { Messages } from './types'; export default class I18nModule extends Module { @@ -39,7 +39,7 @@ export default class I18nModule extends Module & { opts?: KeymapOptions }>; } -const config: KeymapsConfig = { +const config: () => KeymapsConfig = () => ({ defaults: { 'core:undo': { keys: '⌘+z, ctrl+z', @@ -66,6 +66,6 @@ const config: KeymapsConfig = { opts: { prevent: true }, }, }, -}; +}); export default config; diff --git a/packages/core/src/keymaps/index.ts b/packages/core/src/keymaps/index.ts index 244b3e8a4..ce0b0659f 100644 --- a/packages/core/src/keymaps/index.ts +++ b/packages/core/src/keymaps/index.ts @@ -48,7 +48,7 @@ import { hasWin } from '../utils/mixins'; import keymaster from '../utils/keymaster'; import { Module } from '../abstract'; import EditorModel from '../editor/model/Editor'; -import defaults, { Keymap, KeymapOptions, KeymapsConfig } from './config'; +import defConfig, { Keymap, KeymapOptions, KeymapsConfig } from './config'; export type KeymapEvent = 'keymap:add' | 'keymap:remove' | 'keymap:emit' | `keymap:emit:${string}`; @@ -59,7 +59,7 @@ export default class KeymapsModule extends Module; constructor(em: EditorModel) { - super(em, 'Keymaps', defaults); + super(em, 'Keymaps', defConfig()); this.keymaps = {}; } diff --git a/packages/core/src/modal_dialog/config/config.ts b/packages/core/src/modal_dialog/config/config.ts index 9a4f6a995..c917e56f7 100644 --- a/packages/core/src/modal_dialog/config/config.ts +++ b/packages/core/src/modal_dialog/config/config.ts @@ -26,13 +26,13 @@ export interface ModalConfig { extend?: Record; } -const config: ModalConfig = { +const config: () => ModalConfig = () => ({ stylePrefix: 'mdl-', title: '', content: '', backdrop: true, custom: false, extend: {}, -}; +}); export default config; diff --git a/packages/core/src/modal_dialog/index.ts b/packages/core/src/modal_dialog/index.ts index f6112592c..36670c5c8 100644 --- a/packages/core/src/modal_dialog/index.ts +++ b/packages/core/src/modal_dialog/index.ts @@ -38,7 +38,7 @@ import { Module } from '../abstract'; import EditorView from '../editor/view/EditorView'; import EditorModel from '../editor/model/Editor'; import { createText } from '../utils/dom'; -import defaults, { ModalConfig } from './config/config'; +import defConfig, { ModalConfig } from './config/config'; import ModalM from './model/Modal'; import ModalView from './view/ModalView'; import { EventHandler } from '../common'; @@ -54,7 +54,7 @@ export default class ModalModule extends Module { * @private */ constructor(em: EditorModel) { - super(em, 'Modal', defaults); + super(em, 'Modal', defConfig()); this.model = new ModalM(this); this.model.on('change:open', (m: ModalM, enable: boolean) => { diff --git a/packages/core/src/navigator/config/config.ts b/packages/core/src/navigator/config/config.ts index ad31fa1a9..4c5f932c2 100644 --- a/packages/core/src/navigator/config/config.ts +++ b/packages/core/src/navigator/config/config.ts @@ -108,7 +108,7 @@ export interface LayerManagerConfig { extend?: Record; } -const config: LayerManagerConfig = { +const config: () => LayerManagerConfig = () => ({ stylePrefix: '', appendTo: '', sortable: true, @@ -124,6 +124,6 @@ const config: LayerManagerConfig = { onInit: () => {}, onRender: () => {}, extend: {}, -}; +}); export default config; diff --git a/packages/core/src/navigator/index.ts b/packages/core/src/navigator/index.ts index 01cf4a029..ad659580d 100644 --- a/packages/core/src/navigator/index.ts +++ b/packages/core/src/navigator/index.ts @@ -45,7 +45,7 @@ import Module from '../abstract/Module'; import Component from '../dom_components/model/Component'; import EditorModel from '../editor/model/Editor'; import { hasWin, isComponent, isDef } from '../utils/mixins'; -import defaults, { LayerManagerConfig } from './config/config'; +import defConfig, { LayerManagerConfig } from './config/config'; import View from './view/ItemView'; import { ComponentsEvents } from '../dom_components/types'; @@ -91,7 +91,7 @@ export default class LayerManager extends Module { events = events; constructor(em: EditorModel) { - super(em, 'LayerManager', defaults); + super(em, 'LayerManager', defConfig()); bindAll(this, 'componentChanged', '__onRootChange', '__onComponent'); this.model = new ModuleModel(this, { opened: {} }); // @ts-ignore diff --git a/packages/core/src/panels/config/config.ts b/packages/core/src/panels/config/config.ts index c8bfcc444..a227f6f75 100644 --- a/packages/core/src/panels/config/config.ts +++ b/packages/core/src/panels/config/config.ts @@ -34,7 +34,7 @@ export interface PanelsConfig { defaults?: PanelProps[]; } -const config: PanelsConfig = { +const config: () => PanelsConfig = () => ({ stylePrefix: 'pn-', defaults: [ { @@ -109,6 +109,6 @@ const config: PanelsConfig = { ], }, ], -}; +}); export default config; diff --git a/packages/core/src/panels/index.ts b/packages/core/src/panels/index.ts index fca2283b5..76614c833 100644 --- a/packages/core/src/panels/index.ts +++ b/packages/core/src/panels/index.ts @@ -27,7 +27,7 @@ */ import { Module } from '../abstract'; import EditorModel from '../editor/model/Editor'; -import defaults, { PanelsConfig } from './config/config'; +import defConfig, { PanelsConfig } from './config/config'; import Panel, { PanelProperties } from './model/Panel'; import Panels from './model/Panels'; import PanelsView from './view/PanelsView'; @@ -42,13 +42,8 @@ export default class PanelManager extends Module { * @private */ constructor(em: EditorModel) { - super(em, 'Panels', defaults); + super(em, 'Panels', defConfig()); this.panels = new Panels(this, this.config.defaults!); - for (var name in defaults) { - //@ts-ignore - if (!(name in this.config)) this.config[name] = defaults[name]; - } - return this; } /** diff --git a/packages/core/src/parser/config/config.ts b/packages/core/src/parser/config/config.ts index 058045c70..2074fd721 100644 --- a/packages/core/src/parser/config/config.ts +++ b/packages/core/src/parser/config/config.ts @@ -60,6 +60,11 @@ export interface HTMLParserOptions extends OptionAsDocument { */ keepEmptyTextNodes?: boolean; + /** + * Indicate if or how to detect if the passed HTML string should be parsed as a document. + */ + detectDocument?: boolean | ((html: string) => boolean); + /** * Custom transformer to run before passing the input HTML to the parser. * A common use case might be to sanitize the input string. @@ -105,7 +110,7 @@ export interface ParserConfig { optionsHtml?: HTMLParserOptions; } -const config: ParserConfig = { +const config: () => ParserConfig = () => ({ textTags: ['br', 'b', 'i', 'u', 'a', 'ul', 'ol'], textTypes: ['text', 'textnode', 'comment'], parserCss: undefined, @@ -117,6 +122,6 @@ const config: ParserConfig = { allowUnsafeAttrValue: false, keepEmptyTextNodes: false, }, -}; +}); export default config; diff --git a/packages/core/src/parser/index.ts b/packages/core/src/parser/index.ts index c7c8b6048..e4ec87f41 100644 --- a/packages/core/src/parser/index.ts +++ b/packages/core/src/parser/index.ts @@ -26,7 +26,7 @@ */ import { Module } from '../abstract'; import EditorModel from '../editor/model/Editor'; -import defaults, { HTMLParserOptions, ParserConfig } from './config/config'; +import defConfig, { HTMLParserOptions, ParserConfig } from './config/config'; import ParserCss from './model/ParserCss'; import ParserHtml from './model/ParserHtml'; @@ -35,7 +35,7 @@ export default class ParserModule extends Module; constructor(em: EditorModel) { - super(em, 'Parser', defaults); + super(em, 'Parser', defConfig()); const { config } = this; this.parserCss = ParserCss(em, config); this.parserHtml = ParserHtml(em, config); diff --git a/packages/core/src/parser/model/ParserHtml.ts b/packages/core/src/parser/model/ParserHtml.ts index b0a1779c4..e05c5bd1f 100644 --- a/packages/core/src/parser/model/ParserHtml.ts +++ b/packages/core/src/parser/model/ParserHtml.ts @@ -5,6 +5,7 @@ import EditorModel from '../../editor/model/Editor'; import { HTMLParseResult, HTMLParserOptions, ParseNodeOptions, ParserConfig } from '../config/config'; import BrowserParserHtml from './BrowserParserHtml'; import { doctypeToString } from '../../utils/dom'; +import { isDef } from '../../utils/mixins'; const modelAttrStart = 'data-gjs-'; const event = 'parse:html'; @@ -313,12 +314,16 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo const conf = em?.get('Config') || {}; const res: HTMLParseResult = { html: [] }; const cf = { ...config, ...opts }; - const options = { + const preOptions = { ...config.optionsHtml, // @ts-ignore Support previous `configParser.htmlType` option htmlType: config.optionsHtml?.htmlType || config.htmlType, ...opts, }; + const options = { + ...preOptions, + asDocument: this.__checkAsDocument(str, preOptions), + }; const { preParser, asDocument } = options; const input = isFunction(preParser) ? preParser(str, { editor: em?.getEditor()! }) : str; const parseRes = isFunction(cf.parserHtml) ? cf.parserHtml(input, options) : BrowserParserHtml(input, options); @@ -374,7 +379,7 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo } res.html = resHtml; - em?.trigger(event, { input, output: res }); + em?.trigger(event, { input, output: res, options }); return res; }, @@ -392,6 +397,16 @@ const ParserHtml = (em?: EditorModel, config: ParserConfig & { returnArray?: boo toRemove.map((name) => node.removeAttribute(name)); each(nodes, (node) => this.__sanitizeNode(node as HTMLElement, opts)); }, + + __checkAsDocument(str: string, opts: HTMLParserOptions) { + if (isDef(opts.asDocument)) { + return opts.asDocument; + } else if (isFunction(opts.detectDocument)) { + return !!opts.detectDocument(str); + } else if (opts.detectDocument) { + return str.toLowerCase().trim().startsWith(' RichTextEditorConfig = () => ({ stylePrefix: 'rte-', adjustToolbar: true, actions: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'wrap'], custom: false, -}; +}); export default config; diff --git a/packages/core/src/rich_text_editor/index.ts b/packages/core/src/rich_text_editor/index.ts index 48d48f8e6..f7ea78d24 100644 --- a/packages/core/src/rich_text_editor/index.ts +++ b/packages/core/src/rich_text_editor/index.ts @@ -44,7 +44,7 @@ import ComponentView from '../dom_components/view/ComponentView'; import EditorModel from '../editor/model/Editor'; import { createEl, cx, on, removeEl } from '../utils/dom'; import { hasWin, isDef } from '../utils/mixins'; -import defaults, { CustomRTE, RichTextEditorConfig } from './config/config'; +import defConfig, { CustomRTE, RichTextEditorConfig } from './config/config'; import RichTextEditor, { RichTextEditorAction } from './model/RichTextEditor'; import CanvasEvents from '../canvas/types'; import { ComponentsEvents } from '../dom_components/types'; @@ -87,7 +87,7 @@ export default class RichTextEditorModule extends Module SelectorManagerConfig = () => ({ stylePrefix: 'clm-', appendTo: '', selectors: [], @@ -148,6 +148,6 @@ const config: SelectorManagerConfig = { '', componentFirst: false, custom: false, -}; +}); export default config; diff --git a/packages/core/src/selector_manager/index.ts b/packages/core/src/selector_manager/index.ts index 78a363db8..e8905df40 100644 --- a/packages/core/src/selector_manager/index.ts +++ b/packages/core/src/selector_manager/index.ts @@ -76,7 +76,7 @@ import { isString, debounce, isObject, isArray, bindAll } from 'underscore'; import { isComponent, isRule } from '../utils/mixins'; import { Model, Collection, RemoveOptions, SetOptions, Debounced } from '../common'; -import defaults, { SelectorManagerConfig } from './config/config'; +import defConfig, { SelectorManagerConfig } from './config/config'; import Selector from './model/Selector'; import Selectors from './model/Selectors'; import State from './model/State'; @@ -86,7 +86,6 @@ import Component from '../dom_components/model/Component'; import { ItemManagerModule } from '../abstract/Module'; import { StyleModuleParam } from '../style_manager'; import StyleableModel from '../domain_abstract/model/StyleableModel'; -import CssRule from '../css_composer/model/CssRule'; import { ComponentsEvents } from '../dom_components/types'; export type SelectorEvent = 'selector:add' | 'selector:remove' | 'selector:update' | 'selector:state' | 'selector'; @@ -138,7 +137,7 @@ export default class SelectorManager extends ItemManagerModule StorageManagerConfig = () => ({ id: 'gjs-', type: 'local', autosave: true, @@ -103,6 +103,6 @@ const config: StorageManagerConfig = { onLoad: (result) => result, }, }, -}; +}); export default config; diff --git a/packages/core/src/storage_manager/index.ts b/packages/core/src/storage_manager/index.ts index 4db8dbfe6..272d42f45 100644 --- a/packages/core/src/storage_manager/index.ts +++ b/packages/core/src/storage_manager/index.ts @@ -42,7 +42,7 @@ import { isEmpty, isFunction } from 'underscore'; import { Module } from '../abstract'; -import defaults, { StorageManagerConfig } from './config/config'; +import defConfig, { StorageManagerConfig } from './config/config'; import LocalStorage from './model/LocalStorage'; import RemoteStorage from './model/RemoteStorage'; import EditorModel from '../editor/model/Editor'; @@ -65,7 +65,7 @@ export default class StorageManager extends Module< events = StorageEvents; constructor(em: EditorModel) { - super(em, 'StorageManager', defaults); + super(em, 'StorageManager', defConfig()); const { config } = this; if (config._disable) config.type = undefined; this.storages = {}; diff --git a/packages/core/src/style_manager/config/config.ts b/packages/core/src/style_manager/config/config.ts index 5dffb171d..f8462567c 100644 --- a/packages/core/src/style_manager/config/config.ts +++ b/packages/core/src/style_manager/config/config.ts @@ -66,7 +66,7 @@ export interface StyleManagerConfig { pStylePrefix?: string; } -export default { +const config: () => StyleManagerConfig = () => ({ sectors: [ { name: 'General', @@ -128,4 +128,6 @@ export default { showComputed: true, clearProperties: true, avoidComputed: ['width', 'height'], -} as StyleManagerConfig; +}); + +export default config; diff --git a/packages/core/src/style_manager/index.ts b/packages/core/src/style_manager/index.ts index 331568607..998394775 100644 --- a/packages/core/src/style_manager/index.ts +++ b/packages/core/src/style_manager/index.ts @@ -66,7 +66,7 @@ import { isUndefined, isArray, isString, debounce, bindAll } from 'underscore'; import { isComponent } from '../utils/mixins'; import { AddOptions, Debounced, Model } from '../common'; -import defaults, { StyleManagerConfig } from './config/config'; +import defConfig, { StyleManagerConfig } from './config/config'; import Sector, { SectorProperties } from './model/Sector'; import Sectors from './model/Sectors'; import Properties from './model/Properties'; @@ -157,7 +157,7 @@ export default class StyleManager extends ItemManagerModule< * @private */ constructor(em: EditorModel) { - super(em, 'StyleManager', new Sectors([], { em }), stylesEvents, defaults); + super(em, 'StyleManager', new Sectors([], { em }), stylesEvents, defConfig()); bindAll(this, '__clearStateTarget'); const c = this.config; const ppfx = c.pStylePrefix; diff --git a/packages/core/src/trait_manager/config/config.ts b/packages/core/src/trait_manager/config/config.ts index 1b403c4f3..bd149d1ea 100644 --- a/packages/core/src/trait_manager/config/config.ts +++ b/packages/core/src/trait_manager/config/config.ts @@ -22,11 +22,11 @@ export interface TraitManagerConfig { optionsTarget?: Record[]; } -const config: TraitManagerConfig = { +const config: () => TraitManagerConfig = () => ({ stylePrefix: 'trt-', appendTo: '', optionsTarget: [{ value: false }, { value: '_blank' }], custom: false, -}; +}); export default config; diff --git a/packages/core/src/trait_manager/index.ts b/packages/core/src/trait_manager/index.ts index 69193fa3e..31a4a3cc9 100644 --- a/packages/core/src/trait_manager/index.ts +++ b/packages/core/src/trait_manager/index.ts @@ -33,7 +33,7 @@ import { Module } from '../abstract'; import { Model } from '../common'; import Component from '../dom_components/model/Component'; import EditorModel from '../editor/model/Editor'; -import defaults from './config/config'; +import defConfig from './config/config'; import { CustomTrait, TraitCustomData, @@ -81,7 +81,7 @@ export default class TraitManager extends Module { * @private */ constructor(em: EditorModel) { - super(em, 'TraitManager', defaults as any); + super(em, 'TraitManager', defConfig() as any); const { state, config, events } = this; const ppfx = config.pStylePrefix; ppfx && (config.stylePrefix = `${ppfx}${config.stylePrefix}`); diff --git a/packages/core/src/undo_manager/config.ts b/packages/core/src/undo_manager/config.ts index 7be4ecf74..b86804223 100644 --- a/packages/core/src/undo_manager/config.ts +++ b/packages/core/src/undo_manager/config.ts @@ -11,9 +11,9 @@ export interface UndoManagerConfig { trackSelection?: boolean; } -const config: UndoManagerConfig = { +const config: () => UndoManagerConfig = () => ({ maximumStackLength: 500, trackSelection: true, -}; +}); export default config; diff --git a/packages/core/src/undo_manager/index.ts b/packages/core/src/undo_manager/index.ts index 598891365..7e60aa5c0 100644 --- a/packages/core/src/undo_manager/index.ts +++ b/packages/core/src/undo_manager/index.ts @@ -28,7 +28,7 @@ import UndoManager from 'backbone-undo'; import { isArray, isBoolean, isEmpty, unique, times } from 'underscore'; import { Module } from '../abstract'; import EditorModel from '../editor/model/Editor'; -import defaults, { UndoManagerConfig } from './config'; +import defConfig, { UndoManagerConfig } from './config'; export interface UndoGroup { index: number; @@ -47,7 +47,7 @@ export default class UndoManagerModule extends Module { + em.on(CanvasEvents.frameLoad, ({ el, view }) => { + // this seems to fix the issue of the loop + el.onload = null; + }); +}; + export function waitEditorEvent(em: Editor | EditorModel, event: string) { return new Promise((resolve) => em.once(event, resolve)); } diff --git a/packages/core/test/specs/dom_components/index.ts b/packages/core/test/specs/dom_components/index.ts index 73dd95876..152139b0e 100644 --- a/packages/core/test/specs/dom_components/index.ts +++ b/packages/core/test/specs/dom_components/index.ts @@ -319,21 +319,21 @@ describe('DOM Components', () => { describe('Custom components with styles', () => { test('canMove returns false if source is not provided', () => { - const target = obj.addComponent({ type: 'div' }) as Component; + const target = obj.addComponent({ tagName: 'div' }) as Component; const result = obj.canMove(target); expect(result.result).toBe(false); expect(result.reason).toBe(CanMoveReason.InvalidSource); }); test('canMove returns false if target is not provided', () => { - const source = obj.addComponent({ type: 'div' }) as Component; + const source = obj.addComponent({ tagName: 'div' }) as Component; const result = obj.canMove(undefined as any, source); expect(result.result).toBe(false); expect(result.reason).toBe(CanMoveReason.InvalidSource); }); test('canMove returns false when source and target have the same main symbol', () => { - const component = obj.addComponent({ type: 'div' }) as Component; + const component = obj.addComponent({ tagName: 'div' }) as Component; const mainSymbol = obj.addSymbol(component) as Component; const target = obj.addSymbol(mainSymbol) as Component; const source = obj.addSymbol(mainSymbol) as Component; @@ -356,31 +356,31 @@ describe('DOM Components', () => { }); test('canMove returns false when source is not draggable in the target', () => { - const target = obj.addComponent({ type: 'div', droppable: true }) as Component; - const source = obj.addComponent({ type: 'span', draggable: false }) as Component; + const target = obj.addComponent({ tagName: 'div', droppable: true }) as Component; + const source = obj.addComponent({ tagName: 'span', draggable: false }) as Component; const result = obj.canMove(target, source); expect(result.result).toBe(false); expect(result.reason).toBe(CanMoveReason.SourceReject); }); test('canMove returns false when target does not accept the source', () => { - const target = obj.addComponent({ type: 'div', droppable: false }) as Component; - const source = obj.addComponent({ type: 'span', draggable: true }) as Component; + const target = obj.addComponent({ tagName: 'div', droppable: false }) as Component; + const source = obj.addComponent({ tagName: 'span', draggable: true }) as Component; const result = obj.canMove(target, source); expect(result.result).toBe(false); expect(result.reason).toBe(CanMoveReason.TargetReject); }); test('canMove returns true when source is draggable and target is droppable', () => { - const target = obj.addComponent({ type: 'div', droppable: true }) as Component; - const source = obj.addComponent({ type: 'span', draggable: true }) as Component; + const target = obj.addComponent({ tagName: 'div', droppable: true }) as Component; + const source = obj.addComponent({ tagName: 'span', draggable: true }) as Component; const result = obj.canMove(target, source); expect(result.result).toBe(true); }); test('canMove returns false when target is inside the source', () => { - const source = obj.addComponent({ type: 'div' }) as Component; - const target = source.append({ type: 'span' })[0]; + const source = obj.addComponent({ tagName: 'div' }) as Component; + const target = source.append({ tagName: 'span' })[0]; const result = obj.canMove(target, source); expect(result.result).toBe(false); expect(result.reason).toBe(CanMoveReason.TargetReject); @@ -491,6 +491,33 @@ describe('DOM Components', () => { expect(newRoot.toHTML()).toBe(flattenHTML(docHtml)); }); + + test('load projectData containing HTML document, without document detect', () => { + editor.loadProjectData({ + pages: [{ component: docHtml }], + }); + const newRoot = editor.getWrapper()!; + const { head, doctype } = newRoot; + + // Components from the head are moved inside the body + expect(head.components().length).toBe(0); + expect(doctype).toBe(''); + expect(newRoot.components().length).toBe(2); + }); + + test('load projectData containing HTML document, with document detect', () => { + editor.Parser.getConfig().optionsHtml!.detectDocument = true; + editor.loadProjectData({ + pages: [{ component: docHtml }], + }); + const newRoot = editor.getWrapper()!; + + const { head, doctype } = newRoot; + expect(head.components().length).toBe(1); + expect(doctype).toBe(''); + + expect(newRoot.toHTML()).toBe(flattenHTML(docHtml)); + }); }); }); }); diff --git a/packages/core/test/specs/grapesjs/index.ts b/packages/core/test/specs/grapesjs/index.ts index f1905e03d..2d7113260 100644 --- a/packages/core/test/specs/grapesjs/index.ts +++ b/packages/core/test/specs/grapesjs/index.ts @@ -1,6 +1,8 @@ import grapesjs, { Component, Editor, usePlugin } from '../../../src'; import ComponentWrapper from '../../../src/dom_components/model/ComponentWrapper'; +import { EditorConfig } from '../../../src/editor/config/config'; import type { Plugin } from '../../../src/plugin_manager'; +import { fixJsDom, fixJsDomIframe } from '../../common'; type TestPlugin = Plugin<{ cVal: string }>; @@ -24,6 +26,16 @@ describe('GrapesJS', () => { }, }; + const initTestEditor = (config: Partial) => { + const editor = grapesjs.init({ + ...config, + plugins: [fixJsDom, ...(config.plugins || [])], + }); + fixJsDomIframe(editor.getModel().shallow); + + return editor; + }; + beforeAll(() => { editorName = 'editor-fixture'; }); @@ -86,23 +98,33 @@ describe('GrapesJS', () => { expect(editor.getStyle().length).toEqual(0); }); - test.skip('Editor canvas baseCSS can be overwritten', () => { + test('Editor canvas baseCSS can be overwritten', (done) => { config.components = htmlString; config.baseCss = '#wrapper { background-color: #eee; }'; config.protectedCss = ''; - const editor = grapesjs.init(config); - const body = editor.Canvas.getBody(); - expect(body.outerHTML).toContain(config.baseCss); - expect(body.outerHTML.replace(/\s+/g, ' ')).not.toContain('body { margin: 0;'); + const editor = initTestEditor({ + ...config, + components: htmlString, + baseCss: '#wrapper { background-color: #eee; }', + protectedCss: '', + }); + editor.onReady(() => { + const body = editor.Canvas.getBody(); + expect(body.outerHTML).toContain(config.baseCss); + expect(body.outerHTML.replace(/\s+/g, ' ')).not.toContain('body { margin: 0;'); + done(); + }); }); - test.skip('Editor canvas baseCSS defaults to sensible values if not defined', () => { + test('Editor canvas baseCSS defaults to sensible values if not defined', (done) => { config.components = htmlString; config.protectedCss = ''; - grapesjs.init(config); - expect(window.frames[0].document.documentElement.outerHTML.replace(/\s+/g, ' ')).toContain( - 'body { background-color: #fff', - ); + const editor = initTestEditor(config); + editor.onReady(() => { + const htmlEl = editor.Canvas.getDocument().documentElement; + expect(htmlEl.outerHTML.replace(/\s+/g, ' ')).toContain('body { background-color: #fff'); + done(); + }); }); test('Init editor with html', () => { @@ -459,25 +481,32 @@ describe('GrapesJS', () => { }); }); - describe.skip('Component selection', () => { + describe('Component selection', () => { let editor: Editor; let wrapper: ComponentWrapper; let el1: Component; let el2: Component; let el3: Component; - beforeEach(() => { - config.storageManager = { type: 0 }; - config.components = `
-
-
-
-
`; - editor = grapesjs.init(config); + beforeEach((done) => { + editor = grapesjs.init({ + container: `#${editorName}`, + storageManager: false, + plugins: [fixJsDom], + components: `
+
+
+
+
`, + }); + fixJsDomIframe(editor.getModel().shallow); wrapper = editor.DomComponents.getWrapper()!; - el1 = wrapper.find('#el1')[0]; - el2 = wrapper.find('#el2')[0]; - el3 = wrapper.find('#el3')[0]; + editor.onReady(() => { + el1 = wrapper.find('#el1')[0]; + el2 = wrapper.find('#el2')[0]; + el3 = wrapper.find('#el3')[0]; + done(); + }); }); test('Select a single component', () => { diff --git a/packages/core/test/specs/navigator/view/ItemView.js b/packages/core/test/specs/navigator/view/ItemView.ts similarity index 72% rename from packages/core/test/specs/navigator/view/ItemView.js rename to packages/core/test/specs/navigator/view/ItemView.ts index ed025f341..09ee65c88 100644 --- a/packages/core/test/specs/navigator/view/ItemView.js +++ b/packages/core/test/specs/navigator/view/ItemView.ts @@ -1,11 +1,11 @@ -import ItemView from 'navigator/view/ItemView'; -import config from 'navigator/config/config'; -import EditorModel from 'editor/model/Editor'; +import defConfig from '../../../../src/navigator/config/config'; +import EditorModel from '../../../../src/editor/model/Editor'; +import ItemView from '../../../../src/navigator/view/ItemView'; describe('ItemView', () => { - let itemView; + let itemView: ItemView; - const isVisible = (itemView) => { + const isVisible = (itemView: ItemView) => { return itemView.module.isVisible(itemView.model); }; @@ -16,8 +16,8 @@ describe('ItemView', () => { itemView = new ItemView({ model: new defCmp({}, { em }), module: em.get('LayerManager'), - config: { ...config, em }, - }); + config: { ...defConfig(), em }, + } as any); }); describe('.isVisible', () => {