diff --git a/packages/dashboard/src/widget-resize-controller.js b/packages/dashboard/src/widget-resize-controller.js index ea9bbe1b75..70ba942b40 100644 --- a/packages/dashboard/src/widget-resize-controller.js +++ b/packages/dashboard/src/widget-resize-controller.js @@ -89,10 +89,10 @@ export class WidgetResizeController { const currentElementHeight = itemWrapper.firstElementChild.offsetHeight; const rowMinHeight = Math.min(...gridStyle.gridTemplateRows.split(' ').map((height) => parseFloat(height))); - if (this.__resizeHeight > currentElementHeight + gapSize + rowMinHeight / 2) { + if (e.detail.ddy > 0 && this.__resizeHeight > currentElementHeight + gapSize + rowMinHeight / 2) { // Resized vertically above the half of the next row, increase rowspan this.__updateResizedItem(0, 1); - } else if (this.__resizeHeight < currentElementHeight - rowMinHeight / 2) { + } else if (e.detail.ddy < 0 && this.__resizeHeight < currentElementHeight - rowMinHeight / 2) { // Resized vertically below the half of the current row, decrease rowspan this.__updateResizedItem(0, -1); } diff --git a/packages/dashboard/test/dashboard-widget-resizing.test.ts b/packages/dashboard/test/dashboard-widget-resizing.test.ts index d601fb988e..c19c610211 100644 --- a/packages/dashboard/test/dashboard-widget-resizing.test.ts +++ b/packages/dashboard/test/dashboard-widget-resizing.test.ts @@ -1,5 +1,5 @@ import { expect } from '@vaadin/chai-plugins'; -import { fixtureSync, nextFrame } from '@vaadin/testing-helpers'; +import { fire, fixtureSync, nextFrame } from '@vaadin/testing-helpers'; import sinon from 'sinon'; import '../vaadin-dashboard.js'; import { isSafari } from '@vaadin/component-base/src/browser-utils.js'; @@ -11,6 +11,8 @@ import { fireResizeOver, fireResizeStart, getElementFromCell, + getEventCoordinates, + getResizeHandle, onceResized, setMaximumColumnWidth, setMinimumColumnWidth, @@ -201,10 +203,34 @@ describe('dashboard - widget resizing', () => { [0, 2], ]); + const resizeHandle = getResizeHandle(getElementFromCell(dashboard, 1, 0)!); + const { x: initialX, y: initialY } = getEventCoordinates(resizeHandle, 'bottom'); + fireResizeStart(getElementFromCell(dashboard, 1, 0)!); await nextFrame(); - fireResizeOver(getElementFromCell(dashboard, 0, 0)!, 'top'); + const targetElement = getElementFromCell(dashboard, 0, 0)!; + fireResizeOver(targetElement, 'top'); + await nextFrame(); + + const { x: targetX, y: targetY } = getEventCoordinates(targetElement, 'top'); + const event = new MouseEvent('mousemove', { + bubbles: true, + composed: true, + clientX: targetX, + clientY: targetY, + buttons: 1, + }); + targetElement.dispatchEvent(event); + fire(resizeHandle, 'track', { + state: 'track', + x: targetX, + y: targetY - 1, + dx: targetX - initialX, + dy: targetY - initialY - 1, + ddx: 0, + ddy: -1, + }); await nextFrame(); fireResizeEnd(dashboard); @@ -323,6 +349,99 @@ describe('dashboard - widget resizing', () => { ]); }); + (isSafari ? it.skip : it)('should resize only when dragged in the same direction (top -> bottom)', async () => { + setMinimumRowHeight(dashboard, 50); + dashboard.items = [{ id: 0, rowspan: 2 }, { id: 1 }, { id: 2 }]; + await nextFrame(); + + // prettier-ignore + expectLayout(dashboard, [ + [0, 1], + [0, 2], + ]); + + const widget = getElementFromCell(dashboard, 0, 1)!; + const initialHeight = widget.offsetHeight; + fireResizeStart(widget); + await nextFrame(); + + const targetElement = getElementFromCell(dashboard, 1, 1)!; + const { x, y } = getEventCoordinates(targetElement, 'top'); + const minSizeIncreaseDelta = initialHeight / 2 + 1; + const minSizeIncreaseY = y + minSizeIncreaseDelta; + const event = new MouseEvent('mousemove', { + bubbles: true, + composed: true, + clientX: x, + clientY: minSizeIncreaseY, + buttons: 1, + }); + targetElement.dispatchEvent(event); + await nextFrame(); + + let previousHeight = initialHeight; + const resizeHandle = getResizeHandle(widget); + for (let i = 1; i < 5; i++) { + fire(resizeHandle, 'track', { + state: 'track', + x, + y: minSizeIncreaseY + i, + dx: 0, + dy: minSizeIncreaseDelta + i, + ddx: 0, + ddy: 1, + }); + await nextFrame(); + const newHeight = getElementFromCell(dashboard, 0, 1)!.offsetHeight; + expect(newHeight >= previousHeight).to.be.true; + previousHeight = newHeight; + } + }); + + it('should resize only when dragged in the same direction (bottom -> top)', async () => { + setMinimumRowHeight(dashboard, 50); + dashboard.items = [{ id: 0, rowspan: 2 }, { id: 1 }, { id: 2, rowspan: 3 }]; + await nextFrame(); + + const widget = getElementFromCell(dashboard, 2, 1)!; + const initialHeight = widget.offsetHeight; + fireResizeStart(widget); + await nextFrame(); + + const targetElement = getElementFromCell(dashboard, 1, 1)!; + const { x, y } = getEventCoordinates(targetElement, 'top'); + const minSizeDecreaseDelta = initialHeight / 3 + 1; + const minSizeDecreaseY = y - minSizeDecreaseDelta; + const event = new MouseEvent('mousemove', { + bubbles: true, + composed: true, + clientX: x, + clientY: minSizeDecreaseY, + buttons: 1, + }); + targetElement.dispatchEvent(event); + await nextFrame(); + + let previousHeight = initialHeight; + const resizeHandle = getResizeHandle(widget); + + for (let i = 1; i < 5; i++) { + fire(resizeHandle, 'track', { + state: 'track', + x, + y: minSizeDecreaseY - i, + dx: 0, + dy: minSizeDecreaseDelta - i, + ddx: 0, + ddy: -1, + }); + await nextFrame(); + const newHeight = getElementFromCell(dashboard, 1, 1)!.offsetHeight; + expect(newHeight <= previousHeight).to.be.true; + previousHeight = newHeight; + } + }); + it('should dispatch an item resized event', async () => { const resizeEndSpy = sinon.spy(); dashboard.addEventListener('dashboard-item-resized', resizeEndSpy); diff --git a/packages/dashboard/test/helpers.ts b/packages/dashboard/test/helpers.ts index bcc4905b2d..ba3bb9107d 100644 --- a/packages/dashboard/test/helpers.ts +++ b/packages/dashboard/test/helpers.ts @@ -229,7 +229,10 @@ export function createDragEvent(type: string, { x, y }: { x: number; y: number } return event; } -function getEventCoordinates(relativeElement: Element, location: 'top' | 'bottom' | 'start' | 'end') { +export function getEventCoordinates( + relativeElement: Element, + location: 'top' | 'bottom' | 'start' | 'end', +): { x: number; y: number } { const { top, bottom, left, right } = relativeElement.getBoundingClientRect(); const y = location === 'top' ? top : bottom; const dir = document.dir;