mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Refactor config flow dialog to make it reusable (#3488)
* Refactor config flow dialog to make it reusable * More refactor * forgot to save * Render labels correcetly
This commit is contained in:
parent
07b8518162
commit
200e099035
@ -11,7 +11,10 @@ import "../components/ha-form";
|
||||
import "../components/ha-markdown";
|
||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||
import { AuthProvider } from "../data/auth";
|
||||
import { ConfigFlowStep, ConfigFlowStepForm } from "../data/config_entries";
|
||||
import {
|
||||
DataEntryFlowStep,
|
||||
DataEntryFlowStepForm,
|
||||
} from "../data/data_entry_flow";
|
||||
|
||||
type State = "loading" | "error" | "step";
|
||||
|
||||
@ -22,7 +25,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
@property() public oauth2State?: string;
|
||||
@property() private _state: State = "loading";
|
||||
@property() private _stepData: any = {};
|
||||
@property() private _step?: ConfigFlowStep;
|
||||
@property() private _step?: DataEntryFlowStep;
|
||||
@property() private _errorMessage?: string;
|
||||
|
||||
protected render() {
|
||||
@ -87,7 +90,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
private _renderStep(step: ConfigFlowStep) {
|
||||
private _renderStep(step: DataEntryFlowStep) {
|
||||
switch (step.type) {
|
||||
case "abort":
|
||||
return html`
|
||||
@ -192,7 +195,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
document.location.assign(url);
|
||||
}
|
||||
|
||||
private async _updateStep(step: ConfigFlowStep) {
|
||||
private async _updateStep(step: DataEntryFlowStep) {
|
||||
let stepData: any = null;
|
||||
if (
|
||||
this._step &&
|
||||
@ -219,7 +222,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private _computeStepDescription(step: ConfigFlowStepForm) {
|
||||
private _computeStepDescription(step: DataEntryFlowStepForm) {
|
||||
const resourceKey = `ui.panel.page-authorize.form.providers.${
|
||||
step.handler[0]
|
||||
}.step.${step.step_id}.description`;
|
||||
@ -232,7 +235,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
return this.localize(resourceKey, ...args);
|
||||
}
|
||||
|
||||
private _computeLabelCallback(step: ConfigFlowStepForm) {
|
||||
private _computeLabelCallback(step: DataEntryFlowStepForm) {
|
||||
// Returns a callback for ha-form to calculate labels per schema object
|
||||
return (schema) =>
|
||||
this.localize(
|
||||
@ -242,7 +245,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
);
|
||||
}
|
||||
|
||||
private _computeErrorCallback(step: ConfigFlowStepForm) {
|
||||
private _computeErrorCallback(step: DataEntryFlowStepForm) {
|
||||
// Returns a callback for ha-form to calculate error messages
|
||||
return (error) =>
|
||||
this.localize(
|
||||
|
@ -2,15 +2,7 @@ import { HomeAssistant } from "../types";
|
||||
import { createCollection } from "home-assistant-js-websocket";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
export interface DataEntryFlowProgressedEvent {
|
||||
type: "data_entry_flow_progressed";
|
||||
data: {
|
||||
handler: string;
|
||||
flow_id: string;
|
||||
refresh: boolean;
|
||||
};
|
||||
}
|
||||
import { DataEntryFlowStep, DataEntryFlowProgress } from "./data_entry_flow";
|
||||
|
||||
export interface ConfigEntry {
|
||||
entry_id: string;
|
||||
@ -22,80 +14,23 @@ export interface ConfigEntry {
|
||||
supports_options: boolean;
|
||||
}
|
||||
|
||||
export interface FieldSchema {
|
||||
name: string;
|
||||
default?: any;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
export interface ConfigFlowProgress {
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
context: {
|
||||
title_placeholders: { [key: string]: string };
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepForm {
|
||||
type: "form";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
step_id: string;
|
||||
data_schema: FieldSchema[];
|
||||
errors: { [key: string]: string };
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepExternal {
|
||||
type: "external";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
step_id: string;
|
||||
url: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepCreateEntry {
|
||||
type: "create_entry";
|
||||
version: number;
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
title: string;
|
||||
// Config entry ID
|
||||
result: string;
|
||||
description: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepAbort {
|
||||
type: "abort";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
reason: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export type ConfigFlowStep =
|
||||
| ConfigFlowStepForm
|
||||
| ConfigFlowStepExternal
|
||||
| ConfigFlowStepCreateEntry
|
||||
| ConfigFlowStepAbort;
|
||||
|
||||
export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
|
||||
hass.callApi<ConfigFlowStep>("POST", "config/config_entries/flow", {
|
||||
hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
|
||||
handler,
|
||||
});
|
||||
|
||||
export const fetchConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
||||
hass.callApi<ConfigFlowStep>("GET", `config/config_entries/flow/${flowId}`);
|
||||
hass.callApi<DataEntryFlowStep>(
|
||||
"GET",
|
||||
`config/config_entries/flow/${flowId}`
|
||||
);
|
||||
|
||||
export const handleConfigFlowStep = (
|
||||
hass: HomeAssistant,
|
||||
flowId: string,
|
||||
data: { [key: string]: any }
|
||||
) =>
|
||||
hass.callApi<ConfigFlowStep>(
|
||||
hass.callApi<DataEntryFlowStep>(
|
||||
"POST",
|
||||
`config/config_entries/flow/${flowId}`,
|
||||
data
|
||||
@ -105,7 +40,7 @@ export const deleteConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
||||
hass.callApi("DELETE", `config/config_entries/flow/${flowId}`);
|
||||
|
||||
export const getConfigFlowsInProgress = (hass: HomeAssistant) =>
|
||||
hass.callApi<ConfigFlowProgress[]>("GET", "config/config_entries/flow");
|
||||
hass.callApi<DataEntryFlowProgress[]>("GET", "config/config_entries/flow");
|
||||
|
||||
export const getConfigFlowHandlers = (hass: HomeAssistant) =>
|
||||
hass.callApi<string[]>("GET", "config/config_entries/flow_handlers");
|
||||
@ -130,9 +65,9 @@ const subscribeConfigFlowInProgressUpdates = (conn, store) =>
|
||||
|
||||
export const subscribeConfigFlowInProgress = (
|
||||
hass: HomeAssistant,
|
||||
onChange: (flows: ConfigFlowProgress[]) => void
|
||||
onChange: (flows: DataEntryFlowProgress[]) => void
|
||||
) =>
|
||||
createCollection<ConfigFlowProgress[]>(
|
||||
createCollection<DataEntryFlowProgress[]>(
|
||||
"_configFlowProgress",
|
||||
fetchConfigFlowInProgress,
|
||||
subscribeConfigFlowInProgressUpdates,
|
||||
@ -145,7 +80,7 @@ export const getConfigEntries = (hass: HomeAssistant) =>
|
||||
|
||||
export const localizeConfigFlowTitle = (
|
||||
localize: LocalizeFunc,
|
||||
flow: ConfigFlowProgress
|
||||
flow: DataEntryFlowProgress
|
||||
) => {
|
||||
const placeholders = flow.context.title_placeholders || {};
|
||||
const placeholderKeys = Object.keys(placeholders);
|
||||
|
68
src/data/data_entry_flow.ts
Normal file
68
src/data/data_entry_flow.ts
Normal file
@ -0,0 +1,68 @@
|
||||
export interface DataEntryFlowProgressedEvent {
|
||||
type: "data_entry_flow_progressed";
|
||||
data: {
|
||||
handler: string;
|
||||
flow_id: string;
|
||||
refresh: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FieldSchema {
|
||||
name: string;
|
||||
default?: any;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowProgress {
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
context: {
|
||||
title_placeholders: { [key: string]: string };
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepForm {
|
||||
type: "form";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
step_id: string;
|
||||
data_schema: FieldSchema[];
|
||||
errors: { [key: string]: string };
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepExternal {
|
||||
type: "external";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
step_id: string;
|
||||
url: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepCreateEntry {
|
||||
type: "create_entry";
|
||||
version: number;
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
title: string;
|
||||
// Config entry ID
|
||||
result: string;
|
||||
description: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepAbort {
|
||||
type: "abort";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
reason: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export type DataEntryFlowStep =
|
||||
| DataEntryFlowStepForm
|
||||
| DataEntryFlowStepExternal
|
||||
| DataEntryFlowStepCreateEntry
|
||||
| DataEntryFlowStepAbort;
|
@ -22,14 +22,8 @@ import "../../components/dialog/ha-paper-dialog";
|
||||
// tslint:disable-next-line
|
||||
import { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import {
|
||||
fetchConfigFlow,
|
||||
ConfigFlowStep,
|
||||
deleteConfigFlow,
|
||||
getConfigFlowHandlers,
|
||||
} from "../../data/config_entries";
|
||||
import { PolymerChangedEvent } from "../../polymer-types";
|
||||
import { HaConfigFlowParams } from "./show-dialog-config-flow";
|
||||
import { DataEntryFlowDialogParams } from "./show-dialog-data-entry-flow";
|
||||
|
||||
import "./step-flow-pick-handler";
|
||||
import "./step-flow-loading";
|
||||
@ -46,7 +40,7 @@ import {
|
||||
subscribeAreaRegistry,
|
||||
} from "../../data/area_registry";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { caseInsensitiveCompare } from "../../common/string/compare";
|
||||
import { DataEntryFlowStep } from "../../data/data_entry_flow";
|
||||
|
||||
let instance = 0;
|
||||
|
||||
@ -54,20 +48,20 @@ declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"flow-update": {
|
||||
step?: ConfigFlowStep;
|
||||
stepPromise?: Promise<ConfigFlowStep>;
|
||||
step?: DataEntryFlowStep;
|
||||
stepPromise?: Promise<DataEntryFlowStep>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("dialog-config-flow")
|
||||
class ConfigFlowDialog extends LitElement {
|
||||
@customElement("dialog-data-entry-flow")
|
||||
class DataEntryFlowDialog extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
@property() private _params?: HaConfigFlowParams;
|
||||
@property() private _params?: DataEntryFlowDialogParams;
|
||||
@property() private _loading = true;
|
||||
private _instance = instance;
|
||||
@property() private _step:
|
||||
| ConfigFlowStep
|
||||
| DataEntryFlowStep
|
||||
| undefined
|
||||
// Null means we need to pick a config flow
|
||||
| null;
|
||||
@ -77,12 +71,15 @@ class ConfigFlowDialog extends LitElement {
|
||||
private _unsubAreas?: UnsubscribeFunc;
|
||||
private _unsubDevices?: UnsubscribeFunc;
|
||||
|
||||
public async showDialog(params: HaConfigFlowParams): Promise<void> {
|
||||
public async showDialog(params: DataEntryFlowDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._instance = instance++;
|
||||
|
||||
// Create a new config flow. Show picker
|
||||
if (!params.continueFlowId) {
|
||||
if (!params.flowConfig.getFlowHandlers) {
|
||||
throw new Error("No getFlowHandlers defined in flow config");
|
||||
}
|
||||
this._step = null;
|
||||
|
||||
// We only load the handlers once
|
||||
@ -90,13 +87,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
this._loading = true;
|
||||
this.updateComplete.then(() => this._scheduleCenterDialog());
|
||||
try {
|
||||
this._handlers = (await getConfigFlowHandlers(this.hass)).sort(
|
||||
(handlerA, handlerB) =>
|
||||
caseInsensitiveCompare(
|
||||
this.hass.localize(`component.${handlerA}.config.title`),
|
||||
this.hass.localize(`component.${handlerB}.config.title`)
|
||||
)
|
||||
);
|
||||
this._handlers = await params.flowConfig.getFlowHandlers(this.hass);
|
||||
} finally {
|
||||
this._loading = false;
|
||||
}
|
||||
@ -108,7 +99,10 @@ class ConfigFlowDialog extends LitElement {
|
||||
|
||||
this._loading = true;
|
||||
const curInstance = this._instance;
|
||||
const step = await fetchConfigFlow(this.hass, params.continueFlowId);
|
||||
const step = await params.flowConfig.fetchFlow(
|
||||
this.hass,
|
||||
params.continueFlowId
|
||||
);
|
||||
|
||||
// Happens if second showDialog called
|
||||
if (curInstance !== this._instance) {
|
||||
@ -145,6 +139,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
? // Show handler picker
|
||||
html`
|
||||
<step-flow-pick-handler
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.hass=${this.hass}
|
||||
.handlers=${this._handlers}
|
||||
></step-flow-pick-handler>
|
||||
@ -152,6 +147,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
: this._step.type === "form"
|
||||
? html`
|
||||
<step-flow-form
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-form>
|
||||
@ -159,6 +155,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
: this._step.type === "external"
|
||||
? html`
|
||||
<step-flow-external
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-external>
|
||||
@ -166,6 +163,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
: this._step.type === "abort"
|
||||
? html`
|
||||
<step-flow-abort
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-abort>
|
||||
@ -177,6 +175,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<step-flow-create-entry
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.devices=${this._devices}
|
||||
@ -201,8 +200,13 @@ class ConfigFlowDialog extends LitElement {
|
||||
this._step &&
|
||||
this._step.type === "create_entry"
|
||||
) {
|
||||
this._fetchDevices(this._step.result);
|
||||
this._fetchAreas();
|
||||
if (this._params!.flowConfig.loadDevicesAndAreas) {
|
||||
this._fetchDevices(this._step.result);
|
||||
this._fetchAreas();
|
||||
} else {
|
||||
this._devices = [];
|
||||
this._areas = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProps.has("_devices") && this._dialog) {
|
||||
@ -236,7 +240,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
}
|
||||
|
||||
private async _processStep(
|
||||
step: ConfigFlowStep | undefined | Promise<ConfigFlowStep>
|
||||
step: DataEntryFlowStep | undefined | Promise<DataEntryFlowStep>
|
||||
): Promise<void> {
|
||||
if (step instanceof Promise) {
|
||||
this._loading = true;
|
||||
@ -267,7 +271,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
|
||||
// If we created this flow, delete it now.
|
||||
if (this._step && !flowFinished && !this._params.continueFlowId) {
|
||||
deleteConfigFlow(this.hass, this._step.flow_id);
|
||||
this._params.flowConfig.deleteFlow(this.hass, this._step.flow_id);
|
||||
}
|
||||
|
||||
this._params.dialogClosedCallback({
|
||||
@ -319,6 +323,6 @@ class ConfigFlowDialog extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-config-flow": ConfigFlowDialog;
|
||||
"dialog-data-entry-flow": DataEntryFlowDialog;
|
||||
}
|
||||
}
|
@ -1,20 +1,128 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
getConfigFlowHandlers,
|
||||
fetchConfigFlow,
|
||||
handleConfigFlowStep,
|
||||
deleteConfigFlow,
|
||||
createConfigFlow,
|
||||
} from "../../data/config_entries";
|
||||
import { html } from "lit-element";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import {
|
||||
showFlowDialog,
|
||||
DataEntryFlowDialogParams,
|
||||
loadDataEntryFlowDialog,
|
||||
} from "./show-dialog-data-entry-flow";
|
||||
import { caseInsensitiveCompare } from "../../common/string/compare";
|
||||
|
||||
export interface HaConfigFlowParams {
|
||||
continueFlowId?: string;
|
||||
dialogClosedCallback: (params: { flowFinished: boolean }) => void;
|
||||
}
|
||||
|
||||
export const loadConfigFlowDialog = () =>
|
||||
import(/* webpackChunkName: "dialog-config-flow" */ "./dialog-config-flow");
|
||||
export const loadConfigFlowDialog = loadDataEntryFlowDialog;
|
||||
|
||||
export const showConfigFlowDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HaConfigFlowParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-config-flow",
|
||||
dialogImport: loadConfigFlowDialog,
|
||||
dialogParams,
|
||||
dialogParams: Omit<DataEntryFlowDialogParams, "flowConfig">
|
||||
): void =>
|
||||
showFlowDialog(element, dialogParams, {
|
||||
loadDevicesAndAreas: true,
|
||||
getFlowHandlers: (hass) =>
|
||||
getConfigFlowHandlers(hass).then((handlers) =>
|
||||
handlers.sort((handlerA, handlerB) =>
|
||||
caseInsensitiveCompare(
|
||||
hass.localize(`component.${handlerA}.config.title`),
|
||||
hass.localize(`component.${handlerB}.config.title`)
|
||||
)
|
||||
)
|
||||
),
|
||||
createFlow: createConfigFlow,
|
||||
fetchFlow: fetchConfigFlow,
|
||||
handleFlowStep: handleConfigFlowStep,
|
||||
deleteFlow: deleteConfigFlow,
|
||||
|
||||
renderAbortDescription(hass, step) {
|
||||
const description = localizeKey(
|
||||
hass.localize,
|
||||
`component.${step.handler}.config.abort.${step.reason}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: "";
|
||||
},
|
||||
|
||||
renderShowFormStepHeader(hass, step) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||
);
|
||||
},
|
||||
|
||||
renderShowFormStepDescription(hass, step) {
|
||||
const description = localizeKey(
|
||||
hass.localize,
|
||||
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: "";
|
||||
},
|
||||
|
||||
renderShowFormStepFieldLabel(hass, step, field) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.data.${
|
||||
field.name
|
||||
}`
|
||||
);
|
||||
},
|
||||
|
||||
renderShowFormStepFieldError(hass, step, error) {
|
||||
return hass.localize(`component.${step.handler}.config.error.${error}`);
|
||||
},
|
||||
|
||||
renderExternalStepHeader(hass, step) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||
);
|
||||
},
|
||||
|
||||
renderExternalStepDescription(hass, step) {
|
||||
const description = localizeKey(
|
||||
hass.localize,
|
||||
`component.${step.handler}.config.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<p>
|
||||
${hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.external_step.description"
|
||||
)}
|
||||
</p>
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
`;
|
||||
},
|
||||
|
||||
renderCreateEntryDescription(hass, step) {
|
||||
const description = localizeKey(
|
||||
hass.localize,
|
||||
`component.${step.handler}.config.create_entry.${step.description ||
|
||||
"default"}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<p>Created config for ${step.title}.</p>
|
||||
`;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
95
src/dialogs/config-flow/show-dialog-data-entry-flow.ts
Normal file
95
src/dialogs/config-flow/show-dialog-data-entry-flow.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
DataEntryFlowStepCreateEntry,
|
||||
DataEntryFlowStepExternal,
|
||||
DataEntryFlowStepForm,
|
||||
DataEntryFlowStep,
|
||||
DataEntryFlowStepAbort,
|
||||
FieldSchema,
|
||||
} from "../../data/data_entry_flow";
|
||||
|
||||
export interface FlowConfig {
|
||||
loadDevicesAndAreas: boolean;
|
||||
|
||||
getFlowHandlers?: (hass: HomeAssistant) => Promise<string[]>;
|
||||
|
||||
createFlow(hass: HomeAssistant, handler: string): Promise<DataEntryFlowStep>;
|
||||
|
||||
fetchFlow(hass: HomeAssistant, flowId: string): Promise<DataEntryFlowStep>;
|
||||
|
||||
handleFlowStep(
|
||||
hass: HomeAssistant,
|
||||
flowId: string,
|
||||
data: { [key: string]: any }
|
||||
): Promise<DataEntryFlowStep>;
|
||||
|
||||
deleteFlow(hass: HomeAssistant, flowId: string): Promise<unknown>;
|
||||
|
||||
renderAbortDescription(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepAbort
|
||||
): TemplateResult | "";
|
||||
|
||||
renderShowFormStepHeader(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm
|
||||
): string;
|
||||
|
||||
renderShowFormStepDescription(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm
|
||||
): TemplateResult | "";
|
||||
|
||||
renderShowFormStepFieldLabel(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm,
|
||||
field: FieldSchema
|
||||
): string;
|
||||
|
||||
renderShowFormStepFieldError(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm,
|
||||
error: string
|
||||
): string;
|
||||
|
||||
renderExternalStepHeader(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepExternal
|
||||
): string;
|
||||
|
||||
renderExternalStepDescription(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepExternal
|
||||
): TemplateResult | "";
|
||||
|
||||
renderCreateEntryDescription(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepCreateEntry
|
||||
): TemplateResult | "";
|
||||
}
|
||||
|
||||
export interface DataEntryFlowDialogParams {
|
||||
continueFlowId?: string;
|
||||
dialogClosedCallback: (params: { flowFinished: boolean }) => void;
|
||||
flowConfig: FlowConfig;
|
||||
}
|
||||
|
||||
export const loadDataEntryFlowDialog = () =>
|
||||
import(/* webpackChunkName: "dialog-config-flow" */ "./dialog-data-entry-flow");
|
||||
|
||||
export const showFlowDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: Omit<DataEntryFlowDialogParams, "flowConfig">,
|
||||
flowConfig: FlowConfig
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-data-entry-flow",
|
||||
dialogImport: loadDataEntryFlowDialog,
|
||||
dialogParams: {
|
||||
...dialogParams,
|
||||
flowConfig,
|
||||
},
|
||||
});
|
||||
};
|
@ -8,38 +8,27 @@ import {
|
||||
} from "lit-element";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import { ConfigFlowStepAbort } from "../../data/config_entries";
|
||||
import { DataEntryFlowStepAbort } from "../../data/data_entry_flow";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
@customElement("step-flow-abort")
|
||||
class StepFlowAbort extends LitElement {
|
||||
public flowConfig!: FlowConfig;
|
||||
|
||||
@property()
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property()
|
||||
private step!: ConfigFlowStepAbort;
|
||||
private step!: DataEntryFlowStepAbort;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const localize = this.hass.localize;
|
||||
const step = this.step;
|
||||
|
||||
const description = localizeKey(
|
||||
localize,
|
||||
`component.${step.handler}.config.abort.${step.reason}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>Aborted</h2>
|
||||
<div class="content">
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
${this.flowConfig.renderAbortDescription(this.hass, this.step)}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<mwc-button @click="${this._flowDone}">Close</mwc-button>
|
||||
|
@ -12,9 +12,7 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
|
||||
import { ConfigFlowStepCreateEntry } from "../../data/config_entries";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
import {
|
||||
@ -25,14 +23,18 @@ import {
|
||||
AreaRegistryEntry,
|
||||
createAreaRegistryEntry,
|
||||
} from "../../data/area_registry";
|
||||
import { DataEntryFlowStepCreateEntry } from "../../data/data_entry_flow";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
@customElement("step-flow-create-entry")
|
||||
class StepFlowCreateEntry extends LitElement {
|
||||
public flowConfig!: FlowConfig;
|
||||
|
||||
@property()
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property()
|
||||
public step!: ConfigFlowStepCreateEntry;
|
||||
public step!: DataEntryFlowStepCreateEntry;
|
||||
|
||||
@property()
|
||||
public devices!: DeviceRegistryEntry[];
|
||||
@ -42,24 +44,11 @@ class StepFlowCreateEntry extends LitElement {
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const localize = this.hass.localize;
|
||||
const step = this.step;
|
||||
|
||||
const description = localizeKey(
|
||||
localize,
|
||||
`component.${step.handler}.config.create_entry.${step.description ||
|
||||
"default"}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>Success!</h2>
|
||||
<div class="content">
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<p>Created config for ${step.title}.</p>
|
||||
${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
|
||||
${this.devices.length === 0
|
||||
? ""
|
||||
: html`
|
||||
|
@ -9,51 +9,34 @@ import {
|
||||
} from "lit-element";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import {
|
||||
ConfigFlowStepExternal,
|
||||
DataEntryFlowProgressedEvent,
|
||||
fetchConfigFlow,
|
||||
} from "../../data/config_entries";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
import {
|
||||
DataEntryFlowStepExternal,
|
||||
DataEntryFlowProgressedEvent,
|
||||
} from "../../data/data_entry_flow";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
@customElement("step-flow-external")
|
||||
class StepFlowExternal extends LitElement {
|
||||
public flowConfig!: FlowConfig;
|
||||
|
||||
@property()
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property()
|
||||
private step!: ConfigFlowStepExternal;
|
||||
private step!: DataEntryFlowStepExternal;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const localize = this.hass.localize;
|
||||
const step = this.step;
|
||||
|
||||
const description = localizeKey(
|
||||
localize,
|
||||
`component.${step.handler}.config.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>
|
||||
${localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||
)}
|
||||
${this.flowConfig.renderExternalStepHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
<p>
|
||||
${localize(
|
||||
"ui.panel.config.integrations.config_flow.external_step.description"
|
||||
)}
|
||||
</p>
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
${this.flowConfig.renderExternalStepDescription(this.hass, this.step)}
|
||||
<div class="open-button">
|
||||
<a href=${this.step.url} target="_blank">
|
||||
<mwc-button raised>
|
||||
@ -76,7 +59,7 @@ class StepFlowExternal extends LitElement {
|
||||
}
|
||||
|
||||
fireEvent(this, "flow-update", {
|
||||
stepPromise: fetchConfigFlow(this.hass, this.step.flow_id),
|
||||
stepPromise: this.flowConfig.fetchFlow(this.hass, this.step.flow_id),
|
||||
});
|
||||
},
|
||||
"data_entry_flow_progressed"
|
||||
|
@ -15,21 +15,19 @@ import "@polymer/paper-spinner/paper-spinner";
|
||||
import "../../components/ha-form";
|
||||
import "../../components/ha-markdown";
|
||||
import "../../resources/ha-style";
|
||||
import {
|
||||
handleConfigFlowStep,
|
||||
FieldSchema,
|
||||
ConfigFlowStepForm,
|
||||
} from "../../data/config_entries";
|
||||
import { PolymerChangedEvent, applyPolymerEvent } from "../../polymer-types";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
import { DataEntryFlowStepForm, FieldSchema } from "../../data/data_entry_flow";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
@customElement("step-flow-form")
|
||||
class StepFlowForm extends LitElement {
|
||||
public flowConfig!: FlowConfig;
|
||||
|
||||
@property()
|
||||
public step!: ConfigFlowStepForm;
|
||||
public step!: DataEntryFlowStepForm;
|
||||
|
||||
@property()
|
||||
public hass!: HomeAssistant;
|
||||
@ -44,7 +42,6 @@ class StepFlowForm extends LitElement {
|
||||
private _errorMsg?: string;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const localize = this.hass.localize;
|
||||
const step = this.step;
|
||||
|
||||
const allRequiredInfoFilledIn =
|
||||
@ -59,17 +56,9 @@ class StepFlowForm extends LitElement {
|
||||
!["", undefined].includes(this._stepData![field.name])
|
||||
);
|
||||
|
||||
const description = localizeKey(
|
||||
localize,
|
||||
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>
|
||||
${localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||
)}
|
||||
${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
${this._errorMsg
|
||||
@ -77,11 +66,7 @@ class StepFlowForm extends LitElement {
|
||||
<div class="error">${this._errorMsg}</div>
|
||||
`
|
||||
: ""}
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
${this.flowConfig.renderShowFormStepDescription(this.hass, this.step)}
|
||||
<ha-form
|
||||
.data=${this._stepDataProcessed}
|
||||
@data-changed=${this._stepDataChanged}
|
||||
@ -161,7 +146,7 @@ class StepFlowForm extends LitElement {
|
||||
});
|
||||
|
||||
try {
|
||||
const step = await handleConfigFlowStep(
|
||||
const step = await this.flowConfig.handleFlowStep(
|
||||
this.hass,
|
||||
this.step.flow_id,
|
||||
toSendData
|
||||
@ -188,18 +173,11 @@ class StepFlowForm extends LitElement {
|
||||
this._stepData = applyPolymerEvent(ev, this._stepData);
|
||||
}
|
||||
|
||||
private _labelCallback = (schema: FieldSchema): string => {
|
||||
const step = this.step as ConfigFlowStepForm;
|
||||
|
||||
return this.hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.data.${
|
||||
schema.name
|
||||
}`
|
||||
);
|
||||
};
|
||||
private _labelCallback = (field: FieldSchema): string =>
|
||||
this.flowConfig.renderShowFormStepFieldLabel(this.hass, this.step, field);
|
||||
|
||||
private _errorCallback = (error: string) =>
|
||||
this.hass.localize(`component.${this.step.handler}.config.error.${error}`);
|
||||
this.flowConfig.renderShowFormStepFieldError(this.hass, this.step, error);
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
|
@ -11,7 +11,6 @@ import "@polymer/paper-spinner/paper-spinner-lite";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { createConfigFlow } from "../../data/config_entries";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import memoizeOne from "memoize-one";
|
||||
import * as Fuse from "fuse.js";
|
||||
@ -19,6 +18,7 @@ import * as Fuse from "fuse.js";
|
||||
import "../../components/ha-icon-next";
|
||||
import "../../common/search/search-input";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
interface HandlerObj {
|
||||
name: string;
|
||||
@ -27,6 +27,8 @@ interface HandlerObj {
|
||||
|
||||
@customElement("step-flow-pick-handler")
|
||||
class StepFlowPickHandler extends LitElement {
|
||||
public flowConfig!: FlowConfig;
|
||||
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public handlers!: string[];
|
||||
@property() private filter?: string;
|
||||
@ -97,7 +99,10 @@ class StepFlowPickHandler extends LitElement {
|
||||
|
||||
private async _handlerPicked(ev) {
|
||||
fireEvent(this, "flow-update", {
|
||||
stepPromise: createConfigFlow(this.hass, ev.currentTarget.handler.slug),
|
||||
stepPromise: this.flowConfig.createFlow(
|
||||
this.hass,
|
||||
ev.currentTarget.handler.slug
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
getConfigFlowsInProgress,
|
||||
getConfigEntries,
|
||||
ConfigEntry,
|
||||
ConfigFlowProgress,
|
||||
localizeConfigFlowTitle,
|
||||
} from "../data/config_entries";
|
||||
import { compare } from "../common/string/compare";
|
||||
@ -28,13 +27,14 @@ import { debounce } from "../common/util/debounce";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { onboardIntegrationStep } from "../data/onboarding";
|
||||
import { genClientId } from "home-assistant-js-websocket";
|
||||
import { DataEntryFlowProgress } from "../data/data_entry_flow";
|
||||
|
||||
@customElement("onboarding-integrations")
|
||||
class OnboardingIntegrations extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public onboardingLocalize!: LocalizeFunc;
|
||||
@property() private _entries?: ConfigEntry[];
|
||||
@property() private _discovered?: ConfigFlowProgress[];
|
||||
@property() private _discovered?: DataEntryFlowProgress[];
|
||||
private _unsubEvents?: () => void;
|
||||
|
||||
public connectedCallback() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user