Skip to content

Commit

Permalink
feat: auto-update widget title level (#7740)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomivirkki authored Sep 4, 2024
1 parent 6586681 commit 158e64d
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 4 deletions.
14 changes: 13 additions & 1 deletion packages/dashboard/src/title-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
import { SlotChildObserveController } from '@vaadin/component-base/src/slot-child-observe-controller.js';

const DEFAULT_TITLE_LEVEL = '2';

/**
* A controller to manage the widget or section title element.
*/
Expand All @@ -21,6 +23,17 @@ export class TitleController extends SlotChildObserveController {
setTitle(title) {
this.title = title;

const titleLevel =
getComputedStyle(this.host).getPropertyValue('--_vaadin-dashboard-title-level') || DEFAULT_TITLE_LEVEL;
const newTagName = `h${titleLevel}`;
if (this.tagName !== newTagName) {
if (this.defaultNode) {
this.defaultNode.remove();
delete this.defaultNode;
}
this.tagName = newTagName;
}

// Restore the default title, if needed.
const titleNode = this.getSlotChild();
if (!titleNode) {
Expand All @@ -41,7 +54,6 @@ export class TitleController extends SlotChildObserveController {
* @override
*/
restoreDefaultNode() {
this.tagName = 'h2';
this.attachDefaultNode();
}

Expand Down
1 change: 1 addition & 0 deletions packages/dashboard/src/vaadin-dashboard-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class DashboardSection extends ControllerMixin(ElementMixin(PolylitMixin(LitElem
}
::slotted(*) {
--_vaadin-dashboard-title-level: 3;
--_vaadin-dashboard-item-column: span
min(
var(--vaadin-dashboard-item-colspan, 1),
Expand Down
21 changes: 19 additions & 2 deletions packages/dashboard/src/vaadin-dashboard-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme
});
}

/** @protected */
connectedCallback() {
super.connectedCallback();

const undefinedAncestor = this.closest('*:not(:defined)');
if (undefinedAncestor) {
customElements.whenDefined(undefinedAncestor.localName).then(() => queueMicrotask(() => this.__updateTitle()));
} else {
this.__updateTitle();
}
}

/** @protected */
ready() {
super.ready();
Expand All @@ -103,8 +115,13 @@ class DashboardWidget extends ControllerMixin(ElementMixin(PolylitMixin(LitEleme
}

/** @private */
__onWidgetTitleChanged(widgetTitle) {
this.__titleController.setTitle(widgetTitle);
__onWidgetTitleChanged() {
this.__updateTitle();
}

/** @private */
__updateTitle() {
this.__titleController.setTitle(this.widgetTitle);
}
}

Expand Down
134 changes: 133 additions & 1 deletion packages/dashboard/test/dashboard-widget.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { expect } from '@vaadin/chai-plugins';
import { fixtureSync, nextFrame } from '@vaadin/testing-helpers';
import '../vaadin-dashboard-widget.js';
import type { DashboardWidget } from '../vaadin-dashboard-widget.js';
import { DashboardSection } from '../vaadin-dashboard-section.js';
import { DashboardWidget } from '../vaadin-dashboard-widget.js';

describe('dashboard widget', () => {
let widget: DashboardWidget;
Expand Down Expand Up @@ -95,3 +96,134 @@ describe('dashboard widget', () => {
});
});
});

describe('widget title level', () => {
it('should have h2 title by default', async () => {
const widget = fixtureSync(`<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>`);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h2');
});

it('should have h2 title by default on the section', async () => {
const section = fixtureSync(`<vaadin-dashboard-section section-title="foo"></vaadin-dashboard-section>`);
await nextFrame();

const title = section.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h2');
});

it('should have h3 title when rendered inside a section', async () => {
const widget = fixtureSync(`
<vaadin-dashboard-section>
<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>
</vaadin-dashboard-section>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h3');
});

it('should have h2 title after moving out of a section', async () => {
const widget = fixtureSync(`
<div>
<vaadin-dashboard-section>
<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>
</vaadin-dashboard-section>
</div>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

const wrapper = widget.closest('div')!;
wrapper.appendChild(widget);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h2');
});

it('should have h3 title after moving into a section', async () => {
const widget = fixtureSync(`
<div>
<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>
<vaadin-dashboard-section></vaadin-dashboard-section>
</div>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

const section = widget.nextElementSibling as DashboardSection;
section.appendChild(widget);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h3');
});

it('should have h3 title after defining parent section', async () => {
const widget = fixtureSync(`
<my-custom-section>
<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>
</my-custom-section>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

class MyCustomSection extends DashboardSection {}
customElements.define('my-custom-section', MyCustomSection);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h3');
});

it('should have h3 title after defining the widget', async () => {
const widget = fixtureSync(`
<vaadin-dashboard-section>
<my-custom-widget widget-title="foo"></my-custom-widget>
</vaadin-dashboard-section>
`).querySelector('my-custom-widget')!;
await nextFrame();

class MyCustomWidget extends DashboardWidget {}
customElements.define('my-custom-widget', MyCustomWidget);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h3');
});

it('should have h3 title after moving a wrapped widget into a section', async () => {
const widget = fixtureSync(`
<div>
<div id="wrapper">
<vaadin-dashboard-widget widget-title="foo"></vaadin-dashboard-widget>
</div>
<vaadin-dashboard-section></vaadin-dashboard-section>
</div>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

const wrapper = widget.closest('div#wrapper')!;
const section = wrapper.nextElementSibling as DashboardSection;
section.appendChild(wrapper);
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h3');
});

it('should not replace an explicitly defined widget title element', async () => {
const widget = fixtureSync(`
<vaadin-dashboard-section>
<vaadin-dashboard-widget>
<h2 slot="title">foo</h2>
</vaadin-dashboard-widget>
</vaadin-dashboard-section>
`).querySelector('vaadin-dashboard-widget')!;
await nextFrame();

const title = widget.querySelector('[slot="title"]');
expect(title?.localName).to.equal('h2');
});
});

0 comments on commit 158e64d

Please sign in to comment.