From 91e6937ed2e9743ec8014bdb4800c571b979c694 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 10:43:28 +0100 Subject: [PATCH 01/24] feat: add dropzones and event listeners for the tabs in edit --- QualityControl/public/layout/view/header.js | 54 ++++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index b254de5b0..cb543d82d 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -144,14 +144,52 @@ const toolbarEditModeTab = (layout, tab, i) => { const selectTab = () => layout.selectTab(i); return [ - h('.btn-group.flex-fixed', [ - h('button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', { class: linkClass, onclick: selectTab }, tab.name), - selected && [ - editTabButton(layout, linkClass, tab, i), - resizeGridTabDropDown(layout, tab), - deleteTabButton(layout, linkClass, i), - ], - ]), + h( + '.btn-group.flex-fixed', + { + style: 'position: relative;', + draggable: true, + ondragstart: (e) => { + e.dataTransfer.setData('text/plain', tab.id) + console.log(e.dataTransfer.getData('text/plain')) + console.log('dragstart', e) + }, + ondrop: (e) => { + console.log('ondrop', e) + console.log(e.dataTransfer.getData('text/plain')) + } + }, + [ + h('button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', { class: linkClass, onclick: selectTab }, tab.name), + [ + h( + '.before', + { + style: 'position: absolute; left: 0; width: 50%; height: 100%; background-color: rgb(30 80 120 / 25%);', + ondragenter: (e) => console.log('enter before', e), + ondragover: (e) => e.preventDefault(), // prevent default to allow drop + ondragleave: (e) => console.log('leave before', e) + }, + '' + ), + h( + '.after', + { + style: 'position: absolute; right: 0; width: 50%; height: 100%; background-color: rgb(80 20 70 / 25%);', + ondragenter: (e) => console.log('enter after', e), + ondragover: (e) => e.preventDefault(), // prevent default to allow drop + ondragleave: (e) => console.log('leave after', e) + }, + '' + ), + selected && [ + editTabButton(layout, linkClass, tab, i), + resizeGridTabDropDown(layout, tab), + deleteTabButton(layout, linkClass, i), + ], + ].flat().filter(Boolean) + ] + ), ' ', ]; }; From f86ae856a08d27e65f512eafb1e3fc46f94207ad Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:01:30 +0100 Subject: [PATCH 02/24] feat: add ability to reorder the tabs using drag and drop --- QualityControl/public/layout/Layout.js | 50 +++++++++++++++++++++ QualityControl/public/layout/view/header.js | 13 +++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index 58740064f..774ae0249 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -61,6 +61,9 @@ export default class Layout extends BaseViewModel { }); this.cellHeight = 100 / this.gridListSize * 0.95; // %, put some margin at bottom to see below this.cellWidth = 100 / this.gridListSize; // % + + this.dropTargetId = undefined; + this.position = undefined; } /** @@ -777,4 +780,51 @@ export default class Layout extends BaseViewModel { ownsLayout(layoutOwnerId) { return this.model.session.personid == layoutOwnerId; } + + /** + * TODO + * @param {*} tabId TODO + * @param {*} position TODO + */ + setDropTarget(tabId, position) { + this.dropTargetId = tabId; + this.position = position; + } + + /** + * TODO + */ + clearDropTarget() { + this.dropTargetId = undefined + this.position = undefined + } + + /** + * TODO + * @param {*} sourceId TODO + * @param {*} targetId TODO + * @param {*} position TODO + */ + reorderTabs(sourceId, targetId, position) { + const sourceIndex = this.item.tabs.findIndex((t) => t.id === sourceId); + let targetIndex = this.item.tabs.findIndex((t) => t.id === targetId); + + if (sourceIndex === -1 || targetIndex === -1) { + return; + } + + if (position === 'after') { + targetIndex += 1; + } + + const [movedTab] = this.item.tabs.splice(sourceIndex, 1); + + if (sourceIndex < targetIndex) { + targetIndex--; + } + + this.item.tabs.splice(targetIndex, 0, movedTab); + + this.notify(); + } } diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index cb543d82d..693f493b3 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -151,12 +151,9 @@ const toolbarEditModeTab = (layout, tab, i) => { draggable: true, ondragstart: (e) => { e.dataTransfer.setData('text/plain', tab.id) - console.log(e.dataTransfer.getData('text/plain')) - console.log('dragstart', e) }, ondrop: (e) => { - console.log('ondrop', e) - console.log(e.dataTransfer.getData('text/plain')) + layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position) } }, [ @@ -166,9 +163,9 @@ const toolbarEditModeTab = (layout, tab, i) => { '.before', { style: 'position: absolute; left: 0; width: 50%; height: 100%; background-color: rgb(30 80 120 / 25%);', - ondragenter: (e) => console.log('enter before', e), + ondragenter: () => layout.setDropTarget(tab.id, 'before'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop - ondragleave: (e) => console.log('leave before', e) + ondragleave: () => layout.clearDropTarget() }, '' ), @@ -176,9 +173,9 @@ const toolbarEditModeTab = (layout, tab, i) => { '.after', { style: 'position: absolute; right: 0; width: 50%; height: 100%; background-color: rgb(80 20 70 / 25%);', - ondragenter: (e) => console.log('enter after', e), + ondragenter: () => layout.setDropTarget(tab.id, 'after'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop - ondragleave: (e) => console.log('leave after', e) + ondragleave: () => layout.clearDropTarget() }, '' ), From ec3396f24191887910e5cf51eb23e9c0589c6d76 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:30:53 +0100 Subject: [PATCH 03/24] feat: add class styling for drop target --- QualityControl/public/app.css | 22 +++++++++++++++++ QualityControl/public/layout/Layout.js | 4 +++ QualityControl/public/layout/view/header.js | 27 +++++++++++++++------ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index 956128933..3d88635e3 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -187,3 +187,25 @@ .whitespace-nowrap { white-space: nowrap; } + +.drop-zone { + position: absolute; + height: 100%; + width: 50%; + + &.before { + left: 0; + + &.active { + border-left: 2px solid blue; + } + } + + &.after { + right: 0; + + &.active { + border-right: 2px solid blue; + } + } +} diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index 774ae0249..e47e3641c 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -789,6 +789,8 @@ export default class Layout extends BaseViewModel { setDropTarget(tabId, position) { this.dropTargetId = tabId; this.position = position; + + this.notify(); } /** @@ -797,6 +799,8 @@ export default class Layout extends BaseViewModel { clearDropTarget() { this.dropTargetId = undefined this.position = undefined + + this.notify(); } /** diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 693f493b3..79f62f60b 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -143,6 +143,8 @@ const toolbarEditModeTab = (layout, tab, i) => { */ const selectTab = () => layout.selectTab(i); + const dropZoneClass = (position) => layout.dropTargetId === tab.id && layout.position === position ? 'active' : '' + return [ h( '.btn-group.flex-fixed', @@ -150,32 +152,41 @@ const toolbarEditModeTab = (layout, tab, i) => { style: 'position: relative;', draggable: true, ondragstart: (e) => { - e.dataTransfer.setData('text/plain', tab.id) + e.dataTransfer.setData('text/plain', tab.id); }, ondrop: (e) => { - layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position) + layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position); + layout.clearDropTarget(); } }, [ h('button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', { class: linkClass, onclick: selectTab }, tab.name), [ h( - '.before', + '.drop-zone.before', { - style: 'position: absolute; left: 0; width: 50%; height: 100%; background-color: rgb(30 80 120 / 25%);', + class: dropZoneClass('before'), ondragenter: () => layout.setDropTarget(tab.id, 'before'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop - ondragleave: () => layout.clearDropTarget() + ondragleave: () => { + if (layout.dropTargetId === tab.id && layout.position === 'before') { + layout.clearDropTarget(); + } + } }, '' ), h( - '.after', + '.drop-zone.after', { - style: 'position: absolute; right: 0; width: 50%; height: 100%; background-color: rgb(80 20 70 / 25%);', + class: dropZoneClass('after'), ondragenter: () => layout.setDropTarget(tab.id, 'after'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop - ondragleave: () => layout.clearDropTarget() + ondragleave: () => { + if (layout.dropTargetId === tab.id && layout.position === 'after') { + layout.clearDropTarget(); + } + } }, '' ), From 9b93d7af01b638fad16b8c89b430b5f37c5cad21 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:55:20 +0100 Subject: [PATCH 04/24] feat: add the primary color for the drop borders --- QualityControl/public/app.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index 3d88635e3..a574b3117 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -197,7 +197,7 @@ left: 0; &.active { - border-left: 2px solid blue; + border-left: 2px solid var(--color-primary); } } @@ -205,7 +205,7 @@ right: 0; &.active { - border-right: 2px solid blue; + border-right: 2px solid var(--color-primary); } } } From cbeeb8e91043f164586b14d9c00caa07fe9c77db Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:56:25 +0100 Subject: [PATCH 05/24] style: cleanup inline style --- QualityControl/public/layout/view/header.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 79f62f60b..23a07617e 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -147,13 +147,10 @@ const toolbarEditModeTab = (layout, tab, i) => { return [ h( - '.btn-group.flex-fixed', + '.btn-group.flex-fixed.relative', { - style: 'position: relative;', draggable: true, - ondragstart: (e) => { - e.dataTransfer.setData('text/plain', tab.id); - }, + ondragstart: (e) => e.dataTransfer.setData('text/plain', tab.id), ondrop: (e) => { layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position); layout.clearDropTarget(); From 4eb8259c4d90d8192a2f77f37303c2fa314cad17 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:56:46 +0100 Subject: [PATCH 06/24] docs: add documentation to the functions --- QualityControl/public/layout/Layout.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index e47e3641c..1acb507dd 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -782,9 +782,11 @@ export default class Layout extends BaseViewModel { } /** - * TODO - * @param {*} tabId TODO - * @param {*} position TODO + * Sets the current drop target for a drag-and-drop operation. + * This is typically used to render a visual indicator (like a blue line) + * in the UI showing where the dragged tab will be placed. + * @param {string|number} tabId - The ID of the tab currently being hovered over. + * @param {'before'|'after'} position - The side of the target tab where the drop indicator should appear. */ setDropTarget(tabId, position) { this.dropTargetId = tabId; @@ -794,7 +796,9 @@ export default class Layout extends BaseViewModel { } /** - * TODO + * Clears the current drop target state, usually when the drag operation + * is finished or the dragged item is no longer over a valid drop zone. + * This action typically causes the visual drop indicator to be hidden. */ clearDropTarget() { this.dropTargetId = undefined @@ -804,10 +808,12 @@ export default class Layout extends BaseViewModel { } /** - * TODO - * @param {*} sourceId TODO - * @param {*} targetId TODO - * @param {*} position TODO + * Reorders the tabs in the internal array based on the drag source and drop target. + * This function calculates the correct index for insertion, accounting for the + * tab being removed from its original position. + * @param {string|number} sourceId - The ID of the tab that was dragged. + * @param {string|number} targetId - The ID of the tab that the source was dropped onto. + * @param {'before'|'after'} position - The placement relative to the target tab. */ reorderTabs(sourceId, targetId, position) { const sourceIndex = this.item.tabs.findIndex((t) => t.id === sourceId); From 8688ac1ae994aa92ec1b90f3277be389a936187e Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 11:59:11 +0100 Subject: [PATCH 07/24] style: fix linting errors --- QualityControl/public/layout/Layout.js | 4 ++-- QualityControl/public/layout/view/header.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index 1acb507dd..c8aa9194b 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -801,8 +801,8 @@ export default class Layout extends BaseViewModel { * This action typically causes the visual drop indicator to be hidden. */ clearDropTarget() { - this.dropTargetId = undefined - this.position = undefined + this.dropTargetId = undefined; + this.position = undefined; this.notify(); } diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 23a07617e..85bf9d726 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -143,7 +143,7 @@ const toolbarEditModeTab = (layout, tab, i) => { */ const selectTab = () => layout.selectTab(i); - const dropZoneClass = (position) => layout.dropTargetId === tab.id && layout.position === position ? 'active' : '' + const dropZoneClass = (position) => layout.dropTargetId === tab.id && layout.position === position ? 'active' : ''; return [ h( @@ -154,7 +154,7 @@ const toolbarEditModeTab = (layout, tab, i) => { ondrop: (e) => { layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position); layout.clearDropTarget(); - } + }, }, [ h('button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', { class: linkClass, onclick: selectTab }, tab.name), @@ -169,9 +169,9 @@ const toolbarEditModeTab = (layout, tab, i) => { if (layout.dropTargetId === tab.id && layout.position === 'before') { layout.clearDropTarget(); } - } + }, }, - '' + '', ), h( '.drop-zone.after', @@ -183,17 +183,17 @@ const toolbarEditModeTab = (layout, tab, i) => { if (layout.dropTargetId === tab.id && layout.position === 'after') { layout.clearDropTarget(); } - } + }, }, - '' + '', ), selected && [ editTabButton(layout, linkClass, tab, i), resizeGridTabDropDown(layout, tab), deleteTabButton(layout, linkClass, i), ], - ].flat().filter(Boolean) - ] + ].flat().filter(Boolean), + ], ), ' ', ]; From b9be99cd47541b8fb20e72fbe347e9100a0f3edf Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Tue, 16 Dec 2025 12:48:24 +0100 Subject: [PATCH 08/24] fix: pointer events blocking clicking on the tab buttons --- QualityControl/public/app.css | 5 +++++ QualityControl/public/layout/Layout.js | 22 +++++++++++++++++++++ QualityControl/public/layout/view/header.js | 11 ++++++++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index a574b3117..8377a33b5 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -192,6 +192,7 @@ position: absolute; height: 100%; width: 50%; + pointer-events: none; &.before { left: 0; @@ -209,3 +210,7 @@ } } } + +.pointer-events-auto { + pointer-events: auto; +} diff --git a/QualityControl/public/layout/Layout.js b/QualityControl/public/layout/Layout.js index c8aa9194b..81c805581 100644 --- a/QualityControl/public/layout/Layout.js +++ b/QualityControl/public/layout/Layout.js @@ -62,6 +62,7 @@ export default class Layout extends BaseViewModel { this.cellHeight = 100 / this.gridListSize * 0.95; // %, put some margin at bottom to see below this.cellWidth = 100 / this.gridListSize; // % + this.isDragging = false; this.dropTargetId = undefined; this.position = undefined; } @@ -837,4 +838,25 @@ export default class Layout extends BaseViewModel { this.notify(); } + + /** + * Sets the layout state to indicate that a tab drag-and-drop operation has begun. + * It typically triggers a redraw and enables pointer events on all drop zones via CSS. + */ + startDragging() { + this.isDragging = true; + + this.notify(); + } + + /** + * Resets the layout state to indicate that a tab drag-and-drop operation has ended, + * regardless of whether the drop was successful or cancelled. + * It typically triggers a redraw and disables pointer events on the drop zones via CSS. + */ + stopDragging() { + this.isDragging = false; + + this.notify(); + } } diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 85bf9d726..27aa82c5c 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -143,6 +143,7 @@ const toolbarEditModeTab = (layout, tab, i) => { */ const selectTab = () => layout.selectTab(i); + const dragActiveClass = layout.isDragging ? 'pointer-events-auto' : ''; const dropZoneClass = (position) => layout.dropTargetId === tab.id && layout.position === position ? 'active' : ''; return [ @@ -150,10 +151,14 @@ const toolbarEditModeTab = (layout, tab, i) => { '.btn-group.flex-fixed.relative', { draggable: true, - ondragstart: (e) => e.dataTransfer.setData('text/plain', tab.id), + ondragstart: (e) => { + e.dataTransfer.setData('text/plain', tab.id); + layout.startDragging(); + }, ondrop: (e) => { layout.reorderTabs(e.dataTransfer.getData('text/plain'), layout.dropTargetId, layout.position); layout.clearDropTarget(); + layout.stopDragging(); }, }, [ @@ -162,7 +167,7 @@ const toolbarEditModeTab = (layout, tab, i) => { h( '.drop-zone.before', { - class: dropZoneClass('before'), + class: `${dragActiveClass} ${dropZoneClass('before')}`, ondragenter: () => layout.setDropTarget(tab.id, 'before'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop ondragleave: () => { @@ -176,7 +181,7 @@ const toolbarEditModeTab = (layout, tab, i) => { h( '.drop-zone.after', { - class: dropZoneClass('after'), + class: `${dragActiveClass} ${dropZoneClass('after')}`, ondragenter: () => layout.setDropTarget(tab.id, 'after'), ondragover: (e) => e.preventDefault(), // prevent default to allow drop ondragleave: () => { From 268fc2876bb8d4a7baa7cb60ef8482a20f04263c Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 09:20:56 +0100 Subject: [PATCH 09/24] feat: add grab cursor when hovering tabs in edit mode --- QualityControl/public/app.css | 6 +++--- QualityControl/public/layout/view/header.js | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index 8377a33b5..ca6379603 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -152,9 +152,9 @@ } } -.cursor-pointer { - cursor: pointer; -} +.cursor-pointer { cursor: pointer; } +.cursor-grab { cursor: grab; } +.cursor-inherit { cursor: inherit; } .header-layout { &.edit { diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 27aa82c5c..17f5e3bc8 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -148,7 +148,7 @@ const toolbarEditModeTab = (layout, tab, i) => { return [ h( - '.btn-group.flex-fixed.relative', + '.btn-group.flex-fixed.relative.cursor-grab', { draggable: true, ondragstart: (e) => { @@ -162,7 +162,11 @@ const toolbarEditModeTab = (layout, tab, i) => { }, }, [ - h('button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', { class: linkClass, onclick: selectTab }, tab.name), + h( + 'button.br-pill.ph2.btn.btn-tab.whitespace-nowrap', + { id: 'btn-tab', class: `${linkClass} cursor-inherit`, onclick: selectTab }, + tab.name, + ), [ h( '.drop-zone.before', From 6650f8d0faaffd9467feecfeaa798a92a1968fa3 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 09:21:11 +0100 Subject: [PATCH 10/24] test: add test for drag and drop reordering of tabs in edit mode --- .../test/public/pages/layout-show.test.js | 29 +++++++++++++++++++ QualityControl/test/testUtils/dragAndDrop.js | 28 ++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 QualityControl/test/testUtils/dragAndDrop.js diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 1d96724a3..3803c6a09 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -14,6 +14,7 @@ import { strictEqual, ok, deepStrictEqual } from 'node:assert'; import { delay } from '../../testUtils/delay.js'; import { editedMockedLayout } from '../../setup/seeders/layout-show/json-file-mock.js'; +import { getElementCenter } from '../../testUtils/dragAndDrop.js'; /** * Performs a series of automated tests on the layoutShow page using Puppeteer. @@ -297,6 +298,34 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => }, ); + await testParent.test( + 'should reorder tabs via drag and drop in edit mode', + { timeout }, + async () => { + const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1)'; + const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) .drop-zone.after'; + + const sourceCenter = await getElementCenter(page, sourceTabSelector); + const targetCenter = await getElementCenter(page, targetZoneSelector); + + await page.mouse.move(sourceCenter.x, sourceCenter.y); + await page.mouse.down(); + + // We add 'steps' to make the move smoother, which helps trigger event + await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); + + // Wait a moment for the 'active' class to appear in the UI + await page.waitForSelector('.drop-zone.after.active'); + + await page.mouse.up(); + + const tabNames = await page.$$eval('#btn-tab', (elements) => + elements.map((element) => element.textContent.trim())); + + strictEqual(tabNames[1], 'main'); + } + ); + await testParent.test( 'should show normal sidebar after Cancel click', { timeout }, diff --git a/QualityControl/test/testUtils/dragAndDrop.js b/QualityControl/test/testUtils/dragAndDrop.js new file mode 100644 index 000000000..786c35c7d --- /dev/null +++ b/QualityControl/test/testUtils/dragAndDrop.js @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2019-2020 CERN and copyright holders of ALICE O2. + * See http://alice-o2.web.cern.ch/copyright for details of the copyright holders. + * All rights not expressly granted are reserved. + * + * This software is distributed under the terms of the GNU General Public + * License v3 (GPL Version 3), copied verbatim in the file "COPYING". + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +/** + * Helper to get the center of an element. + * @param {object} page - Puppeteer page object. + * @param {string} selector - Element selector to look for. + * @returns {Promise<{x: number, y: number}>} A promise that resolves to the center x & y coordinates. + */ +export const getElementCenter = async (page, selector) => { + const element = await page.waitForSelector(selector); + const box = await element.boundingBox(); + return { + x: box.x + box.width / 2, + y: box.y + box.height / 2 + }; +}; From 0e93f2a5a34403477b4b09fa012db29cd7966d06 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 09:23:47 +0100 Subject: [PATCH 11/24] feat: add title to incite the user to re-arrange the tabs --- QualityControl/public/layout/view/header.js | 1 + 1 file changed, 1 insertion(+) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 17f5e3bc8..99512aed4 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -150,6 +150,7 @@ const toolbarEditModeTab = (layout, tab, i) => { h( '.btn-group.flex-fixed.relative.cursor-grab', { + title: 'Drag the tab to re-arrange them', draggable: true, ondragstart: (e) => { e.dataTransfer.setData('text/plain', tab.id); From bb9bece6486f046e8f762735215a028e90444d3c Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 09:29:08 +0100 Subject: [PATCH 12/24] fix: compare original tab names for consistancy of the test --- QualityControl/test/public/pages/layout-show.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 3803c6a09..1be97eaad 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -302,6 +302,9 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => 'should reorder tabs via drag and drop in edit mode', { timeout }, async () => { + const originalTabNames = await page.$$eval('#btn-tab', (elements) => + elements.map((element) => element.textContent.trim())); + const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1)'; const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) .drop-zone.after'; @@ -322,7 +325,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => const tabNames = await page.$$eval('#btn-tab', (elements) => elements.map((element) => element.textContent.trim())); - strictEqual(tabNames[1], 'main'); + strictEqual(tabNames[1], originalTabNames[0]); } ); From 4f11556da15b5b73ebd336985224fb0ef83b23d6 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 09:38:51 +0100 Subject: [PATCH 13/24] test: add delays for drag and drop to combat slow cicd --- QualityControl/test/public/pages/layout-show.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 1be97eaad..fc6ddddef 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -314,12 +314,16 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => await page.mouse.move(sourceCenter.x, sourceCenter.y); await page.mouse.down(); + await delay(100); + // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active'); + await delay(100); + await page.mouse.up(); const tabNames = await page.$$eval('#btn-tab', (elements) => From 22aafae46880485c0e3bebe8801b5fe83b83b980 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 14:16:11 +0100 Subject: [PATCH 14/24] test: add console log to see where the cicd gets hang up on --- QualityControl/test/public/pages/layout-show.test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index fc6ddddef..086ebb752 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -314,15 +314,13 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => await page.mouse.move(sourceCenter.x, sourceCenter.y); await page.mouse.down(); - await delay(100); - // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); + console.log('before'); // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active'); - - await delay(100); + console.log('after'); await page.mouse.up(); From 62ee82f48cc954545c94235b1001af69f1308169 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 14:47:21 +0100 Subject: [PATCH 15/24] test: update the target selector to select the tab button --- QualityControl/test/public/pages/layout-show.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 086ebb752..f515d9c79 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -305,7 +305,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => const originalTabNames = await page.$$eval('#btn-tab', (elements) => elements.map((element) => element.textContent.trim())); - const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1)'; + const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1) > .btn-tab'; const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) .drop-zone.after'; const sourceCenter = await getElementCenter(page, sourceTabSelector); @@ -317,10 +317,8 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); - console.log('before'); // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active'); - console.log('after'); await page.mouse.up(); From 95fc7db5add270a9eab3a9ed5a6718629fd1c701 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 15:03:38 +0100 Subject: [PATCH 16/24] feat: add pointer-events none while dragging on edit and delete buttons --- QualityControl/public/app.css | 3 +++ QualityControl/public/layout/view/header.js | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/QualityControl/public/app.css b/QualityControl/public/app.css index ca6379603..36bc92181 100644 --- a/QualityControl/public/app.css +++ b/QualityControl/public/app.css @@ -214,3 +214,6 @@ .pointer-events-auto { pointer-events: auto; } +.pointer-events-none { + pointer-events: none; +} diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 99512aed4..e008ae458 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -136,6 +136,7 @@ const toolbarEditMode = (layout) => { const toolbarEditModeTab = (layout, tab, i) => { const selected = layout.tab.name === tab.name; const linkClass = selected ? 'selected' : ''; + const dragDisableClass = layout.isDragging ? 'pointer-events-none' : ''; /** * Handler when user click on a tab to select it @@ -198,9 +199,9 @@ const toolbarEditModeTab = (layout, tab, i) => { '', ), selected && [ - editTabButton(layout, linkClass, tab, i), + editTabButton(layout, `${linkClass} ${dragDisableClass}`, tab, i), resizeGridTabDropDown(layout, tab), - deleteTabButton(layout, linkClass, i), + deleteTabButton(layout, `${linkClass} ${dragDisableClass}`, i), ], ].flat().filter(Boolean), ], From f3588a93044f12662e30a9ae942eecbb1b8dbad8 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 15:10:28 +0100 Subject: [PATCH 17/24] test: update the target zone selector to select the dropdown --- QualityControl/test/public/pages/layout-show.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index f515d9c79..7561fe432 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -306,7 +306,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => elements.map((element) => element.textContent.trim())); const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1) > .btn-tab'; - const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) .drop-zone.after'; + const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) select'; const sourceCenter = await getElementCenter(page, sourceTabSelector); const targetCenter = await getElementCenter(page, targetZoneSelector); From e6b354cb1a89189478c3d8bbb06cfc972ed4961a Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 15:14:57 +0100 Subject: [PATCH 18/24] fix: revert the changes to the delete and edit button --- QualityControl/public/layout/view/header.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index e008ae458..6377a6fb4 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -199,9 +199,9 @@ const toolbarEditModeTab = (layout, tab, i) => { '', ), selected && [ - editTabButton(layout, `${linkClass} ${dragDisableClass}`, tab, i), + editTabButton(layout, linkClass, tab, i), resizeGridTabDropDown(layout, tab), - deleteTabButton(layout, `${linkClass} ${dragDisableClass}`, i), + deleteTabButton(layout, linkClass, i), ], ].flat().filter(Boolean), ], From c6753b1be4abf4c68328be34e3b732441a11b222 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 15:17:07 +0100 Subject: [PATCH 19/24] style: remove unused variable --- QualityControl/public/layout/view/header.js | 1 - 1 file changed, 1 deletion(-) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 6377a6fb4..99512aed4 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -136,7 +136,6 @@ const toolbarEditMode = (layout) => { const toolbarEditModeTab = (layout, tab, i) => { const selected = layout.tab.name === tab.name; const linkClass = selected ? 'selected' : ''; - const dragDisableClass = layout.isDragging ? 'pointer-events-none' : ''; /** * Handler when user click on a tab to select it From d423a16da15e498157f0d762c58d530000cf29fb Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 15:21:44 +0100 Subject: [PATCH 20/24] test: remove source selector to btn-tab --- QualityControl/test/public/pages/layout-show.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 7561fe432..d2b8cbd46 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -305,7 +305,7 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => const originalTabNames = await page.$$eval('#btn-tab', (elements) => elements.map((element) => element.textContent.trim())); - const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1) > .btn-tab'; + const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1)'; const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) select'; const sourceCenter = await getElementCenter(page, sourceTabSelector); From 187e4f025ed991b9c71da6a9cb8f471870765cb6 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 16:02:56 +0100 Subject: [PATCH 21/24] test: add screenshot and points of where the target and source are located --- .../test/public/pages/layout-show.test.js | 98 ++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index d2b8cbd46..093e3bd29 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -300,23 +300,117 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => await testParent.test( 'should reorder tabs via drag and drop in edit mode', - { timeout }, + { timeout: Infinity }, async () => { + const installMouseHelper = async (page) => { + await page.evaluate(() => { + const box = document.createElement('puppeteer-mouse-pointer'); + const style = document.createElement('style'); + style.innerHTML = ` + puppeteer-mouse-pointer { + pointer-events: none; + position: fixed; /* Changed to FIXED to match Puppeteer's viewport logic */ + top: 0; + z-index: 999999; + left: 0; + width: 20px; + height: 20px; + background: rgba(255, 0, 0, 0.8); + border: 3px solid white; + border-radius: 50%; + margin: -10px 0 0 -10px; + padding: 0; + box-sizing: border-box; + } + puppeteer-mouse-pointer.down { + background: rgba(0, 255, 0, 0.9); + transform: scale(1.2); + } + `; + document.head.appendChild(style); + document.body.appendChild(box); + + document.addEventListener('mousemove', event => { + // clientX/Y is relative to the viewport, matching Puppeteer's mouse + box.style.left = event.clientX + 'px'; + box.style.top = event.clientY + 'px'; + }, true); + + document.addEventListener('mousedown', () => { + box.classList.add('down'); + }, true); + + document.addEventListener('mouseup', () => { + box.classList.remove('down'); + }, true); + }); + }; + + const drawDebugDot = async (page, coord, label = '', color = 'red') => { + await page.evaluate(({ x, y, label, color }) => { + const dot = document.createElement('div'); + dot.className = 'debug-dot'; + dot.style.cssText = ` + position: fixed; + left: ${x}px; + top: ${y}px; + width: 12px; + height: 12px; + background-color: ${color}; + border: 2px solid white; + border-radius: 50%; + z-index: 1000000; + pointer-events: none; + margin-left: -6px; + margin-top: -6px; + box-shadow: 0 0 5px rgba(0,0,0,0.5); + `; + + if (label) { + const text = document.createElement('span'); + text.textContent = label; + text.style.cssText = ` + position: absolute; + top: 15px; + left: 50%; + transform: translateX(-50%); + background: black; + color: white; + padding: 2px 5px; + font-size: 10px; + border-radius: 3px; + white-space: nowrap; + `; + dot.appendChild(text); + } + + document.body.appendChild(dot); + }, { x: coord.x, y: coord.y, label, color }); + }; + + await installMouseHelper(page); + const originalTabNames = await page.$$eval('#btn-tab', (elements) => elements.map((element) => element.textContent.trim())); const sourceTabSelector = '.btn-group.flex-fixed.relative:nth-child(1)'; - const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) select'; + const targetZoneSelector = '.btn-group.flex-fixed.relative:nth-child(2) .drop-zone.after'; const sourceCenter = await getElementCenter(page, sourceTabSelector); const targetCenter = await getElementCenter(page, targetZoneSelector); + await drawDebugDot(page, sourceCenter, 'Source', 'blue'); + await drawDebugDot(page, targetCenter, 'Target', 'orange'); + await page.mouse.move(sourceCenter.x, sourceCenter.y); await page.mouse.down(); // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); + await page.screenshot({ + path: 'something.png' + }); // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active'); From 6216434c2746c09b884f0464fe2a9e187d631307 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Wed, 17 Dec 2025 16:56:36 +0100 Subject: [PATCH 22/24] test: add pointer events none when dragging --- QualityControl/public/layout/view/header.js | 6 +- .../test/public/pages/layout-show.test.js | 93 +------------------ 2 files changed, 5 insertions(+), 94 deletions(-) diff --git a/QualityControl/public/layout/view/header.js b/QualityControl/public/layout/view/header.js index 99512aed4..25a69d272 100644 --- a/QualityControl/public/layout/view/header.js +++ b/QualityControl/public/layout/view/header.js @@ -144,6 +144,7 @@ const toolbarEditModeTab = (layout, tab, i) => { const selectTab = () => layout.selectTab(i); const dragActiveClass = layout.isDragging ? 'pointer-events-auto' : ''; + const disableButtonsOnDragClass = layout.isDragging ? 'pointer-events-none' : ''; const dropZoneClass = (position) => layout.dropTargetId === tab.id && layout.position === position ? 'active' : ''; return [ @@ -161,6 +162,7 @@ const toolbarEditModeTab = (layout, tab, i) => { layout.clearDropTarget(); layout.stopDragging(); }, + ondragend: () => layout.stopDragging(), }, [ h( @@ -198,9 +200,9 @@ const toolbarEditModeTab = (layout, tab, i) => { '', ), selected && [ - editTabButton(layout, linkClass, tab, i), + editTabButton(layout, `${disableButtonsOnDragClass} ${linkClass}`, tab, i), resizeGridTabDropDown(layout, tab), - deleteTabButton(layout, linkClass, i), + deleteTabButton(layout, `${disableButtonsOnDragClass} ${linkClass}`, i), ], ].flat().filter(Boolean), ], diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 093e3bd29..443ccc68d 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -300,96 +300,8 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => await testParent.test( 'should reorder tabs via drag and drop in edit mode', - { timeout: Infinity }, + { timeout }, async () => { - const installMouseHelper = async (page) => { - await page.evaluate(() => { - const box = document.createElement('puppeteer-mouse-pointer'); - const style = document.createElement('style'); - style.innerHTML = ` - puppeteer-mouse-pointer { - pointer-events: none; - position: fixed; /* Changed to FIXED to match Puppeteer's viewport logic */ - top: 0; - z-index: 999999; - left: 0; - width: 20px; - height: 20px; - background: rgba(255, 0, 0, 0.8); - border: 3px solid white; - border-radius: 50%; - margin: -10px 0 0 -10px; - padding: 0; - box-sizing: border-box; - } - puppeteer-mouse-pointer.down { - background: rgba(0, 255, 0, 0.9); - transform: scale(1.2); - } - `; - document.head.appendChild(style); - document.body.appendChild(box); - - document.addEventListener('mousemove', event => { - // clientX/Y is relative to the viewport, matching Puppeteer's mouse - box.style.left = event.clientX + 'px'; - box.style.top = event.clientY + 'px'; - }, true); - - document.addEventListener('mousedown', () => { - box.classList.add('down'); - }, true); - - document.addEventListener('mouseup', () => { - box.classList.remove('down'); - }, true); - }); - }; - - const drawDebugDot = async (page, coord, label = '', color = 'red') => { - await page.evaluate(({ x, y, label, color }) => { - const dot = document.createElement('div'); - dot.className = 'debug-dot'; - dot.style.cssText = ` - position: fixed; - left: ${x}px; - top: ${y}px; - width: 12px; - height: 12px; - background-color: ${color}; - border: 2px solid white; - border-radius: 50%; - z-index: 1000000; - pointer-events: none; - margin-left: -6px; - margin-top: -6px; - box-shadow: 0 0 5px rgba(0,0,0,0.5); - `; - - if (label) { - const text = document.createElement('span'); - text.textContent = label; - text.style.cssText = ` - position: absolute; - top: 15px; - left: 50%; - transform: translateX(-50%); - background: black; - color: white; - padding: 2px 5px; - font-size: 10px; - border-radius: 3px; - white-space: nowrap; - `; - dot.appendChild(text); - } - - document.body.appendChild(dot); - }, { x: coord.x, y: coord.y, label, color }); - }; - - await installMouseHelper(page); - const originalTabNames = await page.$$eval('#btn-tab', (elements) => elements.map((element) => element.textContent.trim())); @@ -399,9 +311,6 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => const sourceCenter = await getElementCenter(page, sourceTabSelector); const targetCenter = await getElementCenter(page, targetZoneSelector); - await drawDebugDot(page, sourceCenter, 'Source', 'blue'); - await drawDebugDot(page, targetCenter, 'Target', 'orange'); - await page.mouse.move(sourceCenter.x, sourceCenter.y); await page.mouse.down(); From a501ad2bfb22e84f61ad76ba7befa3cbd5f21216 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 09:54:34 +0100 Subject: [PATCH 23/24] test: remove the redundant screenshot command --- QualityControl/test/public/pages/layout-show.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 443ccc68d..1be97eaad 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -317,9 +317,6 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); - await page.screenshot({ - path: 'something.png' - }); // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active'); From bda861d5176e627c476097bf42a81d55b6e03141 Mon Sep 17 00:00:00 2001 From: Alex Janson Date: Thu, 18 Dec 2025 10:01:42 +0100 Subject: [PATCH 24/24] test: add delay instead of the screenshot command --- QualityControl/test/public/pages/layout-show.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QualityControl/test/public/pages/layout-show.test.js b/QualityControl/test/public/pages/layout-show.test.js index 1be97eaad..2a044a2b4 100644 --- a/QualityControl/test/public/pages/layout-show.test.js +++ b/QualityControl/test/public/pages/layout-show.test.js @@ -317,6 +317,8 @@ export const layoutShowTests = async (url, page, timeout = 5000, testParent) => // We add 'steps' to make the move smoother, which helps trigger event await page.mouse.move(targetCenter.x, targetCenter.y, { steps: 10 }); + await delay(1000); + // Wait a moment for the 'active' class to appear in the UI await page.waitForSelector('.drop-zone.after.active');