diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 555e8618e2c..f5531e2fc71 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -76,6 +76,18 @@ export class ListRenderer extends Component { this.longTouchTimer = null; this.touchStartMs = 0; + /** + * When resizing, it's possible that the pointer is not above the resize + * handle (by some few pixel difference). During this scenario, click event + * will be triggered on the column title which will reorder the column. + * Column resize that triggers a reorder is not a good UX and we prevent this + * using the following state variables: `resizing` and `preventReorder` which + * are set during the column's click (onClickSortColumn), mouseup + * (onColumnTitleMouseUp) and onStartResize events. + */ + this.resizing = false; + this.preventReorder = false; + this.creates = this.props.archInfo.creates.length ? this.props.archInfo.creates : [{ type: "create", string: this.env._t("Add a line") }]; @@ -863,6 +875,10 @@ export class ListRenderer extends Component { } onClickSortColumn(column) { + if (this.preventReorder) { + this.preventReorder = false; + return; + } if (this.props.list.editedRecord || this.props.list.model.useSampleModel) { return; } @@ -870,6 +886,8 @@ export class ListRenderer extends Component { const list = this.props.list; if (this.isSortable(column)) { list.sortBy(fieldName); + // don't resize column when reordering. + this.keepColumnWidths = true; } } @@ -1684,6 +1702,12 @@ export class ListRenderer extends Component { } } + onColumnTitleMouseUp() { + if (this.resizing) { + this.preventReorder = true; + } + } + /** * Handles the resize feature on the column headers * @@ -1691,6 +1715,7 @@ export class ListRenderer extends Component { * @param {MouseEvent} ev */ onStartResize(ev) { + this.resizing = true; const table = this.tableRef.el; const th = ev.target.closest("th"); const handler = th.querySelector(".o_resize"); @@ -1733,6 +1758,7 @@ export class ListRenderer extends Component { // Mouse or keyboard events : stop resize const stopResize = (ev) => { + this.resizing = false; // Ignores the 'left mouse button down' event as it used to start resizing if (ev.type === "mousedown" && ev.which === 1) { return; diff --git a/addons/web/static/src/views/list/list_renderer.xml b/addons/web/static/src/views/list/list_renderer.xml index d843463bb86..299cdedf7d2 100644 --- a/addons/web/static/src/views/list/list_renderer.xml +++ b/addons/web/static/src/views/list/list_renderer.xml @@ -24,6 +24,7 @@ t-att-class="getColumnClass(column) + ' opacity-trigger-hover'" t-on-mouseenter="ev => this.onHoverSortColumn(ev, column)" t-on-mouseleave="ev => this.onHoverSortColumn(ev, column)" + t-on-mouseup="onColumnTitleMouseUp" t-on-click="() => this.onClickSortColumn(column)" t-on-keydown.synthetic="(ev) => this.onCellKeydown(ev)" t-att-data-tooltip-template="isDebugMode ? 'web.FieldTooltip' : false" t-att-data-tooltip-info="isDebugMode ? makeTooltip(column) : false" tabindex="-1"> @@ -37,7 +38,6 @@ diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 7e801c416c4..8d8c2ed3efb 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -23,6 +23,7 @@ import { clickOpenedDropdownItem, clickOpenM2ODropdown, clickSave, + drag, dragAndDrop, editInput, editSelect, @@ -14869,6 +14870,59 @@ QUnit.module("Views", (hooks) => { ); }); + QUnit.test("list: column: resize, reorder, resize again", async function (assert) { + serverData.models.foo.fields.foo.sortable = true; + serverData.models.foo.fields.int_field.sortable = true; + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + `, + }); + + // pointer doesn't perfectly match the resized th. + const PIXEL_TOLERANCE = 3; + const assertAlmostEqual = (v1, v2) => Math.abs(v1 - v2) <= PIXEL_TOLERANCE; + + // 1. Resize column foo to middle of column int_field. + const originalWidths = [...target.querySelectorAll(".o_list_table th")].map( + (th) => th.offsetWidth + ); + const th2 = target.querySelector("th:nth-child(2)"); + const th3 = target.querySelector("th:nth-child(3)"); + const resizeHandle = th2.querySelector(".o_resize"); + + await dragAndDrop(resizeHandle, th3); + + const widthsAfterResize = [...target.querySelectorAll(".o_list_table th")].map( + (th) => th.offsetWidth + ); + + assert.strictEqual(widthsAfterResize[0], originalWidths[0]); + assertAlmostEqual(widthsAfterResize[1], originalWidths[1] + originalWidths[2] / 2); + + // 2. Reorder column foo. + await click(th2); + const widthsAfterReorder = [...target.querySelectorAll(".o_list_table th")].map( + (th) => th.offsetWidth + ); + + assert.strictEqual(widthsAfterResize[0], widthsAfterReorder[0]); + assert.strictEqual(widthsAfterResize[1], widthsAfterReorder[1]); + + // 3. Resize again, this time check sizes while dragging and after drop. + const drop = drag(resizeHandle, th3); + assertAlmostEqual(th2.offsetWidth, widthsAfterReorder[1] + widthsAfterReorder[2] / 2); + + drop(); + await nextTick(); + assertAlmostEqual(th2.offsetWidth, widthsAfterReorder[1] + widthsAfterReorder[2] / 2); + }); + QUnit.test("editable list: resize column headers", async function (assert) { await makeView({ type: "list",