diff --git a/dev/dashboard-layout.html b/dev/dashboard-layout.html
index 97e34f781b..223e0e175c 100644
--- a/dev/dashboard-layout.html
+++ b/dev/dashboard-layout.html
@@ -59,7 +59,7 @@
-
+
54 000€
diff --git a/dev/dashboard.html b/dev/dashboard.html
index 7a69b285b5..10f4c8bddc 100644
--- a/dev/dashboard.html
+++ b/dev/dashboard.html
@@ -62,6 +62,7 @@
items: [
{
title: 'Sales closed this month',
+ rowspan: 2,
content: '54 000€',
type: 'kpi',
},
diff --git a/packages/dashboard/src/vaadin-dashboard-layout-mixin.js b/packages/dashboard/src/vaadin-dashboard-layout-mixin.js
index bca569d49e..b3bd0e7166 100644
--- a/packages/dashboard/src/vaadin-dashboard-layout-mixin.js
+++ b/packages/dashboard/src/vaadin-dashboard-layout-mixin.js
@@ -72,6 +72,7 @@ export const DashboardLayoutMixin = (superClass) =>
}
::slotted(*) {
+ /* The grid-column value applied to children */
--_vaadin-dashboard-item-column: span
min(
var(--vaadin-dashboard-item-colspan, 1),
@@ -79,6 +80,10 @@ export const DashboardLayoutMixin = (superClass) =>
);
grid-column: var(--_vaadin-dashboard-item-column);
+
+ /* The grid-row value applied to children */
+ --_vaadin-dashboard-item-row: span var(--vaadin-dashboard-item-rowspan, 1);
+ grid-row: var(--_vaadin-dashboard-item-row);
}
`;
}
diff --git a/packages/dashboard/src/vaadin-dashboard-section.js b/packages/dashboard/src/vaadin-dashboard-section.js
index 597fd9bea5..ab11921faf 100644
--- a/packages/dashboard/src/vaadin-dashboard-section.js
+++ b/packages/dashboard/src/vaadin-dashboard-section.js
@@ -66,6 +66,8 @@ class DashboardSection extends ControllerMixin(ElementMixin(PolylitMixin(LitElem
);
grid-column: var(--_vaadin-dashboard-item-column);
+ --_vaadin-dashboard-item-row: span var(--vaadin-dashboard-item-rowspan, 1);
+ grid-row: var(--_vaadin-dashboard-item-row);
}
header {
diff --git a/packages/dashboard/src/vaadin-dashboard-widget.js b/packages/dashboard/src/vaadin-dashboard-widget.js
index 8139e91b0e..8efd5dd8da 100644
--- a/packages/dashboard/src/vaadin-dashboard-widget.js
+++ b/packages/dashboard/src/vaadin-dashboard-widget.js
@@ -37,6 +37,7 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme
display: flex;
flex-direction: column;
grid-column: var(--_vaadin-dashboard-item-column);
+ grid-row: var(--_vaadin-dashboard-item-row);
position: relative;
}
diff --git a/packages/dashboard/src/vaadin-dashboard.d.ts b/packages/dashboard/src/vaadin-dashboard.d.ts
index 4d535d5652..9dac015b4b 100644
--- a/packages/dashboard/src/vaadin-dashboard.d.ts
+++ b/packages/dashboard/src/vaadin-dashboard.d.ts
@@ -18,6 +18,11 @@ export interface DashboardItem {
* The column span of the item
*/
colspan?: number;
+
+ /**
+ * The row span of the item
+ */
+ rowspan?: number;
}
export interface DashboardSectionItem {
diff --git a/packages/dashboard/src/vaadin-dashboard.js b/packages/dashboard/src/vaadin-dashboard.js
index 4625651366..6ac2aff8c8 100644
--- a/packages/dashboard/src/vaadin-dashboard.js
+++ b/packages/dashboard/src/vaadin-dashboard.js
@@ -138,6 +138,7 @@ class Dashboard extends ControllerMixin(DashboardLayoutMixin(ElementMixin(Themab
const itemDragged = this.__widgetReorderController.draggedItem === item;
const style = `
${item.colspan ? `--vaadin-dashboard-item-colspan: ${item.colspan};` : ''}
+ ${item.rowspan ? `--vaadin-dashboard-item-rowspan: ${item.rowspan};` : ''}
${itemDragged ? '--_vaadin-dashboard-item-placeholder-display: block;' : ''}
`.trim();
diff --git a/packages/dashboard/test/dashboard-layout.test.ts b/packages/dashboard/test/dashboard-layout.test.ts
index 2a918e4167..5d26f7c7f3 100644
--- a/packages/dashboard/test/dashboard-layout.test.ts
+++ b/packages/dashboard/test/dashboard-layout.test.ts
@@ -15,6 +15,7 @@ import {
setMaximumColumnWidth,
setMinimumColumnWidth,
setMinimumRowHeight,
+ setRowspan,
} from './helpers.js';
describe('dashboard layout', () => {
@@ -261,6 +262,36 @@ describe('dashboard layout', () => {
});
});
+ describe('row span', () => {
+ it('should span multiple rows', async () => {
+ setMinimumRowHeight(dashboard, 100);
+ setRowspan(childElements[0], 2);
+ await nextFrame();
+
+ /* prettier-ignore */
+ expectLayout(dashboard, [
+ [0, 1],
+ [0],
+ ]);
+ });
+
+ it('should span multiple rows on a single column', async () => {
+ setMinimumRowHeight(dashboard, 100);
+ setRowspan(childElements[0], 2);
+ await nextFrame();
+
+ dashboard.style.width = `${columnWidth}px`;
+ await nextFrame();
+
+ /* prettier-ignore */
+ expectLayout(dashboard, [
+ [0],
+ [0],
+ [1],
+ ]);
+ });
+ });
+
describe('gap', () => {
it('should have a default gap', () => {
// Clear the gap used in the tests
@@ -428,6 +459,20 @@ describe('dashboard layout', () => {
]);
});
+ it('should span multiple rows inside a section', async () => {
+ // Using a minimum row height here causes Firefox to crash, disabling for now
+ // setMinimumRowHeight(dashboard, 100);
+ setRowspan(childElements[2], 2);
+ await nextFrame();
+
+ /* prettier-ignore */
+ expectLayout(dashboard, [
+ [0, 1],
+ [2, 3],
+ [2]
+ ]);
+ });
+
it('should use minimum row height for all section rows', async () => {
dashboard.style.width = `${columnWidth}px`;
setMinimumRowHeight(dashboard, 300);
diff --git a/packages/dashboard/test/dashboard.test.ts b/packages/dashboard/test/dashboard.test.ts
index 3fc85a7981..6bad6d5dc2 100644
--- a/packages/dashboard/test/dashboard.test.ts
+++ b/packages/dashboard/test/dashboard.test.ts
@@ -111,6 +111,24 @@ describe('dashboard', () => {
});
});
+ describe('row span', () => {
+ it('should span one row by default', () => {
+ dashboard.style.width = `${columnWidth}px`;
+ const widgets = [getElementFromCell(dashboard, 0, 0), getElementFromCell(dashboard, 1, 0)];
+ expect(widgets[0]).to.not.equal(widgets[1]);
+ });
+
+ it('should span multiple rows', async () => {
+ dashboard.style.width = `${columnWidth}px`;
+ dashboard.items = [{ rowspan: 2, id: 'Item 0' }];
+ await nextFrame();
+
+ const widget = getElementFromCell(dashboard, 0, 0);
+ expect(widget).to.have.property('widgetTitle', 'Item 0 title');
+ expect(getElementFromCell(dashboard, 1, 0)).to.equal(widget);
+ });
+ });
+
describe('section', () => {
beforeEach(async () => {
dashboard.items = [
diff --git a/packages/dashboard/test/helpers.ts b/packages/dashboard/test/helpers.ts
index f6728340ed..1e8b53deb0 100644
--- a/packages/dashboard/test/helpers.ts
+++ b/packages/dashboard/test/helpers.ts
@@ -95,6 +95,13 @@ export function setColspan(element: HTMLElement, colspan?: number): void {
element.style.setProperty('--vaadin-dashboard-item-colspan', colspan !== undefined ? `${colspan}` : null);
}
+/**
+ * Sets the row span of the element
+ */
+export function setRowspan(element: HTMLElement, rowspan?: number): void {
+ element.style.setProperty('--vaadin-dashboard-item-rowspan', rowspan !== undefined ? `${rowspan}` : null);
+}
+
/**
* Sets the gap between the cells of the dashboard.
*/
diff --git a/packages/dashboard/test/typings/dashboard.types.ts b/packages/dashboard/test/typings/dashboard.types.ts
index 6e61a5c9c7..0489bebbb5 100644
--- a/packages/dashboard/test/typings/dashboard.types.ts
+++ b/packages/dashboard/test/typings/dashboard.types.ts
@@ -24,6 +24,11 @@ interface TestDashboardItem extends DashboardItem {
const genericDashboard = document.createElement('vaadin-dashboard');
assertType(genericDashboard);
+assertType<{ colspan?: number; rowspan?: number }>(genericDashboard.items[0] as DashboardItem);
+assertType<{ items: DashboardItem[]; title?: string | null }>(
+ genericDashboard.items[0] as DashboardSectionItem,
+);
+
assertType(genericDashboard);
assertType(genericDashboard);
assertType> | null | undefined>(genericDashboard.items);
@@ -34,8 +39,8 @@ assertType>(narrowedDashboard);
assertType>>(narrowedDashboard.items);
assertType | null | undefined>(narrowedDashboard.renderer);
assertType<
- | { colspan?: number; testProperty: string }
- | { title?: string | null; items: Array<{ colspan?: number; testProperty: string }> }
+ | { colspan?: number; rowspan?: number; testProperty: string }
+ | { title?: string | null; items: Array<{ colspan?: number; rowspan?: number; testProperty: string }> }
>(narrowedDashboard.items[0]);
narrowedDashboard.addEventListener('dashboard-item-reorder-start', (event) => {