mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-23 20:48:11 +00:00
proper resolution of promises
This commit is contained in:
parent
4381ea447c
commit
2dbde9c31a
@ -17,7 +17,7 @@ export const SmartUi = (): ClassDecorator => {
|
||||
};
|
||||
};
|
||||
|
||||
export const ClassInfo = (info: Info): ClassDecorator => {
|
||||
export const ClassInfo = (info: (() => Promise<Info>) | Info): ClassDecorator => {
|
||||
return (target: Function) => {
|
||||
addPropertyToMap(target, "root", target.name, "info", info);
|
||||
};
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { EnumItem, Info, InputType } from "../../../SmartUi/SmartUiComponent";
|
||||
import { ChoiceItem, Info, InputType } from "../../../SmartUi/SmartUiComponent";
|
||||
import { addPropertyToMap } from "./SelfServeUtils";
|
||||
|
||||
const addToMap = (descriptorName: string, descriptorValue: any): PropertyDecorator => {
|
||||
return (target, property) => {
|
||||
const className = (target as Function).name;
|
||||
var propertyType = Reflect.getMetadata("design:type", target, property);
|
||||
addPropertyToMap(target, property.toString(), className, "type", propertyType.name);
|
||||
var propertyType = (Reflect.getMetadata("design:type", target, property).name as string).toLowerCase();
|
||||
|
||||
addPropertyToMap(target, property.toString(), className, "type", propertyType);
|
||||
addPropertyToMap(target, property.toString(), className, "dataFieldName", property.toString());
|
||||
|
||||
if (!className) {
|
||||
throw new Error("property descriptor applied to non static field!");
|
||||
@ -20,11 +22,11 @@ export const OnChange = (
|
||||
return addToMap("onChange", onChange);
|
||||
};
|
||||
|
||||
export const PropertyInfo = (info: Info): PropertyDecorator => {
|
||||
export const PropertyInfo = (info: (() => Promise<Info>) | Info): PropertyDecorator => {
|
||||
return addToMap("info", info);
|
||||
};
|
||||
|
||||
export const Placeholder = (placeholder: string): PropertyDecorator => {
|
||||
export const Placeholder = (placeholder: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("placeholder", placeholder);
|
||||
};
|
||||
|
||||
@ -32,43 +34,47 @@ export const ParentOf = (children: string[]): PropertyDecorator => {
|
||||
return addToMap("parentOf", children);
|
||||
};
|
||||
|
||||
export const Label = (label: string): PropertyDecorator => {
|
||||
export const Label = (label: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("label", label);
|
||||
};
|
||||
|
||||
export const DataFieldName = (dataFieldName: string): PropertyDecorator => {
|
||||
return addToMap("dataFieldName", dataFieldName);
|
||||
};
|
||||
|
||||
export const Min = (min: number): PropertyDecorator => {
|
||||
export const Min = (min: (() => Promise<number>) | number): PropertyDecorator => {
|
||||
return addToMap("min", min);
|
||||
};
|
||||
|
||||
export const Max = (max: number): PropertyDecorator => {
|
||||
export const Max = (max: (() => Promise<number>) | number): PropertyDecorator => {
|
||||
return addToMap("max", max);
|
||||
};
|
||||
|
||||
export const Step = (step: number): PropertyDecorator => {
|
||||
export const Step = (step: (() => Promise<number>) | number): PropertyDecorator => {
|
||||
return addToMap("step", step);
|
||||
};
|
||||
|
||||
export const DefaultValue = (defaultValue: any): PropertyDecorator => {
|
||||
return addToMap("defaultValue", defaultValue);
|
||||
export const DefaultStringValue = (defaultStringValue: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("defaultValue", defaultStringValue);
|
||||
};
|
||||
|
||||
export const TrueLabel = (trueLabel: string): PropertyDecorator => {
|
||||
export const DefaultNumberValue = (defaultNumberValue: (() => Promise<number>) | number): PropertyDecorator => {
|
||||
return addToMap("defaultValue", defaultNumberValue);
|
||||
};
|
||||
|
||||
export const DefaultBooleanValue = (defaultBooleanValue: (() => Promise<boolean>) | boolean): PropertyDecorator => {
|
||||
return addToMap("defaultValue", defaultBooleanValue);
|
||||
};
|
||||
|
||||
export const TrueLabel = (trueLabel: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("trueLabel", trueLabel);
|
||||
};
|
||||
|
||||
export const FalseLabel = (falseLabel: string): PropertyDecorator => {
|
||||
export const FalseLabel = (falseLabel: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("falseLabel", falseLabel);
|
||||
};
|
||||
|
||||
export const Choices = (choices: EnumItem[]): PropertyDecorator => {
|
||||
export const Choices = (choices: (() => Promise<ChoiceItem[]>) | ChoiceItem[]): PropertyDecorator => {
|
||||
return addToMap("choices", choices);
|
||||
};
|
||||
|
||||
export const DefaultKey = (defaultKey: string): PropertyDecorator => {
|
||||
export const DefaultKey = (defaultKey: (() => Promise<string>) | string): PropertyDecorator => {
|
||||
return addToMap("defaultKey", defaultKey);
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ export class SelfServeCmponent extends React.Component {
|
||||
input: {
|
||||
label: "Instance Count",
|
||||
dataFieldName: "instanceCount",
|
||||
type: "Number",
|
||||
type: "number",
|
||||
min: 1,
|
||||
max: 5,
|
||||
step: 1,
|
||||
@ -44,7 +44,7 @@ export class SelfServeCmponent extends React.Component {
|
||||
input: {
|
||||
label: "Instance Size",
|
||||
dataFieldName: "instanceSize",
|
||||
type: "Object",
|
||||
type: "object",
|
||||
choices: [
|
||||
{ label: "1Core4Gb", key: "1Core4Gb", value: "1Core4Gb" },
|
||||
{ label: "2Core8Gb", key: "2Core8Gb", value: "2Core8Gb" },
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "reflect-metadata";
|
||||
import {
|
||||
EnumItem,
|
||||
ChoiceItem,
|
||||
Node,
|
||||
Info,
|
||||
InputTypeValue,
|
||||
@ -9,26 +9,26 @@ import {
|
||||
NumberInput,
|
||||
StringInput,
|
||||
BooleanInput,
|
||||
EnumInput,
|
||||
ChoiceInput,
|
||||
InputType
|
||||
} from "../../../SmartUi/SmartUiComponent";
|
||||
|
||||
export interface CommonInputTypes {
|
||||
id: string;
|
||||
info?: Info;
|
||||
info?: (() => Promise<Info>) | Info;
|
||||
parentOf?: string[];
|
||||
type?: InputTypeValue;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
label?: (() => Promise<string>) | string;
|
||||
placeholder?: (() => Promise<string>) | string;
|
||||
dataFieldName?: string;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
min?: (() => Promise<number>) | number;
|
||||
max?: (() => Promise<number>) | number;
|
||||
step?: (() => Promise<number>) | number;
|
||||
defaultValue?: any;
|
||||
trueLabel?: string;
|
||||
falseLabel?: string;
|
||||
choices?: EnumItem[];
|
||||
defaultKey?: string;
|
||||
trueLabel?: (() => Promise<string>) | string;
|
||||
falseLabel?: (() => Promise<string>) | string;
|
||||
choices?: (() => Promise<ChoiceItem[]>) | ChoiceItem[];
|
||||
defaultKey?: (() => Promise<string>) | string;
|
||||
inputType?: string;
|
||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
||||
onSubmit?: (currentValues: Map<string, InputType>) => Promise<void>;
|
||||
@ -72,7 +72,7 @@ export const addPropertyToMap = (
|
||||
propertyObject = { id: propertyKey };
|
||||
}
|
||||
|
||||
if (getValue(descriptorKey, propertyObject) && descriptorKey !== "type") {
|
||||
if (getValue(descriptorKey, propertyObject) && descriptorKey !== "type" && descriptorKey !== "dataFieldName") {
|
||||
throw new Error("duplicate descriptor");
|
||||
}
|
||||
|
||||
@ -82,42 +82,6 @@ export const addPropertyToMap = (
|
||||
Reflect.defineMetadata(metadataKey, context, target);
|
||||
};
|
||||
|
||||
/*
|
||||
const modifyParentProperty = (children: {[key: string]: any}, parentProperty: string, property: string | symbol) : any => {
|
||||
if (parentProperty in children) {
|
||||
children[parentProperty][property] ={id: property, input: {}}
|
||||
return children
|
||||
} else {
|
||||
const keys = Object.keys(children)
|
||||
for(var i =0; i< keys.length; i++) {
|
||||
children[keys[i]] = modifyParentProperty(children[keys[i]], parentProperty, property)
|
||||
return children
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
export const PropertyParser = (metadataKey: string, parentProperty?: string): PropertyDecorator => {
|
||||
return (target, property) => {
|
||||
let context = Reflect.getMetadata(metadataKey, target)
|
||||
if(!context) {
|
||||
context = {id: "root", info: undefined, input: undefined, children: {} }
|
||||
context.children[property] = {id: property, input: {}}
|
||||
}
|
||||
if (parentProperty) {
|
||||
const prevContextValue = JSON.stringify(context)
|
||||
context.children = modifyParentProperty(context.children, parentProperty, property)
|
||||
if (JSON.stringify(context) === prevContextValue) {
|
||||
throw new Error(`${parentProperty} not defined. declare it before the child property with @Property decorator.`)
|
||||
}
|
||||
} else {
|
||||
context.children[property] = {id: property, input: {}}
|
||||
}
|
||||
Reflect.defineMetadata(metadataKey, context, target)
|
||||
};
|
||||
};
|
||||
*/
|
||||
|
||||
export const toSmartUiDescriptor = (metadataKey: string, target: Object): void => {
|
||||
const context = Reflect.getMetadata(metadataKey, target) as Map<String, CommonInputTypes>;
|
||||
Reflect.defineMetadata(metadataKey, context, target);
|
||||
@ -125,7 +89,7 @@ export const toSmartUiDescriptor = (metadataKey: string, target: Object): void =
|
||||
const root = context.get("root");
|
||||
context.delete("root");
|
||||
|
||||
if (!root || !("onSubmit" in root)) {
|
||||
if (!root?.onSubmit) {
|
||||
throw new Error(
|
||||
"@OnSubmit decorator not declared for the class. Please ensure @SmartUi is the first decorator used for the class."
|
||||
);
|
||||
@ -157,12 +121,12 @@ const addToDescriptor = (
|
||||
let value = context.get(key);
|
||||
if (!value) {
|
||||
// should already be added to root
|
||||
value = getChildFromRoot(key, smartUiDescriptor);
|
||||
if (!value) {
|
||||
const childNode = getChildFromRoot(key, smartUiDescriptor);
|
||||
if (!childNode) {
|
||||
// if not found at root level, error out
|
||||
throw new Error("Either child does not exist or child has been assigned to more than one parent");
|
||||
}
|
||||
root.children.push(value);
|
||||
root.children.push(childNode);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -180,7 +144,7 @@ const addToDescriptor = (
|
||||
root.children.push(element);
|
||||
};
|
||||
|
||||
const getChildFromRoot = (key: String, smartUiDescriptor: Descriptor): CommonInputTypes => {
|
||||
const getChildFromRoot = (key: String, smartUiDescriptor: Descriptor): Node => {
|
||||
let i = 0;
|
||||
const children = smartUiDescriptor.root.children;
|
||||
for (; i < children.length; i++) {
|
||||
@ -197,16 +161,16 @@ const getInput = (value: CommonInputTypes): AnyInput => {
|
||||
if (!value.label || !value.type || !value.dataFieldName) {
|
||||
throw new Error("label, onChange, type and dataFieldName are required.");
|
||||
}
|
||||
console.log(value.type);
|
||||
|
||||
switch (value.type) {
|
||||
case "Number":
|
||||
case "number":
|
||||
if (!value.step || !value.defaultValue || !value.inputType) {
|
||||
throw new Error("step, defaultValue and inputType are needed for number type");
|
||||
}
|
||||
return value as NumberInput;
|
||||
case "String":
|
||||
case "string":
|
||||
return value as StringInput;
|
||||
case "Boolean":
|
||||
case "boolean":
|
||||
if (!value.trueLabel || !value.falseLabel || value.defaultValue === undefined) {
|
||||
throw new Error("truelabel, falselabel and defaultValue are needed for boolean type");
|
||||
}
|
||||
@ -215,6 +179,6 @@ const getInput = (value: CommonInputTypes): AnyInput => {
|
||||
if (!value.choices || !value.defaultKey) {
|
||||
throw new Error("choices and defaultKey are needed for enum type");
|
||||
}
|
||||
return value as EnumInput;
|
||||
return value as ChoiceInput;
|
||||
}
|
||||
};
|
||||
|
@ -1,11 +1,9 @@
|
||||
import {
|
||||
DataFieldName,
|
||||
Label,
|
||||
Min,
|
||||
Max,
|
||||
Step,
|
||||
DefaultKey,
|
||||
DefaultValue,
|
||||
NumberInputType,
|
||||
Choices,
|
||||
ParentOf,
|
||||
@ -13,11 +11,23 @@ import {
|
||||
OnChange,
|
||||
TrueLabel,
|
||||
FalseLabel,
|
||||
Placeholder
|
||||
Placeholder,
|
||||
DefaultNumberValue,
|
||||
DefaultBooleanValue,
|
||||
DefaultStringValue
|
||||
} from "./PropertyDescriptors";
|
||||
import { Descriptor, EnumItem, Info, InputType } from "../../../SmartUi/SmartUiComponent";
|
||||
import { Descriptor, ChoiceItem, Info, InputType } from "../../../SmartUi/SmartUiComponent";
|
||||
import { SmartUi, ClassInfo, SelfServeClass, OnSubmit } from "./ClassDescriptors";
|
||||
|
||||
const getPromise = <T extends (number | string | boolean | ChoiceItem[] | Info)>(value: T) : () => Promise<T> => {
|
||||
const f = async () : Promise<T> => {
|
||||
console.log("delay start")
|
||||
await SqlX.delay(100)
|
||||
console.log("delay end")
|
||||
return value
|
||||
}
|
||||
return f
|
||||
}
|
||||
enum Sizes {
|
||||
OneCore4Gb = "OneCore4Gb",
|
||||
TwoCore8Gb = "TwoCore8Gb",
|
||||
@ -25,41 +35,43 @@ enum Sizes {
|
||||
}
|
||||
|
||||
@SmartUi()
|
||||
@ClassInfo(SqlX.sqlXInfo)
|
||||
@OnSubmit(SqlX.onSubmit)
|
||||
@SelfServeClass()
|
||||
@ClassInfo(getPromise(SqlX.sqlXInfo))
|
||||
@OnSubmit(SqlX.onSubmit)
|
||||
export class SqlX {
|
||||
@PropertyInfo(SqlX.instanceSizeInfo)
|
||||
@Label("Instance Size")
|
||||
@DataFieldName("instanceSize")
|
||||
@Choices(SqlX.instanceSizeOptions)
|
||||
@DefaultKey(Sizes.OneCore4Gb)
|
||||
static instanceSize: EnumItem;
|
||||
|
||||
public static toSmartUiDescriptor = (): Descriptor => {
|
||||
return Reflect.getMetadata(SqlX.name, SqlX) as Descriptor;
|
||||
};
|
||||
|
||||
@PropertyInfo(getPromise(SqlX.instanceSizeInfo))
|
||||
@Label(getPromise("Instance Size"))
|
||||
@Choices(getPromise(SqlX.instanceSizeOptions))
|
||||
@DefaultKey(getPromise(Sizes.OneCore4Gb))
|
||||
static instanceSize: ChoiceItem;
|
||||
|
||||
@OnChange(SqlX.onInstanceCountChange)
|
||||
@Label("Instance Count")
|
||||
@DataFieldName("instanceCount")
|
||||
@Min(1)
|
||||
@Max(5)
|
||||
@Step(1)
|
||||
@DefaultValue(1)
|
||||
@Label(getPromise("Instance Count"))
|
||||
@Min(getPromise(0))
|
||||
@Max(getPromise(5))
|
||||
@Step(getPromise(1))
|
||||
@DefaultNumberValue(getPromise(1))
|
||||
@NumberInputType("slider")
|
||||
@ParentOf(["instanceSize", "instanceName", "isAllowed"])
|
||||
static instanceCount: number;
|
||||
|
||||
@Label("Feature Allowed")
|
||||
@DataFieldName("isAllowed")
|
||||
@DefaultValue(false)
|
||||
@TrueLabel("allowed")
|
||||
@FalseLabel("not allowed")
|
||||
@Label(getPromise("Feature Allowed"))
|
||||
@DefaultBooleanValue(getPromise(false))
|
||||
@TrueLabel(getPromise("allowed"))
|
||||
@FalseLabel(getPromise("not allowed"))
|
||||
static isAllowed: boolean;
|
||||
|
||||
@Label("Instance Name")
|
||||
@DataFieldName("instanceName")
|
||||
@Placeholder("instance name")
|
||||
@Label(getPromise("Instance Name"))
|
||||
@DefaultStringValue(getPromise("asdf"))
|
||||
@Placeholder(getPromise("instance name"))
|
||||
static instanceName: string;
|
||||
|
||||
static instanceSizeOptions: EnumItem[] = [
|
||||
static instanceSizeOptions: ChoiceItem[] = [
|
||||
{ label: Sizes.OneCore4Gb, key: Sizes.OneCore4Gb, value: Sizes.OneCore4Gb },
|
||||
{ label: Sizes.TwoCore8Gb, key: Sizes.TwoCore8Gb, value: Sizes.TwoCore8Gb },
|
||||
{ label: Sizes.FourCore16Gb, key: Sizes.FourCore16Gb, value: Sizes.FourCore16Gb }
|
||||
@ -97,7 +109,8 @@ export class SqlX {
|
||||
);
|
||||
};
|
||||
|
||||
public static toSmartUiDescriptor = (): Descriptor => {
|
||||
return Reflect.getMetadata(SqlX.name, SqlX) as Descriptor;
|
||||
};
|
||||
static delay = (ms: number) => {
|
||||
return new Promise( resolve => setTimeout(resolve, ms) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ describe("SmartUiComponent", () => {
|
||||
input: {
|
||||
label: "Database",
|
||||
dataFieldName: "database",
|
||||
type: "enum",
|
||||
type: "object",
|
||||
choices: [
|
||||
{ label: "Database 1", key: "db1", value: "database1" },
|
||||
{ label: "Database 2", key: "db2", value: "database2" },
|
||||
|
@ -8,7 +8,7 @@ import { Text } from "office-ui-fabric-react/lib/Text";
|
||||
import { InputType } from "../../Tables/Constants";
|
||||
import { RadioSwitchComponent } from "../RadioSwitchComponent/RadioSwitchComponent";
|
||||
import { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack";
|
||||
import { Link, MessageBar, MessageBarType, PrimaryButton } from "office-ui-fabric-react";
|
||||
import { Link, MessageBar, MessageBarType, PrimaryButton, Spinner, SpinnerSize } from "office-ui-fabric-react";
|
||||
|
||||
import * as InputUtils from "./InputUtils";
|
||||
import "./SmartUiComponent.less";
|
||||
@ -21,45 +21,45 @@ import "./SmartUiComponent.less";
|
||||
* - a descriptor of the UX.
|
||||
*/
|
||||
|
||||
export type InputTypeValue = "Number" | "String" | "Boolean" | "Object";
|
||||
export type InputTypeValue = "number" | "string" | "boolean" | "object";
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||
export type EnumItem = { label: string; key: string; value: any };
|
||||
export type ChoiceItem = { label: string; key: string; value: any };
|
||||
|
||||
export type InputType = Number | String | Boolean | EnumItem;
|
||||
export type InputType = Number | String | Boolean | ChoiceItem;
|
||||
|
||||
interface BaseInput {
|
||||
label: string;
|
||||
label: (() => Promise<string>) | string;
|
||||
dataFieldName: string;
|
||||
type: InputTypeValue;
|
||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
||||
placeholder?: string;
|
||||
placeholder?: (() => Promise<string>) | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this only supports integers
|
||||
*/
|
||||
export interface NumberInput extends BaseInput {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step: number;
|
||||
defaultValue: number;
|
||||
min?: (() => Promise<number>) | number;
|
||||
max?: (() => Promise<number>) | number
|
||||
step: (() => Promise<number>) | number
|
||||
defaultValue: (() => Promise<number>) | number
|
||||
inputType: "spin" | "slider";
|
||||
}
|
||||
|
||||
export interface BooleanInput extends BaseInput {
|
||||
trueLabel: string;
|
||||
falseLabel: string;
|
||||
defaultValue: boolean;
|
||||
trueLabel: (() => Promise<string>) | string;
|
||||
falseLabel: (() => Promise<string>) | string;
|
||||
defaultValue: (() => Promise<boolean>) | boolean;
|
||||
}
|
||||
|
||||
export interface StringInput extends BaseInput {
|
||||
defaultValue?: string;
|
||||
defaultValue?: (() => Promise<string>) | string;
|
||||
}
|
||||
|
||||
export interface EnumInput extends BaseInput {
|
||||
choices: EnumItem[];
|
||||
defaultKey: string;
|
||||
export interface ChoiceInput extends BaseInput {
|
||||
choices: (() => Promise<ChoiceItem[]>) | ChoiceItem[];
|
||||
defaultKey: (() => Promise<string>) | string;
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
@ -70,11 +70,11 @@ export interface Info {
|
||||
};
|
||||
}
|
||||
|
||||
export type AnyInput = NumberInput | BooleanInput | StringInput | EnumInput;
|
||||
export type AnyInput = NumberInput | BooleanInput | StringInput | ChoiceInput;
|
||||
|
||||
export interface Node {
|
||||
id: string;
|
||||
info?: Info;
|
||||
info?: (() => Promise<Info>) | Info;
|
||||
input?: AnyInput;
|
||||
children?: Node[];
|
||||
}
|
||||
@ -105,34 +105,98 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
constructor(props: SmartUiComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
currentValues: this.setDefaultValues(),
|
||||
currentValues: undefined,
|
||||
errors: new Map()
|
||||
};
|
||||
|
||||
this.setDefaultValues()
|
||||
}
|
||||
|
||||
private setDefaultValues = (): Map<string, InputType> => {
|
||||
const defaults = new Map();
|
||||
this.setDefaults(this.props.descriptor.root, defaults);
|
||||
return defaults;
|
||||
};
|
||||
private setDefaultValues = async () : Promise<void> => {
|
||||
const defaults = new Map<string, InputType>()
|
||||
await this.setDefaults(this.props.descriptor.root, defaults)
|
||||
this.setState({currentValues: defaults})
|
||||
}
|
||||
|
||||
private setDefaults = (currentNode: Node, defaults: Map<string, InputType>) => {
|
||||
if (currentNode.input?.dataFieldName) {
|
||||
defaults.set(currentNode.input.dataFieldName, this.getDefault(currentNode.input));
|
||||
private setDefaults = async (currentNode: Node, defaults: Map<string, InputType>) : Promise<void> => {
|
||||
if (currentNode.info && currentNode.info instanceof Function) {
|
||||
currentNode.info = await (currentNode.info as Function)()
|
||||
}
|
||||
currentNode.children?.map((child: Node) => this.setDefaults(child, defaults));
|
||||
|
||||
if (currentNode.input) {
|
||||
currentNode.input = await this.getModifiedInput(currentNode.input)
|
||||
defaults.set(currentNode.input.dataFieldName, this.getDefaultValue(currentNode.input));
|
||||
}
|
||||
|
||||
await Promise.all(currentNode.children?.map(async (child: Node) => await this.setDefaults(child, defaults)));
|
||||
};
|
||||
|
||||
private getDefault = (input: AnyInput): InputType => {
|
||||
private getModifiedInput = async (input: AnyInput): Promise<AnyInput> => {
|
||||
|
||||
if (input.label instanceof Function) {
|
||||
input.label = await (input.label as Function)()
|
||||
}
|
||||
|
||||
if (input.placeholder instanceof Function) {
|
||||
input.placeholder = await (input.placeholder as Function)()
|
||||
}
|
||||
|
||||
switch (input.type) {
|
||||
case "String":
|
||||
return (input as StringInput).defaultValue;
|
||||
case "Number":
|
||||
return (input as NumberInput).defaultValue;
|
||||
case "Boolean":
|
||||
return (input as BooleanInput).defaultValue;
|
||||
case "string":
|
||||
const stringInput = input as StringInput
|
||||
if (stringInput.defaultValue instanceof Function) {
|
||||
stringInput.defaultValue = await (stringInput.defaultValue as Function)()
|
||||
}
|
||||
return stringInput;
|
||||
case "number":
|
||||
const numberInput = input as NumberInput
|
||||
if (numberInput.defaultValue instanceof Function) {
|
||||
numberInput.defaultValue = await (numberInput.defaultValue as Function)()
|
||||
}
|
||||
if (numberInput.min instanceof Function) {
|
||||
numberInput.min = await (numberInput.min as Function)()
|
||||
}
|
||||
if (numberInput.max instanceof Function) {
|
||||
numberInput.max = await (numberInput.max as Function)()
|
||||
}
|
||||
if (numberInput.step instanceof Function) {
|
||||
numberInput.step = await (numberInput.step as Function)()
|
||||
}
|
||||
return numberInput;
|
||||
case "boolean":
|
||||
const booleanInput = input as BooleanInput
|
||||
if (booleanInput.defaultValue instanceof Function) {
|
||||
booleanInput.defaultValue = await (booleanInput.defaultValue as Function)()
|
||||
}
|
||||
if (booleanInput.trueLabel instanceof Function) {
|
||||
booleanInput.trueLabel = await (booleanInput.trueLabel as Function)()
|
||||
}
|
||||
if (booleanInput.falseLabel instanceof Function) {
|
||||
booleanInput.falseLabel = await (booleanInput.falseLabel as Function)()
|
||||
}
|
||||
return booleanInput;
|
||||
default:
|
||||
return (input as EnumInput).defaultKey;
|
||||
const enumInput = input as ChoiceInput
|
||||
if (enumInput.defaultKey instanceof Function) {
|
||||
enumInput.defaultKey = await (enumInput.defaultKey as Function)()
|
||||
}
|
||||
if (enumInput.choices instanceof Function) {
|
||||
enumInput.choices = await (enumInput.choices as Function)()
|
||||
}
|
||||
return enumInput
|
||||
}
|
||||
};
|
||||
|
||||
private getDefaultValue = (input: AnyInput): InputType => {
|
||||
switch (input.type) {
|
||||
case "string":
|
||||
return (input as StringInput).defaultValue as string;
|
||||
case "number":
|
||||
return (input as NumberInput).defaultValue as number;
|
||||
case "boolean":
|
||||
return (input as BooleanInput).defaultValue as boolean;
|
||||
default:
|
||||
return (input as ChoiceInput).defaultKey as string;
|
||||
}
|
||||
};
|
||||
|
||||
@ -167,10 +231,10 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
<div>
|
||||
<TextField
|
||||
id={`${input.dataFieldName}-input`}
|
||||
label={input.label}
|
||||
label={input.label as string}
|
||||
type="text"
|
||||
value={input.defaultValue}
|
||||
placeholder={input.placeholder}
|
||||
defaultValue={input.defaultValue as string}
|
||||
placeholder={input.placeholder as string}
|
||||
onChange={(_, newValue) => this.onInputChange(input, newValue)}
|
||||
styles={{
|
||||
subComponentStyles: {
|
||||
@ -233,17 +297,23 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
|
||||
private renderNumberInput(input: NumberInput): JSX.Element {
|
||||
const { label, min, max, defaultValue, dataFieldName, step } = input;
|
||||
const props = { label, min, max, ariaLabel: label, step };
|
||||
const props = {
|
||||
label: label as string,
|
||||
min: min as number,
|
||||
max: max as number,
|
||||
ariaLabel: label as string,
|
||||
step: step as number
|
||||
};
|
||||
|
||||
if (input.inputType === "spin") {
|
||||
return (
|
||||
<div>
|
||||
<SpinButton
|
||||
{...props}
|
||||
defaultValue={defaultValue.toString()}
|
||||
onValidate={newValue => this.onValidate(input, newValue, min, max)}
|
||||
onIncrement={newValue => this.onIncrement(input, newValue, step, max)}
|
||||
onDecrement={newValue => this.onDecrement(input, newValue, step, min)}
|
||||
defaultValue={(defaultValue as number).toString()}
|
||||
onValidate={newValue => this.onValidate(input, newValue, props.min, props.max)}
|
||||
onIncrement={newValue => this.onIncrement(input, newValue, props.step, props.max)}
|
||||
onDecrement={newValue => this.onDecrement(input, newValue, props.step, props.min)}
|
||||
labelPosition={Position.top}
|
||||
styles={{
|
||||
label: {
|
||||
@ -263,7 +333,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
// showValue={true}
|
||||
// valueFormat={}
|
||||
{...props}
|
||||
defaultValue={defaultValue}
|
||||
defaultValue={defaultValue as number}
|
||||
onChange={newValue => this.onInputChange(input, newValue)}
|
||||
styles={{
|
||||
titleLabel: {
|
||||
@ -291,12 +361,12 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
<RadioSwitchComponent
|
||||
choices={[
|
||||
{
|
||||
label: input.falseLabel,
|
||||
label: input.falseLabel as string,
|
||||
key: "false",
|
||||
onSelect: () => this.onInputChange(input, false)
|
||||
},
|
||||
{
|
||||
label: input.trueLabel,
|
||||
label: input.trueLabel as string,
|
||||
key: "true",
|
||||
onSelect: () => this.onInputChange(input, true)
|
||||
}
|
||||
@ -313,19 +383,19 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
);
|
||||
}
|
||||
|
||||
private renderEnumInput(input: EnumInput): JSX.Element {
|
||||
private renderEnumInput(input: ChoiceInput): JSX.Element {
|
||||
const { label, defaultKey: defaultKey, dataFieldName, choices, placeholder } = input;
|
||||
return (
|
||||
<Dropdown
|
||||
label={label}
|
||||
label={label as string}
|
||||
selectedKey={
|
||||
this.state.currentValues.has(dataFieldName)
|
||||
? (this.state.currentValues.get(dataFieldName) as string)
|
||||
: defaultKey
|
||||
: defaultKey as string
|
||||
}
|
||||
onChange={(_, item: IDropdownOption) => this.onInputChange(input, item.key.toString())}
|
||||
placeholder={placeholder}
|
||||
options={choices.map(c => ({
|
||||
placeholder={placeholder as string}
|
||||
options={(choices as ChoiceItem[]).map(c => ({
|
||||
key: c.key,
|
||||
text: c.value
|
||||
}))}
|
||||
@ -342,14 +412,14 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
|
||||
private renderInput(input: AnyInput): JSX.Element {
|
||||
switch (input.type) {
|
||||
case "String":
|
||||
case "string":
|
||||
return this.renderStringInput(input as StringInput);
|
||||
case "Number":
|
||||
case "number":
|
||||
return this.renderNumberInput(input as NumberInput);
|
||||
case "Boolean":
|
||||
case "boolean":
|
||||
return this.renderBooleanInput(input as BooleanInput);
|
||||
default:
|
||||
return this.renderEnumInput(input as EnumInput);
|
||||
return this.renderEnumInput(input as ChoiceInput);
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +428,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
|
||||
return (
|
||||
<Stack tokens={containerStackTokens} className="widgetRendererContainer">
|
||||
{node.info && this.renderInfo(node.info)}
|
||||
{node.info && this.renderInfo(node.info as Info)}
|
||||
{node.input && this.renderInput(node.input)}
|
||||
{node.children && node.children.map(child => <div key={child.id}>{this.renderNode(child)}</div>)}
|
||||
</Stack>
|
||||
@ -367,8 +437,8 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
|
||||
render(): JSX.Element {
|
||||
const containerStackTokens: IStackTokens = { childrenGap: 20 };
|
||||
|
||||
return (
|
||||
this.state.currentValues && this.state.currentValues.size ?
|
||||
<Stack tokens={containerStackTokens}>
|
||||
{this.renderNode(this.props.descriptor.root)}
|
||||
<PrimaryButton
|
||||
@ -377,6 +447,8 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
||||
onClick={async () => await this.props.descriptor.onSubmit(this.state.currentValues)}
|
||||
/>
|
||||
</Stack>
|
||||
:
|
||||
<Spinner size={SpinnerSize.large} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user