diff --git a/packages/dashboard/src/vaadin-dashboard.js b/packages/dashboard/src/vaadin-dashboard.js
index 5d256acd9c..4625651366 100644
--- a/packages/dashboard/src/vaadin-dashboard.js
+++ b/packages/dashboard/src/vaadin-dashboard.js
@@ -119,7 +119,12 @@ class Dashboard extends ControllerMixin(DashboardLayoutMixin(ElementMixin(Themab
if (cell.firstElementChild && cell.firstElementChild.localName === 'vaadin-dashboard-section') {
return;
}
- if (renderer) {
+ if (cell.__item.component instanceof HTMLElement) {
+ if (cell.__item.component.parentElement !== cell) {
+ cell.textContent = '';
+ cell.appendChild(cell.__item.component);
+ }
+ } else if (renderer) {
renderer(cell, this, { item: cell.__item });
} else {
cell.innerHTML = '';
@@ -137,13 +142,20 @@ class Dashboard extends ControllerMixin(DashboardLayoutMixin(ElementMixin(Themab
`.trim();
if (item.items) {
+ const itemHasComponent = item.component instanceof HTMLElement;
+ if (itemHasComponent) {
+ render(this.__renderItemCells(item.items), item.component);
+ }
+
return html`
-
- ${this.__renderItemCells(item.items)}
-
+ ${itemHasComponent
+ ? item.component
+ : html`
+ ${this.__renderItemCells(item.items)}
+ `}
`;
}
diff --git a/packages/dashboard/test/dashboard.test.ts b/packages/dashboard/test/dashboard.test.ts
index 24b75de06d..3fc85a7981 100644
--- a/packages/dashboard/test/dashboard.test.ts
+++ b/packages/dashboard/test/dashboard.test.ts
@@ -1,11 +1,12 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
+import sinon from 'sinon';
import '../vaadin-dashboard.js';
import type { CustomElementType } from '@vaadin/component-base/src/define.js';
import type { Dashboard, DashboardItem } from '../vaadin-dashboard.js';
import { getDraggable, getElementFromCell, setGap, setMaximumColumnWidth, setMinimumColumnWidth } from './helpers.js';
-type TestDashboardItem = DashboardItem & { id: string };
+type TestDashboardItem = DashboardItem & { id: string; component?: Element | string };
describe('dashboard', () => {
let dashboard: Dashboard;
@@ -160,4 +161,63 @@ describe('dashboard', () => {
expect(draggable.getBoundingClientRect().height).to.be.above(0);
});
});
+
+ describe('item components', () => {
+ it('should use the item component as widget', async () => {
+ const widget = fixtureSync('');
+ dashboard.items = [{ id: 'Item 0', component: widget }];
+ await nextFrame();
+
+ expect(getElementFromCell(dashboard, 0, 0)).to.equal(widget);
+ });
+
+ it('should render default widgets if component is not an element', async () => {
+ dashboard.items = [{ id: 'Item 0', component: 'not-an-element' }];
+ await nextFrame();
+
+ const widget = getElementFromCell(dashboard, 0, 0);
+ expect(widget).to.be.ok;
+ expect(widget?.localName).to.equal('vaadin-dashboard-widget');
+ expect(widget).to.have.property('widgetTitle', 'Item 0 title');
+ });
+
+ it('should not call renderer for item components', async () => {
+ const renderer = sinon.spy();
+ dashboard.renderer = renderer;
+ const widget = fixtureSync('');
+ dashboard.items = [{ id: 'Item 0', component: widget }];
+ await nextFrame();
+
+ expect(renderer).to.not.be.called;
+ });
+
+ it('should use the item component as section', async () => {
+ const section = fixtureSync(``);
+ const widget = fixtureSync('');
+ (dashboard as any).items = [
+ {
+ component: section,
+ items: [{ id: 'Item 0', component: widget }],
+ },
+ ];
+ await nextFrame();
+
+ expect(getElementFromCell(dashboard, 0, 0)).to.equal(widget);
+ expect(section.contains(widget)).to.be.true;
+ });
+
+ it('should render default section if component is not an element', async () => {
+ (dashboard as any).items = [{ component: 'not-an-element', title: 'Section', items: [{ id: 'Item 0' }] }];
+ await nextFrame();
+
+ const widget = getElementFromCell(dashboard, 0, 0);
+ expect(widget).to.be.ok;
+ expect(widget?.localName).to.equal('vaadin-dashboard-widget');
+ expect(widget).to.have.property('widgetTitle', 'Item 0 title');
+
+ const section = widget?.closest('vaadin-dashboard-section');
+ expect(section).to.be.ok;
+ expect(section?.sectionTitle).to.equal('Section');
+ });
+ });
});