N°8641 - Dashboard editor front-end first commit for Form SDK integration.

* No dashlet edition
* Dashboard are not persisted
* Unable to load a dashboard from an endpoint (refresh)
* Grid library need proper npm integration
This commit is contained in:
Stephen Abello
2026-01-06 15:23:51 +01:00
parent 3e879c64a7
commit a713e1b56e
167 changed files with 32266 additions and 763 deletions

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,358 @@
import { GridStackEngine } from '../src/gridstack-engine';
describe('gridstack engine:', () => {
'use strict';
let e;
let ePriv; // cast engine for private vars access
let findNode = function (id) {
return e.nodes.find(n => n.id === id);
};
it('should exist setup function.', () => {
expect(GridStackEngine).not.toBeNull();
expect(typeof GridStackEngine).toBe('function');
});
describe('test constructor >', () => {
it('should be setup properly', () => {
ePriv = e = new GridStackEngine();
expect(e.column).toEqual(12);
expect(e.float).toEqual(false);
expect(e.maxRow).toEqual(undefined);
expect(e.nodes).toEqual([]);
expect(e.batchMode).toEqual(undefined);
expect(ePriv.onChange).toEqual(undefined);
});
it('should set params correctly.', () => {
let fkt = () => { };
let arr = [1, 2, 3];
ePriv = e = new GridStackEngine({ column: 1, onChange: fkt, float: true, maxRow: 2, nodes: arr });
expect(e.column).toEqual(1);
expect(e.float).toBe(true);
expect(e.maxRow).toEqual(2);
expect(e.nodes).toEqual(arr);
expect(e.batchMode).toEqual(undefined);
expect(ePriv.onChange).toEqual(fkt);
});
});
describe('batch update', () => {
it('should set float and batchMode when calling batchUpdate.', () => {
ePriv = e = new GridStackEngine({ float: true });
e.batchUpdate();
expect(e.float).toBe(true);
expect(e.batchMode).toBe(true);
});
});
describe('test prepareNode >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
});
it('should prepare a node', () => {
expect(e.prepareNode({}, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ x: 10 }, false)).toEqual(expect.objectContaining({ x: 10, y: 0, h: 1 }));
expect(e.prepareNode({ x: -10 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ y: 10 }, false)).toEqual(expect.objectContaining({ x: 0, y: 10, h: 1 }));
expect(e.prepareNode({ y: -10 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ w: 3 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, w: 3, h: 1 }));
expect(e.prepareNode({ w: 100 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12, h: 1 }));
expect(e.prepareNode({ w: 0 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ w: -190 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ h: 3 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 3 }));
expect(e.prepareNode({ h: 0 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ h: -10 }, false)).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(e.prepareNode({ x: 4, w: 10 }, false)).toEqual(expect.objectContaining({ x: 2, y: 0, w: 10, h: 1 }));
expect(e.prepareNode({ x: 4, w: 10 }, true)).toEqual(expect.objectContaining({ x: 4, y: 0, w: 8, h: 1 }));
});
});
describe('sorting of nodes >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
e.nodes = [{ x: 7, y: 0 }, { x: 4, y: 4 }, { x: 9, y: 0 }, { x: 0, y: 1 }];
});
it('should sort ascending with 12 columns.', () => {
e.sortNodes(1);
expect(e.nodes).toEqual([{ x: 7, y: 0 }, { x: 9, y: 0 }, { x: 0, y: 1 }, { x: 4, y: 4 }]);
});
it('should sort descending with 12 columns.', () => {
e.sortNodes(-1);
expect(e.nodes).toEqual([{ x: 4, y: 4 }, { x: 0, y: 1 }, { x: 9, y: 0 }, { x: 7, y: 0 }]);
});
it('should sort ascending without columns.', () => {
ePriv.column = undefined;
e.sortNodes(1);
expect(e.nodes).toEqual([{ x: 7, y: 0 }, { x: 9, y: 0 }, { x: 0, y: 1 }, { x: 4, y: 4 }]);
});
it('should sort descending without columns.', () => {
ePriv.column = undefined;
e.sortNodes(-1);
expect(e.nodes).toEqual([{ x: 4, y: 4 }, { x: 0, y: 1 }, { x: 9, y: 0 }, { x: 7, y: 0 }]);
});
});
describe('test isAreaEmpty >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine({ float: true });
e.nodes = [
e.prepareNode({ x: 3, y: 2, w: 3, h: 2 })
];
});
it('should be true', () => {
expect(e.isAreaEmpty(0, 0, 3, 2)).toEqual(true);
expect(e.isAreaEmpty(3, 4, 3, 2)).toEqual(true);
});
it('should be false', () => {
expect(e.isAreaEmpty(1, 1, 3, 2)).toEqual(false);
expect(e.isAreaEmpty(2, 3, 3, 2)).toEqual(false);
});
});
describe('test cleanNodes/getDirtyNodes >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine({ float: true });
e.nodes = [
e.prepareNode({ x: 0, y: 0, id: '1', _dirty: true }),
e.prepareNode({ x: 3, y: 2, w: 3, h: 2, id: '2', _dirty: true }),
e.prepareNode({ x: 3, y: 7, w: 3, h: 2, id: '3' })
];
});
beforeEach(() => {
delete ePriv.batchMode;
});
it('should return all dirty nodes', () => {
let nodes = e.getDirtyNodes();
expect(nodes.length).toEqual(2);
expect(nodes[0].id).toEqual('1');
expect(nodes[1].id).toEqual('2');
});
it('should\'n clean nodes if batchMode true', () => {
e.batchMode = true;
e.cleanNodes();
expect(e.getDirtyNodes().length).toBeGreaterThan(0);
});
it('should clean all dirty nodes', () => {
e.cleanNodes();
expect(e.getDirtyNodes().length).toEqual(0);
});
});
describe('test batchUpdate/commit >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
});
it('should work on not float grids', () => {
expect(e.float).toEqual(false);
e.batchUpdate();
e.batchUpdate(); // double for code coverage
expect(e.batchMode).toBe(true);
expect(e.float).toEqual(true);
e.batchUpdate(false);
e.batchUpdate(false);
expect(e.batchMode).not.toBe(true);
expect(e.float).not.toBe(true);
});
it('should work on float grids', () => {
e.float = true;
e.batchUpdate();
expect(e.batchMode).toBe(true);
expect(e.float).toEqual(true);
e.batchUpdate(false);
expect(e.batchMode).not.toBe(true);
expect(e.float).toEqual(true);
});
});
describe('test batchUpdate/commit >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine({ float: true });
});
it('should work on float grids', () => {
expect(e.float).toEqual(true);
e.batchUpdate();
expect(e.batchMode).toBe(true);
expect(e.float).toEqual(true);
e.batchUpdate(false);
expect(e.batchMode).not.toBe(true);
expect(e.float).toEqual(true);
});
});
describe('test _notify >', () => {
let spy;
beforeEach(() => {
spy = {
callback: () => { }
};
vi.spyOn(spy, 'callback');
ePriv = e = new GridStackEngine({ float: true, onChange: spy.callback });
e.nodes = [
e.prepareNode({ x: 0, y: 0, id: '1', _dirty: true }),
e.prepareNode({ x: 3, y: 2, w: 3, h: 2, id: '2', _dirty: true }),
e.prepareNode({ x: 3, y: 7, w: 3, h: 2, id: '3' })
];
});
it('should\'n be called if batchMode true', () => {
e.batchMode = true;
ePriv._notify();
expect(spy.callback).not.toHaveBeenCalled();
});
it('should by called with dirty nodes', () => {
ePriv._notify();
expect(spy.callback).toHaveBeenCalledWith([e.nodes[0], e.nodes[1]]);
});
it('should by called with extra passed node to be removed', () => {
let n1 = { id: -1 };
ePriv._notify([n1]);
expect(spy.callback).toHaveBeenCalledWith([n1, e.nodes[0], e.nodes[1]]);
});
});
describe('test _packNodes >', () => {
describe('using float:false mode >', () => {
beforeEach(() => {
ePriv = e = new GridStackEngine({ float: false });
});
it('shouldn\'t pack one node with y coord eq 0', () => {
e.nodes = [
e.prepareNode({ x: 0, y: 0, w: 1, h: 1, id: '1' }),
];
ePriv._packNodes();
expect(findNode('1')).toEqual(expect.objectContaining({ x: 0, y: 0, h: 1 }));
expect(findNode('1')._dirty).toBeFalsy();
});
it('should pack one node correctly', () => {
e.nodes = [
e.prepareNode({ x: 0, y: 1, w: 1, h: 1, id: '1' }),
];
ePriv._packNodes();
expect(findNode('1')).toEqual(expect.objectContaining({ x: 0, y: 0, _dirty: true }));
});
it('should pack nodes correctly', () => {
e.nodes = [
e.prepareNode({ x: 0, y: 1, w: 1, h: 1, id: '1' }),
e.prepareNode({ x: 0, y: 5, w: 1, h: 1, id: '2' }),
];
ePriv._packNodes();
expect(findNode('1')).toEqual(expect.objectContaining({ x: 0, y: 0, _dirty: true }));
expect(findNode('2')).toEqual(expect.objectContaining({ x: 0, y: 1, _dirty: true }));
});
it('should pack reverse nodes correctly', () => {
e.nodes = [
e.prepareNode({ x: 0, y: 5, w: 1, h: 1, id: '1' }),
e.prepareNode({ x: 0, y: 1, w: 1, h: 1, id: '2' }),
];
ePriv._packNodes();
expect(findNode('2')).toEqual(expect.objectContaining({ x: 0, y: 0, _dirty: true }));
expect(findNode('1')).toEqual(expect.objectContaining({ x: 0, y: 1, _dirty: true }));
});
it('should respect locked nodes', () => {
e.nodes = [
e.prepareNode({ x: 0, y: 1, w: 1, h: 1, id: '1', locked: true }),
e.prepareNode({ x: 0, y: 5, w: 1, h: 1, id: '2' }),
];
ePriv._packNodes();
expect(findNode('1')).toEqual(expect.objectContaining({ x: 0, y: 1, h: 1 }));
expect(findNode('1')._dirty).toBeFalsy();
expect(findNode('2')).toEqual(expect.objectContaining({ x: 0, y: 2, _dirty: true }));
});
});
});
describe('test changedPos >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
});
it('should return true for changed x', () => {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(e.changedPosConstrain(widget, { x: 2, y: 2 })).toEqual(true);
});
it('should return true for changed y', () => {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(e.changedPosConstrain(widget, { x: 1, y: 1 })).toEqual(true);
});
it('should return true for changed width', () => {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(e.changedPosConstrain(widget, { x: 2, y: 2, w: 4, h: 4 })).toEqual(true);
});
it('should return true for changed height', () => {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(e.changedPosConstrain(widget, { x: 1, y: 2, w: 3, h: 3 })).toEqual(true);
});
it('should return false for unchanged position', () => {
let widget = { x: 1, y: 2, w: 3, h: 4 };
expect(e.changedPosConstrain(widget, { x: 1, y: 2, w: 3, h: 4 })).toEqual(false);
});
});
describe('test locked widget >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
});
it('should add widgets around locked one', () => {
let nodes = [
{ x: 0, y: 1, w: 12, h: 1, locked: true, noMove: true, noResize: true, id: '0' },
{ x: 1, y: 0, w: 2, h: 3, id: '1' }
];
// add locked item
e.addNode(nodes[0]);
expect(findNode('0')).toEqual(expect.objectContaining({ x: 0, y: 1, w: 12, h: 1, locked: true }));
// add item that moves past locked one
e.addNode(nodes[1]);
expect(findNode('0')).toEqual(expect.objectContaining({ x: 0, y: 1, w: 12, h: 1, locked: true }));
expect(findNode('1')).toEqual(expect.objectContaining({ x: 1, y: 2, h: 3 }));
// locked item can still be moved directly (what user does)
let node0 = findNode('0');
expect(e.moveNode(node0, { y: 6 })).toEqual(true);
expect(findNode('0')).toEqual(expect.objectContaining({ x: 0, y: 6, h: 1, locked: true }));
// but moves regular one past it
let node1 = findNode('1');
expect(e.moveNode(node1, { x: 6, y: 6 })).toEqual(true);
expect(node1).toEqual(expect.objectContaining({ x: 6, y: 7, w: 2, h: 3 }));
// but moves regular one before (gravity ON)
e.float = false;
expect(e.moveNode(node1, { x: 7, y: 3 })).toEqual(true);
expect(node1).toEqual(expect.objectContaining({ x: 7, y: 0, w: 2, h: 3 }));
// but moves regular one before (gravity OFF)
e.float = true;
expect(e.moveNode(node1, { x: 7, y: 3 })).toEqual(true);
expect(node1).toEqual(expect.objectContaining({ x: 7, y: 3, w: 2, h: 3 }));
});
});
describe('test columnChanged >', () => {
beforeAll(() => {
});
it('12 to 1 and back', () => {
ePriv = e = new GridStackEngine({ column: 12 });
// Add two side-by-side components 6+6 = 12 columns
const left = e.addNode({ x: 0, y: 0, w: 6, h: 1, id: 'left' });
const right = e.addNode({ x: 6, y: 0, w: 6, h: 1, id: 'right' });
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 6, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 6, y: 0, w: 6, h: 1 }));
// Resize to 1 column
e.column = 1;
e.columnChanged(12, 1);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 1, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 1, h: 1 }));
// Resize back to 12 column
e.column = 12;
e.columnChanged(1, 12);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 6, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 6, y: 0, w: 6, h: 1 }));
});
it('24 column to 1 and back', () => {
ePriv = e = new GridStackEngine({ column: 24 });
// Add two side-by-side components 12+12 = 24 columns
const left = e.addNode({ x: 0, y: 0, w: 12, h: 1, id: 'left' });
const right = e.addNode({ x: 12, y: 0, w: 12, h: 1, id: 'right' });
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 12, y: 0, w: 12, h: 1 }));
// Resize to 1 column
e.column = 1;
e.columnChanged(24, 1);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 1, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 0, y: 1, w: 1, h: 1 }));
// Resize back to 24 column
e.column = 24;
e.columnChanged(1, 24);
expect(left).toEqual(expect.objectContaining({ x: 0, y: 0, w: 12, h: 1 }));
expect(right).toEqual(expect.objectContaining({ x: 12, y: 0, w: 12, h: 1 }));
});
});
describe('test compact >', () => {
beforeAll(() => {
ePriv = e = new GridStackEngine();
});
it('do nothing', () => {
e.compact();
});
});
});
//# sourceMappingURL=gridstack-engine-spec.js.map

File diff suppressed because one or more lines are too long

1
node_modules/gridstack/dist/spec/gridstack-spec.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

1780
node_modules/gridstack/dist/spec/gridstack-spec.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,171 @@
import { GridStack } from '../../src/gridstack';
// Integration tests for GridStack HTML scenarios
// These test actual GridStack behavior with DOM manipulation
describe('GridStack Integration Tests', () => {
beforeEach(() => {
// // Clean up DOM before each test
// document.body.innerHTML = '';
// // Add basic CSS for GridStack to function properly
// const style = document.createElement('style');
// style.textContent = `
// .grid-stack { position: relative; }
// .grid-stack-item { position: absolute; }
// .grid-stack-item-content { width: 100%; height: 100%; }
// `;
// document.head.appendChild(style);
});
afterEach(() => {
// // Clean up any GridStack instances
// GridStack.removeAll;
// // Clean up added styles
// const styles = document.head.querySelectorAll('style');
// styles.forEach(style => style.remove());
});
describe('Auto-positioning with no x,y coordinates', () => {
it('should position items in order 5,1,2,4,3 based on their constraints', () => {
// Create the HTML structure from the test file
document.body.innerHTML = `
<div class="grid-stack">
<div class="grid-stack-item upper" gs-w="2" gs-h="2" gs-id="1">
<div class="grid-stack-item-content">item 1</div>
</div>
<div class="grid-stack-item" gs-w="3" gs-h="2" gs-id="2">
<div class="grid-stack-item-content">item 2</div>
</div>
<div class="grid-stack-item" gs-w="9" gs-h="1" gs-id="3">
<div class="grid-stack-item-content">item 3 too big to fit, so next row</div>
</div>
<div class="grid-stack-item" gs-w="3" gs-h="1" gs-id="4">
<div class="grid-stack-item-content">item 4</div>
</div>
<div class="grid-stack-item" gs-x="1" gs-y="1" gs-w="1" gs-h="1" gs-id="5">
<div class="grid-stack-item-content">item 5 first</div>
</div>
</div>
`;
// Initialize GridStack with same options as test
const options = {
cellHeight: 80,
margin: 5,
float: true
};
const grid = GridStack.init(options);
// Get all nodes and their positions
const nodes = grid.engine.nodes;
expect(nodes).toHaveLength(5);
// Item 5 should be positioned (has explicit x=1, y=1 in HTML)
const item5 = nodes.find(n => n.id === '5');
expect(item5).toBeDefined();
expect(item5.w).toBe(1);
expect(item5.h).toBe(1);
// Item 1 should be positioned next (2x2)
const item1 = nodes.find(n => n.id === '1');
expect(item1).toBeDefined();
expect(item1.w).toBe(2);
expect(item1.h).toBe(2);
// Item 2 should be positioned (3x2)
const item2 = nodes.find(n => n.id === '2');
expect(item2).toBeDefined();
expect(item2.w).toBe(3);
expect(item2.h).toBe(2);
// Item 4 should be positioned (3x1)
const item4 = nodes.find(n => n.id === '4');
expect(item4).toBeDefined();
expect(item4.w).toBe(3);
expect(item4.h).toBe(1);
// Item 3 should be on next row (too big to fit - 9x1)
const item3 = nodes.find(n => n.id === '3');
expect(item3).toBeDefined();
expect(item3.w).toBe(9);
expect(item3.h).toBe(1);
// Verify all items are positioned (have valid coordinates)
nodes.forEach(node => {
expect(node.x).toBeGreaterThanOrEqual(0);
expect(node.y).toBeGreaterThanOrEqual(0);
expect(node.w).toBeGreaterThan(0);
expect(node.h).toBeGreaterThan(0);
});
});
});
describe('Grid initialization and basic functionality', () => {
it('should initialize GridStack with items and maintain data integrity', () => {
document.body.innerHTML = `
<div class="grid-stack">
<div class="grid-stack-item" gs-x="0" gs-y="0" gs-w="4" gs-h="2" gs-id="item1">
<div class="grid-stack-item-content">Item 1</div>
</div>
<div class="grid-stack-item" gs-x="4" gs-y="0" gs-w="4" gs-h="4" gs-id="item2">
<div class="grid-stack-item-content">Item 2</div>
</div>
</div>
`;
const grid = GridStack.init();
expect(grid).toBeDefined();
expect(grid.engine.nodes).toHaveLength(2);
const item1 = grid.engine.nodes.find(n => n.id === 'item1');
const item2 = grid.engine.nodes.find(n => n.id === 'item2');
expect(item1).toEqual(expect.objectContaining({
x: 0, y: 0, w: 4, h: 2, id: 'item1'
}));
expect(item2).toEqual(expect.objectContaining({
x: 4, y: 0, w: 4, h: 4, id: 'item2'
}));
});
it('should handle empty grid initialization', () => {
document.body.innerHTML = '<div class="grid-stack"></div>';
const grid = GridStack.init();
expect(grid).toBeDefined();
expect(grid.engine.nodes).toHaveLength(0);
});
it('should add widgets programmatically', () => {
document.body.innerHTML = '<div class="grid-stack"></div>';
const grid = GridStack.init();
const addedEl = grid.addWidget({
x: 0, y: 0, w: 2, h: 2, id: 'new-widget'
});
expect(addedEl).toBeDefined();
expect(grid.engine.nodes).toHaveLength(1);
// Check that the widget was added with valid properties
const node = grid.engine.nodes[0];
expect(node.x).toBe(0);
expect(node.y).toBe(0);
// Note: w and h might default to 1x1 if not explicitly set in the HTML attributes
});
});
describe('Layout and positioning validation', () => {
it('should respect minRow constraints', () => {
document.body.innerHTML = '<div class="grid-stack"></div>';
const grid = GridStack.init({ minRow: 3 });
// Even with no items, grid should maintain minimum rows
expect(grid.getRow()).toBeGreaterThanOrEqual(3);
});
it('should handle widget collision detection', () => {
document.body.innerHTML = `
<div class="grid-stack">
<div class="grid-stack-item" gs-x="0" gs-y="0" gs-w="2" gs-h="2" gs-id="item1">
<div class="grid-stack-item-content">Item 1</div>
</div>
</div>
`;
const grid = GridStack.init();
// Try to add overlapping widget
const widgetEl = grid.addWidget({
x: 1, y: 1, w: 2, h: 2, id: 'overlap'
});
expect(widgetEl).toBeDefined();
expect(grid.engine.nodes).toHaveLength(2);
// Verify that items don't actually overlap (GridStack should handle collision)
// Just verify we have 2 nodes without overlap testing since the API changed
const nodes = grid.engine.nodes;
expect(nodes).toHaveLength(2);
// All nodes should have valid positions
nodes.forEach(node => {
expect(node.x).toBeGreaterThanOrEqual(0);
expect(node.y).toBeGreaterThanOrEqual(0);
expect(node.w).toBeGreaterThan(0);
expect(node.h).toBeGreaterThan(0);
});
});
});
});
//# sourceMappingURL=gridstack-integration.spec.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

100
node_modules/gridstack/dist/spec/regression-spec.js generated vendored Normal file
View File

@@ -0,0 +1,100 @@
import { GridStack } from '../src/gridstack';
describe('regression >', () => {
'use strict';
let grid;
let findEl = function (id) {
return grid.engine.nodes.find(n => n.id === id).el;
};
let findSubEl = function (id, index = 0) {
return grid.engine.nodes[index].subGrid?.engine.nodes.find(n => n.id === id).el;
};
// empty grid
let gridstackEmptyHTML = '<div style="width: 800px; height: 600px" id="gs-cont">' +
' <div class="grid-stack"></div>' +
'</div>';
describe('2492 load() twice >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('', () => {
let items = [
{ x: 0, y: 0, w: 2, content: '0 wide' },
{ x: 1, y: 0, content: '1 over' },
{ x: 2, y: 1, content: '2 float' },
];
let count = 0;
items.forEach(n => n.id = String(count++));
grid = GridStack.init({ cellHeight: 70, margin: 5 }).load(items);
let el0 = findEl('0');
let el1 = findEl('1');
let el2 = findEl('2');
expect(el0.getAttribute('gs-x')).toBe(null);
expect(el0.getAttribute('gs-y')).toBe(null);
expect(el0.children[0].innerHTML).toBe(items[0].content);
expect(parseInt(el1.getAttribute('gs-x'))).toBe(1);
expect(parseInt(el1.getAttribute('gs-y'))).toBe(1);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(2);
expect(el2.getAttribute('gs-y')).toBe(null);
// loading with changed content should be same positions
items.forEach(n => n.content += '*');
grid.load(items);
expect(el0.getAttribute('gs-x')).toBe(null);
expect(el0.getAttribute('gs-y')).toBe(null);
expect(el0.children[0].innerHTML).toBe(items[0].content);
expect(parseInt(el1.getAttribute('gs-x'))).toBe(1);
expect(parseInt(el1.getAttribute('gs-y'))).toBe(1);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(2);
expect(el2.getAttribute('gs-y')).toBe(null);
});
});
describe('2865 nested grid resize >', () => {
beforeEach(() => {
document.body.insertAdjacentHTML('afterbegin', gridstackEmptyHTML);
});
afterEach(() => {
document.body.removeChild(document.getElementById('gs-cont'));
});
it('', () => {
let children = [{}, {}, {}];
let items = [
{ x: 0, y: 0, w: 3, h: 5, sizeToContent: true, subGridOpts: { children, column: 'auto' } }
];
let count = 0;
[...items, ...children].forEach(n => n.id = String(count++));
grid = GridStack.init({ cellHeight: 70, margin: 5, children: items });
let nested = findEl('0');
let el1 = findSubEl('1');
let el2 = findSubEl('2');
let el3 = findSubEl('3');
expect(nested.getAttribute('gs-x')).toBe(null);
expect(nested.getAttribute('gs-y')).toBe(null);
expect(parseInt(nested.getAttribute('gs-w'))).toBe(3);
// TODO: sizeToContent doesn't seem to be called in headless mode ??? works in browser.
// expect(nested.getAttribute('gs-h')).toBe(null); // sizeToContent 5 -> 1 which is null
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(1);
expect(el2.getAttribute('gs-y')).toBe(null);
expect(parseInt(el3.getAttribute('gs-x'))).toBe(2);
expect(el3.getAttribute('gs-y')).toBe(null);
// now resize the nested grid to 2 -> should reflow el3
grid.update(nested, { w: 2 });
expect(nested.getAttribute('gs-x')).toBe(null);
expect(nested.getAttribute('gs-y')).toBe(null);
expect(parseInt(nested.getAttribute('gs-w'))).toBe(2);
// TODO: sizeToContent doesn't seem to be called in headless mode ??? works in browser.
// expect(parseInt(nested.getAttribute('gs-h'))).toBe(2);
expect(el1.getAttribute('gs-x')).toBe(null);
expect(el1.getAttribute('gs-y')).toBe(null);
expect(parseInt(el2.getAttribute('gs-x'))).toBe(1);
expect(el2.getAttribute('gs-y')).toBe(null);
// 3rd item pushed to next row
expect(el3.getAttribute('gs-x')).toBe(null);
expect(parseInt(el3.getAttribute('gs-y'))).toBe(1);
});
});
});
//# sourceMappingURL=regression-spec.js.map

File diff suppressed because one or more lines are too long

1
node_modules/gridstack/dist/spec/utils-spec.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export {};

243
node_modules/gridstack/dist/spec/utils-spec.js generated vendored Normal file
View File

@@ -0,0 +1,243 @@
import { Utils } from '../src/utils';
describe('gridstack utils', () => {
describe('setup of utils', () => {
it('should set gridstack Utils.', () => {
let utils = Utils;
expect(utils).not.toBeNull();
expect(typeof utils).toBe('function');
});
});
describe('test toBool', () => {
it('should return booleans.', () => {
expect(Utils.toBool(true)).toEqual(true);
expect(Utils.toBool(false)).toEqual(false);
});
it('should work with integer.', () => {
expect(Utils.toBool(1)).toEqual(true);
expect(Utils.toBool(0)).toEqual(false);
});
it('should work with Strings.', () => {
expect(Utils.toBool('')).toEqual(false);
expect(Utils.toBool('0')).toEqual(false);
expect(Utils.toBool('no')).toEqual(false);
expect(Utils.toBool('false')).toEqual(false);
expect(Utils.toBool('yes')).toEqual(true);
expect(Utils.toBool('yadda')).toEqual(true);
});
});
describe('test isIntercepted', () => {
let src = { x: 3, y: 2, w: 3, h: 2 };
it('should intercept.', () => {
expect(Utils.isIntercepted(src, { x: 0, y: 0, w: 4, h: 3 })).toEqual(true);
expect(Utils.isIntercepted(src, { x: 0, y: 0, w: 40, h: 30 })).toEqual(true);
expect(Utils.isIntercepted(src, { x: 3, y: 2, w: 3, h: 2 })).toEqual(true);
expect(Utils.isIntercepted(src, { x: 5, y: 3, w: 3, h: 2 })).toEqual(true);
});
it('shouldn\'t intercept.', () => {
expect(Utils.isIntercepted(src, { x: 0, y: 0, w: 3, h: 2 })).toEqual(false);
expect(Utils.isIntercepted(src, { x: 0, y: 0, w: 13, h: 2 })).toEqual(false);
expect(Utils.isIntercepted(src, { x: 1, y: 4, w: 13, h: 2 })).toEqual(false);
expect(Utils.isIntercepted(src, { x: 0, y: 3, w: 3, h: 2 })).toEqual(false);
expect(Utils.isIntercepted(src, { x: 6, y: 3, w: 3, h: 2 })).toEqual(false);
});
});
describe('test parseHeight', () => {
it('should parse height value', () => {
expect(Utils.parseHeight(12)).toEqual(expect.objectContaining({ h: 12, unit: 'px' }));
expect(Utils.parseHeight('12px')).toEqual(expect.objectContaining({ h: 12, unit: 'px' }));
expect(Utils.parseHeight('12.3px')).toEqual(expect.objectContaining({ h: 12.3, unit: 'px' }));
expect(Utils.parseHeight('12.3em')).toEqual(expect.objectContaining({ h: 12.3, unit: 'em' }));
expect(Utils.parseHeight('12.3rem')).toEqual(expect.objectContaining({ h: 12.3, unit: 'rem' }));
expect(Utils.parseHeight('12.3vh')).toEqual(expect.objectContaining({ h: 12.3, unit: 'vh' }));
expect(Utils.parseHeight('12.3vw')).toEqual(expect.objectContaining({ h: 12.3, unit: 'vw' }));
expect(Utils.parseHeight('12.3%')).toEqual(expect.objectContaining({ h: 12.3, unit: '%' }));
expect(Utils.parseHeight('12.5cm')).toEqual(expect.objectContaining({ h: 12.5, unit: 'cm' }));
expect(Utils.parseHeight('12.5mm')).toEqual(expect.objectContaining({ h: 12.5, unit: 'mm' }));
expect(Utils.parseHeight('12.5')).toEqual(expect.objectContaining({ h: 12.5, unit: 'px' }));
expect(() => { Utils.parseHeight('12.5 df'); }).toThrow('Invalid height val = 12.5 df');
});
it('should parse negative height value', () => {
expect(Utils.parseHeight(-12)).toEqual(expect.objectContaining({ h: -12, unit: 'px' }));
expect(Utils.parseHeight('-12px')).toEqual(expect.objectContaining({ h: -12, unit: 'px' }));
expect(Utils.parseHeight('-12.3px')).toEqual(expect.objectContaining({ h: -12.3, unit: 'px' }));
expect(Utils.parseHeight('-12.3em')).toEqual(expect.objectContaining({ h: -12.3, unit: 'em' }));
expect(Utils.parseHeight('-12.3rem')).toEqual(expect.objectContaining({ h: -12.3, unit: 'rem' }));
expect(Utils.parseHeight('-12.3vh')).toEqual(expect.objectContaining({ h: -12.3, unit: 'vh' }));
expect(Utils.parseHeight('-12.3vw')).toEqual(expect.objectContaining({ h: -12.3, unit: 'vw' }));
expect(Utils.parseHeight('-12.3%')).toEqual(expect.objectContaining({ h: -12.3, unit: '%' }));
expect(Utils.parseHeight('-12.3cm')).toEqual(expect.objectContaining({ h: -12.3, unit: 'cm' }));
expect(Utils.parseHeight('-12.3mm')).toEqual(expect.objectContaining({ h: -12.3, unit: 'mm' }));
expect(Utils.parseHeight('-12.5')).toEqual(expect.objectContaining({ h: -12.5, unit: 'px' }));
expect(() => { Utils.parseHeight('-12.5 df'); }).toThrow('Invalid height val = -12.5 df');
});
});
describe('test defaults', () => {
it('should assign missing field or undefined', () => {
let src = {};
expect(src).toEqual({});
expect(Utils.defaults(src, { x: 1, y: 2 })).toEqual({ x: 1, y: 2 });
expect(Utils.defaults(src, { x: 10 })).toEqual({ x: 1, y: 2 });
src.w = undefined;
expect(src).toEqual({ x: 1, y: 2, w: undefined });
expect(Utils.defaults(src, { x: 10, w: 3 })).toEqual({ x: 1, y: 2, w: 3 });
expect(Utils.defaults(src, { h: undefined })).toEqual({ x: 1, y: 2, w: 3, h: undefined });
src = { x: 1, y: 2, sub: { foo: 1, two: 2 } };
expect(src).toEqual({ x: 1, y: 2, sub: { foo: 1, two: 2 } });
expect(Utils.defaults(src, { x: 10, w: 3 })).toEqual({ x: 1, y: 2, w: 3, sub: { foo: 1, two: 2 } });
expect(Utils.defaults(src, { sub: { three: 3 } })).toEqual({ x: 1, y: 2, w: 3, sub: { foo: 1, two: 2, three: 3 } });
});
});
describe('removePositioningStyles', () => {
it('should remove styles', () => {
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = '<div style="position: absolute; left: 1; top: 2; w: 3; h: 4"></div>';
let el = doc.body.children[0];
expect(el.style.position).toEqual('absolute');
// expect(el.style.left).toEqual('1'); // not working!
Utils.removePositioningStyles(el);
expect(el.style.position).toEqual('');
// bogus test
expect(Utils.getScrollElement(el)).not.toBe(null);
// bogus test
Utils.updateScrollPosition(el, { top: 20 }, 10);
});
});
describe('clone', () => {
const a = { first: 1, second: 'text' };
const b = { first: 1, second: { third: 3 } };
const c = { first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } };
it('Should have the same values', () => {
const z = Utils.clone(a);
expect(z).toEqual({ first: 1, second: 'text' });
});
it('Should have 2 in first key, and original unchanged', () => {
const z = Utils.clone(a);
z.first = 2;
expect(a).toEqual({ first: 1, second: 'text' });
expect(z).toEqual({ first: 2, second: 'text' });
});
it('Should have new string in second key, and original unchanged', () => {
const z = Utils.clone(a);
z.second = 2;
expect(a).toEqual({ first: 1, second: 'text' });
expect(z).toEqual({ first: 1, second: 2 });
});
it('new string in both cases - use cloneDeep instead', () => {
const z = Utils.clone(b);
z.second.third = 'share';
expect(b).toEqual({ first: 1, second: { third: 'share' } });
expect(z).toEqual({ first: 1, second: { third: 'share' } });
});
it('Array Should match', () => {
const z = Utils.clone(c);
expect(c).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
expect(z).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
});
it('Array[0] changed in both cases - use cloneDeep instead', () => {
const z = Utils.clone(c);
z.second[0] = 0;
expect(c).toEqual({ first: 1, second: [0, 2, 3], third: { fourth: { fifth: 5 } } });
expect(z).toEqual({ first: 1, second: [0, 2, 3], third: { fourth: { fifth: 5 } } });
});
it('fifth changed in both cases - use cloneDeep instead', () => {
const z = Utils.clone(c);
z.third.fourth.fifth = 'share';
expect(c).toEqual({ first: 1, second: [0, 2, 3], third: { fourth: { fifth: 'share' } } });
expect(z).toEqual({ first: 1, second: [0, 2, 3], third: { fourth: { fifth: 'share' } } });
});
});
describe('cloneDeep', () => {
// reset our test cases
const a = { first: 1, second: 'text' };
const b = { first: 1, second: { third: 3 } };
const c = { first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } };
const d = { first: [1, [2, 3], ['four', 'five', 'six']] };
const e = { first: 1, __skip: { second: 2 } };
const f = { first: 1, _dontskip: { second: 2 } };
it('Should have the same values', () => {
const z = Utils.cloneDeep(a);
expect(z).toEqual({ first: 1, second: 'text' });
});
it('Should have 2 in first key, and original unchanged', () => {
const z = Utils.cloneDeep(a);
z.first = 2;
expect(a).toEqual({ first: 1, second: 'text' });
expect(z).toEqual({ first: 2, second: 'text' });
});
it('Should have new string in second key, and original unchanged', () => {
const z = Utils.cloneDeep(a);
z.second = 2;
expect(a).toEqual({ first: 1, second: 'text' });
expect(z).toEqual({ first: 1, second: 2 });
});
it('Should have new string nested object, and original unchanged', () => {
const z = Utils.cloneDeep(b);
z.second.third = 'diff';
expect(b).toEqual({ first: 1, second: { third: 3 } });
expect(z).toEqual({ first: 1, second: { third: 'diff' } });
});
it('Array Should match', () => {
const z = Utils.cloneDeep(c);
expect(c).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
expect(z).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
});
it('Array[0] changed in z only', () => {
const z = Utils.cloneDeep(c);
z.second[0] = 0;
expect(c).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
expect(z).toEqual({ first: 1, second: [0, 2, 3], third: { fourth: { fifth: 5 } } });
});
it('nested firth element changed only in z', () => {
const z = Utils.cloneDeep(c);
z.third.fourth.fifth = 'diff';
expect(c).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 5 } } });
expect(z).toEqual({ first: 1, second: [1, 2, 3], third: { fourth: { fifth: 'diff' } } });
});
it('nested array only has one item changed', () => {
const z = Utils.cloneDeep(d);
z.first[1] = 'two';
z.first[2][2] = 6;
expect(d).toEqual({ first: [1, [2, 3], ['four', 'five', 'six']] });
expect(z).toEqual({ first: [1, 'two', ['four', 'five', 6]] });
});
it('skip __ items so it mods both instance', () => {
const z = Utils.cloneDeep(e);
z.__skip.second = 'two';
expect(e).toEqual({ first: 1, __skip: { second: 'two' } }); // TODO support clone deep of function workaround
expect(z).toEqual({ first: 1, __skip: { second: 'two' } });
});
it('correctly copy _ item', () => {
const z = Utils.cloneDeep(f);
z._dontskip.second = 'two';
expect(f).toEqual({ first: 1, _dontskip: { second: 2 } });
expect(z).toEqual({ first: 1, _dontskip: { second: 'two' } });
});
});
describe('removeInternalAndSame', () => {
it('should remove internal and same', () => {
const a = { first: 1, second: 'text', _skip: { second: 2 }, arr: [1, 'second', 3] };
const b = { first: 1, second: 'text' };
Utils.removeInternalAndSame(a, b);
expect(a).toEqual({ arr: [1, 'second', 3] });
});
it('should not remove items in an array', () => {
const a = { arr: [1, 2, 3] };
const b = { arr: [1, 3] };
Utils.removeInternalAndSame(a, b);
expect(a).toEqual({ arr: [1, 2, 3] });
});
it('should remove nested object, and make empty', () => {
const a = { obj1: { first: 1, nested: { second: 2 } }, obj2: { first: 1, second: 2 } };
const b = { obj1: { first: 1, nested: { second: 2 } }, obj2: { first: 1, second: 2 } };
Utils.removeInternalAndSame(a, b);
expect(a).toEqual({});
});
it('should remove nested object, and make empty - part 2', () => {
const a = { obj1: { first: 1, nested: { second: 2 } }, obj2: { first: 1, second: 2 } };
const b = { obj1: { first: 1 }, obj2: { first: 1, second: 2 } };
Utils.removeInternalAndSame(a, b);
expect(a).toEqual({ obj1: { nested: { second: 2 } } });
});
});
});
//# sourceMappingURL=utils-spec.js.map

1
node_modules/gridstack/dist/spec/utils-spec.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long