From a590b1b4ed4950b4503cf3306f485db4e5fa5a9b Mon Sep 17 00:00:00 2001 From: Tomi Virkki Date: Thu, 5 Sep 2024 11:03:50 +0300 Subject: [PATCH] feat: support elements in dashboard items (#7761) --- packages/dashboard/src/vaadin-dashboard.js | 26 ++++++--- packages/dashboard/test/dashboard.test.ts | 62 +++++++++++++++++++++- 2 files changed, 80 insertions(+), 8 deletions(-) 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'); + }); + }); });