From ec6a7ae5a4013e886a2ca2f6626ebc09bfff6900 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 22 Mar 2022 17:13:21 +0200 Subject: [PATCH] Add dashboard and widget reference to widget settings form. Add default value method. --- .../json/system/widget_bundles/cards.json | 3 ++- .../add-widget-dialog.component.html | 5 ++-- .../dashboard-page/edit-widget.component.html | 5 ++-- .../data-key-config-dialog.component.html | 2 ++ .../data-key-config-dialog.component.ts | 5 +++- .../widget/data-key-config.component.html | 2 ++ .../widget/data-key-config.component.ts | 9 ++++++- .../components/widget/data-keys.component.ts | 11 +++++++- .../qrcode-widget-settings.component.html | 3 ++- .../qrcode-widget-settings.component.ts | 26 ++++++++++++++----- .../widget/widget-config.component.html | 6 +++++ .../widget/widget-config.component.ts | 25 ++++++++---------- .../widget/widget-settings.component.ts | 14 ++++++++-- ui-ngx/src/app/shared/models/widget.models.ts | 17 +++++++++--- 14 files changed, 96 insertions(+), 37 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index e852bcc9d5..90159f1b2c 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -163,8 +163,9 @@ "templateHtml": "\n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"QR Code\",\n \"properties\": {\n \"qrCodeTextPattern\": {\n \"title\": \"QR code text pattern (for ex. '${entityName} | ${keyName} - some text.')\",\n \"type\": \"string\",\n \"default\": \"${entityName}\"\n },\n \"useQrCodeTextFunction\": {\n \"title\": \"Use QR code text function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"qrCodeTextFunction\": {\n \"title\": \"QR code text function: f(data)\",\n \"type\": \"string\",\n \"default\": \"return data[0]['entityName'];\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useQrCodeTextFunction\",\n {\n \"key\": \"qrCodeTextPattern\",\n \"condition\": \"model.useQrCodeTextFunction !== true\"\n },\n {\n \"key\": \"qrCodeTextFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/qrcode/qrcode_text_fn\",\n \"condition\": \"model.useQrCodeTextFunction === true\"\n }\n ]\n}\n", + "settingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-qrcode-widget-settings", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}" } }, diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html index 27c1e58b7d..99f5e6ebfd 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html @@ -33,9 +33,8 @@ diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html index 1305f08980..c3b80e541f 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html @@ -20,9 +20,8 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html index 371a8f6456..9cfe9611fc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html @@ -33,6 +33,8 @@ [dataKeySettingsSchema]="data.dataKeySettingsSchema" [dataKeySettingsDirective]="data.dataKeySettingsDirective" [entityAliasId]="data.entityAliasId" + [dashboard]="data.dashboard" + [widget]="data.widget" [showPostProcessing]="data.showPostProcessing" [callbacks]="data.callbacks" formControlName="dataKey"> diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts index 48957543d5..4e88f389dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts @@ -22,14 +22,17 @@ import { AppState } from '@core/core.state'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, Widget } from '@shared/models/widget.models'; import { DataKeysCallbacks } from './data-keys.component.models'; import { DataKeyConfigComponent } from '@home/components/widget/data-key-config.component'; +import { Dashboard } from '@shared/models/dashboard.models'; export interface DataKeyConfigDialogData { dataKey: DataKey; dataKeySettingsSchema: any; dataKeySettingsDirective: string; + dashboard: Dashboard; + widget: Widget; entityAliasId?: string; showPostProcessing?: boolean; callbacks?: DataKeysCallbacks; diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html index eba2fc6bc9..f6e9acda1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html @@ -100,6 +100,8 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts index 03d72101f1..9c23be8144 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts @@ -18,7 +18,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { DataKey } from '@shared/models/widget.models'; +import { DataKey, Widget } from '@shared/models/widget.models'; import { ControlValueAccessor, FormBuilder, @@ -41,6 +41,7 @@ import { alarmFields } from '@shared/models/alarm.models'; import { JsFuncComponent } from '@shared/components/js-func.component'; import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; import { WidgetService } from '@core/http/widget.service'; +import { Dashboard } from '@shared/models/dashboard.models'; @Component({ selector: 'tb-data-key-config', @@ -69,6 +70,12 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @Input() callbacks: DataKeysCallbacks; + @Input() + dashboard: Dashboard; + + @Input() + widget: Widget; + @Input() dataKeySettingsSchema: any; diff --git a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts index 63e72905e7..75f15327c5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts @@ -46,7 +46,7 @@ import { MatAutocomplete } from '@angular/material/autocomplete'; import { MatChipInputEvent, MatChipList } from '@angular/material/chips'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { DataKey, DatasourceType, Widget, widgetType } from '@shared/models/widget.models'; import { IAliasController } from '@core/api/widget-api.models'; import { DataKeysCallbacks } from './data-keys.component.models'; import { alarmFields } from '@shared/models/alarm.models'; @@ -61,6 +61,7 @@ import { } from '@home/components/widget/data-key-config-dialog.component'; import { deepClone } from '@core/utils'; import { MatChipDropEvent } from '@app/shared/components/mat-chip-draggable.directive'; +import { Dashboard } from '@shared/models/dashboard.models'; @Component({ selector: 'tb-data-keys', @@ -116,6 +117,12 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie @Input() dataKeySettingsDirective: string; + @Input() + dashboard: Dashboard; + + @Input() + widget: Widget; + @Input() callbacks: DataKeysCallbacks; @@ -399,6 +406,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie dataKey: deepClone(key), dataKeySettingsSchema: this.datakeySettingsSchema, dataKeySettingsDirective: this.dataKeySettingsDirective, + dashboard: this.dashboard, + widget: this.widget, entityAliasId: this.entityAliasId, showPostProcessing: this.widgetType !== widgetType.alarm, callbacks: this.callbacks diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html index 6efcc3611b..29a10e1d58 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.html @@ -28,7 +28,8 @@
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts index c9ed32b85a..06e8d7d079 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/qrcode-widget-settings.component.ts @@ -14,12 +14,12 @@ /// limitations under the License. /// -import { Component } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; - +import { JsFuncComponent } from '@shared/components/js-func.component'; @Component({ selector: 'tb-qrcode-widget-settings', @@ -28,6 +28,8 @@ import { AppState } from '@core/core.state'; }) export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { + @ViewChild('qrCodeTextFunctionComponent', {static: true}) qrCodeTextFunctionComponent: JsFuncComponent; + qrCodeWidgetSettingsForm: FormGroup; constructor(protected store: Store, @@ -39,11 +41,19 @@ export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { return this.qrCodeWidgetSettingsForm; } + protected defaultSettings(): WidgetSettings { + return { + qrCodeTextPattern: '${entityName}', + useQrCodeTextFunction: false, + qrCodeTextFunction: 'return data[0][\'entityName\'];' + }; + } + protected onSettingsSet(settings: WidgetSettings) { this.qrCodeWidgetSettingsForm = this.fb.group({ - qrCodeTextPattern: [settings ? settings.qrCodeTextPattern : '${entityName}', []], - useQrCodeTextFunction: [settings ? settings.useQrCodeTextFunction : false, []], - qrCodeTextFunction: [settings ? settings.qrCodeTextFunction : 'return data[0][\'entityName\'];', []] + qrCodeTextPattern: [settings.qrCodeTextPattern, []], + useQrCodeTextFunction: [settings.useQrCodeTextFunction, []], + qrCodeTextFunction: [settings.qrCodeTextFunction, []] }); } @@ -55,7 +65,9 @@ export class QrCodeWidgetSettingsComponent extends WidgetSettingsComponent { const useQrCodeTextFunction: boolean = this.qrCodeWidgetSettingsForm.get('useQrCodeTextFunction').value; if (useQrCodeTextFunction) { this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([]); - this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([Validators.required]); + this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([Validators.required, + (control: AbstractControl) => this.qrCodeTextFunctionComponent.validate(control as FormControl) + ]); } else { this.qrCodeWidgetSettingsForm.get('qrCodeTextPattern').setValidators([Validators.required]); this.qrCodeWidgetSettingsForm.get('qrCodeTextFunction').setValidators([]); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 9bfbcc71a9..4fc8387a93 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -191,6 +191,8 @@ [aliasController]="aliasController" [datakeySettingsSchema]="modelValue?.dataKeySettingsSchema" [dataKeySettingsDirective]="modelValue?.dataKeySettingsDirective" + [dashboard]="dashboard" + [widget]="widget" [callbacks]="widgetConfigCallbacks" [entityAliasId]="datasourceControl.get('entityAliasId').value" [formControl]="datasourceControl.get('dataKeys')"> @@ -285,6 +287,8 @@ [aliasController]="aliasController" [datakeySettingsSchema]="modelValue?.dataKeySettingsSchema" [dataKeySettingsDirective]="modelValue?.dataKeySettingsDirective" + [dashboard]="dashboard" + [widget]="widget" [callbacks]="widgetConfigCallbacks" [entityAliasId]="alarmSourceSettings.get('entityAliasId').value" formControlName="dataKeys"> @@ -480,6 +484,8 @@ fxLayout="column"> diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index b3325e0de0..52bfb548ea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -25,7 +25,7 @@ import { datasourceTypeTranslationMap, defaultLegendConfig, GroupInfo, - JsonSchema, + JsonSchema, Widget, widgetType } from '@shared/models/widget.models'; import { @@ -65,7 +65,7 @@ import { MatDialog } from '@angular/material/dialog'; import { EntityService } from '@core/http/entity.service'; import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; import { WidgetActionsData } from './action/manage-widget-actions.component.models'; -import { DashboardState } from '@shared/models/dashboard.models'; +import { Dashboard, DashboardState } from '@shared/models/dashboard.models'; import { entityFields } from '@shared/models/entity.models'; import { Filter, Filters } from '@shared/models/query/query.models'; import { FilterDialogComponent, FilterDialogData } from '@home/components/filter/filter-dialog.component'; @@ -126,17 +126,14 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont aliasController: IAliasController; @Input() - entityAliases: EntityAliases; + dashboard: Dashboard; @Input() - filters: Filters; + widget: Widget; @Input() functionsOnly: boolean; - @Input() - dashboardStates: {[id: string]: DashboardState }; - @Input() disabled: boolean; widgetType: widgetType; @@ -831,14 +828,14 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont data: { isAdd: true, allowedEntityTypes, - entityAliases: this.entityAliases, + entityAliases: this.dashboard.configuration.entityAliases, alias: singleEntityAlias } }).afterClosed().pipe( tap((entityAlias) => { if (entityAlias) { - this.entityAliases[entityAlias.id] = entityAlias; - this.aliasController.updateEntityAliases(this.entityAliases); + this.dashboard.configuration.entityAliases[entityAlias.id] = entityAlias; + this.aliasController.updateEntityAliases(this.dashboard.configuration.entityAliases); } }) ); @@ -852,14 +849,14 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { isAdd: true, - filters: this.filters, + filters: this.dashboard.configuration.filters, filter: singleFilter } }).afterClosed().pipe( tap((result) => { if (result) { - this.filters[result.id] = result; - this.aliasController.updateFilters(this.filters); + this.dashboard.configuration.filters[result.id] = result; + this.aliasController.updateFilters(this.dashboard.configuration.filters); } }) ); @@ -881,7 +878,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont } private fetchDashboardStates(query: string): Array { - const stateIds = Object.keys(this.dashboardStates); + const stateIds = Object.keys(this.dashboard.configuration.states); const result = query ? stateIds.filter(this.createFilterForDashboardState(query)) : stateIds; if (result && result.length) { return result; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts index 7b5e3f1b0f..9226c4e1f1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-settings.component.ts @@ -40,8 +40,9 @@ import { deepClone } from '@core/utils'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { JsonFormComponent } from '@shared/components/json-form/json-form.component'; import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; -import { IWidgetSettingsComponent, WidgetSettings } from '@shared/models/widget.models'; +import { IWidgetSettingsComponent, Widget, WidgetSettings } from '@shared/models/widget.models'; import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; +import { Dashboard } from '@shared/models/dashboard.models'; @Component({ selector: 'tb-widget-settings', @@ -62,6 +63,12 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On @Input() disabled: boolean; + @Input() + dashboard: Dashboard; + + @Input() + widget: Widget; + settingsDirectiveValue: string; @Input() @@ -134,6 +141,8 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.changeSubscription = null; } if (this.definedSettingsComponent) { + this.definedSettingsComponent.dashboard = this.dashboard; + this.definedSettingsComponent.widget = this.widget; this.definedSettingsComponent.settings = this.widgetSettingsFormData.model; this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => { this.updateModel(settings); @@ -185,7 +194,8 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On const factory = this.cfr.resolveComponentFactory(componentType); this.definedSettingsComponentRef = this.definedSettingsContainer.createComponent(factory); this.definedSettingsComponent = this.definedSettingsComponentRef.instance; - this.definedSettingsComponent.settings = this.widgetSettingsFormData?.model; + this.definedSettingsComponent.dashboard = this.dashboard; + this.definedSettingsComponent.widget = this.widget; this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => { this.updateModel(settings); }); diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 45c8943753..0d94484f7a 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -33,6 +33,7 @@ import { AbstractControl, FormGroup } from '@angular/forms'; import {RuleChainType} from "@shared/models/rule-chain.models"; import {Observable} from "rxjs"; import {RuleNodeConfiguration} from "@shared/models/rule-node.models"; +import { Dashboard } from '@shared/models/dashboard.models'; export enum widgetType { timeseries = 'timeseries', @@ -587,6 +588,8 @@ export interface WidgetSize { } export interface IWidgetSettingsComponent { + dashboard: Dashboard; + widget: Widget; settings: WidgetSettings; settingsChanged: Observable; validate(); @@ -598,17 +601,21 @@ export interface IWidgetSettingsComponent { export abstract class WidgetSettingsComponent extends PageComponent implements IWidgetSettingsComponent, OnInit, AfterViewInit { + dashboard: Dashboard; + + widget: Widget; + settingsValue: WidgetSettings; private settingsSet = false; set settings(value: WidgetSettings) { - this.settingsValue = value; + this.settingsValue = value || this.defaultSettings(); if (!this.settingsSet) { this.settingsSet = true; - this.setupSettings(value); + this.setupSettings(this.settingsValue); } else { - this.updateSettings(value); + this.updateSettings(this.settingsValue); } } @@ -694,4 +701,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements protected abstract onSettingsSet(settings: WidgetSettings); + protected defaultSettings(): WidgetSettings { + return {}; + } + }