Check if config is compatible with UI editor (#2137)

* Check config on loading UI editor

* Clean up

* Add theme and reload editor on reopen

* Reload config element on card type change

* Clean

* Clean

* Add superstruct for config validation

* Take content logic out off return edit-card

* Reverse  logic to prevent unnecessary yaml load
This commit is contained in:
Bram Kragten 2018-11-29 15:00:30 +01:00 committed by GitHub
parent 5e43d9b6b7
commit 90cea56a1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 28 deletions

View File

@ -87,6 +87,7 @@
"react-big-calendar": "^0.19.2",
"regenerator-runtime": "^0.12.1",
"round-slider": "^1.3.2",
"superstruct": "^0.6.0",
"unfetch": "^4.0.1",
"web-animations-js": "^2.3.1",
"xss": "^1.0.3"

View File

@ -1,11 +1,13 @@
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
import { struct } from "superstruct";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-toggle-button/paper-toggle-button";
import { processEditorEntities } from "../process-editor-entities";
import { EntitiesEditorEvent, EditorTarget } from "../types";
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
import { HomeAssistant } from "../../../../types";
@ -20,6 +22,24 @@ import "../../components/hui-entity-editor";
import "../../../../components/ha-card";
import "../../../../components/ha-icon";
const entitiesConfigStruct = struct.union([
{
entity: "string",
name: "string?",
icon: "string?",
},
"string",
]);
const cardConfigStruct = struct({
type: "string",
id: "string|number",
title: "string|number?",
theme: "string?",
show_header_toggle: "boolean?",
entities: [entitiesConfigStruct],
});
export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
implements LovelaceCardEditor {
public hass?: HomeAssistant;
@ -39,6 +59,8 @@ export class HuiEntitiesCardEditor extends hassLocalizeLitMixin(LitElement)
}
public setConfig(config: Config): void {
config = cardConfigStruct(config);
this._config = { type: "entities", ...config };
this._configEntities = processEditorEntities(config.entities);
}

View File

@ -20,7 +20,7 @@ declare global {
}
const dialogShowEvent = "show-edit-card";
const dialogTag = "hui-dialog-edit-config";
const dialogTag = "hui-dialog-edit-card";
export interface EditCardDialogParams {
cardConfig: LovelaceCardConfig;
@ -81,7 +81,7 @@ export class HuiDialogEditCard extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"hui-dialog-edit-config": HuiDialogEditCard;
"hui-dialog-edit-card": HuiDialogEditCard;
}
}

View File

@ -53,6 +53,8 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
private _loading?: boolean;
private _isToggleAvailable?: boolean;
private _saving: boolean;
private _errorMsg?: TemplateResult;
private _cardType?: string;
static get properties(): PropertyDeclarations {
return {
@ -62,6 +64,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
_configElement: {},
_configValue: {},
_configState: {},
_errorMsg: {},
_uiEditor: {},
_saving: {},
_loading: {},
@ -77,14 +80,11 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
set cardConfig(cardConfig: LovelaceCardConfig) {
this._originalConfig = cardConfig;
if (String(cardConfig.id) !== this._cardId) {
this._loading = true;
this._uiEditor = true;
this._configElement = undefined;
this._configValue = { format: "yaml", value: undefined };
this._configState = "OK";
this._isToggleAvailable = false;
this._uiEditor = true;
this._cardId = String(cardConfig.id);
this._loadConfigElement();
this._loadConfigElement(cardConfig);
}
}
@ -105,6 +105,24 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
}
protected render(): TemplateResult {
let content;
if (!this._configElement !== undefined) {
if (this._uiEditor) {
content = html`
<div class="element-editor">${this._configElement}</div>
`;
} else {
content = html`
<hui-yaml-editor
.hass="${this.hass}"
.cardId="${this._cardId}"
.yaml="${this._configValue!.value}"
@yaml-changed="${this._handleYamlChanged}"
></hui-yaml-editor>
`;
}
}
return html`
${this.renderStyle()}
<paper-dialog with-backdrop>
@ -118,19 +136,13 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
class="${classMap({ hidden: this._loading! })}"
>
${
this._uiEditor && this._configElement !== null
this._errorMsg
? html`
<div class="element-editor">${this._configElement}</div>
`
: html`
<hui-yaml-editor
.hass="${this.hass}"
.cardId="${this._cardId}"
.yaml="${this._configValue!.value}"
@yaml-changed="${this._handleYamlChanged}"
></hui-yaml-editor>
<div class="error">${this._errorMsg}</div>
`
: ""
}
${content}
<hr />
<hui-card-preview .hass="${this.hass}"></hui-card-preview>
</paper-dialog-scrollable>
@ -164,7 +176,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
>
</div>
`
: html``
: ""
}
</paper-dialog>
`;
@ -200,6 +212,10 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
.element-editor {
margin-bottom: 8px;
}
.error {
color: #ef5350;
border-bottom: 1px solid #ef5350;
}
hr {
color: #000;
opacity: 0.12;
@ -213,7 +229,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
`;
}
private _toggleEditor(): void {
private async _toggleEditor(): Promise<void> {
if (!this._isToggleAvailable) {
alert("You can't switch editor.");
return;
@ -235,9 +251,13 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
schema: extYamlSchema,
}),
};
this._configElement.setConfig(this._configValue!
.value as LovelaceCardConfig);
this._uiEditor = !this._uiEditor;
const cardConfig = this._configValue!.value! as LovelaceCardConfig;
if (cardConfig.type !== this._cardType) {
await this._loadConfigElement(cardConfig);
this._cardType = cardConfig.type;
}
this._configElement.setConfig(cardConfig);
}
this._resizeDialog();
}
@ -263,6 +283,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
}
private _closeDialog(): void {
this._cardId = undefined;
this._dialog.close();
}
@ -363,11 +384,16 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
return JSON.stringify(configValue) !== JSON.stringify(this._originalConfig);
}
private async _loadConfigElement(): Promise<void> {
if (!this._originalConfig) {
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<void> {
if (!conf) {
return;
}
const conf = this._originalConfig;
this._errorMsg = undefined;
this._loading = true;
this._configElement = undefined;
this._isToggleAvailable = false;
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
? conf!.type.substr(CUSTOM_TYPE_PREFIX.length)
: `hui-${conf!.type}-card`;
@ -378,20 +404,30 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
try {
configElement = await elClass.getConfigElement();
} catch (err) {
this._configElement = null;
this._uiEditor = false;
this._configElement = null;
return;
}
this._isToggleAvailable = true;
try {
configElement.setConfig(conf);
} catch (err) {
this._errorMsg = html`
Your config is not supported by the UI editor:<br /><b>${err.message}</b
><br />Falling back to YAML editor.
`;
this._uiEditor = false;
this._configElement = null;
return;
}
configElement.setConfig(conf);
configElement.hass = this.hass;
configElement.addEventListener("config-changed", (ev) =>
this._handleUIConfigChanged(ev.detail.config)
);
this._configValue = { format: "json", value: conf };
this._configElement = configElement;
this._isToggleAvailable = true;
this._updatePreview(conf);
}
}

View File

@ -4226,6 +4226,16 @@ clone-buffer@^1.0.0:
resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg=
clone-deep@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713"
integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==
dependencies:
for-own "^1.0.0"
is-plain-object "^2.0.4"
kind-of "^6.0.0"
shallow-clone "^1.0.0"
clone-stats@^0.0.1, clone-stats@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
@ -6452,6 +6462,11 @@ follow-redirects@^1.0.0:
dependencies:
debug "=3.1.0"
for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@ -8556,7 +8571,7 @@ kind-of@^5.0.0:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
kind-of@^6.0.0, kind-of@^6.0.2:
kind-of@^6.0.0, kind-of@^6.0.1, kind-of@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
@ -9716,6 +9731,14 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mixin-object@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e"
integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=
dependencies:
for-in "^0.1.3"
is-extendable "^0.1.1"
mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@ -12267,6 +12290,15 @@ shady-css-parser@^0.1.0:
resolved "https://registry.yarnpkg.com/shady-css-parser/-/shady-css-parser-0.1.0.tgz#534dc79c8ca5884c5ed92a4e5a13d6d863bca428"
integrity sha512-irfJUUkEuDlNHKZNAp2r7zOyMlmbfVJ+kWSfjlCYYUx/7dJnANLCyTzQZsuxy5NJkvtNwSxY5Gj8MOlqXUQPyA==
shallow-clone@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571"
integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==
dependencies:
is-extendable "^0.1.1"
kind-of "^5.0.0"
mixin-object "^2.0.1"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@ -12903,6 +12935,14 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
superstruct@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.6.0.tgz#20d2073526cf683a57f258695e009c4a19134ad0"
integrity sha512-6Y+bh5oFXCMUmGGzcdwd8M2qXMWn9aH3Qu2wV8Cg/Lxu+3fTxJ0dTx54nKd/Sm3lSz3i901xVatzev7c/xN8Lg==
dependencies:
clone-deep "^2.0.1"
kind-of "^6.0.1"
supports-color@3.1.2, supports-color@5.4.0, supports-color@^0.2.0, supports-color@^2.0.0, supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"