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:
Paulus Schoutsen 2019-08-15 09:03:54 -07:00 committed by GitHub
parent 07b8518162
commit 200e099035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 382 additions and 225 deletions

View File

@ -11,7 +11,10 @@ import "../components/ha-form";
import "../components/ha-markdown"; import "../components/ha-markdown";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { AuthProvider } from "../data/auth"; 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"; type State = "loading" | "error" | "step";
@ -22,7 +25,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
@property() public oauth2State?: string; @property() public oauth2State?: string;
@property() private _state: State = "loading"; @property() private _state: State = "loading";
@property() private _stepData: any = {}; @property() private _stepData: any = {};
@property() private _step?: ConfigFlowStep; @property() private _step?: DataEntryFlowStep;
@property() private _errorMessage?: string; @property() private _errorMessage?: string;
protected render() { protected render() {
@ -87,7 +90,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
} }
} }
private _renderStep(step: ConfigFlowStep) { private _renderStep(step: DataEntryFlowStep) {
switch (step.type) { switch (step.type) {
case "abort": case "abort":
return html` return html`
@ -192,7 +195,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
document.location.assign(url); document.location.assign(url);
} }
private async _updateStep(step: ConfigFlowStep) { private async _updateStep(step: DataEntryFlowStep) {
let stepData: any = null; let stepData: any = null;
if ( if (
this._step && this._step &&
@ -219,7 +222,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
}, 100); }, 100);
} }
private _computeStepDescription(step: ConfigFlowStepForm) { private _computeStepDescription(step: DataEntryFlowStepForm) {
const resourceKey = `ui.panel.page-authorize.form.providers.${ const resourceKey = `ui.panel.page-authorize.form.providers.${
step.handler[0] step.handler[0]
}.step.${step.step_id}.description`; }.step.${step.step_id}.description`;
@ -232,7 +235,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
return this.localize(resourceKey, ...args); 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 // Returns a callback for ha-form to calculate labels per schema object
return (schema) => return (schema) =>
this.localize( 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 // Returns a callback for ha-form to calculate error messages
return (error) => return (error) =>
this.localize( this.localize(

View File

@ -2,15 +2,7 @@ import { HomeAssistant } from "../types";
import { createCollection } from "home-assistant-js-websocket"; import { createCollection } from "home-assistant-js-websocket";
import { debounce } from "../common/util/debounce"; import { debounce } from "../common/util/debounce";
import { LocalizeFunc } from "../common/translations/localize"; import { LocalizeFunc } from "../common/translations/localize";
import { DataEntryFlowStep, DataEntryFlowProgress } from "./data_entry_flow";
export interface DataEntryFlowProgressedEvent {
type: "data_entry_flow_progressed";
data: {
handler: string;
flow_id: string;
refresh: boolean;
};
}
export interface ConfigEntry { export interface ConfigEntry {
entry_id: string; entry_id: string;
@ -22,80 +14,23 @@ export interface ConfigEntry {
supports_options: boolean; 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) => export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
hass.callApi<ConfigFlowStep>("POST", "config/config_entries/flow", { hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
handler, handler,
}); });
export const fetchConfigFlow = (hass: HomeAssistant, flowId: string) => 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 = ( export const handleConfigFlowStep = (
hass: HomeAssistant, hass: HomeAssistant,
flowId: string, flowId: string,
data: { [key: string]: any } data: { [key: string]: any }
) => ) =>
hass.callApi<ConfigFlowStep>( hass.callApi<DataEntryFlowStep>(
"POST", "POST",
`config/config_entries/flow/${flowId}`, `config/config_entries/flow/${flowId}`,
data data
@ -105,7 +40,7 @@ export const deleteConfigFlow = (hass: HomeAssistant, flowId: string) =>
hass.callApi("DELETE", `config/config_entries/flow/${flowId}`); hass.callApi("DELETE", `config/config_entries/flow/${flowId}`);
export const getConfigFlowsInProgress = (hass: HomeAssistant) => 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) => export const getConfigFlowHandlers = (hass: HomeAssistant) =>
hass.callApi<string[]>("GET", "config/config_entries/flow_handlers"); hass.callApi<string[]>("GET", "config/config_entries/flow_handlers");
@ -130,9 +65,9 @@ const subscribeConfigFlowInProgressUpdates = (conn, store) =>
export const subscribeConfigFlowInProgress = ( export const subscribeConfigFlowInProgress = (
hass: HomeAssistant, hass: HomeAssistant,
onChange: (flows: ConfigFlowProgress[]) => void onChange: (flows: DataEntryFlowProgress[]) => void
) => ) =>
createCollection<ConfigFlowProgress[]>( createCollection<DataEntryFlowProgress[]>(
"_configFlowProgress", "_configFlowProgress",
fetchConfigFlowInProgress, fetchConfigFlowInProgress,
subscribeConfigFlowInProgressUpdates, subscribeConfigFlowInProgressUpdates,
@ -145,7 +80,7 @@ export const getConfigEntries = (hass: HomeAssistant) =>
export const localizeConfigFlowTitle = ( export const localizeConfigFlowTitle = (
localize: LocalizeFunc, localize: LocalizeFunc,
flow: ConfigFlowProgress flow: DataEntryFlowProgress
) => { ) => {
const placeholders = flow.context.title_placeholders || {}; const placeholders = flow.context.title_placeholders || {};
const placeholderKeys = Object.keys(placeholders); const placeholderKeys = Object.keys(placeholders);

View 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;

View File

@ -22,14 +22,8 @@ import "../../components/dialog/ha-paper-dialog";
// tslint:disable-next-line // tslint:disable-next-line
import { HaPaperDialog } from "../../components/dialog/ha-paper-dialog"; import { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
import {
fetchConfigFlow,
ConfigFlowStep,
deleteConfigFlow,
getConfigFlowHandlers,
} from "../../data/config_entries";
import { PolymerChangedEvent } from "../../polymer-types"; 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-pick-handler";
import "./step-flow-loading"; import "./step-flow-loading";
@ -46,7 +40,7 @@ import {
subscribeAreaRegistry, subscribeAreaRegistry,
} from "../../data/area_registry"; } from "../../data/area_registry";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { caseInsensitiveCompare } from "../../common/string/compare"; import { DataEntryFlowStep } from "../../data/data_entry_flow";
let instance = 0; let instance = 0;
@ -54,20 +48,20 @@ declare global {
// for fire event // for fire event
interface HASSDomEvents { interface HASSDomEvents {
"flow-update": { "flow-update": {
step?: ConfigFlowStep; step?: DataEntryFlowStep;
stepPromise?: Promise<ConfigFlowStep>; stepPromise?: Promise<DataEntryFlowStep>;
}; };
} }
} }
@customElement("dialog-config-flow") @customElement("dialog-data-entry-flow")
class ConfigFlowDialog extends LitElement { class DataEntryFlowDialog extends LitElement {
public hass!: HomeAssistant; public hass!: HomeAssistant;
@property() private _params?: HaConfigFlowParams; @property() private _params?: DataEntryFlowDialogParams;
@property() private _loading = true; @property() private _loading = true;
private _instance = instance; private _instance = instance;
@property() private _step: @property() private _step:
| ConfigFlowStep | DataEntryFlowStep
| undefined | undefined
// Null means we need to pick a config flow // Null means we need to pick a config flow
| null; | null;
@ -77,12 +71,15 @@ class ConfigFlowDialog extends LitElement {
private _unsubAreas?: UnsubscribeFunc; private _unsubAreas?: UnsubscribeFunc;
private _unsubDevices?: UnsubscribeFunc; private _unsubDevices?: UnsubscribeFunc;
public async showDialog(params: HaConfigFlowParams): Promise<void> { public async showDialog(params: DataEntryFlowDialogParams): Promise<void> {
this._params = params; this._params = params;
this._instance = instance++; this._instance = instance++;
// Create a new config flow. Show picker // Create a new config flow. Show picker
if (!params.continueFlowId) { if (!params.continueFlowId) {
if (!params.flowConfig.getFlowHandlers) {
throw new Error("No getFlowHandlers defined in flow config");
}
this._step = null; this._step = null;
// We only load the handlers once // We only load the handlers once
@ -90,13 +87,7 @@ class ConfigFlowDialog extends LitElement {
this._loading = true; this._loading = true;
this.updateComplete.then(() => this._scheduleCenterDialog()); this.updateComplete.then(() => this._scheduleCenterDialog());
try { try {
this._handlers = (await getConfigFlowHandlers(this.hass)).sort( this._handlers = await params.flowConfig.getFlowHandlers(this.hass);
(handlerA, handlerB) =>
caseInsensitiveCompare(
this.hass.localize(`component.${handlerA}.config.title`),
this.hass.localize(`component.${handlerB}.config.title`)
)
);
} finally { } finally {
this._loading = false; this._loading = false;
} }
@ -108,7 +99,10 @@ class ConfigFlowDialog extends LitElement {
this._loading = true; this._loading = true;
const curInstance = this._instance; 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 // Happens if second showDialog called
if (curInstance !== this._instance) { if (curInstance !== this._instance) {
@ -145,6 +139,7 @@ class ConfigFlowDialog extends LitElement {
? // Show handler picker ? // Show handler picker
html` html`
<step-flow-pick-handler <step-flow-pick-handler
.flowConfig=${this._params.flowConfig}
.hass=${this.hass} .hass=${this.hass}
.handlers=${this._handlers} .handlers=${this._handlers}
></step-flow-pick-handler> ></step-flow-pick-handler>
@ -152,6 +147,7 @@ class ConfigFlowDialog extends LitElement {
: this._step.type === "form" : this._step.type === "form"
? html` ? html`
<step-flow-form <step-flow-form
.flowConfig=${this._params.flowConfig}
.step=${this._step} .step=${this._step}
.hass=${this.hass} .hass=${this.hass}
></step-flow-form> ></step-flow-form>
@ -159,6 +155,7 @@ class ConfigFlowDialog extends LitElement {
: this._step.type === "external" : this._step.type === "external"
? html` ? html`
<step-flow-external <step-flow-external
.flowConfig=${this._params.flowConfig}
.step=${this._step} .step=${this._step}
.hass=${this.hass} .hass=${this.hass}
></step-flow-external> ></step-flow-external>
@ -166,6 +163,7 @@ class ConfigFlowDialog extends LitElement {
: this._step.type === "abort" : this._step.type === "abort"
? html` ? html`
<step-flow-abort <step-flow-abort
.flowConfig=${this._params.flowConfig}
.step=${this._step} .step=${this._step}
.hass=${this.hass} .hass=${this.hass}
></step-flow-abort> ></step-flow-abort>
@ -177,6 +175,7 @@ class ConfigFlowDialog extends LitElement {
` `
: html` : html`
<step-flow-create-entry <step-flow-create-entry
.flowConfig=${this._params.flowConfig}
.step=${this._step} .step=${this._step}
.hass=${this.hass} .hass=${this.hass}
.devices=${this._devices} .devices=${this._devices}
@ -201,8 +200,13 @@ class ConfigFlowDialog extends LitElement {
this._step && this._step &&
this._step.type === "create_entry" this._step.type === "create_entry"
) { ) {
if (this._params!.flowConfig.loadDevicesAndAreas) {
this._fetchDevices(this._step.result); this._fetchDevices(this._step.result);
this._fetchAreas(); this._fetchAreas();
} else {
this._devices = [];
this._areas = [];
}
} }
if (changedProps.has("_devices") && this._dialog) { if (changedProps.has("_devices") && this._dialog) {
@ -236,7 +240,7 @@ class ConfigFlowDialog extends LitElement {
} }
private async _processStep( private async _processStep(
step: ConfigFlowStep | undefined | Promise<ConfigFlowStep> step: DataEntryFlowStep | undefined | Promise<DataEntryFlowStep>
): Promise<void> { ): Promise<void> {
if (step instanceof Promise) { if (step instanceof Promise) {
this._loading = true; this._loading = true;
@ -267,7 +271,7 @@ class ConfigFlowDialog extends LitElement {
// If we created this flow, delete it now. // If we created this flow, delete it now.
if (this._step && !flowFinished && !this._params.continueFlowId) { 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({ this._params.dialogClosedCallback({
@ -319,6 +323,6 @@ class ConfigFlowDialog extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"dialog-config-flow": ConfigFlowDialog; "dialog-data-entry-flow": DataEntryFlowDialog;
} }
} }

View File

@ -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 { export const loadConfigFlowDialog = loadDataEntryFlowDialog;
continueFlowId?: string;
dialogClosedCallback: (params: { flowFinished: boolean }) => void;
}
export const loadConfigFlowDialog = () =>
import(/* webpackChunkName: "dialog-config-flow" */ "./dialog-config-flow");
export const showConfigFlowDialog = ( export const showConfigFlowDialog = (
element: HTMLElement, element: HTMLElement,
dialogParams: HaConfigFlowParams dialogParams: Omit<DataEntryFlowDialogParams, "flowConfig">
): void => { ): void =>
fireEvent(element, "show-dialog", { showFlowDialog(element, dialogParams, {
dialogTag: "dialog-config-flow", loadDevicesAndAreas: true,
dialogImport: loadConfigFlowDialog, getFlowHandlers: (hass) =>
dialogParams, 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>
`;
},
}); });
};

View 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,
},
});
};

View File

@ -8,38 +8,27 @@ import {
} from "lit-element"; } from "lit-element";
import "@material/mwc-button"; import "@material/mwc-button";
import { ConfigFlowStepAbort } from "../../data/config_entries"; import { DataEntryFlowStepAbort } from "../../data/data_entry_flow";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { localizeKey } from "../../common/translations/localize";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { configFlowContentStyles } from "./styles"; import { configFlowContentStyles } from "./styles";
import { FlowConfig } from "./show-dialog-data-entry-flow";
@customElement("step-flow-abort") @customElement("step-flow-abort")
class StepFlowAbort extends LitElement { class StepFlowAbort extends LitElement {
public flowConfig!: FlowConfig;
@property() @property()
public hass!: HomeAssistant; public hass!: HomeAssistant;
@property() @property()
private step!: ConfigFlowStepAbort; private step!: DataEntryFlowStepAbort;
protected render(): TemplateResult | void { 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` return html`
<h2>Aborted</h2> <h2>Aborted</h2>
<div class="content"> <div class="content">
${description ${this.flowConfig.renderAbortDescription(this.hass, this.step)}
? html`
<ha-markdown .content=${description}></ha-markdown>
`
: ""}
</div> </div>
<div class="buttons"> <div class="buttons">
<mwc-button @click="${this._flowDone}">Close</mwc-button> <mwc-button @click="${this._flowDone}">Close</mwc-button>

View File

@ -12,9 +12,7 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import { ConfigFlowStepCreateEntry } from "../../data/config_entries";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { localizeKey } from "../../common/translations/localize";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { configFlowContentStyles } from "./styles"; import { configFlowContentStyles } from "./styles";
import { import {
@ -25,14 +23,18 @@ import {
AreaRegistryEntry, AreaRegistryEntry,
createAreaRegistryEntry, createAreaRegistryEntry,
} from "../../data/area_registry"; } from "../../data/area_registry";
import { DataEntryFlowStepCreateEntry } from "../../data/data_entry_flow";
import { FlowConfig } from "./show-dialog-data-entry-flow";
@customElement("step-flow-create-entry") @customElement("step-flow-create-entry")
class StepFlowCreateEntry extends LitElement { class StepFlowCreateEntry extends LitElement {
public flowConfig!: FlowConfig;
@property() @property()
public hass!: HomeAssistant; public hass!: HomeAssistant;
@property() @property()
public step!: ConfigFlowStepCreateEntry; public step!: DataEntryFlowStepCreateEntry;
@property() @property()
public devices!: DeviceRegistryEntry[]; public devices!: DeviceRegistryEntry[];
@ -42,24 +44,11 @@ class StepFlowCreateEntry extends LitElement {
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const localize = this.hass.localize; 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` return html`
<h2>Success!</h2> <h2>Success!</h2>
<div class="content"> <div class="content">
${description ${this.flowConfig.renderCreateEntryDescription(this.hass, this.step)}
? html`
<ha-markdown .content=${description}></ha-markdown>
`
: ""}
<p>Created config for ${step.title}.</p>
${this.devices.length === 0 ${this.devices.length === 0
? "" ? ""
: html` : html`

View File

@ -9,51 +9,34 @@ import {
} from "lit-element"; } from "lit-element";
import "@material/mwc-button"; import "@material/mwc-button";
import {
ConfigFlowStepExternal,
DataEntryFlowProgressedEvent,
fetchConfigFlow,
} from "../../data/config_entries";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { localizeKey } from "../../common/translations/localize";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { configFlowContentStyles } from "./styles"; import { configFlowContentStyles } from "./styles";
import {
DataEntryFlowStepExternal,
DataEntryFlowProgressedEvent,
} from "../../data/data_entry_flow";
import { FlowConfig } from "./show-dialog-data-entry-flow";
@customElement("step-flow-external") @customElement("step-flow-external")
class StepFlowExternal extends LitElement { class StepFlowExternal extends LitElement {
public flowConfig!: FlowConfig;
@property() @property()
public hass!: HomeAssistant; public hass!: HomeAssistant;
@property() @property()
private step!: ConfigFlowStepExternal; private step!: DataEntryFlowStepExternal;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const localize = this.hass.localize; 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` return html`
<h2> <h2>
${localize( ${this.flowConfig.renderExternalStepHeader(this.hass, this.step)}
`component.${step.handler}.config.step.${step.step_id}.title`
)}
</h2> </h2>
<div class="content"> <div class="content">
<p> ${this.flowConfig.renderExternalStepDescription(this.hass, this.step)}
${localize(
"ui.panel.config.integrations.config_flow.external_step.description"
)}
</p>
${description
? html`
<ha-markdown .content=${description}></ha-markdown>
`
: ""}
<div class="open-button"> <div class="open-button">
<a href=${this.step.url} target="_blank"> <a href=${this.step.url} target="_blank">
<mwc-button raised> <mwc-button raised>
@ -76,7 +59,7 @@ class StepFlowExternal extends LitElement {
} }
fireEvent(this, "flow-update", { 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" "data_entry_flow_progressed"

View File

@ -15,21 +15,19 @@ import "@polymer/paper-spinner/paper-spinner";
import "../../components/ha-form"; import "../../components/ha-form";
import "../../components/ha-markdown"; import "../../components/ha-markdown";
import "../../resources/ha-style"; import "../../resources/ha-style";
import {
handleConfigFlowStep,
FieldSchema,
ConfigFlowStepForm,
} from "../../data/config_entries";
import { PolymerChangedEvent, applyPolymerEvent } from "../../polymer-types"; import { PolymerChangedEvent, applyPolymerEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { localizeKey } from "../../common/translations/localize";
import { configFlowContentStyles } from "./styles"; import { configFlowContentStyles } from "./styles";
import { DataEntryFlowStepForm, FieldSchema } from "../../data/data_entry_flow";
import { FlowConfig } from "./show-dialog-data-entry-flow";
@customElement("step-flow-form") @customElement("step-flow-form")
class StepFlowForm extends LitElement { class StepFlowForm extends LitElement {
public flowConfig!: FlowConfig;
@property() @property()
public step!: ConfigFlowStepForm; public step!: DataEntryFlowStepForm;
@property() @property()
public hass!: HomeAssistant; public hass!: HomeAssistant;
@ -44,7 +42,6 @@ class StepFlowForm extends LitElement {
private _errorMsg?: string; private _errorMsg?: string;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const localize = this.hass.localize;
const step = this.step; const step = this.step;
const allRequiredInfoFilledIn = const allRequiredInfoFilledIn =
@ -59,17 +56,9 @@ class StepFlowForm extends LitElement {
!["", undefined].includes(this._stepData![field.name]) !["", undefined].includes(this._stepData![field.name])
); );
const description = localizeKey(
localize,
`component.${step.handler}.config.step.${step.step_id}.description`,
step.description_placeholders
);
return html` return html`
<h2> <h2>
${localize( ${this.flowConfig.renderShowFormStepHeader(this.hass, this.step)}
`component.${step.handler}.config.step.${step.step_id}.title`
)}
</h2> </h2>
<div class="content"> <div class="content">
${this._errorMsg ${this._errorMsg
@ -77,11 +66,7 @@ class StepFlowForm extends LitElement {
<div class="error">${this._errorMsg}</div> <div class="error">${this._errorMsg}</div>
` `
: ""} : ""}
${description ${this.flowConfig.renderShowFormStepDescription(this.hass, this.step)}
? html`
<ha-markdown .content=${description}></ha-markdown>
`
: ""}
<ha-form <ha-form
.data=${this._stepDataProcessed} .data=${this._stepDataProcessed}
@data-changed=${this._stepDataChanged} @data-changed=${this._stepDataChanged}
@ -161,7 +146,7 @@ class StepFlowForm extends LitElement {
}); });
try { try {
const step = await handleConfigFlowStep( const step = await this.flowConfig.handleFlowStep(
this.hass, this.hass,
this.step.flow_id, this.step.flow_id,
toSendData toSendData
@ -188,18 +173,11 @@ class StepFlowForm extends LitElement {
this._stepData = applyPolymerEvent(ev, this._stepData); this._stepData = applyPolymerEvent(ev, this._stepData);
} }
private _labelCallback = (schema: FieldSchema): string => { private _labelCallback = (field: FieldSchema): string =>
const step = this.step as ConfigFlowStepForm; this.flowConfig.renderShowFormStepFieldLabel(this.hass, this.step, field);
return this.hass.localize(
`component.${step.handler}.config.step.${step.step_id}.data.${
schema.name
}`
);
};
private _errorCallback = (error: string) => 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 { static get styles(): CSSResultArray {
return [ return [

View File

@ -11,7 +11,6 @@ import "@polymer/paper-spinner/paper-spinner-lite";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { createConfigFlow } from "../../data/config_entries";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import * as Fuse from "fuse.js"; import * as Fuse from "fuse.js";
@ -19,6 +18,7 @@ import * as Fuse from "fuse.js";
import "../../components/ha-icon-next"; import "../../components/ha-icon-next";
import "../../common/search/search-input"; import "../../common/search/search-input";
import { styleMap } from "lit-html/directives/style-map"; import { styleMap } from "lit-html/directives/style-map";
import { FlowConfig } from "./show-dialog-data-entry-flow";
interface HandlerObj { interface HandlerObj {
name: string; name: string;
@ -27,6 +27,8 @@ interface HandlerObj {
@customElement("step-flow-pick-handler") @customElement("step-flow-pick-handler")
class StepFlowPickHandler extends LitElement { class StepFlowPickHandler extends LitElement {
public flowConfig!: FlowConfig;
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public handlers!: string[]; @property() public handlers!: string[];
@property() private filter?: string; @property() private filter?: string;
@ -97,7 +99,10 @@ class StepFlowPickHandler extends LitElement {
private async _handlerPicked(ev) { private async _handlerPicked(ev) {
fireEvent(this, "flow-update", { fireEvent(this, "flow-update", {
stepPromise: createConfigFlow(this.hass, ev.currentTarget.handler.slug), stepPromise: this.flowConfig.createFlow(
this.hass,
ev.currentTarget.handler.slug
),
}); });
} }

View File

@ -18,7 +18,6 @@ import {
getConfigFlowsInProgress, getConfigFlowsInProgress,
getConfigEntries, getConfigEntries,
ConfigEntry, ConfigEntry,
ConfigFlowProgress,
localizeConfigFlowTitle, localizeConfigFlowTitle,
} from "../data/config_entries"; } from "../data/config_entries";
import { compare } from "../common/string/compare"; import { compare } from "../common/string/compare";
@ -28,13 +27,14 @@ import { debounce } from "../common/util/debounce";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { onboardIntegrationStep } from "../data/onboarding"; import { onboardIntegrationStep } from "../data/onboarding";
import { genClientId } from "home-assistant-js-websocket"; import { genClientId } from "home-assistant-js-websocket";
import { DataEntryFlowProgress } from "../data/data_entry_flow";
@customElement("onboarding-integrations") @customElement("onboarding-integrations")
class OnboardingIntegrations extends LitElement { class OnboardingIntegrations extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public onboardingLocalize!: LocalizeFunc; @property() public onboardingLocalize!: LocalizeFunc;
@property() private _entries?: ConfigEntry[]; @property() private _entries?: ConfigEntry[];
@property() private _discovered?: ConfigFlowProgress[]; @property() private _discovered?: DataEntryFlowProgress[];
private _unsubEvents?: () => void; private _unsubEvents?: () => void;
public connectedCallback() { public connectedCallback() {