From 03828d2a1f633769c5799cce68ab105c71d6dec0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 4 Apr 2022 15:58:58 +0300 Subject: [PATCH] UI: Add alarms table widget/key settings forms --- .../system/widget_bundles/alarm_widgets.json | 6 +- .../alarms-table-key-settings.component.html | 95 +++++++++++++++ .../alarms-table-key-settings.component.ts | 86 ++++++++++++++ ...larms-table-widget-settings.component.html | 110 ++++++++++++++++++ .../alarms-table-widget-settings.component.ts | 104 +++++++++++++++++ .../lib/settings/widget-settings.module.ts | 18 ++- .../assets/locale/locale.constant-en_US.json | 9 +- 7 files changed, 422 insertions(+), 6 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts diff --git a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json index a5b5146004..448fcaf52c 100644 --- a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json @@ -19,8 +19,10 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableFilter\": {\n \"title\": \"Enable alarm filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"reserveSpaceForHiddenAction\": {\n \"title\": \"Hidden cell button actions display mode\",\n \"type\": \"string\",\n \"default\": \"true\"\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableFilter\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n {\n \"key\": \"reserveSpaceForHiddenAction\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"true\",\n \"label\": \"Show empty space instead of hidden cell button action\"\n },\n {\n \"value\": \"false\",\n \"label\": \"Don't reserve space for hidden action buttons\"\n }\n ]\n },\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/row_style_fn\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"defaultColumnVisibility\": {\n \"title\": \"Default column visibility\",\n \"type\": \"string\",\n \"default\": \"visible\"\n },\n \"columnSelectionToDisplay\": {\n \"title\": \"Column selection in 'Columns to Display'\",\n \"type\": \"string\",\n \"default\": \"enabled\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/cell_style_fn\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"helpId\": \"widget/lib/alarm/cell_content_fn\",\n \"condition\": \"model.useCellContentFunction === true\"\n },\n {\n \"key\": \"defaultColumnVisibility\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"visible\",\n \"label\": \"Visible\"\n },\n {\n \"value\": \"hidden\",\n \"label\": \"Hidden\"\n } \n ]\n },\n {\n \"key\": \"columnSelectionToDisplay\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"enabled\",\n \"label\": \"Enabled\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n } \n ]\n }\n ]\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-alarms-table-widget-settings", + "dataKeySettingsDirective": "tb-alarms-table-key-settings", "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html new file mode 100644 index 0000000000..6931d256e8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html @@ -0,0 +1,95 @@ + +
+ + widgets.table.column-width + + +
+ widgets.table.cell-style + + + + + {{ 'widgets.table.use-cell-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
+ widgets.table.cell-content + + + + + {{ 'widgets.table.use-cell-content-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+ + widgets.table.default-column-visibility + + + {{ 'widgets.table.column-visibility-visible' | translate }} + + + {{ 'widgets.table.column-visibility-hidden' | translate }} + + + + + widgets.table.column-selection-to-display + + + {{ 'widgets.table.column-selection-to-display-enabled' | translate }} + + + {{ 'widgets.table.column-selection-to-display-disabled' | translate }} + + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts new file mode 100644 index 0000000000..9f9cbbf5e1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts @@ -0,0 +1,86 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-alarms-table-key-settings', + templateUrl: './alarms-table-key-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class AlarmsTableKeySettingsComponent extends WidgetSettingsComponent { + + alarmsTableKeySettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.alarmsTableKeySettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + columnWidth: '0px', + useCellStyleFunction: false, + cellStyleFunction: '', + useCellContentFunction: false, + cellContentFunction: '', + defaultColumnVisibility: 'visible', + columnSelectionToDisplay: 'enabled' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.alarmsTableKeySettingsForm = this.fb.group({ + columnWidth: [settings.columnWidth, []], + useCellStyleFunction: [settings.useCellStyleFunction, []], + cellStyleFunction: [settings.cellStyleFunction, [Validators.required]], + useCellContentFunction: [settings.useCellContentFunction, []], + cellContentFunction: [settings.cellContentFunction, [Validators.required]], + defaultColumnVisibility: [settings.defaultColumnVisibility, []], + columnSelectionToDisplay: [settings.columnSelectionToDisplay, []], + }); + } + + protected validatorTriggers(): string[] { + return ['useCellStyleFunction', 'useCellContentFunction']; + } + + protected updateValidators(emitEvent: boolean) { + const useCellStyleFunction: boolean = this.alarmsTableKeySettingsForm.get('useCellStyleFunction').value; + const useCellContentFunction: boolean = this.alarmsTableKeySettingsForm.get('useCellContentFunction').value; + if (useCellStyleFunction) { + this.alarmsTableKeySettingsForm.get('cellStyleFunction').enable(); + } else { + this.alarmsTableKeySettingsForm.get('cellStyleFunction').disable(); + } + if (useCellContentFunction) { + this.alarmsTableKeySettingsForm.get('cellContentFunction').enable(); + } else { + this.alarmsTableKeySettingsForm.get('cellContentFunction').disable(); + } + this.alarmsTableKeySettingsForm.get('cellStyleFunction').updateValueAndValidity({emitEvent}); + this.alarmsTableKeySettingsForm.get('cellContentFunction').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html new file mode 100644 index 0000000000..3b5516c26c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html @@ -0,0 +1,110 @@ + +
+
+ widgets.table.common-table-settings + + widgets.table.alarms-table-title + + +
+
+ + {{ 'widgets.table.enable-alarms-selection' | translate }} + + + {{ 'widgets.table.enable-alarms-search' | translate }} + + + {{ 'widgets.table.enable-select-column-display' | translate }} + + + {{ 'widgets.table.enable-alarm-filter' | translate }} + +
+
+ + {{ 'widgets.table.enable-sticky-header' | translate }} + + + {{ 'widgets.table.enable-sticky-action' | translate }} + +
+
+ + widgets.table.hidden-cell-button-display-mode + + + {{ 'widgets.table.show-empty-space-hidden-action' | translate }} + + + {{ 'widgets.table.dont-reserve-space-hidden-action' | translate }} + + + +
+ + {{ 'widgets.table.display-alarm-details' | translate }} + + + {{ 'widgets.table.allow-alarms-ack' | translate }} + + + {{ 'widgets.table.allow-alarms-clear' | translate }} + +
+ + {{ 'widgets.table.display-pagination' | translate }} + + + widgets.table.default-page-size + + +
+ + widgets.table.default-sort-order + + +
+
+
+ widgets.table.row-style + + + + + {{ 'widgets.table.use-row-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts new file mode 100644 index 0000000000..c57ae7d6f5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts @@ -0,0 +1,104 @@ +/// +/// Copyright © 2016-2022 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-alarms-table-widget-settings', + templateUrl: './alarms-table-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent { + + alarmsTableWidgetSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.alarmsTableWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + alarmsTitle: '', + enableSelection: true, + enableSearch: true, + enableSelectColumnDisplay: true, + enableFilter: true, + enableStickyHeader: true, + enableStickyAction: true, + reserveSpaceForHiddenAction: 'true', + displayDetails: true, + allowAcknowledgment: true, + allowClear: true, + displayPagination: true, + defaultPageSize: 10, + defaultSortOrder: '-createdTime', + useRowStyleFunction: false, + rowStyleFunction: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.alarmsTableWidgetSettingsForm = this.fb.group({ + alarmsTitle: [settings.alarmsTitle, []], + enableSelection: [settings.enableSelection, []], + enableSearch: [settings.enableSearch, []], + enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []], + enableFilter: [settings.enableFilter, []], + enableStickyHeader: [settings.enableStickyHeader, []], + enableStickyAction: [settings.enableStickyAction, []], + reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], + displayDetails: [settings.displayDetails, []], + allowAcknowledgment: [settings.allowAcknowledgment, []], + allowClear: [settings.allowClear, []], + displayPagination: [settings.displayPagination, []], + defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + defaultSortOrder: [settings.defaultSortOrder, []], + useRowStyleFunction: [settings.useRowStyleFunction, []], + rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] + }); + } + + protected validatorTriggers(): string[] { + return ['useRowStyleFunction', 'displayPagination']; + } + + protected updateValidators(emitEvent: boolean) { + const useRowStyleFunction: boolean = this.alarmsTableWidgetSettingsForm.get('useRowStyleFunction').value; + const displayPagination: boolean = this.alarmsTableWidgetSettingsForm.get('displayPagination').value; + if (useRowStyleFunction) { + this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').enable(); + } else { + this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').disable(); + } + if (displayPagination) { + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').enable(); + } else { + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').disable(); + } + this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 2beaed1d92..14e5cc21bf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -53,6 +53,12 @@ import { import { EntitiesTableKeySettingsComponent } from '@home/components/widget/lib/settings/cards/entities-table-key-settings.component'; +import { + AlarmsTableWidgetSettingsComponent +} from '@home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component'; +import { + AlarmsTableKeySettingsComponent +} from '@home/components/widget/lib/settings/alarm/alarms-table-key-settings.component'; @NgModule({ declarations: [ @@ -69,7 +75,9 @@ import { EntitiesHierarchyWidgetSettingsComponent, HtmlCardWidgetSettingsComponent, EntitiesTableWidgetSettingsComponent, - EntitiesTableKeySettingsComponent + EntitiesTableKeySettingsComponent, + AlarmsTableWidgetSettingsComponent, + AlarmsTableKeySettingsComponent ], imports: [ CommonModule, @@ -90,7 +98,9 @@ import { EntitiesHierarchyWidgetSettingsComponent, HtmlCardWidgetSettingsComponent, EntitiesTableWidgetSettingsComponent, - EntitiesTableKeySettingsComponent + EntitiesTableKeySettingsComponent, + AlarmsTableWidgetSettingsComponent, + AlarmsTableKeySettingsComponent ] }) export class WidgetSettingsModule { @@ -108,5 +118,7 @@ export const widgetSettingsComponentsMap: {[key: string]: Type