Browse Source

Add ComponentView tests

pull/36/head
Artur Arseniev 11 years ago
parent
commit
2743f81d95
  1. 165
      src/dom_components/main.js
  2. 8
      src/dom_components/model/Component.js
  3. 12
      src/dom_components/model/ComponentImage.js
  4. 12
      src/dom_components/model/ComponentText.js
  5. 3
      src/dom_components/model/Components.js
  6. 5
      src/dom_components/view/ComponentImageView.js
  7. 7
      src/dom_components/view/ComponentTextView.js
  8. 15
      src/dom_components/view/ComponentView.js
  9. 34
      src/dom_components/view/ComponentsView.js
  10. 38
      test/specs/dom_components/main.js
  11. 76
      test/specs/dom_components/view/componentView.js

165
src/dom_components/main.js

@ -1,19 +1,57 @@
/**
*
* - [getWrapper](#getwrapper)
* - [getComponents](#getcomponents)
* - [addComponent](#addcomponent)
* - [render](#render)
*
* With this module is possible to manage all the HTML structure inside the canvas
* You can init the editor with initial components via configuration
*
* ```js
* var editor = new GrapesJS({
* ...
* components: {...} // Check below for the possible properties
* ...
* });
* ```
*
*
* Before using methods you should get first the module from the editor instance, in this way:
*
* ```js
* var ComponentsService = editor.get('Components');
* ```
*
* @module Components
* @param {Object} config Configurations
* @param {Array<Object>} [config.defaults=[]] Array of possible components
* @example
* ...
* components: {
* defaults: [
* {
* style: { background: 'red'}
* components:[
* {style: { background: 'blue'}},
* {style: { background: 'green'}}
* ]
* }
* ],
* }
* ...
*/
define(function(require) {
/**
* @class Components
* @param {Object} Configurations
*
* @return {Object}
* */
function Components(config)
{
var c = config || {},
defaults = require('./config/config'),
Component = require('./model/Component'),
ComponentText = require('./model/ComponentText'),
ComponentImage = require('./model/ComponentImage'),
ComponentView = require('./view/ComponentView'),
ComponentImageView = require('./view/ComponentImageView'),
var Components = function (config){
var c = config || {},
defaults = require('./config/config'),
Component = require('./model/Component'),
ComponentText = require('./model/ComponentText'),
ComponentImage = require('./model/ComponentImage'),
ComponentView = require('./view/ComponentView'),
ComponentImageView = require('./view/ComponentImageView'),
ComponentTextView = require('./view/ComponentTextView');
// Set default options
@ -35,54 +73,115 @@ define(function(require) {
c.wrapper.style = {};
c.wrapper.style.position = 'relative';
this.component = new Component(c.wrapper, { sm: c.em });
var component = new Component(c.wrapper, { sm: c.em });
var obj = {
model: this.component,
model: component,
config: c,
};
this.c = c;
this.ComponentView = new ComponentView(obj);
}
var componentView = new ComponentView(obj);
Components.prototype = {
return {
/**
* Returns main wrapper which will contain all new components
*
* Returns privately the main wrapper
* @return {Object}
* @private
*/
getComponent : function(){
return this.component;
return component;
},
/**
* Returns main wrapper which will contain all new components
*
* @return {Object}
* Returns root component inside the canvas. Something like <body> inside HTML page
* The wrapper doesn't differ from the original Component Model
* @return {Component} Root Component
* @example
* // Change background of the wrapper and set some attribute
* var wrapper = ComponentsService.getWrapper();
* wrapper.set('style', {'background-color': 'red'});
* wrapper.set('attributes', {'title': 'Hello!'});
*/
getWrapper: function(){
return this.getComponent();
},
/**
* Returns children from the wrapper
*
* @return {Object}
* Returns wrapper's children collection. Once you have the collection you can
* add other Components(Models) inside. Each component can have several nested
* components inside and you can nest them as more as you wish.
* @return {Components} Collection of components
* @example
* // Let's add some component
* var wrapperChildren = ComponentsService.getComponents();
* var comp1 = wrapperChildren.add({
* style: { 'background-color': 'red'}
* });
* var comp2 = wrapperChildren.add({
* tagName: 'span',
* attributes: { title: 'Hello!'}
* });
* // Now let's add an other one inside first component
* // First we have to get the collection inside. Each
* // component has 'components' property
* var comp1Children = comp1.get('components');
* // Procede as before. You could also add multiple objects
* comp1Children.add([
* { style: { 'background-color': 'blue'}},
* { style: { height: '100px', width: '100px'}}
* ]);
* // Remove comp2
* wrapperChildren.remove(comp2);
*/
getComponents: function(){
return this.getWrapper().get('components');
},
/**
* Render and returns wrapper
*
* @return {Object}
* Add new components to the wrapper's children. It's the same
* as 'ComponentsService.getComponents().add(...)'
* @param {Object|Component|Array<Object>} component Component/s to add
* @param {string} [component.tagName='div'] Tag name
* @param {string} [component.type=''] Type of the component. Available: ''(default), 'text', 'image'
* @param {boolean} [component.removable=true] If component is removable
* @param {boolean} [component.movable=true] If is possible to move the component around the structure
* @param {boolean} [component.droppable=true] If is possible to drop inside other components
* @param {boolean} [component.badgable=true] If the badge is visible when the component is selected
* @param {boolean} [component.stylable=true] If is possible to style component
* @param {boolean} [component.copyable=true] If is possible to copy&paste the component
* @param {string} [component.content=''] String inside component
* @param {Object} [component.style={}] Style object
* @param {Object} [component.attributes={}] Attribute object
* @return {Component|Array<Component>} Component/s added
* @example
* // Example of a new component with some extra property
* var comp1 = ComponentsService.addComponent({
* tagName: 'div',
* removable: true, // Can't remove it
* movable: true, // Can't move it
* copyable: true, // Disable copy/past
* content: 'Content text', // Text inside component
* style: { color: 'red'},
* attributes: { title: 'here' }
* });
*/
addComponent: function(component){
return this.getComponents().add(component);
},
/**
* Render and returns wrapper element with all components inside.
* Once the wrapper is rendered, and it's what happens when you init the editor,
* the all new components will be added automatically and property changes are all
* updated immediately
* @return {HTMLElement}
*/
render : function(){
return this.ComponentView.render().el;
return componentView.render().el;
},
};
};
return Components;

8
src/dom_components/model/Component.js

@ -1,8 +1,6 @@
define(['backbone','./Components', 'ClassManager/model/ClassTags'],
function (Backbone, Components, ClassTags) {
/**
* @class Component
* */
return Backbone.Model.extend({
defaults: {
@ -36,8 +34,8 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
/**
* Normalize input classes from array to array of objects
* @param {Array} arr
*
* @return {Array}
* @private
*/
normalizeClasses: function(arr){
var res = [];
@ -65,6 +63,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
/**
* Override original clone method
* @private
*/
clone: function()
{
@ -91,6 +90,7 @@ define(['backbone','./Components', 'ClassManager/model/ClassTags'],
* Get name of the component
*
* @return {String}
* @private
* */
getName: function(){
if(!this.name){

12
src/dom_components/model/ComponentImage.js

@ -1,14 +1,12 @@
define(['./Component'],
define(['./Component'],
function (Component) {
/**
* @class ComponentImage
* */
return Component.extend({
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
src : '',
droppable : false,
}),
});
});

12
src/dom_components/model/ComponentText.js

@ -1,14 +1,12 @@
define(['./Component'],
define(['./Component'],
function (Component) {
/**
* @class ComponentText
* */
return Component.extend({
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
content : '',
droppable : false,
}),
});
});

3
src/dom_components/model/Components.js

@ -1,8 +1,5 @@
define([ 'backbone', 'require'],
function (Backbone, require) {
/**
* @class Components
* */
return Backbone.Collection.extend({

5
src/dom_components/view/ComponentImageView.js

@ -1,8 +1,5 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentImageView
* */
return ComponentView.extend({
@ -32,6 +29,7 @@ define(['backbone', './ComponentView'],
* Update src attribute
*
* @return void
* @private
* */
updateSrc: function(){
this.$el.attr('src',this.model.get("src"));
@ -42,6 +40,7 @@ define(['backbone', './ComponentView'],
* @param {Object} e Event
*
* @return void
* @private
* */
openModal: function(e){
var that = this;

7
src/dom_components/view/ComponentTextView.js

@ -1,8 +1,5 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentTextView
* */
return ComponentView.extend({
@ -23,6 +20,7 @@ define(['backbone', './ComponentView'],
* Enable this component to be editable,
* load also the mini toolbar for quick editing
* @param Event
* @private
* */
enableEditing: function(e){
if(this.rte){
@ -38,6 +36,7 @@ define(['backbone', './ComponentView'],
/**
* Disable this component to be editable
* @param Event
* @private
* */
disableEditing: function(e){
if(this.rte){
@ -50,6 +49,7 @@ define(['backbone', './ComponentView'],
/** Isolate disable propagation method
* @param Event
* @private
* */
disablePropagation: function(e){
e.stopPropagation();
@ -59,6 +59,7 @@ define(['backbone', './ComponentView'],
* Update contents of the element
*
* @return void
* @private
**/
updateContents : function(){
this.model.set('content', this.$el.html());

15
src/dom_components/view/ComponentView.js

@ -1,8 +1,6 @@
define(['backbone', './ComponentsView'],
function (Backbone, ComponentsView) {
/**
* @class ComponentView
* */
return Backbone.View.extend({
className : function(){ //load classes from model
@ -14,7 +12,7 @@ define(['backbone', './ComponentsView'],
},
initialize: function(opt){
this.config = opt.config;
this.config = opt.config || {};
this.pfx = this.config.stylePrefix;
this.components = this.model.get('components');
this.attr = this.model.get("attributes");
@ -34,6 +32,7 @@ define(['backbone', './ComponentsView'],
/**
* Import, if possible, classes inside main container
* @private
* */
importClasses: function(){
var clm = this.config.em.get('ClassManager');
@ -48,6 +47,7 @@ define(['backbone', './ComponentsView'],
/**
* Fires on state update. If the state is not empty will add a helper class
* @param {Event} e
* @private
* */
updateState: function(e){
var cl = 'hc-state';
@ -63,6 +63,7 @@ define(['backbone', './ComponentsView'],
/**
* Update item on status change
* @param {Event} e
* @private
* */
updateStatus: function(e){
var s = this.model.get('status'),
@ -83,6 +84,7 @@ define(['backbone', './ComponentsView'],
* This method is called before initialize
*
* @return {Array}|null
* @private
* */
getClasses: function(){
var attr = this.model.get("attributes"),
@ -97,6 +99,7 @@ define(['backbone', './ComponentsView'],
* Update attributes
*
* @return void
* @private
* */
updateAttributes: function(){
var attributes = {},
@ -118,6 +121,7 @@ define(['backbone', './ComponentsView'],
* Update style attribute
*
* @return void
* @private
* */
updateStyle: function(){
this.$el.attr('style', this.getStyleString());
@ -127,6 +131,7 @@ define(['backbone', './ComponentsView'],
* Return style string
*
* @return {String}
* @private
* */
getStyleString: function(){
var style = '';
@ -141,6 +146,7 @@ define(['backbone', './ComponentsView'],
/**
* Update classe attribute
* @private
* */
updateClasses: function(){
var str = '';
@ -158,6 +164,7 @@ define(['backbone', './ComponentsView'],
/**
* Reply to event call
* @param object Event that generated the request
* @private
* */
eventCall: function(event){
event.viewResponse = this;

34
src/dom_components/view/ComponentsView.js

@ -1,41 +1,41 @@
define(['backbone','require'],
function(Backbone, require) {
/**
* @class ComponentsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} Model
*
*
* @return void
* @private
* */
addTo: function(model){
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
},
/**
* Add new object to collection
* @param {Object} Model
* @param {Object} Fragment collection
* @param {Integer} Index of append
*
*
* @return {Object} Object rendered
* @private
* */
addToCollection: function(model, fragmentEl, index){
if(!this.compView)
this.compView = require('./ComponentView');
var fragment = fragmentEl || null,
viewObject = this.compView;
switch(model.get('type')){
case 'text':
if(!this.compViewText)
@ -48,13 +48,13 @@ function(Backbone, require) {
viewObject = this.compViewImage;
break;
}
var view = new viewObject({
model : model,
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
@ -76,10 +76,10 @@ function(Backbone, require) {
p.append(rendered);
}
}
return rendered;
},
render: function($p) {
var fragment = document.createDocumentFragment();
this.$parent = $p || this.$el;
@ -88,9 +88,9 @@ function(Backbone, require) {
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
return this;
}
});
});

38
test/specs/dom_components/main.js

@ -2,22 +2,26 @@ var modulePath = './../../../test/specs/dom_components';
define([
'DomComponents',
modulePath + '/model/Component'
modulePath + '/model/Component',
modulePath + '/view/ComponentView'
],
function(DomComponents,
ComponentModels
ComponentModels,
ComponentView
) {
describe('DOM Components', function() {
describe('Main', function() {
var obj;
beforeEach(function () {
this.obj = new DomComponents();
obj = new DomComponents();
});
afterEach(function () {
delete this.obj;
delete obj;
});
it('Object exists', function() {
@ -25,22 +29,38 @@ define([
});
it('Wrapper exists', function() {
this.obj.getWrapper().should.not.be.empty;
obj.getWrapper().should.not.be.empty;
});
it('No components inside', function() {
this.obj.getComponents().length.should.equal(0);
obj.getComponents().length.should.equal(0);
});
it('Add new component', function() {
var comp = obj.addComponent({});
obj.getComponents().length.should.equal(1);
});
it('Add more components at once', function() {
var comp = obj.addComponent([{},{}]);
obj.getComponents().length.should.equal(2);
});
it('Render wrapper', function() {
sinon.stub(this.obj.ComponentView, "render").returns({ el: '' });
this.obj.render();
this.obj.ComponentView.render.calledOnce.should.equal(true);
obj.render().should.be.ok;
});
it('Add components at init', function() {
obj = new DomComponents({
defaults : [{}, {}, {}]
});
obj.getComponents().length.should.equal(3);
});
});
ComponentModels.run();
ComponentView.run();
});
});

76
test/specs/dom_components/view/componentView.js

@ -0,0 +1,76 @@
var path = 'DomComponents/view/';
define([path + 'ComponentView', 'DomComponents/model/Component'],
function(ComponentView, Component) {
return {
run : function(){
describe('ComponentView', function() {
var $fixtures;
var $fixture;
var model;
var view;
var btnClass = 'btn';
before(function () {
$fixtures = $("#fixtures");
$fixture = $('<div class="components-fixture"></div>');
});
beforeEach(function () {
model = new Component();
view = new ComponentView({
model: model
});
$fixture.empty().appendTo($fixtures);
$fixture.html(view.render().el);
});
afterEach(function () {
view.remove();
});
after(function () {
$fixture.remove();
});
it('Component empty', function() {
$fixture.html().should.be.equal('<div style="" class=""></div>');
});
/*
it('Update class', function() {
model.set('className','test');
view.el.getAttribute('class').should.be.equal(btnClass + ' test');
});
it('Update attributes', function() {
model.set('attributes',{
'data-test': 'test-value'
});
view.el.getAttribute('data-test').should.be.equal('test-value');
});
it('Check enable active', function() {
model.set('active', true, {silent: true});
view.checkActive();
view.el.getAttribute('class').should.be.equal(btnClass + ' active');
});
it('Check disable active', function() {
model.set('active', true, {silent: true});
view.checkActive();
model.set('active', false, {silent: true});
view.checkActive();
view.el.getAttribute('class').should.be.equal(btnClass);
});
it('Renders correctly', function() {
view.render().should.be.ok;
});
*/
});
}
};
});
Loading…
Cancel
Save