Skip to content

Commit 709e06e

Browse files
authored
[OGUI-1806] Fix out of bounds object not rendering until edit mode is entered once (#3267)
- Fixed out of bounds layout object not rendering on refresh - Fixed out of bounds layout object not rendering when reducing the total columns using the JSON edit menu - Fixed ending up on a tab that no longer exists when deleting tabs through the JSON edit menu.
1 parent 4c90f35 commit 709e06e

3 files changed

Lines changed: 132 additions & 12 deletions

File tree

QualityControl/public/layout/Layout.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ export default class Layout extends BaseViewModel {
261261
const result = await this.model.services.layout.saveLayout(this.item);
262262
if (result.isSuccess()) {
263263
await this.model.services.layout.getLayoutsByUserId(this.model.session.personid);
264+
this._tabIndex = this._tabIndex < this.item.tabs.length ? this._tabIndex : 0;
265+
this.selectTab(this._tabIndex);
264266
this.model.notification.show(`Layout "${this.item.name}" has been saved successfully.`, 'success');
265267
} else {
266268
this.item = this.editOriginalClone;
@@ -287,9 +289,7 @@ export default class Layout extends BaseViewModel {
287289
this.gridListSize = parseInt(value, 10);
288290
this.cellHeight = 100 / this.gridListSize * 0.95; // %, put some margin at bottom to see below
289291
this.cellWidth = 100 / this.gridListSize; // %
290-
if (this.editEnabled) {
291-
this.gridList.resizeGrid(this.gridListSize);
292-
}
292+
this.gridList.resizeGrid(this.gridListSize);
293293
this.tab.columns = this.gridListSize;
294294
this.tab.objects.forEach((object) => {
295295
if (object.w > this.tab.columns) {
@@ -306,9 +306,7 @@ export default class Layout extends BaseViewModel {
306306
*/
307307
sortObjectsOfCurrentTab() {
308308
this.gridList.items = this.tab.objects;
309-
if (this.editEnabled) {
310-
this.gridList.resizeGrid(this.gridListSize);
311-
}
309+
this.gridList.resizeGrid(this.gridListSize);
312310
}
313311

314312
/**

QualityControl/test/public/pages/layout-show.test.js

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
import { strictEqual, ok, deepStrictEqual } from 'node:assert';
1515
import { delay } from '../../testUtils/delay.js';
16-
import { editedMockedLayout } from '../../setup/seeders/layout-show/json-file-mock.js';
16+
import {
17+
editedManyObjectsMockedLayout,
18+
editedMockedLayout,
19+
} from '../../setup/seeders/layout-show/json-file-mock.js';
1720
import { getElementCenter } from '../../testUtils/dragAndDrop.js';
1821

1922
/**
@@ -187,7 +190,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) =>
187190
const leftStyle = await page.evaluate(() => document.querySelector('#subcanvas .dropdown-menu').style.left);
188191

189192
strictEqual(leftStyle, '0.1em');
190-
}
193+
},
191194
);
192195

193196
await testParent.test('should have second tab to be empty (according to demo data)', { timeout }, async () => {
@@ -349,7 +352,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) =>
349352
elements.map((element) => element.textContent.trim()));
350353

351354
strictEqual(tabNames[1], originalTabNames[0]);
352-
}
355+
},
353356
);
354357

355358
await testParent.test(
@@ -446,9 +449,69 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) =>
446449
},
447450
);
448451

452+
await testParent.test(
453+
'should display all objects even when the JSON has out of bound entries',
454+
{ timeout: 15000 },
455+
async () => {
456+
const EXPECTED_LAYOUT_OBJECT_COUNT = editedManyObjectsMockedLayout.tabs[0].objects.length;
457+
458+
/**
459+
* Counts the number of visible child elements within #subcanvas.
460+
* Visibility is defined as being within the viewport.
461+
* @returns {number} The amount of visible rendered objects
462+
*/
463+
const getVisibleObjectCount = async () =>
464+
await page.evaluate(() => {
465+
const container = document.querySelector('#subcanvas');
466+
if (!container) {
467+
return 0;
468+
}
469+
470+
// Count elements that intersect with the viewport
471+
return Array.from(container.children).filter((child) => {
472+
const rect = child.getBoundingClientRect();
473+
return rect.bottom >= 0 && rect.top >= 0 && rect.left >= 0 && rect.right <= window.innerWidth;
474+
}).length;
475+
});
476+
477+
// Ensure we are on the main tab
478+
await page.locator('#tab-0').click();
479+
await delay(100);
480+
481+
const pencilButtonPath = '.btn-group > div > button';
482+
await page.locator(pencilButtonPath).click();
483+
484+
const editViaJSONButtonPath = '#editByJson';
485+
await page.locator(editViaJSONButtonPath).click();
486+
487+
const textareaPath = '#layout-json-editor';
488+
const mockedJSON = JSON.stringify(editedManyObjectsMockedLayout);
489+
await page.locator(textareaPath).fill(mockedJSON);
490+
491+
const updateButtonPath = '#updateLayoutButton';
492+
await page.locator(updateButtonPath).click();
493+
await delay(100);
494+
495+
strictEqual(
496+
await getVisibleObjectCount(),
497+
EXPECTED_LAYOUT_OBJECT_COUNT,
498+
`Expected ${EXPECTED_LAYOUT_OBJECT_COUNT} rendered objects after JSON update`,
499+
);
500+
501+
await page.reload({ waitUntil: 'networkidle0' });
502+
await delay(100);
503+
504+
strictEqual(
505+
await getVisibleObjectCount(),
506+
EXPECTED_LAYOUT_OBJECT_COUNT,
507+
`Expected ${EXPECTED_LAYOUT_OBJECT_COUNT} rendered objects after page reload`,
508+
);
509+
},
510+
);
511+
449512
await testParent.test(
450513
'should update layout when clicking "Update layout"',
451-
{ timeout },
514+
{ timeout: 10000 },
452515
async () => {
453516
const pencilButtonPath = '.btn-group > div > button';
454517
await page.locator(pencilButtonPath).click();
@@ -474,16 +537,20 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) =>
474537
);
475538

476539
await testParent.test('should change tab after set tabInterval', { timeout: 15000 }, async () => {
540+
// Ensure we are on the 'a' tab
541+
await page.locator('#tab-1').click();
542+
await delay(100);
543+
477544
const location = await page.evaluate(() => window.location);
478545
strictEqual(location.search, `?page=layoutShow&layoutId=${LAYOUT_ID}&tab=a`);
479-
await delay(11000);
546+
await delay(11111);
480547
const location2 = await page.evaluate(() => window.location);
481548
strictEqual(location2.search, `?page=layoutShow&layoutId=${LAYOUT_ID}&tab=test`);
482549
});
483550

484551
await testParent.test(
485552
'should update layout name in sidebar when name is changed and saved via JSON editor',
486-
{ timeout },
553+
{ timeout: 10000 },
487554
async () => {
488555
const originalSidebarName = await page.evaluate(() => {
489556
const sidebarLayoutLink = document.querySelector('nav a.menu-item.w-wrapped.selected span:nth-child(2)');

QualityControl/test/setup/seeders/layout-show/json-file-mock.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,58 @@ export const editedMockedLayout = {
3030
displayTimestamp: false,
3131
autoTabChange: 11,
3232
};
33+
34+
export const editedManyObjectsMockedLayout = {
35+
name: 'a-test',
36+
tabs: [
37+
{
38+
name: 'main',
39+
objects: [
40+
{
41+
x: 0,
42+
y: 0,
43+
h: 1,
44+
w: 1,
45+
name: 'qc/test/object/1',
46+
options: [],
47+
ignoreDefaults: false,
48+
},
49+
{
50+
x: 1,
51+
y: 0,
52+
h: 1,
53+
w: 1,
54+
name: 'qc/test/object/1',
55+
options: [],
56+
ignoreDefaults: false,
57+
},
58+
{
59+
x: 2,
60+
y: 0,
61+
h: 1,
62+
w: 1,
63+
name: 'qc/test/object/1',
64+
options: [],
65+
ignoreDefaults: false,
66+
},
67+
{
68+
x: 0,
69+
y: 1,
70+
h: 1,
71+
w: 1,
72+
name: 'qc/test/object/1',
73+
options: [],
74+
ignoreDefaults: false,
75+
},
76+
],
77+
columns: 1,
78+
},
79+
{
80+
name: 'a',
81+
objects: [],
82+
columns: 1,
83+
},
84+
],
85+
displayTimestamp: false,
86+
autoTabChange: 11,
87+
};

0 commit comments

Comments
 (0)