diff --git a/packages/dashboard/src/vaadin-dashboard-widget.js b/packages/dashboard/src/vaadin-dashboard-widget.js index 6a15e52e5e..2019c87e38 100644 --- a/packages/dashboard/src/vaadin-dashboard-widget.js +++ b/packages/dashboard/src/vaadin-dashboard-widget.js @@ -54,7 +54,7 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme display: var(--_vaadin-dashboard-widget-actions-display, none); position: absolute; bottom: 0; - right: 0; + inset-inline-end: 0; font-size: 30px; cursor: grab; line-height: 1; @@ -64,6 +64,10 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme content: '\\2921'; } + :host([dir='rtl']) #resize-handle::before { + content: '\\2922'; + } + :host::after { content: ''; z-index: 2; diff --git a/packages/dashboard/src/widget-resize-controller.js b/packages/dashboard/src/widget-resize-controller.js index a720a83e2a..f05919a893 100644 --- a/packages/dashboard/src/widget-resize-controller.js +++ b/packages/dashboard/src/widget-resize-controller.js @@ -62,7 +62,7 @@ export class WidgetResizeController extends EventTarget { return; } - this.__resizeWidth = this.__resizeStartWidth + e.detail.dx; + this.__resizeWidth = this.__resizeStartWidth + (document.dir === 'rtl' ? -e.detail.dx : e.detail.dx); this.__resizeHeight = this.__resizeStartHeight + e.detail.dy; this.__updateWidgetStyles(); diff --git a/packages/dashboard/test/dashboard-widget-resizing.test.ts b/packages/dashboard/test/dashboard-widget-resizing.test.ts index 28bb344a49..72e5982608 100644 --- a/packages/dashboard/test/dashboard-widget-resizing.test.ts +++ b/packages/dashboard/test/dashboard-widget-resizing.test.ts @@ -5,6 +5,7 @@ import '../vaadin-dashboard.js'; import { isSafari } from '@vaadin/component-base/src/browser-utils.js'; import type { Dashboard, DashboardItem } from '../vaadin-dashboard.js'; import { + describeBidirectional, expectLayout, fireResizeEnd, fireResizeOver, @@ -51,89 +52,91 @@ describe('dashboard - widget resizing', () => { }); describe('mouse drag', () => { - it('should resize a widget while dragging (start -> end)', async () => { - // Start dragging the first widget resize handle - fireResizeStart(getElementFromCell(dashboard, 0, 0)!); - await nextFrame(); + describeBidirectional('horizontal', () => { + it('should resize a widget while dragging (start -> end)', async () => { + // Start dragging the first widget resize handle + fireResizeStart(getElementFromCell(dashboard, 0, 0)!); + await nextFrame(); - // Drag over the end edge of the second one - fireResizeOver(getElementFromCell(dashboard, 0, 1)!, 'end'); - await nextFrame(); + // Drag over the end edge of the second one + fireResizeOver(getElementFromCell(dashboard, 0, 1)!, 'end'); + await nextFrame(); - fireResizeEnd(dashboard); - await nextFrame(); + fireResizeEnd(dashboard); + await nextFrame(); - // Expect the widgets to be reordered - // prettier-ignore - expectLayout(dashboard, [ - [0, 0], - [1], - ]); - }); + // Expect the widgets to be reordered + // prettier-ignore + expectLayout(dashboard, [ + [0, 0], + [1], + ]); + }); - it('should not resize if dragged barely over another widget (start -> end)', async () => { - fireResizeStart(getElementFromCell(dashboard, 0, 0)!); - await nextFrame(); + it('should not resize if dragged barely over another widget (start -> end)', async () => { + fireResizeStart(getElementFromCell(dashboard, 0, 0)!); + await nextFrame(); - fireResizeOver(getElementFromCell(dashboard, 0, 1)!, 'start'); - await nextFrame(); + fireResizeOver(getElementFromCell(dashboard, 0, 1)!, 'start'); + await nextFrame(); - fireResizeEnd(dashboard); - await nextFrame(); + fireResizeEnd(dashboard); + await nextFrame(); - // prettier-ignore - expectLayout(dashboard, [ - [0, 1], - ]); - }); + // prettier-ignore + expectLayout(dashboard, [ + [0, 1], + ]); + }); - it('should resize a widget while dragging (end -> start)', async () => { - dashboard.items = [{ id: 0, colspan: 2 }, { id: 1 }]; - await nextFrame(); - // prettier-ignore - expectLayout(dashboard, [ - [0, 0], - [1], - ]); + it('should resize a widget while dragging (end -> start)', async () => { + dashboard.items = [{ id: 0, colspan: 2 }, { id: 1 }]; + await nextFrame(); + // prettier-ignore + expectLayout(dashboard, [ + [0, 0], + [1], + ]); - fireResizeStart(getElementFromCell(dashboard, 0, 1)!); - await nextFrame(); + fireResizeStart(getElementFromCell(dashboard, 0, 1)!); + await nextFrame(); - fireResizeOver(getElementFromCell(dashboard, 0, 0)!, 'start'); - await nextFrame(); + fireResizeOver(getElementFromCell(dashboard, 0, 0)!, 'start'); + await nextFrame(); - fireResizeEnd(dashboard); - await nextFrame(); + fireResizeEnd(dashboard); + await nextFrame(); - // prettier-ignore - expectLayout(dashboard, [ - [0, 1], - ]); - }); + // prettier-ignore + expectLayout(dashboard, [ + [0, 1], + ]); + }); - it('should not resize if dragged barely over another widget (end -> start)', async () => { - dashboard.items = [{ id: 0, colspan: 2 }, { id: 1 }]; - await nextFrame(); - // prettier-ignore - expectLayout(dashboard, [ - [0, 0], - [1], - ]); + it('should not resize if dragged barely over another widget (end -> start)', async () => { + dashboard.items = [{ id: 0, colspan: 2 }, { id: 1 }]; + await nextFrame(); + // prettier-ignore + expectLayout(dashboard, [ + [0, 0], + [1], + ]); - fireResizeStart(getElementFromCell(dashboard, 0, 1)!); - await nextFrame(); + fireResizeStart(getElementFromCell(dashboard, 0, 1)!); + await nextFrame(); - fireResizeOver(getElementFromCell(dashboard, 0, 0)!, 'end'); - await nextFrame(); + fireResizeOver(getElementFromCell(dashboard, 0, 0)!, 'end'); + await nextFrame(); - fireResizeEnd(dashboard); - await nextFrame(); + fireResizeEnd(dashboard); + await nextFrame(); - // prettier-ignore - expectLayout(dashboard, [ - [0, 0], - [1], - ]); + // prettier-ignore + expectLayout(dashboard, [ + [0, 0], + [1], + ]); + }); }); it('should resize a widget while dragging (top -> bottom)', async () => { diff --git a/packages/dashboard/test/helpers.ts b/packages/dashboard/test/helpers.ts index a3c59c48ba..e953e9b7da 100644 --- a/packages/dashboard/test/helpers.ts +++ b/packages/dashboard/test/helpers.ts @@ -37,13 +37,15 @@ function _getRowHeights(dashboard: Element): number[] { } function _getElementFromCell(dashboard: HTMLElement, rowIndex: number, columnIndex: number, rowHeights: number[]) { - const { top, left } = dashboard.getBoundingClientRect(); + const { top, left, right } = dashboard.getBoundingClientRect(); const columnWidths = getColumnWidths(dashboard); - const x = left + columnWidths.slice(0, columnIndex).reduce((sum, width) => sum + width, 0); + const columnOffset = columnWidths.slice(0, columnIndex).reduce((sum, width) => sum + width, 0); + const rtl = document.dir === 'rtl'; + const x = rtl ? right - columnOffset : left + columnOffset; const y = top + rowHeights.slice(0, rowIndex).reduce((sum, height) => sum + height, 0); return document - .elementsFromPoint(x + columnWidths[columnIndex] / 2, y + rowHeights[rowIndex] - 1) + .elementsFromPoint(x + (columnWidths[columnIndex] / 2) * (rtl ? -1 : 1), y + rowHeights[rowIndex] - 1) .reverse() .find( (element) => @@ -279,3 +281,18 @@ export function fireResizeEnd(dragOverTarget: Element): void { export function getRemoveButton(section: DashboardWidget | DashboardSection): HTMLElement { return section.shadowRoot!.querySelector('#remove-button') as HTMLElement; } + +export function describeBidirectional(name: string, tests: () => void): void { + describe(name, tests); + describe(`${name} (RTL)`, () => { + before(() => { + document.dir = 'rtl'; + }); + + after(() => { + document.dir = 'ltr'; // Reset to default after tests + }); + + tests(); + }); +}