UNPKG

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
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)