Browse Source

Merge pull request #3393 from HCL-TECH-SOFTWARE/droppable-updates Closes #3392

Update droppable config to allow a function
pull/3426/head
Artur Arseniev 5 years ago
committed by GitHub
parent
commit
798288b7fa
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/dom_components/model/Component.js
  2. 26
      src/utils/Sorter.js
  3. 228
      test/specs/utils/Sorter.js

2
src/dom_components/model/Component.js

@ -58,7 +58,7 @@ export const keyUpdateInside = `${keyUpdate}-inside`;
* You can also specify a query string to indentify elements, * You can also specify a query string to indentify elements,
* eg. `'.some-class[title=Hello], [data-gjs-type=column]'` means you can drag the component only inside elements * eg. `'.some-class[title=Hello], [data-gjs-type=column]'` means you can drag the component only inside elements
* containing `some-class` class and `Hello` title, and `column` components. Default: `true` * containing `some-class` class and `Hello` title, and `column` components. Default: `true`
* @property {Boolean|String} [droppable=true] Indicates if it's possible to drop other components inside. You can use * @property {Boolean|String|Function} [droppable=true] Indicates if it's possible to drop other components inside. You can use
* a query string as with `draggable`. Default: `true` * a query string as with `draggable`. Default: `true`
* @property {Boolean} [badgable=true] Set to false if you don't want to see the badge (with the name) over the component. Default: `true` * @property {Boolean} [badgable=true] Set to false if you don't want to see the badge (with the name) over the component. Default: `true`
* @property {Boolean|Array<String>} [stylable=true] True if it's possible to style the component. * @property {Boolean|Array<String>} [stylable=true] True if it's possible to style the component.

26
src/utils/Sorter.js

@ -558,7 +558,8 @@ export default Backbone.View.extend({
const $parent = parent && $(parent); const $parent = parent && $(parent);
if (style.overflow && style.overflow !== 'visible') return; if (style.overflow && style.overflow !== 'visible') return;
if ($el.css('float') !== 'none') return; const propFloat = $el.css('float');
if (propFloat && propFloat !== 'none') return;
if ( if (
$parent && $parent &&
$parent.css('display') == 'flex' && $parent.css('display') == 'flex' &&
@ -622,13 +623,22 @@ export default Backbone.View.extend({
// Check if the target could accept the source // Check if the target could accept the source
let droppable = trgModel.get('droppable'); let droppable = trgModel.get('droppable');
droppable = droppable instanceof Backbone.Collection ? 1 : droppable; if (typeof droppable === 'function') {
droppable = droppable instanceof Array ? droppable.join(', ') : droppable; let res = droppable(srcModel, trgModel);
result.dropInfo = droppable; result.droppable = res;
droppable = isString(droppable) ? this.matches(src, droppable) : droppable; result.dropInfo = res;
droppable = droppable = res;
draggable && this.isTextableActive(srcModel, trgModel) ? 1 : droppable; } else {
result.droppable = droppable; droppable = droppable instanceof Backbone.Collection ? 1 : droppable;
droppable = droppable instanceof Array ? droppable.join(', ') : droppable;
result.dropInfo = droppable;
droppable = isString(droppable)
? this.matches(src, droppable)
: droppable;
droppable =
draggable && this.isTextableActive(srcModel, trgModel) ? 1 : droppable;
result.droppable = droppable;
}
if (!droppable || !draggable) { if (!droppable || !draggable) {
result.valid = false; result.valid = false;

228
test/specs/utils/Sorter.js

@ -1,8 +1,13 @@
// import Sorter from '../../../src/utils/Sorter.js' import Backbone from 'backbone';
import Sorter from '../../../src/utils/Sorter.js';
import ComponentView from 'dom_components/view/ComponentView';
import ComponentTextView from 'dom_components/view/ComponentTextView';
import Component from 'dom_components/model/Component';
const $ = Backbone.$;
// TODO: Migrate this file to Jest // TODO: Migrate this file to Jest
describe.skip('Sorter', () => { describe('Sorter', () => {
var fixture; var fixture;
var obj; var obj;
var parent; var parent;
@ -21,32 +26,32 @@ describe.skip('Sorter', () => {
obj = new Sorter({ container: '.parent1' }); obj = new Sorter({ container: '.parent1' });
document.body.appendChild(fixture); document.body.appendChild(fixture);
fixture.appendChild(parent); fixture.appendChild(parent);
html = html = `
'<div id="el1" style="overflow: hidden;">' + <div id="el1" style="overflow: hidden;">
'<div id="el2">ba' + <div id="el2">ba
'<p id="baa">baa</p>' + <p id="baa">baa</p>
'<span id="elspan">bab</span>' + <span id="elspan">bab</span>
'<span id="bac" style="display:block;">bac</span>' + <span id="bac" style="display:block;">bac</span>
'<div id="eldiv">eldiv</div>' + <div id="eldiv">eldiv</div>
'</div>' + </div>
'</div>' + </div>
'<div id="a">' + <div id="a">
'<div id="aa">aa' + <div id="aa">aa
'<p id="aaa">aaa</p>' + <p id="aaa">aaa</p>
'<span id="aab">aab</span>' + <span id="aab">aab</span>
'<span id="aac" style="display:block;">aac</span>' + <span id="aac" style="display:block;">aac</span>
'</div>' + </div>
'<div id="ab" style="float: left;">ab</div>' + <div id="ab" style="float: left;">ab</div>
'<div id="ac" style="position: absolute;">ac' + <div id="ac" style="position: absolute;">ac
'<div id="aca" style="float: left;">aca</div>' + <div id="aca" style="float: left;">aca</div>
'<div id="acb">acb</div>' + <div id="acb">acb</div>
'</div>' + </div>
'<div id="ad" style="overflow: hidden;">ad' + <div id="ad" style="overflow: hidden;">ad
'<p id="ada">ada</p>' + <p id="ada">ada</p>
'<span id="adb">adb</span>' + <span id="adb">adb</span>
'<span id="adc" style="display:block;">adc</span>' + <span id="adc" style="display:block;">adc</span>
'</div>' + </div>
'</div>'; </div>`;
}); });
afterEach(() => { afterEach(() => {
@ -60,36 +65,36 @@ describe.skip('Sorter', () => {
var el = document.createElement('div'); var el = document.createElement('div');
el.setAttribute('class', 'test test2'); el.setAttribute('class', 'test test2');
parent.appendChild(el); parent.appendChild(el);
obj.matches(el, '.test').should.equal(true); expect(obj.matches(el, '.test')).toEqual(true);
obj.matches(el, '.test2').should.equal(true); expect(obj.matches(el, '.test2')).toEqual(true);
obj.matches(el, '.test3').should.equal(false); expect(obj.matches(el, '.test3')).toEqual(false);
}); });
test('matches id', () => { test('matches id', () => {
var el = document.createElement('div'); var el = document.createElement('div');
el.setAttribute('id', 'test2'); el.setAttribute('id', 'test2');
parent.appendChild(el); parent.appendChild(el);
obj.matches(el, '#test2').should.equal(true); expect(obj.matches(el, '#test2')).toEqual(true);
obj.matches(el, '.test2').should.equal(false); expect(obj.matches(el, '.test2')).toEqual(false);
obj.matches(el, '#test').should.equal(false); expect(obj.matches(el, '#test')).toEqual(false);
}); });
test('matches tag', () => { test('matches tag', () => {
var el = document.createElement('span'); var el = document.createElement('span');
parent.appendChild(el); parent.appendChild(el);
obj.matches(el, 'span').should.equal(true); expect(obj.matches(el, 'span')).toEqual(true);
obj.matches(el, 'div').should.equal(false); expect(obj.matches(el, 'div')).toEqual(false);
obj.matches(el, '*').should.equal(true); expect(obj.matches(el, '*')).toEqual(true);
}); });
test('Creates placeholder', () => { test('Creates placeholder', () => {
obj.createPlaceholder().className.should.equal('placeholder'); expect(obj.createPlaceholder().className).toEqual('placeholder');
}); });
test('isInFlow to overflow hidden', () => { test('isInFlow to overflow hidden', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#el1'); var el = parent.querySelector('#el1');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
test('isInFlow inner to overflow', () => { test('isInFlow inner to overflow', () => {
@ -99,13 +104,13 @@ describe.skip('Sorter', () => {
console.log('phantom issue'); console.log('phantom issue');
return; return;
} }
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for span', () => { test('isInFlow for span', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#elspan'); var el = parent.querySelector('#elspan');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
test('isInFlow for div #a', () => { test('isInFlow for div #a', () => {
@ -115,7 +120,7 @@ describe.skip('Sorter', () => {
console.log('phantom issue'); console.log('phantom issue');
return; return;
} }
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for div #aa', () => { test('isInFlow for div #aa', () => {
@ -125,7 +130,7 @@ describe.skip('Sorter', () => {
console.log('phantom issue'); console.log('phantom issue');
return; return;
} }
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for p #aaa', () => { test('isInFlow for p #aaa', () => {
@ -135,13 +140,13 @@ describe.skip('Sorter', () => {
console.log('phantom issue'); console.log('phantom issue');
return; return;
} }
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for span #aab', () => { test('isInFlow for span #aab', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#aab'); var el = parent.querySelector('#aab');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
test('isInFlow for span #aac with display block', () => { test('isInFlow for span #aac with display block', () => {
@ -150,19 +155,19 @@ describe.skip('Sorter', () => {
if (!el) if (!el)
// in phantom doesnt work // in phantom doesnt work
return; return;
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for div #ab with float left', () => { test('isInFlow for div #ab with float left', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#ab'); var el = parent.querySelector('#ab');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
test('isInFlow for div #ac in absolute', () => { test('isInFlow for div #ac in absolute', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#ac'); var el = parent.querySelector('#ac');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
test('isInFlow for div #acb inside absolute', () => { test('isInFlow for div #acb inside absolute', () => {
@ -172,13 +177,13 @@ describe.skip('Sorter', () => {
console.log('phantom issue'); console.log('phantom issue');
return; return;
} }
obj.isInFlow(el).should.equal(true); expect(obj.isInFlow(el)).toEqual(true);
}); });
test('isInFlow for div #ad overflow hidden', () => { test('isInFlow for div #ad overflow hidden', () => {
parent.innerHTML = html; parent.innerHTML = html;
var el = parent.querySelector('#ad'); var el = parent.querySelector('#ad');
obj.isInFlow(el).should.equal(false); expect(obj.isInFlow(el)).toEqual(false);
}); });
describe('Closest method', () => { describe('Closest method', () => {
@ -197,22 +202,22 @@ describe.skip('Sorter', () => {
test('Closest by class', () => { test('Closest by class', () => {
var el = document.createElement('div'); var el = document.createElement('div');
parent3.appendChild(el); parent3.appendChild(el);
obj.closest(el, '.parent2').should.deep.equal(parent2); expect(obj.closest(el, '.parent2')).toEqual(parent2);
obj.closest(el, '.parent3').should.deep.equal(parent3); expect(obj.closest(el, '.parent3')).toEqual(parent3);
obj.closest(el, '.parent1').should.deep.equal(parent); expect(obj.closest(el, '.parent1')).toEqual(parent);
}); });
test('Closest by tag', () => { test('Closest by tag', () => {
var el = document.createElement('div'); var el = document.createElement('div');
el.setAttribute('class', 'el'); el.setAttribute('class', 'el');
parent3.appendChild(el); parent3.appendChild(el);
obj.closest(el, 'span').should.deep.equal(parent2); expect(obj.closest(el, 'span')).toEqual(parent2);
obj.closest(el, 'div').should.deep.equal(parent3); expect(obj.closest(el, 'div')).toEqual(parent3);
obj.closest(el, '*').should.deep.equal(parent3); expect(obj.closest(el, '*')).toEqual(parent3);
}); });
}); });
describe('With elements', () => { describe.skip('With elements', () => {
var vertDims; var vertDims;
var parent2; var parent2;
var parent3; var parent3;
@ -262,8 +267,8 @@ describe.skip('Sorter', () => {
test('startSort inits correctly inits', () => { test('startSort inits correctly inits', () => {
obj.startSort(el); obj.startSort(el);
obj.moved.should.equal(0); expect(obj.moved).toEqual(0);
obj.plh.style.display.should.equal('none'); expect(obj.plh.style.display).toEqual('none');
}); });
test('onMove', () => { test('onMove', () => {
@ -274,7 +279,7 @@ describe.skip('Sorter', () => {
pageY: 0, pageY: 0,
target: target target: target
}); });
obj.moved.should.equal(1); expect(obj.moved).toEqual(1);
}); });
test('getDim from element', () => { test('getDim from element', () => {
@ -282,7 +287,7 @@ describe.skip('Sorter', () => {
var top = subPos.top; var top = subPos.top;
var left = subPos.left; var left = subPos.left;
var result = [top, left, 50, 100]; var result = [top, left, 50, 100];
obj.getDim(sib1).should.deep.equal(result); expect(obj.getDim(sib1)).toEqual(result);
}); });
test('getChildrenDim from element', () => { test('getChildrenDim from element', () => {
@ -301,15 +306,15 @@ describe.skip('Sorter', () => {
[top + 100, left + 0, 50, 100, true], [top + 100, left + 0, 50, 100, true],
[top + 100, left + 100, 50, 70, true] [top + 100, left + 100, 50, 70, true]
]; ];
ch.should.deep.equal(result); expect(ch).toEqual(result);
}); });
test('nearBorders', () => { test('nearBorders', () => {
obj.borderOffset = 10; obj.borderOffset = 10;
var dim = [0, 0, 100, 200]; var dim = [0, 0, 100, 200];
obj.nearBorders(dim, 20, 15).should.equal(false); expect(obj.nearBorders(dim, 20, 15)).toEqual(false);
obj.nearBorders(dim, 3, 4).should.equal(true); expect(obj.nearBorders(dim, 3, 4)).toEqual(true);
obj.nearBorders(dim, 500, 500).should.equal(true); expect(obj.nearBorders(dim, 500, 500)).toEqual(true);
}); });
test('dimsFromTarget', () => { test('dimsFromTarget', () => {
@ -342,14 +347,15 @@ describe.skip('Sorter', () => {
dims = dims.map(function(v) { dims = dims.map(function(v) {
return v.slice(0, 5); return v.slice(0, 5);
}); });
dims.should.deep.equal(resultParent);
expect(dims).toEqual(resultParent);
// Inside target // Inside target
var dims = obj.dimsFromTarget(sib3, leftSib3 + 15, topSib3 + 15); var dims = obj.dimsFromTarget(sib3, leftSib3 + 15, topSib3 + 15);
dims = dims.map(function(v) { dims = dims.map(function(v) {
return v.slice(0, 5); return v.slice(0, 5);
}); });
dims.should.deep.equal(resultChildren); expect(dims).toEqual(resultChildren);
// Exactly on border // Exactly on border
var bOffset = obj.borderOffset; var bOffset = obj.borderOffset;
@ -361,7 +367,7 @@ describe.skip('Sorter', () => {
dims = dims.map(function(v) { dims = dims.map(function(v) {
return v.slice(0, 5); return v.slice(0, 5);
}); });
dims.should.deep.equal(resultChildren); expect(dims).toEqual(resultChildren);
// Slightly near border // Slightly near border
var dims = obj.dimsFromTarget( var dims = obj.dimsFromTarget(
@ -372,7 +378,7 @@ describe.skip('Sorter', () => {
dims = dims.map(function(v) { dims = dims.map(function(v) {
return v.slice(0, 5); return v.slice(0, 5);
}); });
dims.should.deep.equal(resultParent); expect(dims).toEqual(resultParent);
}); });
describe('findPosition', () => { describe('findPosition', () => {
@ -380,23 +386,23 @@ describe.skip('Sorter', () => {
test('Vertical dimensions', () => { test('Vertical dimensions', () => {
var result = { index: 0, method: 'before' }; var result = { index: 0, method: 'before' };
obj.findPosition(vertDims, -10, -10).should.deep.equal(result); expect(obj.findPosition(vertDims, -10, -10)).toEqual(result);
obj.findPosition(vertDims, 0, 0).should.deep.equal(result); expect(obj.findPosition(vertDims, 0, 0)).toEqual(result);
obj.findPosition(vertDims, 10, 10).should.deep.equal(result); expect(obj.findPosition(vertDims, 10, 10)).toEqual(result);
var result = { index: 1, method: 'before' }; var result = { index: 1, method: 'before' };
obj.findPosition(vertDims, 10, 30).should.deep.equal(result); expect(obj.findPosition(vertDims, 10, 30)).toEqual(result);
obj.findPosition(vertDims, 10, 70).should.deep.equal(result); expect(obj.findPosition(vertDims, 10, 70)).toEqual(result);
var result = { index: 2, method: 'before' }; var result = { index: 2, method: 'before' };
obj.findPosition(vertDims, 10, 76).should.deep.equal(result); expect(obj.findPosition(vertDims, 10, 76)).toEqual(result);
var result = { index: 3, method: 'before' }; var result = { index: 3, method: 'before' };
obj.findPosition(vertDims, 100, 140).should.deep.equal(result); expect(obj.findPosition(vertDims, 100, 140)).toEqual(result);
obj.findPosition(vertDims, 100, 160).should.deep.equal(result); expect(obj.findPosition(vertDims, 100, 160)).toEqual(result);
var result = { index: 3, method: 'after' }; var result = { index: 3, method: 'after' };
obj.findPosition(vertDims, 1000, 1000).should.deep.equal(result); expect(obj.findPosition(vertDims, 1000, 1000)).toEqual(result);
}); });
}); });
@ -414,19 +420,73 @@ describe.skip('Sorter', () => {
var pos = { index: 2, method: 'before' }; var pos = { index: 2, method: 'before' };
obj.movePlaceholder(plh, vertDims, pos); obj.movePlaceholder(plh, vertDims, pos);
var style = plh.style; var style = plh.style;
style.top.should.equal('100px'); expect(style.top).toEqual('100px');
style.left.should.equal('30px'); expect(style.left).toEqual('30px');
style.width.should.equal('100px'); expect(style.width).toEqual('100px');
}); });
test('Vertical dimensions with after position', () => { test('Vertical dimensions with after position', () => {
var pos = { index: 1, method: 'after' }; var pos = { index: 1, method: 'after' };
obj.movePlaceholder(plh, vertDims, pos); obj.movePlaceholder(plh, vertDims, pos);
var style = plh.style; var style = plh.style;
style.top.should.equal('100px'); expect(style.top).toEqual('100px');
style.left.should.equal('20px'); expect(style.left).toEqual('20px');
style.width.should.equal('70px'); expect(style.width).toEqual('70px');
}); });
}); });
}); });
describe('Valid Target with components', () => {
var parentModel;
var parentView;
var sorter;
beforeEach(() => {
parentModel = new Component({
droppable: (srcModel, trgModel) => {
if (srcModel.getEl().className === 'canDrop') {
return true;
}
return false;
}
});
parentView = new ComponentTextView({
model: parentModel
});
sorter = new Sorter({ container: parentView.el });
});
afterEach(() => {
parentView.remove();
});
test('Droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true, // Can't move it
content: 'Content text', // Text inside component
attributes: { class: 'canDrop' }
});
var srcView = new ComponentTextView({
model: srcModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
});
test('Not droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'cannotDrop' }
});
var srcView = new ComponentTextView({
model: srcModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
});
});
}); });

Loading…
Cancel
Save