gridstack
Version:
TypeScript/JS lib for dashboard layout and creation, responsive, mobile support, no external dependencies, with many wrappers (React, Angular, Vue, Ember, knockout...)
1,050 lines • 81.8 kB
JavaScript
import { GridStack } from '../src/gridstack';
import { Utils } from '../src/utils';
describe('gridstack >', () => {
'use strict';
let grid;
let grids;
let find = function (id) {
return grid.engine.nodes.find(n => n.id === id);
};
let findEl = function (id) {
return find(id).el;
};
// grid has 4x2 and 4x4 top-left aligned - used on most test cases
let gridHTML = '<div class="grid-stack">' +
' <div class="grid-stack-item" gs-x="0" gs-y="0" gs-w="4" gs-h="2" gs-id="gsItem1" id="item1">' +
' <div class="grid-stack-item-content">item 1 text</div>' +
' </div>' +
' <div class="grid-stack-item" gs-x="4" gs-y="0" gs-w="4" gs-h="4" gs-id="gsItem2" id="item2">' +
' <div class="grid-stack-item-content">item 2 text</div>' +
' </div>' +
'</div>';
let gridstackHTML = '<div style="width: 800px; height: 600px" id="gs-cont">' + gridHTML + '</div>';
let gridstackSmallHTML = '<div style="width: 400px; height: 600px" id="gs-cont">' + gridHTML + '</div>';
// empty grid
let gridstackEmptyHTML = '<div style="width: 800px; height: 600px" id="gs-cont">' +
' <div class="grid-stack">' +
' </div>' +
'</div>';
// nested empty grids
let gridstackNestedHTML = '<div style="width: 800px; height: 600px" id="gs-cont">' +
' <div class="grid-stack">' +
' <div class="grid-stack-item">' +
' <div class="grid-stack-item-content">item 1</div>' +
' <div class="grid-stack-item-content">' +
' <div class="grid-stack sub1"></div>' +
' </div>' +
' </div>' +
' </div>' +
'</div>';
// generic widget with no param
let widgetHTML = '<div id="item3"><div class="grid-stack-item-content"> hello </div></div>';
describe('grid.init() / initAll() >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('use selector >', () => {
grid = GridStack.init(undefined, '.grid-stack');
expect(grid).not.toBe(null);
});
it('use selector no dot >', () => {
grid = GridStack.init(null, 'grid-stack');
expect(grid).not.toBe(null);
});
it('use wrong selector >', () => {
grid = GridStack.init(null, 'BAD_SELECTOR_TEST');
expect(grid).toEqual(null);
});
it('initAll use selector >', () => {
grids = GridStack.initAll(undefined, '.grid-stack');
expect(grids.length).toBe(1);
});
it('initAll use selector no dot >', () => {
grids = GridStack.initAll(undefined, 'grid-stack');
expect(grids.length).toBe(1);
});
});
describe('grid.setAnimation >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should add class grid-stack-animate to the container. >', () => {
grid = GridStack.init({ animate: true });
expect(grid.el.classList.contains('grid-stack-animate')).toBe(true);
grid.el.classList.remove('grid-stack-animate');
expect(grid.el.classList.contains('grid-stack-animate')).toBe(false);
grid.setAnimation(true);
expect(grid.el.classList.contains('grid-stack-animate')).toBe(true);
});
it('should remove class grid-stack-animate from the container. >', () => {
grid = GridStack.init({ animate: true });
grid.setAnimation(false);
expect(grid.el.classList.contains('grid-stack-animate')).toBe(false);
});
});
describe('grid._setStaticClass >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should add class grid-stack-static to the container. >', () => {
grid = GridStack.init({ staticGrid: true });
expect(grid.el.classList.contains('grid-stack-static')).toBe(true);
grid.setStatic(false);
expect(grid.el.classList.contains('grid-stack-static')).toBe(false);
grid.setStatic(true);
expect(grid.el.classList.contains('grid-stack-static')).toBe(true);
});
it('should remove class grid-stack-static from the container. >', () => {
grid = GridStack.init({ staticGrid: false });
expect(grid.el.classList.contains('grid-stack-static')).toBe(false);
grid.setStatic(true);
expect(grid.el.classList.contains('grid-stack-static')).toBe(true);
});
});
// Note: Pixel-accurate coordinate tests moved to E2E tests
// where real browser layout engines can provide accurate getBoundingClientRect()
// describe('grid.getCellFromPixel >', () => {});
// Note: Exact pixel calculation tests moved to E2E tests
// where real browser layout engines can provide accurate offsetWidth
// describe('grid.cellWidth >', () => {});
describe('grid.cellHeight >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should set and get cellHeight correctly >', () => {
let cellHeight = 80;
let margin = 5;
let options = {
cellHeight,
margin,
column: 12
};
grid = GridStack.init(options);
let rows = parseInt(grid.el.getAttribute('gs-current-row'));
expect(grid.getRow()).toBe(rows);
expect(grid.getCellHeight()).toBe(cellHeight);
// Note: Exact pixel height calculation tests moved to E2E tests
// where real browser layout engines can provide accurate getComputedStyle() values
grid.cellHeight(grid.getCellHeight()); // should be no-op
expect(grid.getCellHeight()).toBe(cellHeight);
cellHeight = 120; // should change
grid.cellHeight(cellHeight);
expect(grid.getCellHeight()).toBe(cellHeight);
cellHeight = 20; // should change
grid.cellHeight(cellHeight);
expect(grid.getCellHeight()).toBe(cellHeight);
});
it('should be square >', () => {
grid = GridStack.init({ cellHeight: 'auto' });
expect(grid.cellWidth()).toBe(grid.getCellHeight());
});
});
describe('grid.column >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should have no changes >', () => {
grid = GridStack.init();
expect(grid.getColumn()).toBe(12);
grid.column(12);
expect(grid.getColumn()).toBe(12);
});
it('should set construct CSS class >', () => {
grid = GridStack.init({ column: 1 });
expect(grid.el.classList.contains('gs-1')).toBe(true);
grid.column(2);
expect(grid.el.classList.contains('gs-1')).toBe(false);
expect(grid.el.classList.contains('gs-2')).toBe(true);
});
it('should set CSS class >', () => {
grid = GridStack.init();
expect(grid.el.classList.contains('grid-stack')).toBe(true);
grid.column(1);
expect(grid.el.classList.contains('gs-1')).toBe(true);
});
it('should SMALL change column number, no relayout >', () => {
let options = {
column: 12
};
grid = GridStack.init(options);
let items = Utils.getElements('.grid-stack-item');
grid.column(9);
expect(grid.getColumn()).toBe(9);
items.forEach(el => expect(el.getAttribute('gs-y')).toBe(null));
grid.column(12);
expect(grid.getColumn()).toBe(12);
items.forEach(el => expect(el.getAttribute('gs-y')).toBe(null));
});
it('no sizing, no moving >', () => {
grid = GridStack.init({ column: 12 });
let items = Utils.getElements('.grid-stack-item');
grid.column(8, 'none');
expect(grid.getColumn()).toBe(8);
items.forEach(el => {
expect(parseInt(el.getAttribute('gs-w'))).toBe(4);
expect(el.getAttribute('gs-y')).toBe(null);
});
});
it('no sizing, but moving down >', () => {
grid = GridStack.init({ column: 12 });
let items = Utils.getElements('.grid-stack-item');
grid.column(7, 'move');
expect(grid.getColumn()).toBe(7);
items.forEach(el => expect(parseInt(el.getAttribute('gs-w'))).toBe(4));
expect(items[0].getAttribute('gs-y')).toBe(null);
expect(parseInt(items[1].getAttribute('gs-y'))).toBe(2);
});
it('should change column number and re-layout items >', () => {
let options = {
column: 12,
float: true
};
grid = GridStack.init(options);
let el1 = document.getElementById('item1');
let el2 = document.getElementById('item2');
// items start at 4x2 and 4x4
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(parseInt(el1.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(4);
expect(el2.getAttribute('gs-y')).toBe(null);
expect(parseInt(el2.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
// 1 column will have item1, item2
grid.column(1);
expect(grid.getColumn()).toBe(1);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(el1.getAttribute('gs-w')).toBe(null);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(el2.getAttribute('gs-x')).toBe(null);
expect(parseInt(el2.getAttribute('gs-y'))).toBe(2);
expect(el2.getAttribute('gs-w')).toBe(null);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
// add default 1x1 item to the end (1 column)
let el3 = grid.addWidget({ content: 'new' });
expect(el3).not.toBe(null);
expect(el3.getAttribute('gs-x')).toBe(null);
expect(parseInt(el3.getAttribute('gs-y'))).toBe(6);
expect(el3.getAttribute('gs-w')).toBe(null);
expect(el3.getAttribute('gs-h')).toBe(null);
// back to 12 column and initial layout (other than new item3)
grid.column(12);
expect(grid.getColumn()).toBe(12);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(parseInt(el1.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(4);
expect(el2.getAttribute('gs-y')).toBe(null);
expect(parseInt(el2.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
// TODO: we don't remembers autoPlacement (cleared multiple places)
// expect(parseInt(el3.getAttribute('gs-x'))).toBe(8);
// expect(el3.getAttribute('gs-y')).toBe(null);
expect(el3.getAttribute('gs-x')).toBe(null);
expect(parseInt(el3.getAttribute('gs-y'))).toBe(6);
expect(el3.getAttribute('gs-w')).toBe(null);
expect(el3.getAttribute('gs-h')).toBe(null);
// back to 1 column
grid.column(1);
expect(grid.getColumn()).toBe(1);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(el1.getAttribute('gs-w')).toBe(null);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(el2.getAttribute('gs-x')).toBe(null);
expect(parseInt(el2.getAttribute('gs-y'))).toBe(2);
expect(el2.getAttribute('gs-w')).toBe(null);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
expect(el3.getAttribute('gs-x')).toBe(null);
expect(parseInt(el3.getAttribute('gs-y'))).toBe(6);
expect(el3.getAttribute('gs-w')).toBe(null);
expect(el3.getAttribute('gs-h')).toBe(null);
// move item2 to beginning to [3][1][2] vertically
grid.update(el3, { x: 0, y: 0 });
expect(el3.getAttribute('gs-x')).toBe(null);
expect(el3.getAttribute('gs-y')).toBe(null);
expect(el3.getAttribute('gs-w')).toBe(null);
expect(el3.getAttribute('gs-h')).toBe(null);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(parseInt(el1.getAttribute('gs-y'))).toBe(1);
expect(el1.getAttribute('gs-w')).toBe(null);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(el2.getAttribute('gs-x')).toBe(null);
expect(parseInt(el2.getAttribute('gs-y'))).toBe(3);
expect(el2.getAttribute('gs-w')).toBe(null);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
// back to 12 column, el3 to be beginning still, but [1][2] to be in 1 columns still but wide 4x2 and 4x still
grid.column(12);
expect(grid.getColumn()).toBe(12);
expect(el3.getAttribute('gs-x')).toBe(null); // 8 TEST WHY
expect(el3.getAttribute('gs-y')).toBe(null);
expect(el3.getAttribute('gs-w')).toBe(null);
expect(el3.getAttribute('gs-h')).toBe(null);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(parseInt(el1.getAttribute('gs-y'))).toBe(1);
expect(parseInt(el1.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(4);
expect(parseInt(el2.getAttribute('gs-y'))).toBe(1);
expect(parseInt(el2.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
// 2 column will have item1, item2, item3 in 1 column still but half the width
grid.column(1); // test convert from small, should use 12 layout still
grid.column(2);
expect(grid.getColumn()).toBe(2);
expect(el3.getAttribute('gs-x')).toBe(null); // 1 TEST WHY
expect(el3.getAttribute('gs-y')).toBe(null);
expect(el3.getAttribute('gs-w')).toBe(null); // 1 as we scaled from 12 columns
expect(el3.getAttribute('gs-h')).toBe(null);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(parseInt(el1.getAttribute('gs-y'))).toBe(1);
expect(el1.getAttribute('gs-w')).toBe(null);
expect(parseInt(el1.getAttribute('gs-h'))).toBe(2);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(1);
expect(parseInt(el2.getAttribute('gs-y'))).toBe(1);
expect(el2.getAttribute('gs-w')).toBe(null);
expect(parseInt(el2.getAttribute('gs-h'))).toBe(4);
});
});
describe('grid.column larger layout >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('24 layout in 12 column to 1 back 12 then 24 >', () => {
const children = [{ x: 0, y: 0, w: 12, h: 1, id: 'left' }, { x: 12, y: 0, w: 12, h: 1, id: 'right' }];
children.forEach(c => c.content = c.id);
grid = GridStack.init({ children });
const left = find('left');
const right = find('right');
// side-by-side components 12+12 = 24 columns but only have 12!
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 12 }));
// Resize to 1 column
grid.column(1);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 1 }));
// Resize back to 12 column
grid.column(12);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 12 }));
// Resize to 24 column
grid.column(24);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12 }));
expect(right).toEqual(expect.objectContaining({ x: 12, y: 0, w: 12 }));
});
it('24 column to 12, 1 back 12,24 >', () => {
const children = [{ x: 0, y: 0, w: 12, h: 1, id: 'left' }, { x: 12, y: 0, w: 12, h: 1, id: 'right' }];
children.forEach(c => c.content = c.id);
grid = GridStack.init({ column: 24, children });
const left = find('left');
const right = find('right');
// side-by-side components 12+12 = 24 columns
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12 }));
expect(right).toEqual(expect.objectContaining({ x: 12, y: 0, w: 12 }));
// Resize to 12 column
grid.column(12);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 6 }));
expect(right).toEqual(expect.objectContaining({ x: 6, y: 0, w: 6 }));
// Resize to 1 column
grid.column(1);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 1 }));
// back to 12 column
grid.column(12);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 6 }));
expect(right).toEqual(expect.objectContaining({ x: 6, y: 0, w: 6 }));
// back to 24 column
grid.column(24);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12 }));
expect(right).toEqual(expect.objectContaining({ x: 12, y: 0, w: 12 }));
});
});
// describe('oneColumnModeDomSort >', () => {
// beforeEach(() => {
// document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
// });
// afterEach(() => {
// document.body.removeChild(document.getElementById('gs-cont'));
// });
// it('should support default going to 1 column >', () => {
// let options = {
// column: 12,
// float: true
// };
// grid = GridStack.init(options);
// grid.batchUpdate();
// grid.batchUpdate();
// let el1 = grid.addWidget({w:1, h:1});
// let el2 = grid.addWidget({x:2, y:0, w:2, h:1});
// let el3 = grid.addWidget({x:1, y:0, w:1, h:2});
// grid.batchUpdate(false);
// grid.batchUpdate(false);
// // items are item1[1x1], item3[1x1], item2[2x1]
// expect(el1.getAttribute('gs-x')).toBe(null);
// expect(el1.getAttribute('gs-y')).toBe(null);
// expect(el1.getAttribute('gs-w')).toBe(null);
// expect(el1.getAttribute('gs-h')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-x'))).toBe(1);
// expect(el3.getAttribute('gs-y')).toBe(null);
// expect(el3.getAttribute('gs-w')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-h'))).toBe(2);
// expect(parseInt(el2.getAttribute('gs-x'))).toBe(2);
// expect(el2.getAttribute('gs-y')).toBe(null);
// expect(parseInt(el2.getAttribute('gs-w'))).toBe(2);
// expect(el2.getAttribute('gs-h')).toBe(null);
// // items are item1[1x1], item3[1x2], item2[1x1] in 1 column
// grid.column(1);
// expect(el1.getAttribute('gs-x')).toBe(null);
// expect(el1.getAttribute('gs-y')).toBe(null);
// expect(el1.getAttribute('gs-w')).toBe(null);
// expect(el1.getAttribute('gs-h')).toBe(null);
// expect(el3.getAttribute('gs-x')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-y'))).toBe(1);
// expect(el3.getAttribute('gs-w')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-h'))).toBe(2);
// expect(el2.getAttribute('gs-x')).toBe(null);
// expect(parseInt(el2.getAttribute('gs-y'))).toBe(3);
// expect(el2.getAttribute('gs-w')).toBe(null);
// expect(el2.getAttribute('gs-h')).toBe(null);
// });
// it('should support oneColumnModeDomSort ON going to 1 column >', () => {
// let options = {
// column: 12,
// oneColumnModeDomSort: true,
// float: true
// };
// grid = GridStack.init(options);
// let el1 = grid.addWidget({w:1, h:1});
// let el2 = grid.addWidget({x:2, y:0, w:2, h:1});
// let el3 = grid.addWidget({x:1, y:0, w:1, h:2});
// // items are item1[1x1], item3[1x1], item2[2x1]
// expect(el1.getAttribute('gs-x')).toBe(null);
// expect(el1.getAttribute('gs-y')).toBe(null);
// expect(el1.getAttribute('gs-w')).toBe(null);
// expect(el1.getAttribute('gs-h')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-x'))).toBe(1);
// expect(el3.getAttribute('gs-y')).toBe(null);
// expect(el3.getAttribute('gs-w')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-h'))).toBe(2);
// expect(parseInt(el2.getAttribute('gs-x'))).toBe(2);
// expect(el2.getAttribute('gs-y')).toBe(null);
// expect(parseInt(el2.getAttribute('gs-w'))).toBe(2);
// expect(el2.getAttribute('gs-h')).toBe(null);
// // items are item1[1x1], item2[1x1], item3[1x2] in 1 column dom ordered
// grid.column(1);
// expect(el1.getAttribute('gs-x')).toBe(null);
// expect(el1.getAttribute('gs-y')).toBe(null);
// expect(el1.getAttribute('gs-w')).toBe(null);
// expect(el1.getAttribute('gs-h')).toBe(null);
// expect(el2.getAttribute('gs-x')).toBe(null);
// expect(parseInt(el2.getAttribute('gs-y'))).toBe(1);
// expect(el2.getAttribute('gs-w')).toBe(null);
// expect(el2.getAttribute('gs-h')).toBe(null);
// expect(el3.getAttribute('gs-x')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-y'))).toBe(2);
// expect(el3.getAttribute('gs-w')).toBe(null);
// expect(parseInt(el3.getAttribute('gs-h'))).toBe(2);
// });
// });
// describe('disableOneColumnMode >', () => {
// beforeEach(() => {
// document.body.insertAdjacentHTML('afterbegin', gridstackSmallHTML); // smaller default to 1 column
// });
// afterEach(() => {
// document.body.removeChild(document.getElementById('gs-cont'));
// });
// it('should go to 1 column >', () => {
// grid = GridStack.init();
// expect(grid.getColumn()).toBe(1);
// });
// it('should go to 1 column with large minW >', () => {
// grid = GridStack.init({oneColumnSize: 1000});
// expect(grid.getColumn()).toBe(1);
// });
// it('should stay at 12 with minW >', () => {
// grid = GridStack.init({oneColumnSize: 300});
// expect(grid.getColumn()).toBe(12);
// });
// it('should stay at 12 column >', () => {
// grid = GridStack.init({disableOneColumnMode: true});
// expect(grid.getColumn()).toBe(12);
// });
// });
describe('grid.minRow >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should default to row 0 when empty >', () => {
let options = {};
grid = GridStack.init(options);
expect(grid.getRow()).toBe(4);
expect(grid.opts.minRow).toBe(0);
expect(grid.opts.maxRow).toBe(0);
grid.removeAll();
expect(grid.getRow()).toBe(0);
});
it('should default to row 2 when empty >', () => {
let options = { minRow: 2 };
grid = GridStack.init(options);
expect(grid.getRow()).toBe(4);
expect(grid.opts.minRow).toBe(2);
expect(grid.opts.maxRow).toBe(0);
grid.removeAll();
expect(grid.engine.getRow()).toBe(0);
expect(grid.getRow()).toBe(2);
});
it('should set min = max = 3 rows >', () => {
let options = { row: 3 };
grid = GridStack.init(options);
expect(grid.getRow()).toBe(3); // shrink elements to fit maxRow!
expect(grid.opts.minRow).toBe(3);
expect(grid.opts.maxRow).toBe(3);
grid.removeAll();
expect(grid.engine.getRow()).toBe(0);
expect(grid.getRow()).toBe(3);
});
it('willItFit() >', () => {
// default 4x2 and 4x4 so anything pushing more than 1 will fail
grid = GridStack.init({ maxRow: 5 });
expect(grid.willItFit({ x: 0, y: 0, w: 1, h: 1 })).toBe(true);
expect(grid.willItFit({ x: 0, y: 0, w: 1, h: 3 })).toBe(true);
expect(grid.willItFit({ x: 0, y: 0, w: 1, h: 4 })).toBe(false);
expect(grid.willItFit({ x: 0, y: 0, w: 12, h: 1 })).toBe(true);
expect(grid.willItFit({ x: 0, y: 0, w: 12, h: 2 })).toBe(false);
});
it('willItFit() not modifying node #1687 >', () => {
// default 4x2 and 4x4 so anything pushing more than 1 will fail
grid = GridStack.init({ maxRow: 5 });
let node = { x: 0, y: 0, w: 1, h: 1, _id: 1, _temporaryRemoved: true };
expect(grid.willItFit(node)).toBe(true);
expect(node._temporaryRemoved).toBe(true);
expect(node._id).toBe(1);
});
});
describe('grid attributes >', () => {
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should have row attr >', () => {
let HTML = '<div style="w: 800px; h: 600px" id="gs-cont">' +
' <div class="grid-stack" gs-row="4" gs-current-height="1"></div>' + // old attr current-height
'</div>';
document.body.insertAdjacentHTML('afterbegin', HTML);
grid = GridStack.init();
expect(grid.getRow()).toBe(4);
expect(grid.opts.minRow).toBe(4);
expect(grid.opts.maxRow).toBe(4);
grid.addWidget({ h: 6 });
expect(grid.engine.getRow()).toBe(4);
expect(grid.getRow()).toBe(4);
});
});
describe('grid.min/max width/height >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should set gs-min-w to 2. >', () => {
grid = GridStack.init();
let items = Utils.getElements('.grid-stack-item');
items.forEach(el => grid.update(el, { minW: 2, maxW: 3, minH: 4, maxH: 5 }));
items.forEach(el => {
expect(el.gridstackNode.minW).toBe(2);
expect(el.gridstackNode.maxW).toBe(3);
expect(el.gridstackNode.minH).toBe(4);
expect(el.gridstackNode.maxH).toBe(5);
expect(el.getAttribute('gs-min-w')).toBe(null);
expect(el.getAttribute('gs-max-w')).toBe(null);
expect(el.getAttribute('gs-min-h')).toBe(null);
expect(el.getAttribute('gs-max-h')).toBe(null);
});
// remove all constrain
grid.update('grid-stack-item', { minW: 0, maxW: null, minH: undefined, maxH: 0 });
items.forEach(el => {
expect(el.gridstackNode.minW).toBe(undefined);
expect(el.gridstackNode.maxW).toBe(undefined);
expect(el.gridstackNode.minH).toBe(undefined);
expect(el.gridstackNode.maxH).toBe(undefined);
expect(el.getAttribute('gs-min-w')).toBe(null);
expect(el.getAttribute('gs-max-w')).toBe(null);
expect(el.getAttribute('gs-min-h')).toBe(null);
expect(el.getAttribute('gs-max-h')).toBe(null);
});
});
});
describe('grid.isAreaEmpty >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should set return false. >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let shouldBeFalse = grid.isAreaEmpty(1, 1, 1, 1);
expect(shouldBeFalse).toBe(false);
});
it('should set return true. >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let shouldBeTrue = grid.isAreaEmpty(5, 5, 1, 1);
expect(shouldBeTrue).toBe(true);
});
});
describe('grid.removeAll >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should remove all children by default >', () => {
grid = GridStack.init();
grid.removeAll();
expect(grid.engine.nodes).toEqual([]);
expect(document.getElementById('item1')).toBe(null);
});
it('should remove all children >', () => {
grid = GridStack.init();
grid.removeAll(true);
expect(grid.engine.nodes).toEqual([]);
expect(document.getElementById('item1')).toBe(null);
});
it('should remove gridstack part, leave DOM behind >', () => {
grid = GridStack.init();
grid.removeAll(false);
expect(grid.engine.nodes).toEqual([]);
expect(document.getElementById('item1')).not.toBe(null);
});
});
describe('grid.removeWidget >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should remove first item (default), then second (false) >', () => {
grid = GridStack.init();
expect(grid.engine.nodes.length).toEqual(2);
let el1 = document.getElementById('item1');
expect(el1).not.toBe(null);
grid.removeWidget(el1);
expect(grid.engine.nodes.length).toEqual(1);
expect(document.getElementById('item1')).toBe(null);
expect(document.getElementById('item2')).not.toBe(null);
let el2 = document.getElementById('item2');
grid.removeWidget(el2, false);
expect(grid.engine.nodes.length).toEqual(0);
expect(document.getElementById('item2')).not.toBe(null);
});
});
describe('grid method _packNodes with float >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should allow same x, y coordinates for widgets. >', () => {
let options = {
cellHeight: 80,
margin: 5,
float: true
};
grid = GridStack.init(options);
let items = Utils.getElements('.grid-stack-item');
items.forEach(oldEl => {
let el = grid.makeWidget(oldEl);
expect(oldEl.getAttribute('gs-x')).toBe(el.getAttribute('gs-x'));
expect(oldEl.getAttribute('gs-y')).toBe(el.getAttribute('gs-y'));
});
});
it('should not allow same x, y coordinates for widgets. >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let items = Utils.getElements('.grid-stack-item');
items.forEach(oldEl => {
let el = oldEl.cloneNode(true);
el = grid.makeWidget(el);
expect(el.gridstackNode?.x).not.toBe(oldEl.gridstackNode?.x);
});
});
});
describe('grid method addWidget with all parameters >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should keep all widget options the same (autoPosition off >', () => {
grid = GridStack.init({ float: true });
;
let w = grid.addWidget({ x: 6, y: 7, w: 2, h: 3, autoPosition: false,
minW: 1, maxW: 4, minH: 2, maxH: 5, id: 'coolWidget' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(6);
expect(parseInt(w.getAttribute('gs-y'))).toBe(7);
expect(parseInt(w.getAttribute('gs-w'))).toBe(2);
expect(parseInt(w.getAttribute('gs-h'))).toBe(3);
expect(w.getAttribute('gs-auto-position')).toBe(null);
expect(w.getAttribute('gs-id')).toBe('coolWidget');
// should move widget to top with float=false
expect(grid.getFloat()).toBe(true);
grid.float(false);
expect(grid.getFloat()).toBe(false);
expect(parseInt(w.getAttribute('gs-x'))).toBe(6);
expect(parseInt(w.getAttribute('gs-y'))).toBe(4); // <--- from 7 to 4 below second original widget
expect(parseInt(w.getAttribute('gs-w'))).toBe(2);
expect(parseInt(w.getAttribute('gs-h'))).toBe(3);
expect(w.getAttribute('gs-auto-position')).toBe(null);
expect(w.getAttribute('gs-id')).toBe('coolWidget');
// should not move again (no-op)
grid.float(true);
expect(grid.getFloat()).toBe(true);
expect(parseInt(w.getAttribute('gs-x'))).toBe(6);
expect(parseInt(w.getAttribute('gs-y'))).toBe(4);
expect(parseInt(w.getAttribute('gs-w'))).toBe(2);
expect(parseInt(w.getAttribute('gs-h'))).toBe(3);
expect(w.getAttribute('gs-auto-position')).toBe(null);
expect(w.getAttribute('gs-id')).toBe('coolWidget');
});
});
describe('grid method addWidget with autoPosition true >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should change x, y coordinates for widgets. >', () => {
grid = GridStack.init({ float: true });
let w = grid.addWidget({ x: 9, y: 7, w: 2, h: 3, autoPosition: true });
expect(parseInt(w.getAttribute('gs-x'), 10)).not.toBe(9);
expect(parseInt(w.getAttribute('gs-y'), 10)).not.toBe(7);
});
});
describe('grid method addWidget with widget options >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should autoPosition (missing X,Y) >', () => {
grid = GridStack.init();
let w = grid.addWidget({ h: 2, id: 'optionWidget' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(parseInt(w.getAttribute('gs-h'))).toBe(2);
// expect(w.getAttribute('gs-auto-position')).toBe('true');
expect(w.getAttribute('gs-id')).toBe('optionWidget');
});
it('should autoPosition (missing X) >', () => {
grid = GridStack.init();
let w = grid.addWidget({ y: 9, h: 2, id: 'optionWidget' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(parseInt(w.getAttribute('gs-h'))).toBe(2);
// expect(w.getAttribute('gs-auto-position')).toBe('true');
expect(w.getAttribute('gs-id')).toBe('optionWidget');
});
it('should autoPosition (missing Y) >', () => {
grid = GridStack.init();
let w = grid.addWidget({ x: 9, h: 2, id: 'optionWidget' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(parseInt(w.getAttribute('gs-h'))).toBe(2);
// expect(w.getAttribute('gs-auto-position')).toBe('true');
expect(w.getAttribute('gs-id')).toBe('optionWidget');
});
it('should autoPosition (correct X, missing Y) >', () => {
grid = GridStack.init();
let w = grid.addWidget({ x: 8, h: 2, id: 'optionWidget' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(parseInt(w.getAttribute('gs-h'))).toBe(2);
// expect(w.getAttribute('gs-auto-position')).toBe('true');
expect(w.getAttribute('gs-id')).toBe('optionWidget');
});
it('should autoPosition (empty options) >', () => {
grid = GridStack.init();
let w = grid.addWidget({});
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(w.getAttribute('gs-h')).toBe(null);
// expect(w.getAttribute('gs-auto-position')).toBe('true');
});
});
describe('addWidget() >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('bad string options should use default >', () => {
grid = GridStack.init();
let w = grid.addWidget({ x: 'foo', y: null, w: 'bar', h: '' });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(w.getAttribute('gs-w')).toBe(null);
expect(w.getAttribute('gs-h')).toBe(null);
});
it('makeWidget attr should be retained >', () => {
grid = GridStack.init({ float: true });
const d = document.createElement('div');
d.innerHTML = '<div class="grid-stack-item" gs-w="3" gs-max-w="4" gs-id="gsfoo" id="foo"><div class="grid-stack-item-content">foo content</div></div>';
grid.el.appendChild(d.firstChild);
let w = grid.makeWidget('foo');
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(parseInt(w.getAttribute('gs-w'))).toBe(3);
expect(w.gridstackNode.maxW).toBe(4);
expect(w.getAttribute('gs-h')).toBe(null);
expect(w.getAttribute('gs-id')).toBe('gsfoo');
});
it('makeWidget width option override >', () => {
grid = GridStack.init({ float: true });
const d = document.createElement('div');
d.innerHTML = '<div class="grid-stack-item" gs-w="3" gs-max-w="4" gs-id="gsfoo" id="foo"><div class="grid-stack-item-content">foo content</div></div>';
grid.el.appendChild(d.firstChild);
let w = grid.makeWidget('foo', { x: null, y: null, w: 2 });
expect(parseInt(w.getAttribute('gs-x'))).toBe(8);
expect(w.getAttribute('gs-y')).toBe(null);
expect(parseInt(w.getAttribute('gs-w'))).toBe(2);
expect(w.getAttribute('gs-h')).toBe(null);
});
});
describe('makeWidget() >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('passing element >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget(el);
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing element float=true >', () => {
grid = GridStack.init({ float: true });
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget(el);
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing class >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div class="item"><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget('.item');
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing class no dot >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div class="item"><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget('item');
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing id >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div id="item"><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget('#item');
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing id no # >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div id="item"><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget('item');
expect(w.getAttribute('gs-x')).toBe(null);
});
it('passing id as number >', () => {
grid = GridStack.init();
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div id="1"><div class="grid-stack-item-content"></div></div>';
let el = doc.body.children[0];
grid.el.appendChild(el);
let w = grid.makeWidget('1');
expect(w.getAttribute('gs-x')).toBe(null);
});
});
describe('method getFloat() >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should match true/false only >', () => {
grid = GridStack.init({ float: true });
expect(grid.getFloat()).toBe(true);
grid.float(0);
expect(grid.getFloat()).toBe(false);
grid.float(null);
expect(grid.getFloat()).toBe(false);
grid.float(undefined);
expect(grid.getFloat()).toBe(false);
grid.float(false);
expect(grid.getFloat()).toBe(false);
});
});
describe('grid.destroy >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.getElementById('gs-cont').remove();
});
it('should cleanup gridstack >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let gridEl = grid.el;
grid.destroy();
expect(gridEl.parentElement).toBe(null);
expect(grid.el).toBe(undefined);
expect(grid.engine).toBe(undefined);
});
it('should cleanup gridstack but leave elements >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let gridEl = grid.el;
grid.destroy(false);
expect(gridEl.parentElement).not.toBe(null);
expect(Utils.getElements('.grid-stack-item').length).toBe(2);
expect(grid.el).toBe(undefined);
expect(grid.engine).toBe(undefined);
grid.destroy(); // sanity check for call twice!
});
});
describe('grid.resize >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should resize widget >', () => {
let options = {
cellHeight: 80,
margin: 5
};
grid = GridStack.init(options);
let items = Utils.getElements('.grid-stack-item');
grid.update(items[0], { w: 5, h: 5 });
expect(parseInt(items[0].getAttribute('gs-w'))).toBe(5);
expect(parseInt(items[0].getAttribute('gs-h'))).toBe(5);
});
});
describe('grid.move >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should move widget >', () => {
let options = {
cellHeight: 80,
margin: 5,
float: true
};
grid = GridStack.init(options);
let items = Utils.getElements('.grid-stack-item');
grid.update(items[0], { x: 5, y: 5 });
expect(parseInt(items[0].getAttribute('gs-x'))).toBe(5);
expect(parseInt(items[0].getAttribute('gs-y'))).toBe(5);
});
});
describe('grid.update >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('should move and resize widget >', () => {
grid = GridStack.init({ float: true });
let el = Utils.getElements('.grid-stack-item')[1];
expect(parseInt(el.getAttribute('gs-w'))).toBe(4);
grid.update(el, { x: 5, y: 4, h: 2 });
expect(parseInt(el.getAttribute('gs-x'))).toBe(5);
expect(parseInt(el.getAttribute('gs-y'))).toBe(4);
expect(parseInt(el.getAttribute('gs-w'))).toBe(4);
expect(parseInt(el.getAttribute('gs-h'))).toBe(2);
});
it('should change noMove >', () => {
grid = GridStack.init({ float: true });
let items = Utils.getElements('.grid-stack-item');
let el = items[1];
let dd = GridStack.getDD();
grid.update(el, { noMove: true, noResize: false });
expect(el.getAttribute('gs-no-move')).toBe('true');
expect(el.getAttribute('gs-no-resize')).toBe(null); // false is no-op
expect(dd.isResizable(el)).toBe(true);
expect(dd.isDraggable(el)).toBe(false)