Skip to content

Commit 3782d9e

Browse files
chintankavathiaspike-rabbit
authored andcommitted
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 1a20450 commit 3782d9e

3 files changed

Lines changed: 182 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
@@ -96,6 +96,25 @@ export class DashboardPageComponent {
9696
}
9797
}
9898
},
99+
{
100+
id: 'native-note-widget-web-component',
101+
name: 'Note (native web-component)',
102+
description:
103+
'Framework-agnostic native web component widget showcasing integration of vanilla custom elements in the flexible dashboard',
104+
componentFactory: {
105+
factoryType: 'web-component',
106+
url: `${environment.webComponentsBaseUrl}/webcomponent-widgets.js`,
107+
componentName: 'native-note-widget',
108+
editorComponentName: 'native-note-widget-editor'
109+
},
110+
defaults: {
111+
width: 6,
112+
height: 2
113+
},
114+
payload: {
115+
message: 'Private notes (native web-component)'
116+
}
117+
},
99118
DOWNLOAD_WIDGET,
100119
UPLOAD_WIDGET
101120
];

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: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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+
if (this.editHintEl) {
40+
this.editHintEl.hidden = !value;
41+
}
42+
}
43+
44+
private render(): void {
45+
if (!this.messageEl) {
46+
return;
47+
}
48+
const message = (this.config?.payload?.message as string) ?? '';
49+
this.messageEl.textContent = message;
50+
}
51+
52+
connectedCallback(): void {
53+
this.innerHTML = `
54+
<p class="message"></p>
55+
<p class="edit-hint" hidden>Click edit to configure this native web-component widget.</p>
56+
`;
57+
this.messageEl = this.querySelector('.message')!;
58+
this.editHintEl = this.querySelector('.edit-hint')!;
59+
this.editHintEl.hidden = !this._editable;
60+
this.render();
61+
this.dispatchEvent(
62+
new CustomEvent('configChange', {
63+
detail: {
64+
primaryActions: [
65+
{
66+
type: 'action',
67+
label: 'User',
68+
icon: 'element-user',
69+
action: () => alert('Custom primary action')
70+
}
71+
],
72+
secondaryActions: [
73+
{
74+
type: 'action',
75+
label: 'Greenleaf',
76+
icon: 'element-greenleaf',
77+
action: () => alert('Custom secondary action')
78+
}
79+
],
80+
primaryEditActions: [
81+
{
82+
type: 'action',
83+
label: 'Custom Action',
84+
icon: 'element-airquality-good',
85+
action: () => alert('Widget specific edit action')
86+
}
87+
],
88+
secondaryEditActions: [
89+
{
90+
type: 'action',
91+
label: 'Light',
92+
icon: 'element-light-ceiling',
93+
action: () => alert('Widget specific secondary edit action')
94+
}
95+
]
96+
}
97+
})
98+
);
99+
}
100+
}
101+
102+
// --- Native Note Widget Editor ---
103+
104+
class NativeNoteWidgetEditor extends HTMLElement {
105+
private _config: WidgetConfig = {};
106+
private headingInput!: HTMLInputElement;
107+
private messageInput!: HTMLInputElement;
108+
109+
get config(): WidgetConfig {
110+
return this._config;
111+
}
112+
113+
set config(value: WidgetConfig) {
114+
this._config = value ?? {};
115+
this.render();
116+
}
117+
118+
private render(): void {
119+
if (!this.headingInput) {
120+
return;
121+
}
122+
this.headingInput.value = this.config?.heading ?? '';
123+
this.messageInput.value = (this.config?.payload?.message as string) ?? '';
124+
}
125+
126+
private onChange(): void {
127+
const heading = this.headingInput.value;
128+
const message = this.messageInput.value;
129+
130+
this._config.heading = heading;
131+
this._config.payload ??= {};
132+
this._config.payload.message = message;
133+
134+
this.dispatchEvent(new CustomEvent('configChange', { detail: this._config }));
135+
this.dispatchEvent(
136+
new CustomEvent('statusChanges', {
137+
detail: { invalid: heading.trim().length === 0, modified: true }
138+
})
139+
);
140+
}
141+
142+
connectedCallback(): void {
143+
this.innerHTML = `
144+
<div class="mb-6">
145+
<label for="native-heading" class="form-label">Title</label>
146+
<input type="text" class="form-control" id="native-heading" />
147+
</div>
148+
<div class="mb-6">
149+
<label for="native-message" class="form-label">Message</label>
150+
<input type="text" class="form-control" id="native-message" />
151+
</div>
152+
`;
153+
this.headingInput = this.querySelector('#native-heading')!;
154+
this.messageInput = this.querySelector('#native-message')!;
155+
this.headingInput.addEventListener('input', () => this.onChange());
156+
this.messageInput.addEventListener('input', () => this.onChange());
157+
this.render();
158+
}
159+
}
160+
161+
customElements.define('native-note-widget', NativeNoteWidget);
162+
customElements.define('native-note-widget-editor', NativeNoteWidgetEditor);

0 commit comments

Comments
 (0)