diff --git a/src/editor/index.js b/src/editor/index.js index c692c0134..b2c0d2719 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -415,7 +415,7 @@ module.exports = config => { /** * Add component to selection - * @param {Component|HTMLElement} el Component to select + * @param {Component|HTMLElement|Array} el Component to select * @return {this} * @example * editor.selectAdd(model); @@ -427,7 +427,7 @@ module.exports = config => { /** * Remove component from selection - * @param {Component|HTMLElement} el Component to select + * @param {Component|HTMLElement|Array} el Component to select * @return {this} * @example * editor.selectRemove(model); @@ -439,7 +439,7 @@ module.exports = config => { /** * Toggle component selection - * @param {Component|HTMLElement} el Component to select + * @param {Component|HTMLElement|Array} el Component to select * @return {this} * @example * editor.selectToggle(model); diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 360f4ee3c..7898410eb 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -237,13 +237,14 @@ module.exports = Backbone.Model.extend({ const els = multiple ? el : [el]; const selected = this.get('selected'); - // Don't remove alredy selected components - multiple && selected.remove(selected.filter(sel => !contains(els, sel))); + // If an array is passed remove all selected + // expect those yet to be selected + multiple && this.removeSelected(selected.filter(s => !contains(els, s))); els.forEach(el => { const model = getModel(el, $); if (model && !model.get('selectable')) return; - !multiple && selected.remove(selected.filter(sel => sel !== model)); + !multiple && this.removeSelected(selected.filter(s => s !== model)); this.addSelected(model, opts); }); }, @@ -256,8 +257,12 @@ module.exports = Backbone.Model.extend({ */ addSelected(el, opts = {}) { const model = getModel(el, $); - if (model && !model.get('selectable')) return; - this.get('selected').push(model, opts); + const models = isArray(model) ? model : [model]; + + models.forEach(model => { + if (model && !model.get('selectable')) return; + this.get('selected').push(model, opts); + }); }, /** @@ -278,11 +283,15 @@ module.exports = Backbone.Model.extend({ */ toggleSelected(el, opts = {}) { const model = getModel(el, $); - if (this.get('selected').contains(model)) { - this.removeSelected(model, opts); - } else { - this.addSelected(model, opts); - } + const models = isArray(model) ? model : [model]; + + models.forEach(model => { + if (this.get('selected').contains(model)) { + this.removeSelected(model, opts); + } else { + this.addSelected(model, opts); + } + }); }, /** diff --git a/test/specs/grapesjs/index.js b/test/specs/grapesjs/index.js index c32241a06..4457f341e 100644 --- a/test/specs/grapesjs/index.js +++ b/test/specs/grapesjs/index.js @@ -414,5 +414,97 @@ describe('GrapesJS', () => { expect(editor.getStyle().length).toEqual(2); expect(css).toEqual(`${protCss}.test2{color:red;}.test3{color:blue;}`); }); + + describe.only('Component selection', () => { + let editor, wrapper, el1, el2, el3; + + beforeEach(() => { + config.storageManager = { type: 0 }; + config.components = `
+
+
+
+
`; + editor = obj.init(config); + wrapper = editor.DomComponents.getWrapper(); + el1 = wrapper.find('#el1')[0]; + el2 = wrapper.find('#el2')[0]; + el3 = wrapper.find('#el3')[0]; + }); + + test('Select a single component', () => { + expect(editor.getSelected()).toBeFalsy(); + expect(editor.getSelectedAll().length).toBe(0); + // Select via component + editor.select(el1); + expect(editor.getSelected()).toBe(el1); + expect(editor.getSelectedAll().length).toBe(1); + // Select via element + editor.select(el2.getEl()); + expect(editor.getSelected()).toBe(el2); + expect(editor.getSelectedAll().length).toBe(1); + // Deselect via empty array + editor.select([]); + expect(editor.getSelected()).toBeFalsy(); + expect(editor.getSelectedAll().length).toBe(0); + }); + + test('Select multiple components', () => { + // Select at first el1 and el3 + editor.select([el1, el3.getEl()]); + expect(editor.getSelected()).toBe(el3); + expect(editor.getSelectedAll().length).toBe(2); + // Add el2 + editor.selectAdd(el2); + expect(editor.getSelected()).toBe(el2); + expect(editor.getSelectedAll().length).toBe(3); + // Remove el1 + editor.selectRemove(el1); + expect(editor.getSelected()).toBe(el2); + expect(editor.getSelectedAll().length).toBe(2); + // Add el1 via toggle + editor.selectToggle(el1); + expect(editor.getSelected()).toBe(el1); + expect(editor.getSelectedAll().length).toBe(3); + // Leave selected only el3 + editor.selectRemove([el1, el2]); + expect(editor.getSelected()).toBe(el3); + expect(editor.getSelectedAll().length).toBe(1); + // Toggle all + editor.selectToggle([el1, el2, el3]); + expect(editor.getSelected()).toBe(el2); + expect(editor.getSelectedAll().length).toBe(2); + // Add mutiple + editor.selectAdd([el2, el3]); + expect(editor.getSelected()).toBe(el3); + expect(editor.getSelectedAll().length).toBe(3); + }); + + test('Selection events', () => { + //selectAdd selectRemove selectToggle + const toSpy = { + selected() {}, + deselected() {}, + toggled() {} + }; + const selected = jest.spyOn(toSpy, 'selected'); + const deselected = jest.spyOn(toSpy, 'deselected'); + const toggled = jest.spyOn(toSpy, 'toggled'); + editor.on('component:selected', selected); + editor.on('component:deselected', deselected); + editor.on('component:toggled', toggled); + + editor.select(el1); // selected=1 + editor.selectAdd(el1); // selected=1 + editor.selectAdd([el2, el3]); // selected=3 + editor.selectToggle([el1, el3]); // deselected=2 + editor.selectRemove(el2); // deselected=3 + editor.select(el1); // selected=4 + + expect(selected).toBeCalledTimes(4); + expect(deselected).toBeCalledTimes(3); + expect(toggled).toBeCalledTimes(7); + }); + }); }); });