Skip to content

Commit b6e86e7

Browse files
chore(dashboards-demo): add native web component widget to showcase framework-agnostic integration
Add `native-note-widget` and `native-note-widget-editor` as vanilla custom elements. Registered in the flexible dashboard widget catalog alongside existing Angular-based web component widgets.
1 parent 60bc1c1 commit b6e86e7

3 files changed

Lines changed: 180 additions & 0 deletions

File tree

projects/dashboards-demo/src/app/pages/dashboard/dashboard.component.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,25 @@ export class DashboardPageComponent {
9494
}
9595
}
9696
},
97+
{
98+
id: 'native-note-widget-web-component',
99+
name: 'Note (native web-component)',
100+
description:
101+
'Framework-agnostic native web component widget showcasing integration of vanilla custom elements in the flexible dashboard.',
102+
componentFactory: {
103+
factoryType: 'web-component',
104+
url: `${environment.webComponentsBaseUrl}/webcomponent-widgets.js`,
105+
componentName: 'native-note-widget',
106+
editorComponentName: 'native-note-widget-editor'
107+
},
108+
defaults: {
109+
width: 6,
110+
height: 2
111+
},
112+
payload: {
113+
message: 'Your private notes (native web-component).'
114+
}
115+
},
97116
DOWNLOAD_WIDGET,
98117
UPLOAD_WIDGET
99118
];

projects/dashboards-demo/webcomponents/src/app/app.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { SiThemeService } from '@siemens/element-ng/theme';
1212
import { ChartWidgetComponent } from './chart-widget/chart-widget.component';
1313
import { ContactWidgetEditorComponent } from './contact-widget-editor/contact-widget-editor.component';
1414
import { ContactWidgetComponent } from './contact-widget/contact-widget.component';
15+
import './native-note-widget/native-note-widget';
1516
import { NoteWidgetEditorComponent } from './note-widget-editor/note-widget-editor.component';
1617
import { NoteWidgetComponent } from './note-widget/note-widget.component';
1718

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* Copyright (c) Siemens 2016 - 2026
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* Framework-agnostic native web component widgets for use in SiFlexibleDashboard.
6+
* Demonstrates that dashboard widgets can be implemented without any framework dependency.
7+
* No Shadow DOM — reuses CSS classes from the parent Angular application.
8+
*/
9+
10+
interface WidgetConfig {
11+
id?: string;
12+
heading?: string;
13+
payload?: Record<string, unknown>;
14+
}
15+
16+
// --- Native Note Widget ---
17+
18+
class NativeNoteWidget extends HTMLElement {
19+
private _config: WidgetConfig = {};
20+
private _editable = false;
21+
private messageEl!: HTMLParagraphElement;
22+
private editHintEl!: HTMLParagraphElement;
23+
24+
get config(): WidgetConfig {
25+
return this._config;
26+
}
27+
28+
set config(value: WidgetConfig) {
29+
this._config = value ?? {};
30+
this.render();
31+
}
32+
33+
get editable(): boolean {
34+
return this._editable;
35+
}
36+
37+
set editable(value: boolean) {
38+
this._editable = value;
39+
this.editHintEl.hidden = !value;
40+
}
41+
42+
private render(): void {
43+
if (!this.messageEl) {
44+
return;
45+
}
46+
const message = (this.config?.payload?.message as string) ?? '';
47+
this.messageEl.textContent = message;
48+
}
49+
50+
connectedCallback(): void {
51+
this.innerHTML = `
52+
<p class="message"></p>
53+
<p class="edit-hint" hidden>Click edit to configure this native web-component widget.</p>
54+
`;
55+
this.messageEl = this.querySelector('.message')!;
56+
this.editHintEl = this.querySelector('.edit-hint')!;
57+
this.editHintEl.hidden = !this._editable;
58+
this.render();
59+
this.dispatchEvent(
60+
new CustomEvent('configChange', {
61+
detail: {
62+
primaryActions: [
63+
{
64+
type: 'action',
65+
label: 'User',
66+
icon: 'element-user',
67+
action: () => alert('Custom primary action.')
68+
}
69+
],
70+
secondaryActions: [
71+
{
72+
type: 'action',
73+
label: 'Greenleaf',
74+
icon: 'element-greenleaf',
75+
action: () => alert('Custom secondary action.')
76+
}
77+
],
78+
primaryEditActions: [
79+
{
80+
type: 'action',
81+
label: 'Custom Action',
82+
icon: 'element-airquality-good',
83+
action: () => alert('Widget specific edit action.')
84+
}
85+
],
86+
secondaryEditActions: [
87+
{
88+
type: 'action',
89+
label: 'Light',
90+
icon: 'element-light-ceiling',
91+
action: () => alert('Widget specific secondary edit action.')
92+
}
93+
]
94+
}
95+
})
96+
);
97+
}
98+
}
99+
100+
// --- Native Note Widget Editor ---
101+
102+
class NativeNoteWidgetEditor extends HTMLElement {
103+
private _config: WidgetConfig = {};
104+
private headingInput!: HTMLInputElement;
105+
private messageInput!: HTMLInputElement;
106+
107+
get config(): WidgetConfig {
108+
return this._config;
109+
}
110+
111+
set config(value: WidgetConfig) {
112+
this._config = value ?? {};
113+
this.render();
114+
}
115+
116+
private render(): void {
117+
if (!this.headingInput) {
118+
return;
119+
}
120+
this.headingInput.value = this.config?.heading ?? '';
121+
this.messageInput.value = (this.config?.payload?.message as string) ?? '';
122+
}
123+
124+
private onChange(): void {
125+
const heading = this.headingInput.value;
126+
const message = this.messageInput.value;
127+
128+
this._config.heading = heading;
129+
this._config.payload ??= {};
130+
this._config.payload.message = message;
131+
132+
this.dispatchEvent(new CustomEvent('configChange', { detail: this._config }));
133+
this.dispatchEvent(
134+
new CustomEvent('statusChanges', {
135+
detail: { invalid: heading.trim().length === 0, modified: true }
136+
})
137+
);
138+
}
139+
140+
connectedCallback(): void {
141+
this.innerHTML = `
142+
<div class="mb-6">
143+
<label for="native-heading" class="form-label">Title</label>
144+
<input type="text" class="form-control" id="native-heading" />
145+
</div>
146+
<div class="mb-6">
147+
<label for="native-message" class="form-label">Message</label>
148+
<input type="text" class="form-control" id="native-message" />
149+
</div>
150+
`;
151+
this.headingInput = this.querySelector('#native-heading')!;
152+
this.messageInput = this.querySelector('#native-message')!;
153+
this.headingInput.addEventListener('input', () => this.onChange());
154+
this.messageInput.addEventListener('input', () => this.onChange());
155+
this.render();
156+
}
157+
}
158+
159+
customElements.define('native-note-widget', NativeNoteWidget);
160+
customElements.define('native-note-widget-editor', NativeNoteWidgetEditor);

0 commit comments

Comments
 (0)