From 63fb6dc86c71cacfe7ac7725570709d1a25863f5 Mon Sep 17 00:00:00 2001 From: Sahinur Date: Tue, 13 Oct 2026 10:38:57 +0600 Subject: [PATCH] Keep table cell widths on the fallback ghost of a (#2455) The fallback ghost is position: fixed (or absolute), and a positioned table row is no longer laid out by its table. Its cells end up in an anonymous table and shrink to fit their content, so a dragged row with forceFallback loses the table's column widths. When dragEl is a table row, lay the ghost out as a fixed-layout table and give every cell the width of the matching cell in the original row. --- src/Sortable.js | 15 ++++++++++ tests/Sortable.test.js | 22 ++++++++++++++ tests/table.html | 65 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 tests/table.html diff --git a/src/Sortable.js b/src/Sortable.js index 72f571c82..383f4ad96 100644 --- a/src/Sortable.js +++ b/src/Sortable.js @@ -916,6 +916,21 @@ Sortable.prototype = /** @lends Sortable.prototype */ { css(ghostEl, 'zIndex', '100000'); css(ghostEl, 'pointerEvents', 'none'); + // A positioned table row is no longer laid out by its table, so its cells + // would shrink to fit their content. Keep each cell's original width (#2455) + if (css(dragEl, 'display') === 'table-row') { + css(ghostEl, 'display', 'table'); + css(ghostEl, 'tableLayout', 'fixed'); + + Array.prototype.forEach.call(dragEl.children, function (cell, i) { + let ghostCell = ghostEl.children[i]; + + if (ghostCell) { + css(ghostCell, 'box-sizing', 'border-box'); + css(ghostCell, 'width', getRect(cell, true, PositionGhostAbsolutely, true, container).width); + } + }); + } Sortable.ghost = ghostEl; diff --git a/tests/Sortable.test.js b/tests/Sortable.test.js index 59ce11f46..98dc6fd40 100644 --- a/tests/Sortable.test.js +++ b/tests/Sortable.test.js @@ -384,3 +384,25 @@ test('Do not insert into empty list if outside emptyInsertThreshold', async brow }) .expect(dragStartPosition.innerText).eql(dragEl.innerText); }); + + +fixture `Table rows` + .page `./table.html`; + +['tbody1', 'tbody2'].forEach(id => { + test(`Fallback ghost keeps the cell widths of the dragged row (#${id})`, async browser => { + const tbody = Selector(`#${id}`); + const dragEl = await tbody.child(0)(); + + await browser + .dragToElement(dragEl, tbody.child(2)) + .expect(tbody.child(2).innerText).eql(dragEl.innerText); + + const { row, ghost } = await browser.eval(() => window.cellWidths[id], { dependencies: { id } }); + + await browser.expect(ghost.length).eql(row.length); + for (let i = 0; i < row.length; i++) { + await browser.expect(Math.abs(ghost[i] - row[i])).lt(leeway); + } + }); +}); diff --git a/tests/table.html b/tests/table.html new file mode 100644 index 000000000..f831f3e63 --- /dev/null +++ b/tests/table.html @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + +
1ax
2by
3cz
+ + + + + + + + + + + +
#A much longer header for the second columnThird
1ax
2by
3cz
+ + + + + + + +