mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 17:56:46 +00:00
More comprehensive YAML config errors + dynamic checks for action configs (#8217)
This commit is contained in:
parent
5c0e151bc2
commit
0a3172dfdb
@ -120,7 +120,7 @@
|
|||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"roboto-fontface": "^0.10.0",
|
"roboto-fontface": "^0.10.0",
|
||||||
"sortablejs": "^1.10.2",
|
"sortablejs": "^1.10.2",
|
||||||
"superstruct": "^0.10.12",
|
"superstruct": "^0.10.13",
|
||||||
"tinykeys": "^1.1.1",
|
"tinykeys": "^1.1.1",
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
"vis-data": "^7.1.1",
|
"vis-data": "^7.1.1",
|
||||||
|
45
src/common/structs/handle-errors.ts
Normal file
45
src/common/structs/handle-errors.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { StructError } from "superstruct";
|
||||||
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
export const handleStructError = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
err: Error
|
||||||
|
): { warnings: string[]; errors?: string[] } => {
|
||||||
|
if (!(err instanceof StructError)) {
|
||||||
|
return { warnings: [err.message], errors: undefined };
|
||||||
|
}
|
||||||
|
const errors: string[] = [];
|
||||||
|
const warnings: string[] = [];
|
||||||
|
for (const failure of err.failures()) {
|
||||||
|
if (failure.value === undefined) {
|
||||||
|
errors.push(
|
||||||
|
hass.localize(
|
||||||
|
"ui.errors.config.key_missing",
|
||||||
|
"key",
|
||||||
|
failure.path.join(".")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (failure.type === "never") {
|
||||||
|
warnings.push(
|
||||||
|
hass.localize(
|
||||||
|
"ui.errors.config.key_not_expected",
|
||||||
|
"key",
|
||||||
|
failure.path.join(".")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
warnings.push(
|
||||||
|
hass.localize(
|
||||||
|
"ui.errors.config.key_wrong_type",
|
||||||
|
"key",
|
||||||
|
failure.path.join("."),
|
||||||
|
"type_correct",
|
||||||
|
failure.type,
|
||||||
|
"type_wrong",
|
||||||
|
JSON.stringify(failure.value)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { warnings, errors };
|
||||||
|
};
|
@ -27,7 +27,7 @@ import type { Action } from "../../../../data/script";
|
|||||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { handleStructError } from "../../../lovelace/common/structs/handle-errors";
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||||
import "./types/ha-automation-action-choose";
|
import "./types/ha-automation-action-choose";
|
||||||
import "./types/ha-automation-action-condition";
|
import "./types/ha-automation-action-condition";
|
||||||
import "./types/ha-automation-action-delay";
|
import "./types/ha-automation-action-delay";
|
||||||
@ -251,7 +251,7 @@ export default class HaAutomationActionRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
private _handleUiModeNotAvailable(ev: CustomEvent) {
|
||||||
this._warnings = handleStructError(ev.detail);
|
this._warnings = handleStructError(this.hass, ev.detail).warnings;
|
||||||
if (!this._yamlMode) {
|
if (!this._yamlMode) {
|
||||||
this._yamlMode = true;
|
this._yamlMode = true;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import type { HaYamlEditor } from "../../../../../components/ha-yaml-editor";
|
|||||||
import { ServiceAction } from "../../../../../data/script";
|
import { ServiceAction } from "../../../../../data/script";
|
||||||
import type { PolymerChangedEvent } from "../../../../../polymer-types";
|
import type { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { EntityIdOrAll } from "../../../../lovelace/common/structs/is-entity-id";
|
import { EntityIdOrAll } from "../../../../../common/structs/is-entity-id";
|
||||||
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
|
||||||
|
|
||||||
const actionStruct = object({
|
const actionStruct = object({
|
||||||
|
@ -52,6 +52,8 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
font-family: var(--code-font-family, monospace);
|
font-family: var(--code-font-family, monospace);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import { StructError } from "superstruct";
|
|
||||||
|
|
||||||
export const handleStructError = (err: Error): string[] => {
|
|
||||||
if (!(err instanceof StructError)) {
|
|
||||||
return [err.message];
|
|
||||||
}
|
|
||||||
const errors: string[] = [];
|
|
||||||
for (const failure of err.failures()) {
|
|
||||||
if (failure.type === "never") {
|
|
||||||
errors.push(
|
|
||||||
`Key "${failure.path.join(".")}" is not supported by the UI editor.`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
errors.push(
|
|
||||||
`The value of "${failure.path.join(
|
|
||||||
"."
|
|
||||||
)}" is not supported by the UI editor, we support "${
|
|
||||||
failure.type
|
|
||||||
}" but received "${JSON.stringify(failure.value)}".`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
};
|
|
@ -20,7 +20,7 @@ import {
|
|||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { HistoryGraphCardConfig } from "../../cards/types";
|
import { HistoryGraphCardConfig } from "../../cards/types";
|
||||||
import { EntityId } from "../../common/structs/is-entity-id";
|
import { EntityId } from "../../../../common/structs/is-entity-id";
|
||||||
import "../../components/hui-entity-editor";
|
import "../../components/hui-entity-editor";
|
||||||
import { EntityConfig } from "../../entity-rows/types";
|
import { EntityConfig } from "../../entity-rows/types";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
export class GUISupportError extends Error {
|
export class GUISupportError extends Error {
|
||||||
public warnings?: string[] = [];
|
public warnings?: string[];
|
||||||
|
|
||||||
constructor(message: string, warnings?: string[]) {
|
public errors?: string[];
|
||||||
|
|
||||||
|
constructor(message: string, warnings?: string[], errors?: string[]) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = "GUISupportError";
|
this.name = "GUISupportError";
|
||||||
this.warnings = warnings;
|
this.warnings = warnings;
|
||||||
|
this.errors = errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import type {
|
|||||||
LovelaceConfig,
|
LovelaceConfig,
|
||||||
} from "../../../data/lovelace";
|
} from "../../../data/lovelace";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { handleStructError } from "../common/structs/handle-errors";
|
import { handleStructError } from "../../../common/structs/handle-errors";
|
||||||
import type { LovelaceRowConfig } from "../entity-rows/types";
|
import type { LovelaceRowConfig } from "../entity-rows/types";
|
||||||
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
import type { LovelaceGenericElementEditor } from "../types";
|
import type { LovelaceGenericElementEditor } from "../types";
|
||||||
@ -63,14 +63,16 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _configElementType?: string;
|
@internalProperty() private _configElementType?: string;
|
||||||
|
|
||||||
@internalProperty() private _GUImode = true;
|
@internalProperty() private _guiMode = true;
|
||||||
|
|
||||||
// Error: Configuration broken - do not save
|
// Error: Configuration broken - do not save
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _errors?: string[];
|
||||||
|
|
||||||
// Warning: GUI editor can't handle configuration - ok to save
|
// Warning: GUI editor can't handle configuration - ok to save
|
||||||
@internalProperty() private _warnings?: string[];
|
@internalProperty() private _warnings?: string[];
|
||||||
|
|
||||||
|
@internalProperty() private _guiSupported?: boolean;
|
||||||
|
|
||||||
@internalProperty() private _loading = false;
|
@internalProperty() private _loading = false;
|
||||||
|
|
||||||
@query("ha-code-editor") _yamlEditor?: HaCodeEditor;
|
@query("ha-code-editor") _yamlEditor?: HaCodeEditor;
|
||||||
@ -86,9 +88,9 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
this._yaml = _yaml;
|
this._yaml = _yaml;
|
||||||
try {
|
try {
|
||||||
this._config = safeLoad(this.yaml);
|
this._config = safeLoad(this.yaml);
|
||||||
this._error = undefined;
|
this._errors = undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = err.message;
|
this._errors = [err.message];
|
||||||
}
|
}
|
||||||
this._setConfig();
|
this._setConfig();
|
||||||
}
|
}
|
||||||
@ -103,44 +105,51 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._yaml = undefined;
|
this._yaml = undefined;
|
||||||
this._error = undefined;
|
this._errors = undefined;
|
||||||
this._setConfig();
|
this._setConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setConfig(): void {
|
private _setConfig(): void {
|
||||||
if (!this._error) {
|
if (!this._errors) {
|
||||||
try {
|
try {
|
||||||
this._updateConfigElement();
|
this._updateConfigElement();
|
||||||
this._error = undefined;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = err.message;
|
this._errors = [err.message];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "config-changed", {
|
fireEvent(this, "config-changed", {
|
||||||
config: this.value! as any,
|
config: this.value! as any,
|
||||||
error: this._error,
|
error: this._errors?.join(", "),
|
||||||
guiModeAvailable: !(this.hasWarning || this.hasError),
|
guiModeAvailable: !(
|
||||||
|
this.hasWarning ||
|
||||||
|
this.hasError ||
|
||||||
|
this._guiSupported === false
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasWarning(): boolean {
|
public get hasWarning(): boolean {
|
||||||
return this._warnings !== undefined;
|
return this._warnings !== undefined && this._warnings.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasError(): boolean {
|
public get hasError(): boolean {
|
||||||
return this._error !== undefined;
|
return this._errors !== undefined && this._errors.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get GUImode(): boolean {
|
public get GUImode(): boolean {
|
||||||
return this._GUImode;
|
return this._guiMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set GUImode(guiMode: boolean) {
|
public set GUImode(guiMode: boolean) {
|
||||||
this._GUImode = guiMode;
|
this._guiMode = guiMode;
|
||||||
fireEvent(this as HTMLElement, "GUImode-changed", {
|
fireEvent(this as HTMLElement, "GUImode-changed", {
|
||||||
guiMode,
|
guiMode,
|
||||||
guiModeAvailable: !(this.hasWarning || this.hasError),
|
guiModeAvailable: !(
|
||||||
|
this.hasWarning ||
|
||||||
|
this.hasError ||
|
||||||
|
this._guiSupported === false
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,29 +203,44 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
mode="yaml"
|
mode="yaml"
|
||||||
autofocus
|
autofocus
|
||||||
.value=${this.yaml}
|
.value=${this.yaml}
|
||||||
.error=${Boolean(this._error)}
|
.error=${Boolean(this._errors)}
|
||||||
.rtl=${computeRTL(this.hass)}
|
.rtl=${computeRTL(this.hass)}
|
||||||
@value-changed=${this._handleYAMLChanged}
|
@value-changed=${this._handleYAMLChanged}
|
||||||
@keydown=${this._ignoreKeydown}
|
@keydown=${this._ignoreKeydown}
|
||||||
></ha-code-editor>
|
></ha-code-editor>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
${this._error
|
${this._guiSupported === false && this.configElementType
|
||||||
? html`
|
? html`
|
||||||
<div class="error">
|
<div class="info">
|
||||||
${this._error}
|
${this.hass.localize(
|
||||||
|
"ui.errors.config.editor_not_available",
|
||||||
|
"type",
|
||||||
|
this.configElementType
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this._warnings
|
${this.hasError
|
||||||
? html`
|
? html`
|
||||||
<div class="warning">
|
<div class="error">
|
||||||
UI editor is not supported for this config:
|
${this.hass.localize("ui.errors.config.error_detected")}:
|
||||||
<br />
|
<br />
|
||||||
<ul>
|
<ul>
|
||||||
${this._warnings.map((warning) => html`<li>${warning}</li>`)}
|
${this._errors!.map((error) => html`<li>${error}</li>`)}
|
||||||
</ul>
|
</ul>
|
||||||
You can still edit your config in YAML.
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.hasWarning
|
||||||
|
? html`
|
||||||
|
<div class="warning">
|
||||||
|
${this.hass.localize("ui.errors.config.editor_not_supported")}:
|
||||||
|
<br />
|
||||||
|
<ul>
|
||||||
|
${this._warnings!.map((warning) => html`<li>${warning}</li>`)}
|
||||||
|
</ul>
|
||||||
|
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@ -261,14 +285,18 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
let configElement: LovelaceGenericElementEditor | undefined;
|
let configElement: LovelaceGenericElementEditor | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._error = undefined;
|
this._errors = undefined;
|
||||||
this._warnings = undefined;
|
this._warnings = undefined;
|
||||||
|
|
||||||
if (this._configElementType !== this.configElementType) {
|
if (this._configElementType !== this.configElementType) {
|
||||||
// If the type has changed, we need to load a new GUI editor
|
// If the type has changed, we need to load a new GUI editor
|
||||||
|
this._guiSupported = false;
|
||||||
|
this._configElement = undefined;
|
||||||
|
|
||||||
if (!this.configElementType) {
|
if (!this.configElementType) {
|
||||||
throw new Error(`No type defined`);
|
throw new Error(
|
||||||
|
this.hass.localize("ui.errors.config.no_type_provided")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._configElementType = this.configElementType;
|
this._configElementType = this.configElementType;
|
||||||
@ -276,37 +304,41 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
this._loading = true;
|
this._loading = true;
|
||||||
configElement = await this.getConfigElement();
|
configElement = await this.getConfigElement();
|
||||||
|
|
||||||
if (!configElement) {
|
if (configElement) {
|
||||||
throw new Error(
|
configElement.hass = this.hass;
|
||||||
`No visual editor available for: ${this.configElementType}`
|
if ("lovelace" in configElement) {
|
||||||
|
configElement.lovelace = this.lovelace;
|
||||||
|
}
|
||||||
|
configElement.addEventListener("config-changed", (ev) =>
|
||||||
|
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
configElement.hass = this.hass;
|
this._configElement = configElement;
|
||||||
if ("lovelace" in configElement) {
|
this._guiSupported = true;
|
||||||
configElement.lovelace = this.lovelace;
|
|
||||||
}
|
}
|
||||||
configElement.addEventListener("config-changed", (ev) =>
|
|
||||||
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
|
||||||
);
|
|
||||||
|
|
||||||
this._configElement = configElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup GUI editor and check that it can handle the current config
|
if (this._configElement) {
|
||||||
try {
|
// Setup GUI editor and check that it can handle the current config
|
||||||
this._configElement!.setConfig(this.value);
|
try {
|
||||||
} catch (err) {
|
this._configElement.setConfig(this.value);
|
||||||
throw new GUISupportError(
|
} catch (err) {
|
||||||
"Config is not supported",
|
const msgs = handleStructError(this.hass, err);
|
||||||
handleStructError(err)
|
throw new GUISupportError(
|
||||||
);
|
"Config is not supported",
|
||||||
|
msgs.warnings,
|
||||||
|
msgs.errors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.GUImode = false;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof GUISupportError) {
|
if (err instanceof GUISupportError) {
|
||||||
this._warnings = err.warnings ?? [err.message];
|
this._warnings = err.warnings ?? [err.message];
|
||||||
|
this._errors = err.errors || undefined;
|
||||||
} else {
|
} else {
|
||||||
this._error = err;
|
this._errors = [err.message];
|
||||||
}
|
}
|
||||||
this.GUImode = false;
|
this.GUImode = false;
|
||||||
} finally {
|
} finally {
|
||||||
@ -331,8 +363,10 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
}
|
}
|
||||||
.error,
|
.error,
|
||||||
.warning {
|
.warning,
|
||||||
|
.info {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
@ -340,9 +374,14 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
.warning {
|
.warning {
|
||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.warning ul {
|
.warning ul,
|
||||||
|
.error ul {
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
.warning li,
|
||||||
|
.error li {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
ha-circular-progress {
|
ha-circular-progress {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -2,6 +2,9 @@ import {
|
|||||||
any,
|
any,
|
||||||
array,
|
array,
|
||||||
boolean,
|
boolean,
|
||||||
|
dynamic,
|
||||||
|
enums,
|
||||||
|
literal,
|
||||||
number,
|
number,
|
||||||
object,
|
object,
|
||||||
optional,
|
optional,
|
||||||
@ -92,12 +95,58 @@ export interface EditSubElementEvent {
|
|||||||
subElementConfig: SubElementEditorConfig;
|
subElementConfig: SubElementEditorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actionConfigStruct = object({
|
export const actionConfigStruct = dynamic((_value, ctx) => {
|
||||||
action: string(),
|
const test = actionConfigMap[ctx.branch[0][ctx.path[0]].action];
|
||||||
navigation_path: optional(string()),
|
return test || actionConfigStructType;
|
||||||
url_path: optional(string()),
|
});
|
||||||
service: optional(string()),
|
|
||||||
|
const actionConfigStructUser = object({
|
||||||
|
user: string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionConfigStructConfirmation = union([
|
||||||
|
boolean(),
|
||||||
|
object({
|
||||||
|
text: optional(string()),
|
||||||
|
excemptions: optional(array(actionConfigStructUser)),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const actionConfigStructUrl = object({
|
||||||
|
action: literal("url"),
|
||||||
|
url_path: string(),
|
||||||
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionConfigStructService = object({
|
||||||
|
action: literal("call-service"),
|
||||||
|
service: string(),
|
||||||
service_data: optional(object()),
|
service_data: optional(object()),
|
||||||
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionConfigStructNavigate = object({
|
||||||
|
action: literal("navigate"),
|
||||||
|
navigation_path: string(),
|
||||||
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionConfigMap = {
|
||||||
|
url: actionConfigStructUrl,
|
||||||
|
navigate: actionConfigStructNavigate,
|
||||||
|
"call-service": actionConfigStructService,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actionConfigStructType = object({
|
||||||
|
action: enums([
|
||||||
|
"none",
|
||||||
|
"toggle",
|
||||||
|
"more-info",
|
||||||
|
"call-service",
|
||||||
|
"url",
|
||||||
|
"navigate",
|
||||||
|
]),
|
||||||
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttonEntitiesRowConfigStruct = object({
|
const buttonEntitiesRowConfigStruct = object({
|
||||||
|
@ -757,6 +757,18 @@
|
|||||||
"day": "{count} {count, plural,\n one {day}\n other {days}\n}",
|
"day": "{count} {count, plural,\n one {day}\n other {days}\n}",
|
||||||
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n}"
|
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n}"
|
||||||
},
|
},
|
||||||
|
"errors": {
|
||||||
|
"config": {
|
||||||
|
"no_type_provided": "No type provided.",
|
||||||
|
"error_detected": "Configuration errors detected",
|
||||||
|
"editor_not_available": "No visual editor available for type \"{type}\".",
|
||||||
|
"editor_not_supported": "Visual editor is not supported for this configuration",
|
||||||
|
"edit_in_yaml_supported": "You can still edit your config in YAML.",
|
||||||
|
"key_missing": "Required key \"{key}\" is missing.",
|
||||||
|
"key_not_expected": "Key \"{key}\" is not expected or not supported by the visual editor.",
|
||||||
|
"key_wrong_type": "The provided value for \"{key}\" is not supported by the visual editor editor. We support ({type_correct}) but received ({type_wrong})."
|
||||||
|
}
|
||||||
|
},
|
||||||
"login-form": {
|
"login-form": {
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"remember": "Remember",
|
"remember": "Remember",
|
||||||
|
@ -12758,10 +12758,10 @@ subarg@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.1.0"
|
minimist "^1.1.0"
|
||||||
|
|
||||||
superstruct@^0.10.12:
|
superstruct@^0.10.13:
|
||||||
version "0.10.12"
|
version "0.10.13"
|
||||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.10.12.tgz#7b2c8adaf61b75257265eac3b588f30017f996f0"
|
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.10.13.tgz#705535a5598ff231bd976601a7b6b534a71a821b"
|
||||||
integrity sha512-FiNhfegyytDI0QxrrEoeGknFM28SnoHqCBpkWewUm8jRNj74NVxLpiiePvkOo41Ze/aKMSHa/twWjNF81mKaQQ==
|
integrity sha512-W4SitSZ9MOyMPbHreoZVEneSZyPEeNGbdfJo/7FkJyRs/M3wQRFzq+t3S/NBwlrFSWdx1ONLjLb9pB+UKe4IqQ==
|
||||||
|
|
||||||
supports-color@6.0.0:
|
supports-color@6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user